From 8e37f56bad8124b9e15eb68ee5b97ee3041e0352 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 24 Aug 2017 22:41:24 +0200 Subject: [PATCH 001/162] Set version to 0.4.17 --- CMakeLists.txt | 2 +- Changelog.md | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 87a141a9c..0a65071dc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ include(EthPolicy) eth_policy() # project name and version should be set after cmake_policy CMP0048 -set(PROJECT_VERSION "0.4.16") +set(PROJECT_VERSION "0.4.17") project(solidity VERSION ${PROJECT_VERSION}) option(SOLC_LINK_STATIC "Link solc executable statically on supported platforms" OFF) diff --git a/Changelog.md b/Changelog.md index b78742063..f4915db12 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,9 @@ +### 0.4.17 (unreleased) + +Features: + +Bugfixes: + ### 0.4.16 (2017-08-24) Features: From 20e3b98d1fd7e95b7b03e10e68373283666d1c3c Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 25 Aug 2017 10:39:53 +0100 Subject: [PATCH 002/162] Reorder ABI for readability --- libsolidity/interface/ABI.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/libsolidity/interface/ABI.cpp b/libsolidity/interface/ABI.cpp index 3df9d1f8f..49df843d8 100644 --- a/libsolidity/interface/ABI.cpp +++ b/libsolidity/interface/ABI.cpp @@ -32,13 +32,14 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef) for (auto it: _contractDef.interfaceFunctions()) { auto externalFunctionType = it.second->interfaceFunctionType(); + solAssert(!!externalFunctionType, ""); Json::Value method; method["type"] = "function"; method["name"] = it.second->declaration().name(); // TODO: deprecate constant in a future release - method["constant"] = it.second->stateMutability() == StateMutability::Pure || it.second->stateMutability() == StateMutability::View; - method["payable"] = it.second->isPayable(); - method["stateMutability"] = stateMutabilityToString(it.second->stateMutability()); + method["constant"] = externalFunctionType->stateMutability() == StateMutability::Pure || it.second->stateMutability() == StateMutability::View; + method["payable"] = externalFunctionType->isPayable(); + method["stateMutability"] = stateMutabilityToString(externalFunctionType->stateMutability()); method["inputs"] = formatTypeList( externalFunctionType->parameterNames(), externalFunctionType->parameterTypes(), @@ -53,15 +54,15 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef) } if (_contractDef.constructor()) { + auto externalFunctionType = FunctionType(*_contractDef.constructor(), false).interfaceFunctionType(); + solAssert(!!externalFunctionType, ""); Json::Value method; method["type"] = "constructor"; - auto externalFunction = FunctionType(*_contractDef.constructor(), false).interfaceFunctionType(); - solAssert(!!externalFunction, ""); - method["payable"] = externalFunction->isPayable(); - method["stateMutability"] = stateMutabilityToString(externalFunction->stateMutability()); + method["payable"] = externalFunctionType->isPayable(); + method["stateMutability"] = stateMutabilityToString(externalFunctionType->stateMutability()); method["inputs"] = formatTypeList( - externalFunction->parameterNames(), - externalFunction->parameterTypes(), + externalFunctionType->parameterNames(), + externalFunctionType->parameterTypes(), _contractDef.isLibrary() ); abi.append(method); From 70e89a5dac5d2a1c4ec01f6ccbcf660809c81c4c Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 27 Jul 2017 23:28:49 +0100 Subject: [PATCH 003/162] Introduce JumpdestRemover optimisation step --- Changelog.md | 1 + libevmasm/Assembly.cpp | 30 ++++++++++++---- libevmasm/Assembly.h | 8 +++-- libevmasm/JumpdestRemover.cpp | 68 +++++++++++++++++++++++++++++++++++ libevmasm/JumpdestRemover.h | 50 ++++++++++++++++++++++++++ 5 files changed, 147 insertions(+), 10 deletions(-) create mode 100644 libevmasm/JumpdestRemover.cpp create mode 100644 libevmasm/JumpdestRemover.h diff --git a/Changelog.md b/Changelog.md index f4915db12..c3482c4b2 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,7 @@ ### 0.4.17 (unreleased) Features: + * Optimizer: Add new optimization step to remove unused ``JUMPDEST``s. Bugfixes: diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index 0a3bf6b8b..8c1f92965 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -349,6 +350,7 @@ Assembly& Assembly::optimise(bool _enable, bool _isCreation, size_t _runs) { OptimiserSettings settings; settings.isCreation = _isCreation; + settings.runJumpdestRemover = true; settings.runPeephole = true; if (_enable) { @@ -357,18 +359,21 @@ Assembly& Assembly::optimise(bool _enable, bool _isCreation, size_t _runs) settings.runConstantOptimiser = true; } settings.expectedExecutionsPerDeployment = _runs; - optimiseInternal(settings); + optimise(settings); return *this; } -Assembly& Assembly::optimise(OptimiserSettings _settings) +Assembly& Assembly::optimise(OptimiserSettings const& _settings) { - optimiseInternal(_settings); + optimiseInternal(_settings, {}); return *this; } -map Assembly::optimiseInternal(OptimiserSettings _settings) +map Assembly::optimiseInternal( + OptimiserSettings const& _settings, + std::set const& _tagsReferencedFromOutside +) { // Run optimisation for sub-assemblies. for (size_t subId = 0; subId < m_subs.size(); ++subId) @@ -376,7 +381,10 @@ map Assembly::optimiseInternal(OptimiserSettings _settings) OptimiserSettings settings = _settings; // Disable creation mode for sub-assemblies. settings.isCreation = false; - map subTagReplacements = m_subs[subId]->optimiseInternal(settings); + map subTagReplacements = m_subs[subId]->optimiseInternal( + settings, + JumpdestRemover::referencedTags(m_items, subId) + ); // Apply the replacements (can be empty). BlockDeduplicator::applyTagReplacement(m_items, subTagReplacements, subId); } @@ -387,6 +395,13 @@ map Assembly::optimiseInternal(OptimiserSettings _settings) { count = 0; + if (_settings.runJumpdestRemover) + { + JumpdestRemover jumpdestOpt(m_items); + if (jumpdestOpt.optimise(_tagsReferencedFromOutside)) + count++; + } + if (_settings.runPeephole) { PeepholeOptimiser peepOpt(m_items); @@ -473,8 +488,9 @@ LinkerObject const& Assembly::assemble() const for (auto const& sub: m_subs) { sub->assemble(); - if (!sub->m_tagPositionsInBytecode.empty()) - subTagSize = max(subTagSize, *max_element(sub->m_tagPositionsInBytecode.begin(), sub->m_tagPositionsInBytecode.end())); + for (size_t tagPos: sub->m_tagPositionsInBytecode) + if (tagPos != size_t(-1) && tagPos > subTagSize) + subTagSize = tagPos; } LinkerObject& ret = m_assembledObject; diff --git a/libevmasm/Assembly.h b/libevmasm/Assembly.h index 451b4ea0d..680cb1af2 100644 --- a/libevmasm/Assembly.h +++ b/libevmasm/Assembly.h @@ -100,6 +100,7 @@ public: struct OptimiserSettings { bool isCreation = false; + bool runJumpdestRemover = false; bool runPeephole = false; bool runDeduplicate = false; bool runCSE = false; @@ -110,7 +111,7 @@ public: }; /// Execute optimisation passes as defined by @a _settings and return the optimised assembly. - Assembly& optimise(OptimiserSettings _settings); + Assembly& optimise(OptimiserSettings const& _settings); /// Modify (if @a _enable is set) and return the current assembly such that creation and /// execution gas usage is optimised. @a _isCreation should be true for the top-level assembly. @@ -128,8 +129,9 @@ public: protected: /// Does the same operations as @a optimise, but should only be applied to a sub and - /// returns the replaced tags. - std::map optimiseInternal(OptimiserSettings _settings); + /// returns the replaced tags. Also takes an argument containing the tags of this assembly + /// that are referenced in a super-assembly. + std::map optimiseInternal(OptimiserSettings const& _settings, std::set const& _tagsReferencedFromOutside); unsigned bytesRequired(unsigned subTagSize) const; diff --git a/libevmasm/JumpdestRemover.cpp b/libevmasm/JumpdestRemover.cpp new file mode 100644 index 000000000..b60167982 --- /dev/null +++ b/libevmasm/JumpdestRemover.cpp @@ -0,0 +1,68 @@ +/* + 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 . +*/ +/** + * @author Alex Beregszaszi + * Removes unused JUMPDESTs. + */ + +#include "JumpdestRemover.h" + +#include + +#include + +using namespace std; +using namespace dev::eth; +using namespace dev; + + +bool JumpdestRemover::optimise(set const& _tagsReferencedFromOutside) +{ + set references{referencedTags(m_items, -1)}; + references.insert(_tagsReferencedFromOutside.begin(), _tagsReferencedFromOutside.end()); + + size_t initialSize = m_items.size(); + /// Remove tags which are never referenced. + auto pend = remove_if( + m_items.begin(), + m_items.end(), + [&](AssemblyItem const& _item) + { + if (_item.type() != Tag) + return false; + auto asmIdAndTag = _item.splitForeignPushTag(); + solAssert(asmIdAndTag.first == size_t(-1), "Sub-assembly tag used as label."); + size_t tag = asmIdAndTag.second; + return !references.count(tag); + } + ); + m_items.erase(pend, m_items.end()); + return m_items.size() != initialSize; +} + +set JumpdestRemover::referencedTags(AssemblyItems const& _items, size_t _subId) +{ + set ret; + for (auto const& item: _items) + if (item.type() == PushTag) + { + auto subAndTag = item.splitForeignPushTag(); + if (subAndTag.first == _subId) + ret.insert(subAndTag.second); + } + return ret; +} diff --git a/libevmasm/JumpdestRemover.h b/libevmasm/JumpdestRemover.h new file mode 100644 index 000000000..2dad09272 --- /dev/null +++ b/libevmasm/JumpdestRemover.h @@ -0,0 +1,50 @@ +/* + 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 . +*/ +/** + * @author Alex Beregszaszi + * Removes unused JUMPDESTs. + */ +#pragma once + +#include +#include +#include + +namespace dev +{ +namespace eth +{ +class AssemblyItem; +using AssemblyItems = std::vector; + +class JumpdestRemover +{ +public: + explicit JumpdestRemover(AssemblyItems& _items): m_items(_items) {} + + bool optimise(std::set const& _tagsReferencedFromOutside); + + /// @returns a set of all tags from the given sub-assembly that are referenced + /// from the given list of items. + static std::set referencedTags(AssemblyItems const& _items, size_t _subId); + +private: + AssemblyItems& m_items; +}; + +} +} From d15526f877bd3320f50c78227da282c3a6b242e4 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 15 Aug 2017 15:24:57 +0200 Subject: [PATCH 004/162] Update tests. --- test/libsolidity/Assembly.cpp | 4 ++-- test/libsolidity/JSONCompiler.cpp | 12 ++++++------ test/libsolidity/StandardCompiler.cpp | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index 99a2996ee..56ac8cf5c 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -119,11 +119,11 @@ BOOST_AUTO_TEST_CASE(location_test) shared_ptr n = make_shared(""); AssemblyItems items = compileContract(sourceCode); vector locations = - vector(19, SourceLocation(2, 75, n)) + + vector(18, SourceLocation(2, 75, n)) + vector(32, SourceLocation(20, 72, n)) + vector{SourceLocation(42, 51, n), SourceLocation(65, 67, n)} + vector(2, SourceLocation(58, 67, n)) + - vector(3, SourceLocation(20, 72, n)); + vector(2, SourceLocation(20, 72, n)); checkAssemblyLocations(items, locations); } diff --git a/test/libsolidity/JSONCompiler.cpp b/test/libsolidity/JSONCompiler.cpp index 0fe7636c9..541cfbf0d 100644 --- a/test/libsolidity/JSONCompiler.cpp +++ b/test/libsolidity/JSONCompiler.cpp @@ -120,18 +120,18 @@ BOOST_AUTO_TEST_CASE(basic_compilation) BOOST_CHECK(contract["bytecode"].isString()); BOOST_CHECK_EQUAL( dev::test::bytecodeSansMetadata(contract["bytecode"].asString()), - "60606040523415600e57600080fd5b5b603680601c6000396000f30060606040525b600080fd00" + "60606040523415600e57600080fd5b603580601b6000396000f3006060604052600080fd00" ); BOOST_CHECK(contract["runtimeBytecode"].isString()); BOOST_CHECK_EQUAL( dev::test::bytecodeSansMetadata(contract["runtimeBytecode"].asString()), - "60606040525b600080fd00" + "6060604052600080fd00" ); BOOST_CHECK(contract["functionHashes"].isObject()); BOOST_CHECK(contract["gasEstimates"].isObject()); BOOST_CHECK_EQUAL( dev::jsonCompactPrint(contract["gasEstimates"]), - "{\"creation\":[62,10800],\"external\":{},\"internal\":{}}" + "{\"creation\":[61,10600],\"external\":{},\"internal\":{}}" ); BOOST_CHECK(contract["metadata"].isString()); BOOST_CHECK(dev::test::isValidMetadata(contract["metadata"].asString())); @@ -162,18 +162,18 @@ BOOST_AUTO_TEST_CASE(single_compilation) BOOST_CHECK(contract["bytecode"].isString()); BOOST_CHECK_EQUAL( dev::test::bytecodeSansMetadata(contract["bytecode"].asString()), - "60606040523415600e57600080fd5b5b603680601c6000396000f30060606040525b600080fd00" + "60606040523415600e57600080fd5b603580601b6000396000f3006060604052600080fd00" ); BOOST_CHECK(contract["runtimeBytecode"].isString()); BOOST_CHECK_EQUAL( dev::test::bytecodeSansMetadata(contract["runtimeBytecode"].asString()), - "60606040525b600080fd00" + "6060604052600080fd00" ); BOOST_CHECK(contract["functionHashes"].isObject()); BOOST_CHECK(contract["gasEstimates"].isObject()); BOOST_CHECK_EQUAL( dev::jsonCompactPrint(contract["gasEstimates"]), - "{\"creation\":[62,10800],\"external\":{},\"internal\":{}}" + "{\"creation\":[61,10600],\"external\":{},\"internal\":{}}" ); BOOST_CHECK(contract["metadata"].isString()); BOOST_CHECK(dev::test::isValidMetadata(contract["metadata"].asString())); diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index 79848c36d..24f915c07 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -198,19 +198,19 @@ BOOST_AUTO_TEST_CASE(basic_compilation) BOOST_CHECK(contract["evm"]["bytecode"]["object"].isString()); BOOST_CHECK_EQUAL( dev::test::bytecodeSansMetadata(contract["evm"]["bytecode"]["object"].asString()), - "60606040523415600e57600080fd5b5b603680601c6000396000f30060606040525b600080fd00" + "60606040523415600e57600080fd5b603580601b6000396000f3006060604052600080fd00" ); BOOST_CHECK(contract["evm"]["assembly"].isString()); BOOST_CHECK(contract["evm"]["assembly"].asString().find( " /* \"fileA\":0:14 contract A { } */\n mstore(0x40, 0x60)\n jumpi(tag_1, iszero(callvalue))\n" - " 0x0\n dup1\n revert\ntag_1:\ntag_2:\n dataSize(sub_0)\n dup1\n dataOffset(sub_0)\n 0x0\n codecopy\n 0x0\n" + " 0x0\n dup1\n revert\ntag_1:\n dataSize(sub_0)\n dup1\n dataOffset(sub_0)\n 0x0\n codecopy\n 0x0\n" " return\nstop\n\nsub_0: assembly {\n /* \"fileA\":0:14 contract A { } */\n" - " mstore(0x40, 0x60)\n tag_1:\n 0x0\n dup1\n revert\n\n" + " mstore(0x40, 0x60)\n 0x0\n dup1\n revert\n\n" " auxdata: 0xa165627a7a7230582") == 0); BOOST_CHECK(contract["evm"]["gasEstimates"].isObject()); BOOST_CHECK_EQUAL( dev::jsonCompactPrint(contract["evm"]["gasEstimates"]), - "{\"creation\":{\"codeDepositCost\":\"10800\",\"executionCost\":\"62\",\"totalCost\":\"10862\"}}" + "{\"creation\":{\"codeDepositCost\":\"10600\",\"executionCost\":\"61\",\"totalCost\":\"10661\"}}" ); BOOST_CHECK(contract["metadata"].isString()); BOOST_CHECK(dev::test::isValidMetadata(contract["metadata"].asString())); From 223235c97e4a1b110053a891cb6154046f8e62e6 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 15 Aug 2017 16:05:09 +0200 Subject: [PATCH 005/162] Add test for jumpdest removal. --- test/libevmasm/Optimiser.cpp | 84 ++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/test/libevmasm/Optimiser.cpp b/test/libevmasm/Optimiser.cpp index 6656f15be..9dc495810 100644 --- a/test/libevmasm/Optimiser.cpp +++ b/test/libevmasm/Optimiser.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -840,6 +841,89 @@ BOOST_AUTO_TEST_CASE(peephole_double_push) ); } +BOOST_AUTO_TEST_CASE(jumpdest_removal) +{ + AssemblyItems items{ + AssemblyItem(Tag, 2), + AssemblyItem(PushTag, 1), + u256(5), + AssemblyItem(Tag, 10), + AssemblyItem(Tag, 3), + u256(6), + AssemblyItem(Tag, 1), + Instruction::JUMP, + }; + AssemblyItems expectation{ + AssemblyItem(PushTag, 1), + u256(5), + u256(6), + AssemblyItem(Tag, 1), + Instruction::JUMP + }; + JumpdestRemover jdr(items); + BOOST_REQUIRE(jdr.optimise({})); + BOOST_CHECK_EQUAL_COLLECTIONS( + items.begin(), items.end(), + expectation.begin(), expectation.end() + ); +} + +BOOST_AUTO_TEST_CASE(jumpdest_removal_subassemblies) +{ + // This tests that tags from subassemblies are not removed + // if they are referenced by a super-assembly. Furthermore, + // tag unifications (due to block deduplication) is also + // visible at the super-assembly. + + Assembly main; + AssemblyPointer sub = make_shared(); + + sub->append(u256(1)); + auto t1 = sub->newTag(); + sub->append(t1); + sub->append(u256(2)); + sub->append(Instruction::JUMP); + auto t2 = sub->newTag(); + sub->append(t2); // Identical to T1, will be unified + sub->append(u256(2)); + sub->append(Instruction::JUMP); + auto t3 = sub->newTag(); + sub->append(t3); + auto t4 = sub->newTag(); + sub->append(t4); + auto t5 = sub->newTag(); + sub->append(t5); // This will be removed + sub->append(u256(7)); + sub->append(t4.pushTag()); + sub->append(Instruction::JUMP); + + size_t subId = size_t(main.appendSubroutine(sub).data()); + main.append(t1.toSubAssemblyTag(subId)); + main.append(t1.toSubAssemblyTag(subId)); + main.append(u256(8)); + + main.optimise(true); + + AssemblyItems expectationMain{ + AssemblyItem(PushSubSize, 0), + t1.toSubAssemblyTag(subId).pushTag(), + t1.toSubAssemblyTag(subId).pushTag(), + u256(8) + }; + BOOST_CHECK_EQUAL_COLLECTIONS( + main.items().begin(), main.items().end(), + expectationMain.begin(), expectationMain.end() + ); + + AssemblyItems expectationSub{ + u256(1), t1.tag(), u256(2), Instruction::JUMP, t4.tag(), u256(7), t4.pushTag(), Instruction::JUMP + }; + BOOST_CHECK_EQUAL_COLLECTIONS( + sub->items().begin(), sub->items().end(), + expectationSub.begin(), expectationSub.end() + ); +} + BOOST_AUTO_TEST_CASE(cse_sub_zero) { checkCSE({ From bfc7d71f518bf2ad19bb906d62815fee34bed966 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 25 Aug 2017 11:57:33 +0200 Subject: [PATCH 006/162] Cleanup: Remove ABI.h --- libdevcore/ABI.h | 100 ---------------------------- test/ExecutionFramework.h | 36 +++++----- test/contracts/AuctionRegistrar.cpp | 1 - 3 files changed, 18 insertions(+), 119 deletions(-) delete mode 100644 libdevcore/ABI.h diff --git a/libdevcore/ABI.h b/libdevcore/ABI.h deleted file mode 100644 index 8b9e5c980..000000000 --- a/libdevcore/ABI.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - 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 . -*/ -/** @file ABI.h - * @author Gav Wood - * @date 2014 - */ - -#pragma once - -#include -#include -#include -#include - -namespace dev -{ -namespace eth -{ - -inline string32 toString32(std::string const& _s) -{ - string32 ret; - for (unsigned i = 0; i < 32; ++i) - ret[i] = i < _s.size() ? _s[i] : 0; - return ret; -} - -template struct ABISerialiser {}; -template struct ABISerialiser> { static bytes serialise(FixedHash const& _t) { static_assert(N <= 32, "Cannot serialise hash > 32 bytes."); static_assert(N > 0, "Cannot serialise zero-length hash."); return bytes(32 - N, 0) + _t.asBytes(); } }; -template <> struct ABISerialiser { static bytes serialise(u256 const& _t) { return h256(_t).asBytes(); } }; -template <> struct ABISerialiser { static bytes serialise(u160 const& _t) { return bytes(12, 0) + h160(_t).asBytes(); } }; -template <> struct ABISerialiser { static bytes serialise(string32 const& _t) { bytes ret; bytesConstRef((byte const*)_t.data(), 32).populate(bytesRef(&ret)); return ret; } }; -template <> struct ABISerialiser -{ - static bytes serialise(std::string const& _t) - { - bytes ret = h256(u256(32)).asBytes() + h256(u256(_t.size())).asBytes(); - ret.resize(ret.size() + (_t.size() + 31) / 32 * 32); - bytesConstRef(&_t).populate(bytesRef(&ret).cropped(64)); - return ret; - } -}; - -inline bytes abiInAux() { return {}; } -template bytes abiInAux(T const& _t, U const& ... _u) -{ - return ABISerialiser::serialise(_t) + abiInAux(_u ...); -} - -template bytes abiIn(std::string _id, T const& ... _t) -{ - return keccak256(_id).ref().cropped(0, 4).toBytes() + abiInAux(_t ...); -} - -template struct ABIDeserialiser {}; -template struct ABIDeserialiser> { static FixedHash deserialise(bytesConstRef& io_t) { static_assert(N <= 32, "Parameter sizes must be at most 32 bytes."); FixedHash ret; io_t.cropped(32 - N, N).populate(ret.ref()); io_t = io_t.cropped(32); return ret; } }; -template <> struct ABIDeserialiser { static u256 deserialise(bytesConstRef& io_t) { u256 ret = fromBigEndian(io_t.cropped(0, 32)); io_t = io_t.cropped(32); return ret; } }; -template <> struct ABIDeserialiser { static u160 deserialise(bytesConstRef& io_t) { u160 ret = fromBigEndian(io_t.cropped(12, 20)); io_t = io_t.cropped(32); return ret; } }; -template <> struct ABIDeserialiser { static string32 deserialise(bytesConstRef& io_t) { string32 ret; io_t.cropped(0, 32).populate(bytesRef((byte*)ret.data(), 32)); io_t = io_t.cropped(32); return ret; } }; -template <> struct ABIDeserialiser -{ - static std::string deserialise(bytesConstRef& io_t) - { - unsigned o = (uint16_t)u256(h256(io_t.cropped(0, 32))); - unsigned s = (uint16_t)u256(h256(io_t.cropped(o, 32))); - std::string ret; - ret.resize(s); - io_t.cropped(o + 32, s).populate(bytesRef((byte*)ret.data(), s)); - io_t = io_t.cropped(32); - return ret; - } -}; - -template T abiOut(bytes const& _data) -{ - bytesConstRef o(&_data); - return ABIDeserialiser::deserialise(o); -} - -template T abiOut(bytesConstRef& _data) -{ - return ABIDeserialiser::deserialise(_data); -} - -} -} diff --git a/test/ExecutionFramework.h b/test/ExecutionFramework.h index 76d0fd8cf..ba385deee 100644 --- a/test/ExecutionFramework.h +++ b/test/ExecutionFramework.h @@ -22,13 +22,13 @@ #pragma once -#include +#include +#include -#include "TestHelper.h" -#include "RPCSession.h" - -#include #include +#include + +#include namespace dev { @@ -40,11 +40,11 @@ namespace test using Address = h160; // The various denominations; here for ease of use where needed within code. - static const u256 ether = exp10<18>(); - static const u256 finney = exp10<15>(); - static const u256 szabo = exp10<12>(); - static const u256 shannon = exp10<9>(); - static const u256 wei = exp10<0>(); + static const u256 wei = 1; + static const u256 shannon = u256("1000000000"); + static const u256 szabo = shannon * 1000; + static const u256 finney = szabo * 1000; + static const u256 ether = finney * 1000; class ExecutionFramework { @@ -217,25 +217,25 @@ public: bytes const& ret = call(_name + "(string)", u256(0x20), _arg.length(), _arg); BOOST_REQUIRE(ret.size() == 0x20); BOOST_CHECK(std::count(ret.begin(), ret.begin() + 12, 0) == 12); - return eth::abiOut(ret); + return u160(u256(h256(ret))); } std::string callAddressReturnsString(std::string const& _name, u160 const& _arg) { - bytesConstRef ret = ref(call(_name + "(address)", _arg)); - BOOST_REQUIRE(ret.size() >= 0x20); - u256 offset = eth::abiOut(ret); + bytesConstRef const ret(&call(_name + "(address)", _arg)); + BOOST_REQUIRE(ret.size() >= 0x40); + u256 offset(h256(ret.cropped(0, 0x20))); BOOST_REQUIRE_EQUAL(offset, 0x20); - u256 len = eth::abiOut(ret); - BOOST_REQUIRE_EQUAL(ret.size(), ((len + 0x1f) / 0x20) * 0x20); - return ret.cropped(0, size_t(len)).toString(); + u256 len(h256(ret.cropped(0x20, 0x20))); + BOOST_REQUIRE_EQUAL(ret.size(), 0x40 + ((len + 0x1f) / 0x20) * 0x20); + return ret.cropped(0x40, size_t(len)).toString(); } h256 callStringReturnsBytes32(std::string const& _name, std::string const& _arg) { bytes const& ret = call(_name + "(string)", u256(0x20), _arg.length(), _arg); BOOST_REQUIRE(ret.size() == 0x20); - return eth::abiOut(ret); + return h256(ret); } private: diff --git a/test/contracts/AuctionRegistrar.cpp b/test/contracts/AuctionRegistrar.cpp index 773b14b94..d56edc562 100644 --- a/test/contracts/AuctionRegistrar.cpp +++ b/test/contracts/AuctionRegistrar.cpp @@ -23,7 +23,6 @@ #include #include #include -#include #include using namespace std; From 157b5f1ae9dfdec8e82c9d1febb12d5d60dbc465 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 25 Aug 2017 12:13:26 +0200 Subject: [PATCH 007/162] Cleanup of SHA3.h --- libdevcore/SHA3.h | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/libdevcore/SHA3.h b/libdevcore/SHA3.h index 1a5610667..d1e2cc983 100644 --- a/libdevcore/SHA3.h +++ b/libdevcore/SHA3.h @@ -23,8 +23,9 @@ #pragma once +#include + #include -#include "FixedHash.h" namespace dev { @@ -47,10 +48,4 @@ inline h256 keccak256(std::string const& _input) { return keccak256(bytesConstRe /// Calculate Keccak-256 hash of the given input (presented as a FixedHash), returns a 256-bit hash. template inline h256 keccak256(FixedHash const& _input) { return keccak256(_input.ref()); } -/// Calculate Keccak-256 hash of the given input, possibly interpreting it as nibbles, and return the hash as a string filled with binary data. -inline std::string keccak256(std::string const& _input, bool _isNibbles) { return asString((_isNibbles ? keccak256(fromHex(_input)) : keccak256(bytesConstRef(&_input))).asBytes()); } - -/// Calculate Keccak-256 MAC -inline void keccak256mac(bytesConstRef _secret, bytesConstRef _plain, bytesRef _output) { keccak256(_secret.toBytes() + _plain.toBytes()).ref().populate(_output); } - } From bb493bf52d174ee1095db268453ad500de15c6a5 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 24 Aug 2017 10:34:18 +0100 Subject: [PATCH 008/162] Require 0.4.16 for view/pure in docs examples --- docs/contracts.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index 50e7f3d1f..973386d51 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -471,7 +471,7 @@ Functions can be declared ``view`` in which case they promise not to modify the :: - pragma solidity ^0.4.0; + pragma solidity ^0.4.16; contract C { function f(uint a, uint b) view returns (uint) { @@ -498,7 +498,7 @@ Functions can be declared ``pure`` in which case they promise not to read from o :: - pragma solidity ^0.4.0; + pragma solidity ^0.4.16; contract C { function f(uint a, uint b) pure returns (uint) { From f791ca3957e30ba89763d3cf3327ed3e58d21b15 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 24 Aug 2017 15:32:23 +0100 Subject: [PATCH 009/162] Clarify ABI regarding constant --- docs/abi-spec.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index c0969cae3..fffd9a2c1 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -293,9 +293,9 @@ The JSON format for a contract's interface is given by an array of function and/ * `name`: the name of the parameter; * `type`: the canonical type of the parameter. - `outputs`: an array of objects similar to `inputs`, can be omitted if function doesn't return anything; -- `constant`: `true` if function is :ref:`specified to not modify blockchain state `); - `payable`: `true` if function accepts ether, defaults to `false`; -- `stateMutability`: a string with one of the following values: `pure` (:ref:`specified to not read blockchain state `), `view` (same as `constant` above), `nonpayable` and `payable` (same as `payable` above). +- `stateMutability`: a string with one of the following values: `pure` (:ref:`specified to not read blockchain state `), `view` (:ref:`specified to not modify the blockchain state `), `nonpayable` and `payable` (same as `payable` above). +- `constant`: `true` if function is either `pure` or `view` `type` can be omitted, defaulting to `"function"`. From 2af949baaa31c328060008fe5b4dc251657f3d9d Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 25 Aug 2017 11:12:26 +0100 Subject: [PATCH 010/162] Explain the limitations of view and pure --- docs/contracts.rst | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/docs/contracts.rst b/docs/contracts.rst index 973386d51..ef09d9351 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -469,6 +469,17 @@ View Functions Functions can be declared ``view`` in which case they promise not to modify the state. +The following statements are considered modifying the state: + +#. Writing to state variables. +#. :ref:`Emitting events. `. +#. :ref:`Creating other contracts `. +#. Using ``selfdestruct``. +#. Sending Ether via calls. +#. Calling any function not marked ``view`` or ``pure``. +#. Using low-level calls. +#. Using inline assembly that contains certain opcodes. + :: pragma solidity ^0.4.16; @@ -496,6 +507,13 @@ Pure Functions Functions can be declared ``pure`` in which case they promise not to read from or modify the state. +In addition to the list of state modifying statements explained above, the following are considered reading from the state: +#. Reading from state variables. +#. Accessing ``this.balance`` or ``
.balance``. +#. Accessing any of the members of ``block``, ``tx``, ``msg`` (with the exception of ``msg.sig`` and ``msg.data``). +#. Calling any function not marked ``pure``. +#. Using inline assembly that contains certain opcodes. + :: pragma solidity ^0.4.16; From e03dfd84761e72bb7cc5eaa50a9d676148519682 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 25 Aug 2017 10:40:19 +0100 Subject: [PATCH 011/162] Remove unused statements from the Natspec headers --- libsolidity/analysis/DocStringAnalyser.cpp | 2 +- libsolidity/interface/Natspec.h | 27 ---------------------- 2 files changed, 1 insertion(+), 28 deletions(-) diff --git a/libsolidity/analysis/DocStringAnalyser.cpp b/libsolidity/analysis/DocStringAnalyser.cpp index 9a846b318..d08c4eb5c 100644 --- a/libsolidity/analysis/DocStringAnalyser.cpp +++ b/libsolidity/analysis/DocStringAnalyser.cpp @@ -72,7 +72,7 @@ void DocStringAnalyser::handleCallable( DocumentedAnnotation& _annotation ) { - static const set validTags = set{"author", "dev", "notice", "return", "param", "why3"}; + static const set validTags = set{"author", "dev", "notice", "return", "param"}; parseDocStrings(_node, _annotation, validTags, "functions"); set validParams; diff --git a/libsolidity/interface/Natspec.h b/libsolidity/interface/Natspec.h index 9ac3efea5..0701f8219 100644 --- a/libsolidity/interface/Natspec.h +++ b/libsolidity/interface/Natspec.h @@ -36,27 +36,8 @@ namespace solidity // Forward declarations class ContractDefinition; -class Type; -using TypePointer = std::shared_ptr; struct DocTag; -enum class DocTagType: uint8_t -{ - None = 0, - Dev, - Notice, - Param, - Return, - Author, - Title -}; - -enum class CommentOwner -{ - Contract, - Function -}; - class Natspec { public: @@ -71,14 +52,6 @@ public: static Json::Value devDocumentation(ContractDefinition const& _contractDef); private: - /// @returns a json value suitable for a list of types in function input or output - /// parameters or other places. If @a _forLibrary is true, complex types are referenced - /// by name, otherwise they are anonymously expanded. - static Json::Value formatTypeList( - std::vector const& _names, - std::vector const& _types, - bool _forLibrary - ); /// @returns concatenation of all content under the given tag name. static std::string extractDoc(std::multimap const& _tags, std::string const& _name); }; From b79401b1659dfa609ec41ff2405f3c34a1e58baf Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 25 Aug 2017 14:46:50 +0200 Subject: [PATCH 012/162] Do not include soltest and .so files in zip archives. --- scripts/release.sh | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/scripts/release.sh b/scripts/release.sh index a2f4d98af..ebc7759f5 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -35,47 +35,12 @@ ZIP_TEMP_DIR=$(pwd)/build/zip/ # There is an implicit assumption here that we HAVE to run from root directory. REPO_ROOT=$(pwd) -if [[ "$OSTYPE" == "darwin"* ]]; then - DLL_EXT=dylib -else - DLL_EXT=so -fi - mkdir -p $ZIP_TEMP_DIR # Copy all the solidity executables into a temporary directory prior to ZIP creation cp $REPO_ROOT/build/lllc/lllc $ZIP_TEMP_DIR cp $REPO_ROOT/build/solc/solc $ZIP_TEMP_DIR -cp $REPO_ROOT/build/soltest/soltest $ZIP_TEMP_DIR - -# Copy all the dynamic libraries into a temporary directory prior to ZIP creation. -# There are a lot of these, and it would be great if we didn't have to worry about them. -# There is work-in-progress to support static-linkage on the UNIX platforms, which -# is most promising on Alpine Linux using musl. macOS doesn't support statically -# linked binaries (ie. executables which make direct system calls to the kernel. -# -# See https://developer.apple.com/library/mac/qa/qa1118/_index.html. -# See https://github.com/ethereum/webthree-umbrella/issues/495. - -cp $REPO_ROOT/build/libdevcore/*.$DLL_EXT $ZIP_TEMP_DIR -cp $REPO_ROOT/build/libevmasm/*.$DLL_EXT $ZIP_TEMP_DIR -cp $REPO_ROOT/build/libsolidity/*.$DLL_EXT $ZIP_TEMP_DIR - -# For macOS, we also copy the dynamic libraries for our external dependencies. -# When building from source on your own machine, these libraries will be installed -# globally, using Homebrew, but we don't want to rely on that for these ZIPs, so -# we copy these into the ZIP temporary directory too. -# -# TODO - So what happens for Linux and other UNIX distros in this case? -# There will be runtime dependencies on equivalent SO files being present, likely in -# a completely analogous way. Does that mean that ZIPs are actually useless on such -# distros, because there will be symbol links to global install locations (distro-specific) -# and those files will just be missing on the target machines? - -if [[ "$OSTYPE" == "darwin"* ]]; then - cp /usr/local/opt/jsoncpp/lib/libjsoncpp.1.dylib $ZIP_TEMP_DIR -fi # For macOS, we run a fix-up script which alters all of the symbolic links within # the executables and dynamic libraries such that the ZIP becomes self-contained, by From 4b0e30d2592ef5561d98263696cb5550daa121e9 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 25 Aug 2017 12:07:02 +0200 Subject: [PATCH 013/162] Cleanup of Common.h --- libdevcore/Common.h | 58 +++++---------------------------- libdevcore/CommonData.h | 3 +- libdevcore/FixedHash.h | 10 ++++-- libevmasm/BlockDeduplicator.cpp | 5 ++- libevmasm/ExpressionClasses.h | 6 ++-- libsolidity/ast/Types.h | 1 + 6 files changed, 25 insertions(+), 58 deletions(-) diff --git a/libdevcore/Common.h b/libdevcore/Common.h index 9d6dd408b..2543855d3 100644 --- a/libdevcore/Common.h +++ b/libdevcore/Common.h @@ -37,13 +37,7 @@ #pragma warning(disable:3682) //call through incomplete class #endif -#include -#include -#include -#include -#include -#include -#include +#include #if defined(__GNUC__) #pragma warning(push) @@ -67,14 +61,13 @@ #pragma GCC diagnostic pop #endif // defined(__GNUC__) -#include "vector_ref.h" +#include +#include +#include +#include using byte = uint8_t; -// Quote a given token stream to turn it into a string. -#define DEV_QUOTED_HELPER(s) #s -#define DEV_QUOTED(s) DEV_QUOTED_HELPER(s) - namespace dev { @@ -85,32 +78,15 @@ using bytesConstRef = vector_ref; // Numeric types. using bigint = boost::multiprecision::number>; -using u64 = boost::multiprecision::number>; -using u128 = boost::multiprecision::number>; -using u256 = boost::multiprecision::number>; -using s256 = boost::multiprecision::number>; -using u160 = boost::multiprecision::number>; -using s160 = boost::multiprecision::number>; -using u512 = boost::multiprecision::number>; -using s512 = boost::multiprecision::number>; -using u256s = std::vector; -using u160s = std::vector; -using u256Set = std::set; -using u160Set = std::set; +using u256 = boost::multiprecision::number>; +using s256 = boost::multiprecision::number>; +using u160 = boost::multiprecision::number>; // Map types. using StringMap = std::map; -// Hash types. -using StringHashMap = std::unordered_map; - // String types. using strings = std::vector; -// Fixed-length string types. -using string32 = std::array; - -// Null/Invalid values for convenience. -static const bytes NullBytes; /// Interprets @a _u as a two's complement signed number and returns the resulting s256. inline s256 u2s(u256 _u) @@ -143,16 +119,6 @@ inline std::ostream& operator<<(std::ostream& os, bytes const& _bytes) return os; } -template inline u256 exp10() -{ - return exp10() * u256(10); -} - -template <> inline u256 exp10<0>() -{ - return u256(1); -} - /// RAII utility class whose destructor calls a given function. class ScopeGuard { @@ -164,12 +130,4 @@ private: std::function m_f; }; -enum class WithExisting: int -{ - Trust = 0, - Verify, - Rescue, - Kill -}; - } diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index 5df8986a8..765707f80 100644 --- a/libdevcore/CommonData.h +++ b/libdevcore/CommonData.h @@ -26,11 +26,10 @@ #include #include -#include -#include #include #include #include +#include namespace dev { diff --git a/libdevcore/FixedHash.h b/libdevcore/FixedHash.h index 141e9ffd0..5bc700950 100644 --- a/libdevcore/FixedHash.h +++ b/libdevcore/FixedHash.h @@ -23,12 +23,16 @@ #pragma once +#include + +#include +#include + #include #include #include -#include -#include -#include "CommonData.h" +#include +#include namespace dev { diff --git a/libevmasm/BlockDeduplicator.cpp b/libevmasm/BlockDeduplicator.cpp index d21be07e7..b7c695311 100644 --- a/libevmasm/BlockDeduplicator.cpp +++ b/libevmasm/BlockDeduplicator.cpp @@ -22,10 +22,13 @@ */ #include -#include + #include #include +#include +#include + using namespace std; using namespace dev; using namespace dev::eth; diff --git a/libevmasm/ExpressionClasses.h b/libevmasm/ExpressionClasses.h index 5d53b2921..6b426e971 100644 --- a/libevmasm/ExpressionClasses.h +++ b/libevmasm/ExpressionClasses.h @@ -23,11 +23,13 @@ #pragma once +#include +#include + #include #include #include -#include -#include +#include namespace dev { diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index ce2d3bf8c..310c34fea 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -36,6 +36,7 @@ #include #include #include +#include namespace dev { From f6dba97fe174c95f9dd5defdd8f443c121f73956 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 25 Aug 2017 12:17:44 +0100 Subject: [PATCH 014/162] Warn on using literals in tight packing --- Changelog.md | 1 + libsolidity/analysis/TypeChecker.cpp | 22 ++++++++++ .../SolidityNameAndTypeResolution.cpp | 44 +++++++++++++++++++ 3 files changed, 67 insertions(+) diff --git a/Changelog.md b/Changelog.md index c3482c4b2..670182af7 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,7 @@ Features: * Optimizer: Add new optimization step to remove unused ``JUMPDEST``s. + * Type Checker: Warn on using literals as tight packing parameters in ``keccak256``, ``sha3``, ``sha256`` and ``ripemd160``. Bugfixes: diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 99f3c64cb..d594a0602 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1446,6 +1446,28 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) _functionCall.annotation().type = make_shared(functionType->returnParameterTypes()); TypePointers parameterTypes = functionType->parameterTypes(); + + if (!functionType->padArguments()) + { + for (size_t i = 0; i < arguments.size(); ++i) + { + auto const& argType = type(*arguments[i]); + if (auto literal = dynamic_cast(argType.get())) + { + /* If no mobile type is available an error will be raised elsewhere. */ + if (literal->mobileType()) + m_errorReporter.warning( + _functionCall.location(), + "The type of \"" + + argType->toString() + + "\" was inferred as " + + literal->mobileType()->toString() + + ". This is probably not desired. Use an explicit type to silence this warning." + ); + } + } + } + if (!functionType->takesArbitraryParameters() && parameterTypes.size() != arguments.size()) { string msg = diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 380978e81..15b061257 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -6744,6 +6744,50 @@ BOOST_AUTO_TEST_CASE(reject_interface_constructors) CHECK_ERROR(text, TypeError, "Wrong argument count for constructor call: 1 arguments given but expected 0."); } +BOOST_AUTO_TEST_CASE(tight_packing_literals) +{ + char const* text = R"( + contract C { + function f() returns (bytes32) { + return keccak256(1); + } + } + )"; + CHECK_WARNING(text, "The type of \"int_const 1\" was inferred as uint8."); + text = R"( + contract C { + function f() returns (bytes32) { + return keccak256(uint8(1)); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + text = R"( + contract C { + function f() returns (bytes32) { + return sha3(1); + } + } + )"; + CHECK_WARNING(text, "The type of \"int_const 1\" was inferred as uint8."); + text = R"( + contract C { + function f() returns (bytes32) { + return sha256(1); + } + } + )"; + CHECK_WARNING(text, "The type of \"int_const 1\" was inferred as uint8."); + text = R"( + contract C { + function f() returns (bytes32) { + return ripemd160(1); + } + } + )"; + CHECK_WARNING(text, "The type of \"int_const 1\" was inferred as uint8."); +} + BOOST_AUTO_TEST_SUITE_END() } From ef8355da7f301253303743187d00498791399ab3 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 24 Aug 2017 14:50:05 +0100 Subject: [PATCH 015/162] Remove fallthrough from literal parsing --- libsolidity/parsing/Parser.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index ddfdb6672..2f28db3ed 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -1309,18 +1309,21 @@ ASTPointer Parser::parsePrimaryExpression() Literal::SubDenomination subdenomination = static_cast(m_scanner->currentToken()); m_scanner->next(); expression = nodeFactory.createNode(token, literal, subdenomination); - break; } - if (Token::isTimeSubdenomination(m_scanner->peekNextToken())) + else if (Token::isTimeSubdenomination(m_scanner->peekNextToken())) { ASTPointer literal = getLiteralAndAdvance(); nodeFactory.markEndPosition(); Literal::SubDenomination subdenomination = static_cast(m_scanner->currentToken()); m_scanner->next(); expression = nodeFactory.createNode(token, literal, subdenomination); - break; } - // fall-through + else + { + nodeFactory.markEndPosition(); + expression = nodeFactory.createNode(token, getLiteralAndAdvance()); + } + break; case Token::StringLiteral: nodeFactory.markEndPosition(); expression = nodeFactory.createNode(token, getLiteralAndAdvance()); @@ -1357,9 +1360,9 @@ ASTPointer Parser::parsePrimaryExpression() } nodeFactory.markEndPosition(); expectToken(oppositeToken); - return nodeFactory.createNode(components, isArray); + expression = nodeFactory.createNode(components, isArray); + break; } - default: if (Token::isElementaryTypeName(token)) { From f2317670faf1984fe1b92696a50439bd0b33d39b Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 25 Aug 2017 14:24:07 +0100 Subject: [PATCH 016/162] Remove two other fall-through cases in Parser --- libsolidity/parsing/Parser.cpp | 6 ++++-- libsolidity/parsing/Scanner.cpp | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 2f28db3ed..8429bf791 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -903,11 +903,13 @@ ASTPointer Parser::parseStatement() { statement = ASTNodeFactory(*this).createNode(docString); m_scanner->next(); - break; } - // fall-through + else + statement = parseSimpleStatement(docString); + break; default: statement = parseSimpleStatement(docString); + break; } expectToken(Token::Semicolon); return statement; diff --git a/libsolidity/parsing/Scanner.cpp b/libsolidity/parsing/Scanner.cpp index fdca23ea1..6541f6c2f 100644 --- a/libsolidity/parsing/Scanner.cpp +++ b/libsolidity/parsing/Scanner.cpp @@ -435,7 +435,7 @@ void Scanner::scanToken() m_nextToken.location.start = sourcePos(); switch (m_char) { - case '\n': // fall-through + case '\n': case ' ': case '\t': token = selectToken(Token::Whitespace); From e088f48c55aad32568ad6681be11dde263f4272b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 18 Aug 2017 12:38:08 +0200 Subject: [PATCH 017/162] Travis CI: Cleanup emscripten build script --- scripts/travis-emscripten/build_emscripten.sh | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/scripts/travis-emscripten/build_emscripten.sh b/scripts/travis-emscripten/build_emscripten.sh index f92b3c445..2b4fb4a74 100755 --- a/scripts/travis-emscripten/build_emscripten.sh +++ b/scripts/travis-emscripten/build_emscripten.sh @@ -50,7 +50,7 @@ sed -i 's|using gcc ;|using gcc : : /usr/local/bin/em++ ;|g' ./project-config.ja sed -i 's|$(archiver\[1\])|/usr/local/bin/emar|g' ./tools/build/src/tools/gcc.jam sed -i 's|$(ranlib\[1\])|/usr/local/bin/emranlib|g' ./tools/build/src/tools/gcc.jam ./b2 link=static variant=release threading=single runtime-link=static \ - thread system regex date_time chrono filesystem unit_test_framework program_options random + system regex filesystem unit_test_framework program_options find . -name 'libboost*.a' -exec cp {} . \; rm -rf b2 libs doc tools more bin.v2 status ) @@ -68,29 +68,19 @@ emcmake cmake \ -DBoost_USE_STATIC_LIBS=1 \ -DBoost_USE_STATIC_RUNTIME=1 \ -DBoost_INCLUDE_DIR="$WORKSPACE"/boost_1_57_0/ \ - -DBoost_CHRONO_LIBRARY="$WORKSPACE"/boost_1_57_0/libboost_chrono.a \ - -DBoost_CHRONO_LIBRARIES="$WORKSPACE"/boost_1_57_0/libboost_chrono.a \ - -DBoost_DATE_TIME_LIBRARY="$WORKSPACE"/boost_1_57_0/libboost_date_time.a \ - -DBoost_DATE_TIME_LIBRARIES="$WORKSPACE"/boost_1_57_0/libboost_date_time.a \ -DBoost_FILESYSTEM_LIBRARY="$WORKSPACE"/boost_1_57_0/libboost_filesystem.a \ -DBoost_FILESYSTEM_LIBRARIES="$WORKSPACE"/boost_1_57_0/libboost_filesystem.a \ -DBoost_PROGRAM_OPTIONS_LIBRARY="$WORKSPACE"/boost_1_57_0/libboost_program_options.a \ -DBoost_PROGRAM_OPTIONS_LIBRARIES="$WORKSPACE"/boost_1_57_0/libboost_program_options.a \ - -DBoost_RANDOM_LIBRARY="$WORKSPACE"/boost_1_57_0/libboost_random.a \ - -DBoost_RANDOM_LIBRARIES="$WORKSPACE"/boost_1_57_0/libboost_random.a \ -DBoost_REGEX_LIBRARY="$WORKSPACE"/boost_1_57_0/libboost_regex.a \ -DBoost_REGEX_LIBRARIES="$WORKSPACE"/boost_1_57_0/libboost_regex.a \ -DBoost_SYSTEM_LIBRARY="$WORKSPACE"/boost_1_57_0/libboost_system.a \ -DBoost_SYSTEM_LIBRARIES="$WORKSPACE"/boost_1_57_0/libboost_system.a \ - -DBoost_THREAD_LIBRARY="$WORKSPACE"/boost_1_57_0/libboost_thread.a \ - -DBoost_THREAD_LIBRARIES="$WORKSPACE"/boost_1_57_0/libboost_thread.a \ -DBoost_UNIT_TEST_FRAMEWORK_LIBRARY="$WORKSPACE"/boost_1_57_0/libboost_unit_test_framework.a \ -DBoost_UNIT_TEST_FRAMEWORK_LIBRARIES="$WORKSPACE"/boost_1_57_0/libboost_unit_test_framework.a \ - -DDev_DEVCORE_LIBRARY="$WORKSPACE"/solidity/build/libdevcore/libsoldevcore.a \ - -DEth_EVMASM_LIBRARY="$WORKSPACE"/solidity/build/libevmasm/libsolevmasm.a \ - -DETH_STATIC=1 -DTESTS=0 \ + -DTESTS=0 \ .. -emmake make -j 4 +make -j 4 cd .. cp build/solc/soljson.js ./ From 6e9f93e0437d72e712a968c6d9c8973b30b4d5af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 21 Aug 2017 08:19:55 +0200 Subject: [PATCH 018/162] Emscripten, CI: Use CMake toolchain file --- scripts/travis-emscripten/build_emscripten.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/travis-emscripten/build_emscripten.sh b/scripts/travis-emscripten/build_emscripten.sh index 2b4fb4a74..9c7cbb2bf 100755 --- a/scripts/travis-emscripten/build_emscripten.sh +++ b/scripts/travis-emscripten/build_emscripten.sh @@ -61,7 +61,8 @@ echo -en 'travis_fold:start:compiling_solidity\\r' cd $WORKSPACE mkdir -p build cd build -emcmake cmake \ +cmake \ + -DCMAKE_TOOLCHAIN_FILE=$EMSCRIPTEN/cmake/Modules/Platform/Emscripten.cmake \ -DCMAKE_BUILD_TYPE=Release \ -DEMSCRIPTEN=1 \ -DBoost_FOUND=1 \ @@ -83,9 +84,8 @@ emcmake cmake \ make -j 4 cd .. -cp build/solc/soljson.js ./ mkdir -p upload -cp soljson.js upload/ +cp build/solc/soljson.js upload/ OUTPUT_SIZE=`ls -la build/solc/soljson.js` From a5ce02d3334ab6086a04dd0b0a051f0ded868c09 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 25 Aug 2017 15:44:03 +0200 Subject: [PATCH 019/162] Leave artifact also on root. --- scripts/travis-emscripten/build_emscripten.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/travis-emscripten/build_emscripten.sh b/scripts/travis-emscripten/build_emscripten.sh index 9c7cbb2bf..5259dc7f5 100755 --- a/scripts/travis-emscripten/build_emscripten.sh +++ b/scripts/travis-emscripten/build_emscripten.sh @@ -86,8 +86,9 @@ make -j 4 cd .. mkdir -p upload cp build/solc/soljson.js upload/ +cp build/solc/soljson.js ./ -OUTPUT_SIZE=`ls -la build/solc/soljson.js` +OUTPUT_SIZE=`ls -la soljson.js` echo "Emscripten output size: $OUTPUT_SIZE" From 82e4f6f604b3fa84e16e4bb45bcb7093a8a1eb70 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 25 Aug 2017 12:10:28 +0200 Subject: [PATCH 020/162] Cleanup of FixedHash.h --- libdevcore/FixedHash.h | 140 +------------------------------------ libevmasm/AssemblyItem.cpp | 5 +- 2 files changed, 7 insertions(+), 138 deletions(-) diff --git a/libdevcore/FixedHash.h b/libdevcore/FixedHash.h index 5bc700950..cd6e1da1f 100644 --- a/libdevcore/FixedHash.h +++ b/libdevcore/FixedHash.h @@ -31,16 +31,10 @@ #include #include #include -#include -#include namespace dev { -/// Compile-time calculation of Log2 of constant values. -template struct StaticLog2 { enum { result = 1 + StaticLog2::result }; }; -template <> struct StaticLog2<1> { enum { result = 0 }; }; - /// Fixed-size raw-byte array container type, with an API optimised for storing hashes. /// Transparently converts to/from the corresponding arithmetic type; this will /// assume the data contained in the hash is big-endian. @@ -54,9 +48,6 @@ public: /// The size of the container. enum { size = N }; - /// A dummy flag to avoid accidental construction from pointer. - enum ConstructFromPointerType { ConstructFromPointer }; - /// Method to convert from a string. enum ConstructFromStringType { FromHex, FromBinary }; @@ -81,9 +72,6 @@ public: /// Explicitly construct, copying from a byte array. explicit FixedHash(bytesConstRef _b, ConstructFromHashType _t = FailIfDifferent) { if (_b.size() == N) memcpy(m_data.data(), _b.data(), std::min(_b.size(), N)); else { m_data.fill(0); if (_t != FailIfDifferent) { auto c = std::min(_b.size(), N); for (unsigned i = 0; i < c; ++i) m_data[_t == AlignRight ? N - 1 - i : i] = _b[_t == AlignRight ? _b.size() - 1 - i : i]; } } } - /// Explicitly construct, copying from a bytes in memory with given pointer. - explicit FixedHash(byte const* _bs, ConstructFromPointerType) { memcpy(m_data.data(), _bs, N); } - /// Explicitly construct, copying from a string. explicit FixedHash(std::string const& _s, ConstructFromStringType _t = FromHex, ConstructFromHashType _ht = FailIfDifferent): FixedHash(_t == FromHex ? fromHex(_s, WhenError::Throw) : dev::asBytes(_s), _ht) {} @@ -96,37 +84,16 @@ public: // The obvious comparison operators. bool operator==(FixedHash const& _c) const { return m_data == _c.m_data; } bool operator!=(FixedHash const& _c) const { return m_data != _c.m_data; } + /// Required to sort objects of this type or use them as map keys. bool operator<(FixedHash const& _c) const { for (unsigned i = 0; i < N; ++i) if (m_data[i] < _c.m_data[i]) return true; else if (m_data[i] > _c.m_data[i]) return false; return false; } - bool operator>=(FixedHash const& _c) const { return !operator<(_c); } - bool operator<=(FixedHash const& _c) const { return operator==(_c) || operator<(_c); } - bool operator>(FixedHash const& _c) const { return !operator<=(_c); } - // The obvious binary operators. - FixedHash& operator^=(FixedHash const& _c) { for (unsigned i = 0; i < N; ++i) m_data[i] ^= _c.m_data[i]; return *this; } - FixedHash operator^(FixedHash const& _c) const { return FixedHash(*this) ^= _c; } - FixedHash& operator|=(FixedHash const& _c) { for (unsigned i = 0; i < N; ++i) m_data[i] |= _c.m_data[i]; return *this; } - FixedHash operator|(FixedHash const& _c) const { return FixedHash(*this) |= _c; } - FixedHash& operator&=(FixedHash const& _c) { for (unsigned i = 0; i < N; ++i) m_data[i] &= _c.m_data[i]; return *this; } - FixedHash operator&(FixedHash const& _c) const { return FixedHash(*this) &= _c; } FixedHash operator~() const { FixedHash ret; for (unsigned i = 0; i < N; ++i) ret[i] = ~m_data[i]; return ret; } - // Big-endian increment. - FixedHash& operator++() { for (unsigned i = size; i > 0 && !++m_data[--i]; ) {} return *this; } - - /// @returns true if all one-bits in @a _c are set in this object. - bool contains(FixedHash const& _c) const { return (*this & _c) == _c; } - /// @returns a particular byte from the hash. byte& operator[](unsigned _i) { return m_data[_i]; } /// @returns a particular byte from the hash. byte operator[](unsigned _i) const { return m_data[_i]; } - /// @returns an abridged version of the hash as a user-readable hex string. - std::string abridged() const { return toHex(ref().cropped(0, 4)) + "\342\200\246"; } - - /// @returns a version of the hash as a user-readable hex string that leaves out the middle part. - std::string abridgedMiddle() const { return toHex(ref().cropped(0, 4)) + "\342\200\246" + toHex(ref().cropped(N - 4)); } - /// @returns the hash as a user-readable hex string. std::string hex() const { return toHex(ref()); } @@ -151,54 +118,17 @@ public: /// @returns a constant reference to the object's data as an STL array. std::array const& asArray() const { return m_data; } - struct hash - { - /// Make a hash of the object's data. - size_t operator()(FixedHash const& _value) const { return boost::hash_range(_value.m_data.cbegin(), _value.m_data.cend()); } - }; - - template inline FixedHash& shiftBloom(FixedHash const& _h) - { - return (*this |= _h.template bloomPart()); - } - - template inline bool containsBloom(FixedHash const& _h) - { - return contains(_h.template bloomPart()); - } - - template inline FixedHash bloomPart() const - { - unsigned const c_bloomBits = M * 8; - unsigned const c_mask = c_bloomBits - 1; - unsigned const c_bloomBytes = (StaticLog2::result + 7) / 8; - - static_assert((M & (M - 1)) == 0, "M must be power-of-two"); - static_assert(P * c_bloomBytes <= N, "out of range"); - - FixedHash ret; - byte const* p = data(); - for (unsigned i = 0; i < P; ++i) - { - unsigned index = 0; - for (unsigned j = 0; j < c_bloomBytes; ++j, ++p) - index = (index << 8) | *p; - index &= c_mask; - ret[M - 1 - index / 8] |= (1 << (index % 8)); - } - return ret; - } - /// Returns the index of the first bit set to one, or size() * 8 if no bits are set. inline unsigned firstBitSet() const { unsigned ret = 0; for (auto d: m_data) if (d) + { for (;; ++ret, d <<= 1) if (d & 0x80) return ret; - else {} + } else ret += 8; return ret; @@ -210,21 +140,6 @@ private: std::array m_data; ///< The binary data. }; -/// Fast equality operator for h256. -template<> inline bool FixedHash<32>::operator==(FixedHash<32> const& _other) const -{ - const uint64_t* hash1 = (const uint64_t*)data(); - const uint64_t* hash2 = (const uint64_t*)_other.data(); - return (hash1[0] == hash2[0]) && (hash1[1] == hash2[1]) && (hash1[2] == hash2[2]) && (hash1[3] == hash2[3]); -} - -/// Fast std::hash compatible hash function object for h256. -template<> inline size_t FixedHash<32>::hash::operator()(FixedHash<32> const& value) const -{ - uint64_t const* data = reinterpret_cast(value.data()); - return boost::hash_range(data, data + 4); -} - /// Stream I/O for the FixedHash class. template inline std::ostream& operator<<(std::ostream& _out, FixedHash const& _h) @@ -238,56 +153,7 @@ inline std::ostream& operator<<(std::ostream& _out, FixedHash const& _h) } // Common types of FixedHash. -using h2048 = FixedHash<256>; -using h1024 = FixedHash<128>; -using h520 = FixedHash<65>; -using h512 = FixedHash<64>; using h256 = FixedHash<32>; using h160 = FixedHash<20>; -using h128 = FixedHash<16>; -using h64 = FixedHash<8>; -using h512s = std::vector; -using h256s = std::vector; -using h160s = std::vector; -using h256Set = std::set; -using h160Set = std::set; -using h256Hash = std::unordered_set; -using h160Hash = std::unordered_set; - -/// Convert the given value into h160 (160-bit unsigned integer) using the right 20 bytes. -inline h160 right160(h256 const& _t) -{ - h160 ret; - memcpy(ret.data(), _t.data() + 12, 20); - return ret; -} - -/// Convert the given value into h160 (160-bit unsigned integer) using the left 20 bytes. -inline h160 left160(h256 const& _t) -{ - h160 ret; - memcpy(&ret[0], _t.data(), 20); - return ret; -} - -inline std::string toString(h256s const& _bs) -{ - std::ostringstream out; - out << "[ "; - for (auto i: _bs) - out << i.abridged() << ", "; - out << "]"; - return out.str(); -} } - -namespace std -{ - /// Forward std::hash to dev::FixedHash::hash. - template<> struct hash: dev::h64::hash {}; - template<> struct hash: dev::h128::hash {}; - template<> struct hash: dev::h160::hash {}; - template<> struct hash: dev::h256::hash {}; - template<> struct hash: dev::h512::hash {}; -} diff --git a/libevmasm/AssemblyItem.cpp b/libevmasm/AssemblyItem.cpp index 419a8c0b1..1af266b6a 100644 --- a/libevmasm/AssemblyItem.cpp +++ b/libevmasm/AssemblyItem.cpp @@ -249,8 +249,11 @@ ostream& dev::eth::operator<<(ostream& _out, AssemblyItem const& _item) _out << " PushProgramSize"; break; case PushLibraryAddress: - _out << " PushLibraryAddress " << hex << h256(_item.data()).abridgedMiddle() << dec; + { + string hash(h256((_item.data())).hex()); + _out << " PushLibraryAddress " << hash.substr(0, 8) + "..." + hash.substr(hash.length() - 8); break; + } case UndefinedItem: _out << " ???"; break; From d60d4b3031001a188b2449c97e2d617d98c77f0e Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 25 Aug 2017 15:37:10 +0100 Subject: [PATCH 021/162] Remove duplicate work from CompilerStack.analyze() --- libsolidity/interface/CompilerStack.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 363f45ddd..5837c6425 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -201,8 +201,6 @@ bool CompilerStack::analyze() for (ASTPointer const& node: source->ast->nodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) { - m_globalContext->setCurrentContract(*contract); - resolver.updateDeclaration(*m_globalContext->currentThis()); TypeChecker typeChecker(m_errorReporter); if (typeChecker.checkTypeRequirements(*contract)) { @@ -211,14 +209,6 @@ bool CompilerStack::analyze() } else noErrors = false; - - // Note that we now reference contracts by their fully qualified names, and - // thus contracts can only conflict if declared in the same source file. This - // already causes a double-declaration error elsewhere, so we do not report - // an error here and instead silently drop any additional contracts we find. - - if (m_contracts.find(contract->fullyQualifiedName()) == m_contracts.end()) - m_contracts[contract->fullyQualifiedName()].contract = contract; } if (noErrors) From 670df8e874662901fe16b6c1995c5eeadf1283a1 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 25 Aug 2017 15:39:20 +0100 Subject: [PATCH 022/162] Attach natspec before type checking --- libsolidity/interface/CompilerStack.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 5837c6425..1f6fd12fb 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -188,6 +188,9 @@ bool CompilerStack::analyze() if (!resolver.updateDeclaration(*m_globalContext->currentSuper())) return false; if (!resolver.resolveNamesAndTypes(*contract)) return false; + contract->setDevDocumentation(Natspec::devDocumentation(*contract)); + contract->setUserDocumentation(Natspec::userDocumentation(*contract)); + // Note that we now reference contracts by their fully qualified names, and // thus contracts can only conflict if declared in the same source file. This // already causes a double-declaration error elsewhere, so we do not report @@ -202,12 +205,7 @@ bool CompilerStack::analyze() if (ContractDefinition* contract = dynamic_cast(node.get())) { TypeChecker typeChecker(m_errorReporter); - if (typeChecker.checkTypeRequirements(*contract)) - { - contract->setDevDocumentation(Natspec::devDocumentation(*contract)); - contract->setUserDocumentation(Natspec::userDocumentation(*contract)); - } - else + if (!typeChecker.checkTypeRequirements(*contract)) noErrors = false; } From e6f55fb95e913a5167ebd1f43a0eebb8b6d17daf Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 25 Aug 2017 15:43:26 +0100 Subject: [PATCH 023/162] Do not create a new TypeChecker instance for every contract --- libsolidity/interface/CompilerStack.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 1f6fd12fb..7e4518b94 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -200,14 +200,12 @@ bool CompilerStack::analyze() m_contracts[contract->fullyQualifiedName()].contract = contract; } + TypeChecker typeChecker(m_errorReporter); for (Source const* source: m_sourceOrder) for (ASTPointer const& node: source->ast->nodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) - { - TypeChecker typeChecker(m_errorReporter); if (!typeChecker.checkTypeRequirements(*contract)) noErrors = false; - } if (noErrors) { From 8bc76ecf58b3e57ccfbc69da5e9633bae490ca92 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 25 Aug 2017 15:49:33 +0100 Subject: [PATCH 024/162] Update SolidityNameAndTypeResolution to match CompilerStack --- test/libsolidity/SolidityNameAndTypeResolution.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 380978e81..9333b62db 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -87,16 +87,15 @@ parseAnalyseAndReturnError(string const& _source, bool _reportWarnings = false, success = false; } if (success) + { + TypeChecker typeChecker(errorReporter); for (ASTPointer const& node: sourceUnit->nodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) { - globalContext->setCurrentContract(*contract); - resolver.updateDeclaration(*globalContext->currentThis()); - - TypeChecker typeChecker(errorReporter); bool success = typeChecker.checkTypeRequirements(*contract); BOOST_CHECK(success || !errorReporter.errors().empty()); } + } if (success) if (!PostTypeChecker(errorReporter).check(*sourceUnit)) success = false; From 5c73a80418db42970c06959945cd0b5b413806f9 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 25 Aug 2017 14:33:35 +0100 Subject: [PATCH 025/162] Remove fallthrough from ArrayUtils --- libsolidity/codegen/ArrayUtils.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index 67ca22f14..e17188c26 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -913,10 +913,10 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType, bool _doBoundsCheck) c switch (location) { case DataLocation::Memory: - if (_arrayType.isDynamicallySized()) - m_context << u256(32) << Instruction::ADD; - // fall-through case DataLocation::CallData: + if (location == DataLocation::Memory && _arrayType.isDynamicallySized()) + m_context << u256(32) << Instruction::ADD; + if (!_arrayType.isByteArray()) { m_context << Instruction::SWAP1; From 51d6141f7ecec6bb09f6c12711017eb51bd9c22c Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 25 Aug 2017 16:00:36 +0100 Subject: [PATCH 026/162] Mark to places fall-through --- libsolidity/codegen/CompilerUtils.cpp | 1 + libsolidity/codegen/ExpressionCompiler.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index a0fc5d55e..146472f92 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -829,6 +829,7 @@ void CompilerUtils::convertType( break; } } + // fall-through default: // All other types should not be convertible to non-equal types. solAssert(_typeOnStack == _targetType, "Invalid type conversion requested."); diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 639bfc324..45c2170c2 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1047,6 +1047,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) if (!alsoSearchInteger) break; } + // fall-through case Type::Category::Integer: if (member == "balance") { From da56bde58f6099b199a109f528045953a5fe1bab Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 25 Aug 2017 16:02:05 +0100 Subject: [PATCH 027/162] Move the break in switch for readability --- libsolidity/parsing/Parser.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 8429bf791..4fc8fd134 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -1263,15 +1263,15 @@ ASTPointer Parser::parseLeftHandSideExpression( nodeFactory.markEndPosition(); expectToken(Token::RBrack); expression = nodeFactory.createNode(expression, index); + break; } - break; case Token::Period: { m_scanner->next(); nodeFactory.markEndPosition(); expression = nodeFactory.createNode(expression, expectIdentifierToken()); + break; } - break; case Token::LParen: { m_scanner->next(); @@ -1281,8 +1281,8 @@ ASTPointer Parser::parseLeftHandSideExpression( nodeFactory.markEndPosition(); expectToken(Token::RParen); expression = nodeFactory.createNode(expression, arguments, names); + break; } - break; default: return expression; } From 774363eb74023b90cfff0d9e6328103eceecc183 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 21 Aug 2017 22:22:20 +0100 Subject: [PATCH 028/162] Add copy constructor to ErrorReporter (since it has assignment operator) --- libsolidity/interface/ErrorReporter.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libsolidity/interface/ErrorReporter.h b/libsolidity/interface/ErrorReporter.h index 241d6b438..6f7ef51dd 100644 --- a/libsolidity/interface/ErrorReporter.h +++ b/libsolidity/interface/ErrorReporter.h @@ -39,6 +39,9 @@ public: explicit ErrorReporter(ErrorList& _errors): m_errorList(_errors) { } + ErrorReporter(ErrorReporter const& _errorReporter) noexcept: + m_errorList(_errorReporter.m_errorList) { } + ErrorReporter& operator=(ErrorReporter const& _errorReporter); void warning(std::string const& _description); From 6951a371ecb7cdb93bed44b902a04a45f4f8249c Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 25 Aug 2017 12:25:05 +0200 Subject: [PATCH 029/162] Cleanup of vector_ref.h --- libdevcore/vector_ref.h | 39 ++++++++------------------------------ libevmasm/LinkerObject.cpp | 2 +- 2 files changed, 9 insertions(+), 32 deletions(-) diff --git a/libdevcore/vector_ref.h b/libdevcore/vector_ref.h index 0f5431811..b4dcff651 100644 --- a/libdevcore/vector_ref.h +++ b/libdevcore/vector_ref.h @@ -23,6 +23,8 @@ public: using value_type = _T; using element_type = _T; using mutable_value_type = typename std::conditional::value, typename std::remove_const<_T>::type, _T>::type; + using string_type = typename std::conditional::value, std::string const, std::string>::type; + using vector_type = typename std::conditional::value, std::vector::type> const, std::vector<_T>>::type; static_assert(std::is_pod::value, "vector_ref can only be used with PODs due to its low-level treatment of data."); @@ -30,18 +32,13 @@ public: /// Creates a new vector_ref to point to @a _count elements starting at @a _data. vector_ref(_T* _data, size_t _count): m_data(_data), m_count(_count) {} /// Creates a new vector_ref pointing to the data part of a string (given as pointer). - vector_ref(typename std::conditional::value, std::string const*, std::string*>::type _data): m_data(reinterpret_cast<_T*>(_data->data())), m_count(_data->size() / sizeof(_T)) {} - /// Creates a new vector_ref pointing to the data part of a vector (given as pointer). - vector_ref(typename std::conditional::value, std::vector::type> const*, std::vector<_T>*>::type _data): m_data(_data->data()), m_count(_data->size()) {} + vector_ref(string_type* _data): m_data(reinterpret_cast<_T*>(_data->data())), m_count(_data->size() / sizeof(_T)) {} /// Creates a new vector_ref pointing to the data part of a string (given as reference). - vector_ref(typename std::conditional::value, std::string const&, std::string&>::type _data): m_data(reinterpret_cast<_T*>(_data.data())), m_count(_data.size() / sizeof(_T)) {} -#if DEV_LDB - vector_ref(ldb::Slice const& _s): m_data(reinterpret_cast<_T*>(_s.data())), m_count(_s.size() / sizeof(_T)) {} -#endif + vector_ref(string_type& _data): vector_ref(&_data) {} + /// Creates a new vector_ref pointing to the data part of a vector (given as pointer). + vector_ref(vector_type* _data): m_data(_data->data()), m_count(_data->size()) {} explicit operator bool() const { return m_data && m_count; } - bool contentsEqual(std::vector const& _c) const { if (!m_data || m_count == 0) return _c.empty(); else return _c.size() == m_count && !memcmp(_c.data(), m_data, m_count * sizeof(_T)); } - std::vector toVector() const { return std::vector(m_data, m_data + m_count); } std::vector toBytes() const { return std::vector(reinterpret_cast(m_data), reinterpret_cast(m_data) + m_count * sizeof(_T)); } std::string toString() const { return std::string((char const*)m_data, ((char const*)m_data) + m_count * sizeof(_T)); } @@ -50,25 +47,14 @@ public: _T* data() const { return m_data; } /// @returns the number of elements referenced (not necessarily number of bytes). - size_t count() const { return m_count; } - /// @returns the number of elements referenced (not necessarily number of bytes). size_t size() const { return m_count; } bool empty() const { return !m_count; } - /// @returns a new vector_ref pointing at the next chunk of @a size() elements. - vector_ref<_T> next() const { if (!m_data) return *this; else return vector_ref<_T>(m_data + m_count, m_count); } /// @returns a new vector_ref which is a shifted and shortened view of the original data. /// If this goes out of bounds in any way, returns an empty vector_ref. /// If @a _count is ~size_t(0), extends the view to the end of the data. vector_ref<_T> cropped(size_t _begin, size_t _count) const { if (m_data && _begin <= m_count && _count <= m_count && _begin + _count <= m_count) return vector_ref<_T>(m_data + _begin, _count == ~size_t(0) ? m_count - _begin : _count); else return vector_ref<_T>(); } /// @returns a new vector_ref which is a shifted view of the original data (not going beyond it). vector_ref<_T> cropped(size_t _begin) const { if (m_data && _begin <= m_count) return vector_ref<_T>(m_data + _begin, m_count - _begin); else return vector_ref<_T>(); } - void retarget(_T* _d, size_t _s) { m_data = _d; m_count = _s; } - void retarget(std::vector<_T> const& _t) { m_data = _t.data(); m_count = _t.size(); } - template bool overlapsWith(vector_ref _t) const { void const* f1 = data(); void const* t1 = data() + size(); void const* f2 = _t.data(); void const* t2 = _t.data() + _t.size(); return f1 < t2 && t1 > f2; } - /// Copies the contents of this vector_ref to the contents of @a _t, up to the max size of @a _t. - void copyTo(vector_ref::type> _t) const { if (overlapsWith(_t)) memmove(_t.data(), m_data, std::min(_t.size(), m_count) * sizeof(_T)); else memcpy(_t.data(), m_data, std::min(_t.size(), m_count) * sizeof(_T)); } - /// Copies the contents of this vector_ref to the contents of @a _t, and zeros further trailing elements in @a _t. - void populate(vector_ref::type> _t) const { copyTo(_t); memset(_t.data() + m_count, 0, std::max(_t.size(), m_count) - m_count); } _T* begin() { return m_data; } _T* end() { return m_data + m_count; } @@ -81,20 +67,11 @@ public: bool operator==(vector_ref<_T> const& _cmp) const { return m_data == _cmp.m_data && m_count == _cmp.m_count; } bool operator!=(vector_ref<_T> const& _cmp) const { return !operator==(_cmp); } -#if DEV_LDB - operator ldb::Slice() const { return ldb::Slice((char const*)m_data, m_count * sizeof(_T)); } -#endif - void reset() { m_data = nullptr; m_count = 0; } private: - _T* m_data; - size_t m_count; + _T* m_data = nullptr; + size_t m_count = 0; }; -template vector_ref<_T const> ref(_T const& _t) { return vector_ref<_T const>(&_t, 1); } -template vector_ref<_T> ref(_T& _t) { return vector_ref<_T>(&_t, 1); } -template vector_ref<_T const> ref(std::vector<_T> const& _t) { return vector_ref<_T const>(&_t); } -template vector_ref<_T> ref(std::vector<_T>& _t) { return vector_ref<_T>(&_t); } - } diff --git a/libevmasm/LinkerObject.cpp b/libevmasm/LinkerObject.cpp index 06607089e..8b7d9e06a 100644 --- a/libevmasm/LinkerObject.cpp +++ b/libevmasm/LinkerObject.cpp @@ -38,7 +38,7 @@ void LinkerObject::link(map const& _libraryAddresses) std::map remainingRefs; for (auto const& linkRef: linkReferences) if (h160 const* address = matchLibrary(linkRef.second, _libraryAddresses)) - address->ref().copyTo(ref(bytecode).cropped(linkRef.first, 20)); + copy(address->data(), address->data() + 20, bytecode.begin() + linkRef.first); else remainingRefs.insert(linkRef); linkReferences.swap(remainingRefs); From 8e5f9c59814f7e8b3b390965b17d9b867d36d544 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 25 Aug 2017 20:29:43 +0100 Subject: [PATCH 030/162] Removed unused natspec members of ContractDefinition --- libsolidity/ast/AST.cpp | 20 -------------------- libsolidity/ast/AST.h | 10 ---------- libsolidity/interface/CompilerStack.cpp | 3 --- 3 files changed, 33 deletions(-) diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 7f4dea0ef..2e4ae72ac 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -218,26 +218,6 @@ vector, FunctionTypePointer>> const& ContractDefinition::inter return *m_interfaceFunctionList; } -Json::Value const& ContractDefinition::devDocumentation() const -{ - return m_devDocumentation; -} - -Json::Value const& ContractDefinition::userDocumentation() const -{ - return m_userDocumentation; -} - -void ContractDefinition::setDevDocumentation(Json::Value const& _devDocumentation) -{ - m_devDocumentation = _devDocumentation; -} - -void ContractDefinition::setUserDocumentation(Json::Value const& _userDocumentation) -{ - m_userDocumentation = _userDocumentation; -} - vector const& ContractDefinition::inheritableMembers() const { if (!m_inheritableMembers) diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 4592a1907..cecc2f04e 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -392,12 +392,6 @@ public: /// Returns the fallback function or nullptr if no fallback function was specified. FunctionDefinition const* fallbackFunction() const; - Json::Value const& userDocumentation() const; - void setUserDocumentation(Json::Value const& _userDocumentation); - - Json::Value const& devDocumentation() const; - void setDevDocumentation(Json::Value const& _devDocumentation); - virtual TypePointer type() const override; virtual ContractDefinitionAnnotation& annotation() const override; @@ -409,10 +403,6 @@ private: std::vector> m_subNodes; ContractKind m_contractKind; - // parsed Natspec documentation of the contract. - Json::Value m_userDocumentation; - Json::Value m_devDocumentation; - std::vector m_linearizedBaseContracts; mutable std::unique_ptr, FunctionTypePointer>>> m_interfaceFunctionList; mutable std::unique_ptr> m_interfaceEvents; diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 7e4518b94..259694dab 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -188,9 +188,6 @@ bool CompilerStack::analyze() if (!resolver.updateDeclaration(*m_globalContext->currentSuper())) return false; if (!resolver.resolveNamesAndTypes(*contract)) return false; - contract->setDevDocumentation(Natspec::devDocumentation(*contract)); - contract->setUserDocumentation(Natspec::userDocumentation(*contract)); - // Note that we now reference contracts by their fully qualified names, and // thus contracts can only conflict if declared in the same source file. This // already causes a double-declaration error elsewhere, so we do not report From 5e73ea7fbcda336db9968271c259cca8adbb9cba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Edstr=C3=B6m?= Date: Sun, 27 Aug 2017 14:36:41 +0300 Subject: [PATCH 031/162] Correct package name for installing solc on Arch --- docs/installing-solidity.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index 782bb606b..716077452 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -99,7 +99,7 @@ Arch Linux also has packages, albeit limited to the latest development version: .. code:: bash - pacman -S solidity-git + pacman -S solidity Homebrew is missing pre-built bottles at the time of writing, following a Jenkins to TravisCI migration, but Homebrew From dd2cc899bbf71c1234d3bc1f2ea55c4716dbfd0d Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 28 Aug 2017 11:35:18 +0100 Subject: [PATCH 032/162] Fix lists and links --- docs/abi-spec.rst | 1 + docs/contracts.rst | 1 + docs/miscellaneous.rst | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index fffd9a2c1..ab7a8b325 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -214,6 +214,7 @@ In total:: 0xfce353f661626300000000000000000000000000000000000000000000000000000000006465660000000000000000000000000000000000000000000000000000000000 If we wanted to call `sam` with the arguments `"dave"`, `true` and `[1,2,3]`, we would pass 292 bytes total, broken down into: + - `0xa5643bf2`: the Method ID. This is derived from the signature `sam(bytes,bool,uint256[])`. Note that `uint` is replaced with its canonical representation `uint256`. - `0x0000000000000000000000000000000000000000000000000000000000000060`: the location of the data part of the first parameter (dynamic type), measured in bytes from the start of the arguments block. In this case, `0x60`. - `0x0000000000000000000000000000000000000000000000000000000000000001`: the second parameter: boolean true. diff --git a/docs/contracts.rst b/docs/contracts.rst index ef09d9351..fbf1be870 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -508,6 +508,7 @@ Pure Functions Functions can be declared ``pure`` in which case they promise not to read from or modify the state. In addition to the list of state modifying statements explained above, the following are considered reading from the state: + #. Reading from state variables. #. Accessing ``this.balance`` or ``
.balance``. #. Accessing any of the members of ``block``, ``tx``, ``msg`` (with the exception of ``msg.sig`` and ``msg.data``). diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index e78c4807a..b014a4524 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -330,7 +330,7 @@ Encoding of the Metadata Hash in the Bytecode Because we might support other ways to retrieve the metadata file in the future, the mapping ``{"bzzr0": }`` is stored -[CBOR](https://tools.ietf.org/html/rfc7049)-encoded. Since the beginning of that +`CBOR `_-encoded. Since the beginning of that encoding is not easy to find, its length is added in a two-byte big-endian encoding. The current version of the Solidity compiler thus adds the following to the end of the deployed bytecode:: From 412cc6bc5842f0ad2b6f2b0885f48be1dd1b4e8a Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 28 Aug 2017 11:48:23 +0100 Subject: [PATCH 033/162] Highlight opcodes in docs --- docs/assembly.rst | 9 +++++---- docs/contracts.rst | 2 +- docs/miscellaneous.rst | 16 ++++++++-------- docs/types.rst | 8 ++++---- 4 files changed, 18 insertions(+), 17 deletions(-) diff --git a/docs/assembly.rst b/docs/assembly.rst index 6495699f7..3c2146c17 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -9,9 +9,10 @@ This assembly language can also be used as "inline assembly" inside Solidity source code. We start with describing how to use inline assembly and how it differs from standalone assembly and then specify assembly itself. -TODO: Write about how scoping rules of inline assembly are a bit different -and the complications that arise when for example using internal functions -of libraries. Furthermore, write about the symbols defined by the compiler. +.. note:: + TODO: Write about how scoping rules of inline assembly are a bit different + and the complications that arise when for example using internal functions + of libraries. Furthermore, write about the symbols defined by the compiler. .. _inline-assembly: @@ -1005,7 +1006,7 @@ that modifies the stack and with every label that is annotated with a stack adjustment. Every time a new local variable is introduced, it is registered together with the current stack height. If a variable is accessed (either for copying its value or for -assignment), the appropriate DUP or SWAP instruction is selected depending +assignment), the appropriate ``DUP`` or ``SWAP`` instruction is selected depending on the difference between the current stack height and the stack height at the point the variable was introduced. diff --git a/docs/contracts.rst b/docs/contracts.rst index fbf1be870..aa3f8fa6c 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -1119,7 +1119,7 @@ are all compiled as calls (``DELEGATECALL``) to an external contract/library. If you use libraries, take care that an actual external function call is performed. ``msg.sender``, ``msg.value`` and ``this`` will retain their values -in this call, though (prior to Homestead, because of the use of `CALLCODE`, ``msg.sender`` and +in this call, though (prior to Homestead, because of the use of ``CALLCODE``, ``msg.sender`` and ``msg.value`` changed, though). The following example shows how to use memory types and diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index b014a4524..55dc31850 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -145,13 +145,13 @@ Different types have different rules for cleaning up invalid values: Internals - The Optimizer ************************* -The Solidity optimizer operates on assembly, so it can be and also is used by other languages. It splits the sequence of instructions into basic blocks at JUMPs and JUMPDESTs. Inside these blocks, the instructions are analysed and every modification to the stack, to memory or storage is recorded as an expression which consists of an instruction and a list of arguments which are essentially pointers to other expressions. The main idea is now to find expressions that are always equal (on every input) and combine them into an expression class. The optimizer first tries to find each new expression in a list of already known expressions. If this does not work, the expression is simplified according to rules like ``constant + constant = sum_of_constants`` or ``X * 1 = X``. Since this is done recursively, we can also apply the latter rule if the second factor is a more complex expression where we know that it will always evaluate to one. Modifications to storage and memory locations have to erase knowledge about storage and memory locations which are not known to be different: If we first write to location x and then to location y and both are input variables, the second could overwrite the first, so we actually do not know what is stored at x after we wrote to y. On the other hand, if a simplification of the expression x - y evaluates to a non-zero constant, we know that we can keep our knowledge about what is stored at x. +The Solidity optimizer operates on assembly, so it can be and also is used by other languages. It splits the sequence of instructions into basic blocks at ``JUMPs`` and ``JUMPDESTs``. Inside these blocks, the instructions are analysed and every modification to the stack, to memory or storage is recorded as an expression which consists of an instruction and a list of arguments which are essentially pointers to other expressions. The main idea is now to find expressions that are always equal (on every input) and combine them into an expression class. The optimizer first tries to find each new expression in a list of already known expressions. If this does not work, the expression is simplified according to rules like ``constant + constant = sum_of_constants`` or ``X * 1 = X``. Since this is done recursively, we can also apply the latter rule if the second factor is a more complex expression where we know that it will always evaluate to one. Modifications to storage and memory locations have to erase knowledge about storage and memory locations which are not known to be different: If we first write to location x and then to location y and both are input variables, the second could overwrite the first, so we actually do not know what is stored at x after we wrote to y. On the other hand, if a simplification of the expression x - y evaluates to a non-zero constant, we know that we can keep our knowledge about what is stored at x. -At the end of this process, we know which expressions have to be on the stack in the end and have a list of modifications to memory and storage. This information is stored together with the basic blocks and is used to link them. Furthermore, knowledge about the stack, storage and memory configuration is forwarded to the next block(s). If we know the targets of all JUMP and JUMPI instructions, we can build a complete control flow graph of the program. If there is only one target we do not know (this can happen as in principle, jump targets can be computed from inputs), we have to erase all knowledge about the input state of a block as it can be the target of the unknown JUMP. If a JUMPI is found whose condition evaluates to a constant, it is transformed to an unconditional jump. +At the end of this process, we know which expressions have to be on the stack in the end and have a list of modifications to memory and storage. This information is stored together with the basic blocks and is used to link them. Furthermore, knowledge about the stack, storage and memory configuration is forwarded to the next block(s). If we know the targets of all ``JUMP`` and ``JUMPI`` instructions, we can build a complete control flow graph of the program. If there is only one target we do not know (this can happen as in principle, jump targets can be computed from inputs), we have to erase all knowledge about the input state of a block as it can be the target of the unknown ``JUMP``. If a ``JUMPI`` is found whose condition evaluates to a constant, it is transformed to an unconditional jump. As the last step, the code in each block is completely re-generated. A dependency graph is created from the expressions on the stack at the end of the block and every operation that is not part of this graph is essentially dropped. Now code is generated that applies the modifications to memory and storage in the order they were made in the original code (dropping modifications which were found not to be needed) and finally, generates all values that are required to be on the stack in the correct place. -These steps are applied to each basic block and the newly generated code is used as replacement if it is smaller. If a basic block is split at a JUMPI and during the analysis, the condition evaluates to a constant, the JUMPI is replaced depending on the value of the constant, and thus code like +These steps are applied to each basic block and the newly generated code is used as replacement if it is smaller. If a basic block is split at a ``JUMPI`` and during the analysis, the condition evaluates to a constant, the ``JUMPI`` is replaced depending on the value of the constant, and thus code like :: @@ -361,7 +361,7 @@ In order to verify the compilation, sources can be retrieved from Swarm via the link in the metadata file. The compiler of the correct version (which is checked to be part of the "official" compilers) is invoked on that input with the specified settings. The resulting -bytecode is compared to the data of the creation transaction or CREATE opcode data. +bytecode is compared to the data of the creation transaction or ``CREATE`` opcode data. This automatically verifies the metadata since its hash is part of the bytecode. Excess data corresponds to the constructor input data, which should be decoded according to the interface and presented to the user. @@ -372,7 +372,7 @@ Tips and Tricks *************** * Use ``delete`` on arrays to delete all its elements. -* Use shorter types for struct elements and sort them such that short types are grouped together. This can lower the gas costs as multiple SSTORE operations might be combined into a single (SSTORE costs 5000 or 20000 gas, so this is what you want to optimise). Use the gas price estimator (with optimiser enabled) to check! +* Use shorter types for struct elements and sort them such that short types are grouped together. This can lower the gas costs as multiple ``SSTORE`` operations might be combined into a single (``SSTORE`` costs 5000 or 20000 gas, so this is what you want to optimise). Use the gas price estimator (with optimiser enabled) to check! * Make your state variables public - the compiler will create :ref:`getters ` for you automatically. * If you end up checking conditions on input or state a lot at the beginning of your functions, try using :ref:`modifiers`. * If your contract has a function called ``send`` but you want to use the built-in send-function, use ``address(contractVariable).send(amount)``. @@ -469,7 +469,7 @@ Global Variables - ``require(bool condition)``: abort execution and revert state changes if condition is ``false`` (use for malformed input or error in external component) - ``revert()``: abort execution and revert state changes - ``keccak256(...) returns (bytes32)``: compute the Ethereum-SHA-3 (Keccak-256) hash of the (tightly packed) arguments -- ``sha3(...) returns (bytes32)``: an alias to `keccak256` +- ``sha3(...) returns (bytes32)``: an alias to ``keccak256`` - ``sha256(...) returns (bytes32)``: compute the SHA-256 hash of the (tightly packed) arguments - ``ripemd160(...) returns (bytes20)``: compute the RIPEMD-160 hash of the (tightly packed) arguments - ``ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)``: recover address associated with the public key from elliptic curve signature, return zero on error @@ -478,7 +478,7 @@ Global Variables - ``this`` (current contract's type): the current contract, explicitly convertible to ``address`` - ``super``: the contract one level higher in the inheritance hierarchy - ``selfdestruct(address recipient)``: destroy the current contract, sending its funds to the given address -- ``suicide(address recipieint)``: an alias to `selfdestruct`` +- ``suicide(address recipieint)``: an alias to ``selfdestruct`` - ``
.balance`` (``uint256``): balance of the :ref:`address` in Wei - ``
.send(uint256 amount) returns (bool)``: send given amount of Wei to :ref:`address`, returns ``false`` on failure - ``
.transfer(uint256 amount)``: send given amount of Wei to :ref:`address`, throws on failure @@ -519,7 +519,7 @@ Reserved Keywords These keywords are reserved in Solidity. They might become part of the syntax in the future: ``abstract``, ``after``, ``case``, ``catch``, ``default``, ``final``, ``in``, ``inline``, ``let``, ``match``, ``null``, -``of``, ``pure``, ``relocatable``, ``static``, ``switch``, ``try``, ``type``, ``typeof``, ``view``. +``of``, ``relocatable``, ``static``, ``switch``, ``try``, ``type``, ``typeof``. Language Grammar ================ diff --git a/docs/types.rst b/docs/types.rst index fb88b0067..aa4589de5 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -54,7 +54,7 @@ Operators: * Bit operators: ``&``, ``|``, ``^`` (bitwise exclusive or), ``~`` (bitwise negation) * Arithmetic operators: ``+``, ``-``, unary ``-``, unary ``+``, ``*``, ``/``, ``%`` (remainder), ``**`` (exponentiation), ``<<`` (left shift), ``>>`` (right shift) -Division always truncates (it is just compiled to the DIV opcode of the EVM), but it does not truncate if both +Division always truncates (it is just compiled to the ``DIV`` opcode of the EVM), but it does not truncate if both operators are :ref:`literals` (or literal expressions). Division by zero and modulus with zero throws a runtime exception. @@ -467,10 +467,10 @@ context, there is always a default, but it can be overridden by appending either ``storage`` or ``memory`` to the type. The default for function parameters (including return parameters) is ``memory``, the default for local variables is ``storage`` and the location is forced to ``storage`` for state variables (obviously). -There is also a third data location, "calldata", which is a non-modifiable, +There is also a third data location, ``calldata``, which is a non-modifiable, non-persistent area where function arguments are stored. Function parameters -(not return parameters) of external functions are forced to "calldata" and -behave mostly like memory. +(not return parameters) of external functions are forced to ``calldata`` and +behave mostly like ``memory``. Data locations are important because they change how assignments behave: assignments between storage and memory and also to a state variable (even from other state variables) From e649ce71bd120ade2ee2133f6fa763afa3928077 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 28 Aug 2017 11:48:35 +0100 Subject: [PATCH 034/162] Refer to internal ABI documentation --- docs/miscellaneous.rst | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 55dc31850..e9b01340c 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -84,10 +84,8 @@ Layout of Call Data ******************* When a Solidity contract is deployed and when it is called from an -account, the input data is assumed to be in the format in `the ABI -specification -`_. The -ABI specification requires arguments to be padded to multiples of 32 +account, the input data is assumed to be in the format in :ref:`the ABI +specification `. The ABI specification requires arguments to be padded to multiples of 32 bytes. The internal function calls use a different convention. From 2b26d6b9d7edc86f99db625328d4a3810ab0ee06 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 28 Aug 2017 11:48:48 +0100 Subject: [PATCH 035/162] Require 0.4.12 for the VectorSum example (for loop) --- docs/assembly.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/assembly.rst b/docs/assembly.rst index 3c2146c17..f5abcdc83 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -77,7 +77,7 @@ you really know what you are doing. .. code:: - pragma solidity ^0.4.0; + pragma solidity ^0.4.12; library VectorSum { // This function is less efficient because the optimizer currently fails to From 5a5d21d0c8d24ca8a6685d023cf07b2d586addcd Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 28 Aug 2017 12:03:39 +0100 Subject: [PATCH 036/162] Add note about throw being deprecated --- docs/control-structures.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 796e92389..052549031 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -389,6 +389,9 @@ There are two other ways to trigger exceptions: The ``revert`` function can be u revert the current call. In the future it might be possible to also include details about the error in a call to ``revert``. The ``throw`` keyword can also be used as an alternative to ``revert()``. +.. note:: + From version 0.4.13 the ``throw`` keyword is deprecated and will be phased out in the future. + When exceptions happen in a sub-call, they "bubble up" (i.e. exceptions are rethrown) automatically. Exceptions to this rule are ``send`` and the low-level functions ``call``, ``delegatecall`` and ``callcode`` -- those return ``false`` in case of an exception instead of "bubbling up". From 550d646b9727603f39aeaa432d718cd2bb51cf85 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 28 Aug 2017 12:14:41 +0100 Subject: [PATCH 037/162] Explain safe purchase (even number) --- docs/solidity-by-example.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst index ca6b970c9..139c8a423 100644 --- a/docs/solidity-by-example.rst +++ b/docs/solidity-by-example.rst @@ -535,6 +535,9 @@ Safe Remote Purchase enum State { Created, Locked, Inactive } State public state; + // Ensure that `msg.value` is an even number. + // Division will truncate if it is an odd number. + // Check via multiplication that it wasn't an odd number. function Purchase() payable { seller = msg.sender; value = msg.value / 2; From 122e65f8f496b0b20406cc968a2e48e6434d1a8f Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 28 Aug 2017 14:50:18 +0200 Subject: [PATCH 038/162] Crash fix, parseTypeName can return null. --- Changelog.md | 2 ++ libsolidity/parsing/Parser.cpp | 5 ++++- test/libsolidity/SolidityParser.cpp | 12 ++++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 670182af7..498553698 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,8 @@ Features: * Type Checker: Warn on using literals as tight packing parameters in ``keccak256``, ``sha3``, ``sha256`` and ``ripemd160``. Bugfixes: + * Parser: Crash fix related to parseTypeName. + ### 0.4.16 (2017-08-24) diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 4fc8fd134..ce8a9f011 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -1244,7 +1244,10 @@ ASTPointer Parser::parseLeftHandSideExpression( { expectToken(Token::New); ASTPointer typeName(parseTypeName(false)); - nodeFactory.setEndPositionFromNode(typeName); + if (typeName) + nodeFactory.setEndPositionFromNode(typeName); + else + nodeFactory.markEndPosition(); expression = nodeFactory.createNode(typeName); } else diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index a39e0958a..60ca03c97 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -1602,6 +1602,18 @@ BOOST_AUTO_TEST_CASE(interface) BOOST_CHECK(successParse(text)); } +BOOST_AUTO_TEST_CASE(newInvalidTypeName) +{ + char const* text = R"( + contract C { + function f() { + new var; + } + } + )"; + CHECK_PARSE_ERROR(text, "Expected explicit type name"); +} + BOOST_AUTO_TEST_SUITE_END() } From ceba40c410784431b8e7a8a945e7a3150c7bf5b3 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 15 Aug 2017 12:35:06 +0100 Subject: [PATCH 039/162] Reorder some methods in CompilerStack for readability --- libsolidity/interface/CompilerStack.h | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 361b8a45e..2756e57d6 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -116,6 +116,9 @@ public: m_optimizeRuns = _runs; } + /// @arg _metadataLiteralSources When true, store sources as literals in the contract metadata. + void useMetadataLiteralSources(bool _metadataLiteralSources) { m_metadataLiteralSources = _metadataLiteralSources; } + /// Adds a source object (e.g. file) to the parser. After this, parse has to be called again. /// @returns true if a source object by the name already existed and was replaced. bool addSource(std::string const& _name, std::string const& _content, bool _isLibrary = false); @@ -125,7 +128,7 @@ public: bool parse(); /// Performs the analysis steps (imports, scopesetting, syntaxCheck, referenceResolving, - /// typechecking, staticAnalysis) on previously set sources + /// typechecking, staticAnalysis) on previously parsed sources. /// @returns false on error. bool analyze(); @@ -133,9 +136,6 @@ public: /// @returns false on error. bool parseAndAnalyze(); - /// @returns a list of the contract names in the sources. - std::vector contractNames() const; - /// Compiles the source units that were previously added and parsed. /// @returns false on error. bool compile(); @@ -158,6 +158,9 @@ public: /// start line, start column, end line, end column std::tuple positionFromSourceLocation(SourceLocation const& _sourceLocation) const; + /// @returns a list of the contract names in the sources. + std::vector contractNames() const; + /// @returns either the contract's name or a mixture of its name and source file, sanitized for filesystem use std::string const filesystemFriendlyName(std::string const& _contractName) const; @@ -210,7 +213,6 @@ public: /// @returns the Contract Metadata std::string const& metadata(std::string const& _contractName) const; - void useMetadataLiteralSources(bool _metadataLiteralSources) { m_metadataLiteralSources = _metadataLiteralSources; } /// @returns a JSON representing the estimated gas usage for contract creation, internal and external functions Json::Value gasEstimates(std::string const& _contractName) const; From c7cb00d49a1ed17434f836c5b44cd1d38c4a735f Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 28 Aug 2017 16:31:26 +0100 Subject: [PATCH 040/162] Mark event non-payable and not view --- libsolidity/ast/Types.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 5e61cdee7..65b098408 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2030,7 +2030,9 @@ FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal } FunctionType::FunctionType(VariableDeclaration const& _varDecl): - m_kind(Kind::External), m_stateMutability(StateMutability::View), m_declaration(&_varDecl) + m_kind(Kind::External), + m_stateMutability(StateMutability::View), + m_declaration(&_varDecl) { TypePointers paramTypes; vector paramNames; @@ -2090,7 +2092,9 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl): } FunctionType::FunctionType(EventDefinition const& _event): - m_kind(Kind::Event), m_stateMutability(StateMutability::View), m_declaration(&_event) + m_kind(Kind::Event), + m_stateMutability(StateMutability::NonPayable), + m_declaration(&_event) { TypePointers params; vector paramNames; From aa94000a91be455af7b9436db172ffa86cc57494 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 28 Aug 2017 13:35:28 +0100 Subject: [PATCH 041/162] Mark all built in functions with appropriate statemutability --- libsolidity/analysis/GlobalContext.cpp | 20 ++++++++++---------- libsolidity/analysis/TypeChecker.cpp | 5 ++++- libsolidity/ast/Types.cpp | 4 +++- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp index a54b8c8d4..62dbd3943 100644 --- a/libsolidity/analysis/GlobalContext.cpp +++ b/libsolidity/analysis/GlobalContext.cpp @@ -43,13 +43,13 @@ m_magicVariables(vector>{make_shared< make_shared("selfdestruct", make_shared(strings{"address"}, strings{}, FunctionType::Kind::Selfdestruct)), make_shared("addmod", - make_shared(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Kind::AddMod)), + make_shared(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Kind::AddMod, false, StateMutability::Pure)), make_shared("mulmod", - make_shared(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Kind::MulMod)), + make_shared(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Kind::MulMod, false, StateMutability::Pure)), make_shared("sha3", - make_shared(strings(), strings{"bytes32"}, FunctionType::Kind::SHA3, true)), + make_shared(strings(), strings{"bytes32"}, FunctionType::Kind::SHA3, true, StateMutability::Pure)), make_shared("keccak256", - make_shared(strings(), strings{"bytes32"}, FunctionType::Kind::SHA3, true)), + make_shared(strings(), strings{"bytes32"}, FunctionType::Kind::SHA3, true, StateMutability::Pure)), make_shared("log0", make_shared(strings{"bytes32"}, strings{}, FunctionType::Kind::Log0)), make_shared("log1", @@ -61,17 +61,17 @@ m_magicVariables(vector>{make_shared< make_shared("log4", make_shared(strings{"bytes32", "bytes32", "bytes32", "bytes32", "bytes32"}, strings{}, FunctionType::Kind::Log4)), make_shared("sha256", - make_shared(strings(), strings{"bytes32"}, FunctionType::Kind::SHA256, true)), + make_shared(strings(), strings{"bytes32"}, FunctionType::Kind::SHA256, true, StateMutability::Pure)), make_shared("ecrecover", - make_shared(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Kind::ECRecover)), + make_shared(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Kind::ECRecover, false, StateMutability::Pure)), make_shared("ripemd160", - make_shared(strings(), strings{"bytes20"}, FunctionType::Kind::RIPEMD160, true)), + make_shared(strings(), strings{"bytes20"}, FunctionType::Kind::RIPEMD160, true, StateMutability::Pure)), make_shared("assert", - make_shared(strings{"bool"}, strings{}, FunctionType::Kind::Assert)), + make_shared(strings{"bool"}, strings{}, FunctionType::Kind::Assert, false, StateMutability::Pure)), make_shared("require", - make_shared(strings{"bool"}, strings{}, FunctionType::Kind::Require)), + make_shared(strings{"bool"}, strings{}, FunctionType::Kind::Require, false, StateMutability::Pure)), make_shared("revert", - make_shared(strings(), strings(), FunctionType::Kind::Revert))}) + make_shared(strings(), strings(), FunctionType::Kind::Revert, false, StateMutability::Pure))}) { } diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index d594a0602..c70b0497b 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1626,7 +1626,10 @@ void TypeChecker::endVisit(NewExpression const& _newExpression) TypePointers{type}, strings(), strings(), - FunctionType::Kind::ObjectCreation + FunctionType::Kind::ObjectCreation, + false, + nullptr, + StateMutability::Pure ); _newExpression.annotation().isPure = true; } diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 65b098408..4bffd9a51 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2569,6 +2569,8 @@ u256 FunctionType::externalIdentifier() const bool FunctionType::isPure() const { + // FIXME: replace this with m_stateMutability == StateMutability::Pure once + // the callgraph analyzer is in place return m_kind == Kind::SHA3 || m_kind == Kind::ECRecover || @@ -2865,7 +2867,7 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const return MemberList::MemberMap({ {"coinbase", make_shared(0, IntegerType::Modifier::Address)}, {"timestamp", make_shared(256)}, - {"blockhash", make_shared(strings{"uint"}, strings{"bytes32"}, FunctionType::Kind::BlockHash)}, + {"blockhash", make_shared(strings{"uint"}, strings{"bytes32"}, FunctionType::Kind::BlockHash, false, StateMutability::View)}, {"difficulty", make_shared(256)}, {"number", make_shared(256)}, {"gaslimit", make_shared(256)} From 79e84a8fa43b55838100f9c24dec04b1e721c65c Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 28 Aug 2017 14:40:28 +0100 Subject: [PATCH 042/162] Swap declaration/statemutability in FunctionType constructor --- libsolidity/analysis/TypeChecker.cpp | 1 - libsolidity/ast/Types.cpp | 13 ++++++------- libsolidity/ast/Types.h | 3 +-- libsolidity/codegen/ExpressionCompiler.cpp | 2 +- 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index c70b0497b..7d5a0c8ce 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1628,7 +1628,6 @@ void TypeChecker::endVisit(NewExpression const& _newExpression) strings(), FunctionType::Kind::ObjectCreation, false, - nullptr, StateMutability::Pure ); _newExpression.annotation().isPure = true; diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 4bffd9a51..22751c458 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2164,7 +2164,6 @@ FunctionTypePointer FunctionType::newExpressionType(ContractDefinition const& _c strings{""}, Kind::Creation, false, - nullptr, stateMutability ); } @@ -2416,8 +2415,8 @@ FunctionTypePointer FunctionType::interfaceFunctionType() const m_returnParameterNames, m_kind, m_arbitraryParameters, - m_declaration, - m_stateMutability + m_stateMutability, + m_declaration ); } @@ -2444,8 +2443,8 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con strings(), Kind::SetValue, false, - nullptr, StateMutability::NonPayable, + nullptr, m_gasSet, m_valueSet ) @@ -2461,8 +2460,8 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con strings(), Kind::SetGas, false, - nullptr, StateMutability::NonPayable, + nullptr, m_gasSet, m_valueSet ) @@ -2599,8 +2598,8 @@ TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) con m_returnParameterNames, m_kind, m_arbitraryParameters, - m_declaration, m_stateMutability, + m_declaration, m_gasSet || _setGas, m_valueSet || _setValue, m_bound @@ -2648,8 +2647,8 @@ FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary, bool _bound) m_returnParameterNames, kind, m_arbitraryParameters, - m_declaration, m_stateMutability, + m_declaration, m_gasSet, m_valueSet, _bound diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 310c34fea..de6dcee9a 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -899,7 +899,6 @@ public: strings(), _kind, _arbitraryParameters, - nullptr, _stateMutability ) { @@ -916,8 +915,8 @@ public: strings _returnParameterNames = strings(), Kind _kind = Kind::Internal, bool _arbitraryParameters = false, - Declaration const* _declaration = nullptr, StateMutability _stateMutability = StateMutability::NonPayable, + Declaration const* _declaration = nullptr, bool _gasSet = false, bool _valueSet = false, bool _bound = false diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 45c2170c2..5c9b743ac 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -644,8 +644,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) strings(), FunctionType::Kind::BareCall, false, - nullptr, StateMutability::NonPayable, + nullptr, true, true ), From 0e11e5af10de880340a005f63f778909c8664c90 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 25 Aug 2017 10:41:48 +0100 Subject: [PATCH 043/162] Include all overloaded events in ABI --- Changelog.md | 2 +- libsolidity/ast/AST.cpp | 12 ++++++++++-- test/libsolidity/SolidityABIJSON.cpp | 20 ++++++++++++++++++++ 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index 498553698..4af4419d7 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,9 +5,9 @@ Features: * Type Checker: Warn on using literals as tight packing parameters in ``keccak256``, ``sha3``, ``sha256`` and ``ripemd160``. Bugfixes: + * ABI JSON: Include all overloaded events. * Parser: Crash fix related to parseTypeName. - ### 0.4.16 (2017-08-24) Features: diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 2e4ae72ac..a805322b8 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -176,11 +176,19 @@ vector const& ContractDefinition::interfaceEvents() cons m_interfaceEvents.reset(new vector()); for (ContractDefinition const* contract: annotation().linearizedBaseContracts) for (EventDefinition const* e: contract->events()) - if (eventsSeen.count(e->name()) == 0) + { + /// NOTE: this requires the "internal" version of an Event, + /// though here internal strictly refers to visibility, + /// and not to function encoding (jump vs. call) + auto const& function = e->functionType(true); + solAssert(function, ""); + string eventSignature = function->externalSignature(); + if (eventsSeen.count(eventSignature) == 0) { - eventsSeen.insert(e->name()); + eventsSeen.insert(eventSignature); m_interfaceEvents->push_back(e); } + } } return *m_interfaceEvents; } diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp index 0512ba1fd..4b9223de3 100644 --- a/test/libsolidity/SolidityABIJSON.cpp +++ b/test/libsolidity/SolidityABIJSON.cpp @@ -423,6 +423,8 @@ BOOST_AUTO_TEST_CASE(events) function f(uint a) returns(uint d) { return a * 7; } event e1(uint b, address indexed c); event e2(); + event e2(uint a); + event e3() anonymous; } )"; char const* interface = R"([ @@ -467,6 +469,24 @@ BOOST_AUTO_TEST_CASE(events) "type": "event", "anonymous": false, "inputs": [] + }, + { + "name": "e2", + "type": "event", + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "a", + "type": "uint256" + } + ] + }, + { + "name": "e3", + "type": "event", + "anonymous": true, + "inputs": [] } ])"; From 7da86daa17a1a3086161de08e71cc26c7ae8bcfc Mon Sep 17 00:00:00 2001 From: gcolvin Date: Mon, 28 Aug 2017 12:34:30 -0600 Subject: [PATCH 044/162] Reconcile EVM 1.5 instruction numbers with working draft EIP615 --- libevmasm/Instruction.h | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/libevmasm/Instruction.h b/libevmasm/Instruction.h index 89a25fb70..afbef71d0 100644 --- a/libevmasm/Instruction.h +++ b/libevmasm/Instruction.h @@ -87,13 +87,6 @@ enum class Instruction: uint8_t DIFFICULTY, ///< get the block's difficulty GASLIMIT, ///< get the block's gas limit - JUMPTO = 0x4a, ///< alter the program counter to a jumpdest -- not part of Instructions.cpp - JUMPIF, ///< conditionally alter the program counter -- not part of Instructions.cpp - JUMPV, ///< alter the program counter to a jumpdest -- not part of Instructions.cpp - JUMPSUB, ///< alter the program counter to a beginsub -- not part of Instructions.cpp - JUMPSUBV, ///< alter the program counter to a beginsub -- not part of Instructions.cpp - RETURNSUB, ///< return to subroutine jumped from -- not part of Instructions.cpp - POP = 0x50, ///< remove item from stack MLOAD, ///< load word from memory MSTORE, ///< save word to memory @@ -106,8 +99,6 @@ enum class Instruction: uint8_t MSIZE, ///< get the size of active memory GAS, ///< get the amount of available gas JUMPDEST, ///< set a potential jump destination - BEGINSUB, ///< set a potential jumpsub destination -- not part of Instructions.cpp - BEGINDATA, ///< begine the data section -- not part of Instructions.cpp PUSH1 = 0x60, ///< place 1 byte item on stack PUSH2, ///< place 2 byte item on stack @@ -182,6 +173,17 @@ enum class Instruction: uint8_t LOG3, ///< Makes a log entry; 3 topics. LOG4, ///< Makes a log entry; 4 topics. + JUMPTO = 0xb0, ///< alter the program counter to a jumpdest -- not part of Instructions.cpp + JUMPIF, ///< conditionally alter the program counter -- not part of Instructions.cpp + JUMPV, ///< alter the program counter to a jumpdest -- not part of Instructions.cpp + JUMPSUB, ///< alter the program counter to a beginsub -- not part of Instructions.cpp + JUMPSUBV, ///< alter the program counter to a beginsub -- not part of Instructions.cpp + BEGINSUB, ///< set a potential jumpsub destination -- not part of Instructions.cpp + BEGINDATA, ///< begin the data section -- not part of Instructions.cpp + RETURNSUB, ///< return to subroutine jumped from -- not part of Instructions.cpp + PUTLOCAL, ///< pop top of stack to local variable -- not part of Instructions.cpp + GETLOCAL, ///< push local variable to top of stack -- not part of Instructions.cpp + CREATE = 0xf0, ///< create a new account with associated code CALL, ///< message-call into an account CALLCODE, ///< message-call with another account's code only From 7fb4a64136af18f2b61aef71ecc33ccbbc790ec6 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 29 Aug 2017 01:01:47 +0100 Subject: [PATCH 045/162] Move all file specific globals to anonymous namespace --- libdevcore/CommonIO.cpp | 5 +++++ libdevcore/SwarmHash.cpp | 4 ++++ libevmasm/PeepholeOptimiser.cpp | 5 +++++ libsolidity/ast/Types.cpp | 5 +++++ libsolidity/codegen/ContractCompiler.cpp | 5 +++++ lllc/main.cpp | 6 +++--- test/ExecutionFramework.cpp | 6 ++++-- test/fuzzer.cpp | 5 +++++ 8 files changed, 36 insertions(+), 5 deletions(-) diff --git a/libdevcore/CommonIO.cpp b/libdevcore/CommonIO.cpp index 528294557..5d47937b0 100644 --- a/libdevcore/CommonIO.cpp +++ b/libdevcore/CommonIO.cpp @@ -35,6 +35,9 @@ using namespace std; using namespace dev; +namespace +{ + template inline _T contentsGeneric(std::string const& _file) { @@ -56,6 +59,8 @@ inline _T contentsGeneric(std::string const& _file) return ret; } +} + string dev::contentsString(string const& _file) { return contentsGeneric(_file); diff --git a/libdevcore/SwarmHash.cpp b/libdevcore/SwarmHash.cpp index 781886681..1c718200e 100644 --- a/libdevcore/SwarmHash.cpp +++ b/libdevcore/SwarmHash.cpp @@ -24,6 +24,8 @@ using namespace std; using namespace dev; +namespace +{ bytes toLittleEndian(size_t _size) { @@ -59,6 +61,8 @@ h256 swarmHashIntermediate(string const& _input, size_t _offset, size_t _length) return swarmHashSimple(ref, _length); } +} + h256 dev::swarmHash(string const& _input) { return swarmHashIntermediate(_input, 0, _input.size()); diff --git a/libevmasm/PeepholeOptimiser.cpp b/libevmasm/PeepholeOptimiser.cpp index e94a8ba4c..31fdd3176 100644 --- a/libevmasm/PeepholeOptimiser.cpp +++ b/libevmasm/PeepholeOptimiser.cpp @@ -30,6 +30,9 @@ using namespace dev; // TODO: Extend this to use the tools from ExpressionClasses.cpp +namespace +{ + struct OptimiserState { AssemblyItems const& items; @@ -246,6 +249,8 @@ void applyMethods(OptimiserState& _state, Method, OtherMethods... _other) applyMethods(_state, _other...); } +} + bool PeepholeOptimiser::optimise() { OptimiserState state {m_items, 0, std::back_inserter(m_optimisedItems)}; diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 65b098408..15475034d 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -304,6 +304,9 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ContractDefinition return members; } +namespace +{ + bool isValidShiftAndAmountType(Token::Value _operator, Type const& _shiftAmountType) { // Disable >>> here. @@ -317,6 +320,8 @@ bool isValidShiftAndAmountType(Token::Value _operator, Type const& _shiftAmountT return false; } +} + IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier): m_bits(_bits), m_modifier(_modifier) { diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index e53f1b94e..24d3d959f 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -39,6 +39,9 @@ using namespace std; using namespace dev; using namespace dev::solidity; +namespace +{ + /** * Simple helper class to ensure that the stack height is the same at certain places in the code. */ @@ -53,6 +56,8 @@ private: unsigned stackHeight; }; +} + void ContractCompiler::compileContract( ContractDefinition const& _contract, std::map const& _contracts diff --git a/lllc/main.cpp b/lllc/main.cpp index adf181c7d..06a0fc81b 100644 --- a/lllc/main.cpp +++ b/lllc/main.cpp @@ -39,7 +39,7 @@ static string const VersionString = (string(SOL_VERSION_PRERELEASE).empty() ? "" : "-" + string(SOL_VERSION_PRERELEASE)) + (string(SOL_VERSION_BUILDINFO).empty() ? "" : "+" + string(SOL_VERSION_BUILDINFO)); -void help() +static void help() { cout << "Usage lllc [OPTIONS] " << endl @@ -54,7 +54,7 @@ void help() exit(0); } -void version() +static void version() { cout << "LLLC, the Lovely Little Language Compiler " << endl; cout << "Version: " << VersionString << endl; @@ -74,7 +74,7 @@ specified default locale if it is valid, and if not then it will modify the environment the process is running in to use a sensible default. This also means that users do not need to install language packs for their OS. */ -void setDefaultOrCLocale() +static void setDefaultOrCLocale() { #if __unix__ if (!std::setlocale(LC_ALL, "")) diff --git a/test/ExecutionFramework.cpp b/test/ExecutionFramework.cpp index f4e5fcef5..b2de814a9 100644 --- a/test/ExecutionFramework.cpp +++ b/test/ExecutionFramework.cpp @@ -31,8 +31,8 @@ using namespace dev::test; namespace // anonymous { - h256 const EmptyTrie("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"); -} + +h256 const EmptyTrie("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"); string getIPCSocketPath() { @@ -43,6 +43,8 @@ string getIPCSocketPath() return ipcPath; } +} + ExecutionFramework::ExecutionFramework() : m_rpc(RPCSession::instance(getIPCSocketPath())), m_optimize(dev::test::Options::get().optimize), diff --git a/test/fuzzer.cpp b/test/fuzzer.cpp index cf99755f1..c3a321f7a 100644 --- a/test/fuzzer.cpp +++ b/test/fuzzer.cpp @@ -40,6 +40,9 @@ typedef void (*CStyleReadFileCallback)(char const* _path, char** o_contents, cha extern char const* compileStandard(char const* _input, CStyleReadFileCallback _readCallback); } +namespace +{ + bool quiet = false; string contains(string const& _haystack, vector const& _needles) @@ -169,6 +172,8 @@ void testCompiler() } } +} + int main(int argc, char** argv) { po::options_description options( From 4136ff9a5997fca89ec5305d904d6b07f3d3e2f5 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 28 Aug 2017 20:40:38 +0200 Subject: [PATCH 046/162] Extract base from NameAndType and use compiler stack. --- test/libsolidity/AnalysisFramework.cpp | 127 +++++++++ test/libsolidity/AnalysisFramework.h | 113 ++++++++ .../SolidityNameAndTypeResolution.cpp | 258 +++--------------- 3 files changed, 275 insertions(+), 223 deletions(-) create mode 100644 test/libsolidity/AnalysisFramework.cpp create mode 100644 test/libsolidity/AnalysisFramework.h diff --git a/test/libsolidity/AnalysisFramework.cpp b/test/libsolidity/AnalysisFramework.cpp new file mode 100644 index 000000000..173d46bc1 --- /dev/null +++ b/test/libsolidity/AnalysisFramework.cpp @@ -0,0 +1,127 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * Framework for testing features from the analysis phase of compiler. + */ + +#include + +#include +#include + +#include + +#include + +#include + +using namespace std; +using namespace dev; +using namespace dev::solidity; +using namespace dev::solidity::test; + +pair> +AnalysisFramework::parseAnalyseAndReturnError( + string const& _source, + bool _reportWarnings, + bool _insertVersionPragma, + bool _allowMultipleErrors +) +{ + m_compiler.reset(); + m_compiler.addSource("", _insertVersionPragma ? "pragma solidity >=0.0;\n" + _source : _source); + if (!m_compiler.parse()) + { + printErrors(); + BOOST_ERROR("Parsing contract failed in analysis test suite."); + } + + m_compiler.analyze(); + + std::shared_ptr firstError; + for (auto const& currentError: m_compiler.errors()) + { + solAssert(currentError->comment(), ""); + if (currentError->comment()->find("This is a pre-release compiler version") == 0) + continue; + + if (_reportWarnings == (currentError->type() == Error::Type::Warning)) + { + if (firstError && !_allowMultipleErrors) + { + printErrors(); + BOOST_FAIL("Multiple errors found."); + } + if (!firstError) + firstError = currentError; + } + } + + return make_pair(&m_compiler.ast(), firstError); +} + +SourceUnit const* AnalysisFramework::parseAndAnalyse(string const& _source) +{ + auto sourceAndError = parseAnalyseAndReturnError(_source); + BOOST_REQUIRE(!!sourceAndError.first); + BOOST_REQUIRE(!sourceAndError.second); + return sourceAndError.first; +} + +bool AnalysisFramework::success(string const& _source) +{ + return !parseAnalyseAndReturnError(_source).second; +} + +Error AnalysisFramework::expectError(std::string const& _source, bool _warning, bool _allowMultiple) +{ + auto sourceAndError = parseAnalyseAndReturnError(_source, _warning, true, _allowMultiple); + BOOST_REQUIRE(!!sourceAndError.second); + BOOST_REQUIRE(!!sourceAndError.first); + return *sourceAndError.second; +} + +void AnalysisFramework::printErrors() +{ + for (auto const& error: m_compiler.errors()) + SourceReferenceFormatter::printExceptionInformation( + std::cerr, + *error, + (error->type() == Error::Type::Warning) ? "Warning" : "Error", + [&](std::string const& _sourceName) -> solidity::Scanner const& { return m_compiler.scanner(_sourceName); } + ); +} + +ContractDefinition const* AnalysisFramework::retrieveContract(SourceUnit const& _source, unsigned index) +{ + ContractDefinition* contract = nullptr; + unsigned counter = 0; + for (shared_ptr const& node: _source.nodes()) + if ((contract = dynamic_cast(node.get())) && counter == index) + return contract; + + return nullptr; +} + +FunctionTypePointer AnalysisFramework::retrieveFunctionBySignature( + ContractDefinition const& _contract, + std::string const& _signature +) +{ + FixedHash<4> hash(dev::keccak256(_signature)); + return _contract.interfaceFunctions()[hash]; +} diff --git a/test/libsolidity/AnalysisFramework.h b/test/libsolidity/AnalysisFramework.h new file mode 100644 index 000000000..0cdedaea4 --- /dev/null +++ b/test/libsolidity/AnalysisFramework.h @@ -0,0 +1,113 @@ +/* + 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 . +*/ +/** + * Framework for testing features from the analysis phase of compiler. + */ + +#pragma once + +#include + +#include + +#include +#include +#include + +namespace dev +{ +namespace solidity +{ + +class Type; +class FunctionType; +using TypePointer = std::shared_ptr; +using FunctionTypePointer = std::shared_ptr; + +namespace test +{ + +class AnalysisFramework +{ + +protected: + std::pair> + parseAnalyseAndReturnError( + std::string const& _source, + bool _reportWarnings = false, + bool _insertVersionPragma = true, + bool _allowMultipleErrors = false + ); + + SourceUnit const* parseAndAnalyse(std::string const& _source); + bool success(std::string const& _source); + Error expectError(std::string const& _source, bool _warning = false, bool _allowMultiple = false); + + void printErrors(); + + ContractDefinition const* retrieveContract(SourceUnit const& _source, unsigned index); + FunctionTypePointer retrieveFunctionBySignature( + ContractDefinition const& _contract, + std::string const& _signature + ); + + dev::solidity::CompilerStack m_compiler; +}; + + +#define CHECK_ERROR_OR_WARNING(text, typ, substring, warning, allowMulti) \ +do \ +{ \ + Error err = expectError((text), (warning), (allowMulti)); \ + BOOST_CHECK(err.type() == (Error::Type::typ)); \ + BOOST_CHECK(searchErrorMessage(err, (substring))); \ +} while(0) + +// [checkError(text, type, substring)] asserts that the compilation down to typechecking +// emits an error of type [type] and with a message containing [substring]. +#define CHECK_ERROR(text, type, substring) \ +CHECK_ERROR_OR_WARNING(text, type, substring, false, false) + +// [checkError(text, type, substring)] asserts that the compilation down to typechecking +// emits an error of type [type] and with a message containing [substring]. +#define CHECK_ERROR_ALLOW_MULTI(text, type, substring) \ +CHECK_ERROR_OR_WARNING(text, type, substring, false, true) + +// [checkWarning(text, substring)] asserts that the compilation down to typechecking +// emits a warning and with a message containing [substring]. +#define CHECK_WARNING(text, substring) \ +CHECK_ERROR_OR_WARNING(text, Warning, substring, true, false) + +// [checkWarningAllowMulti(text, substring)] aserts that the compilation down to typechecking +// emits a warning and with a message containing [substring]. +#define CHECK_WARNING_ALLOW_MULTI(text, substring) \ +CHECK_ERROR_OR_WARNING(text, Warning, substring, true, true) + +// [checkSuccess(text)] asserts that the compilation down to typechecking succeeds. +#define CHECK_SUCCESS(text) do { BOOST_CHECK(success((text))); } while(0) + +#define CHECK_SUCCESS_NO_WARNINGS(text) \ +do \ +{ \ + auto sourceAndError = parseAnalyseAndReturnError((text), true); \ + BOOST_CHECK(sourceAndError.second == nullptr); \ +} \ +while(0) + +} +} +} diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index cf55ffac8..a05371035 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -20,22 +20,14 @@ * Unit tests for the name and type resolution of the solidity parser. */ -#include +#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include #include +#include + #include using namespace std; @@ -47,187 +39,7 @@ namespace solidity namespace test { -namespace -{ - -pair, std::shared_ptr> -parseAnalyseAndReturnError(string const& _source, bool _reportWarnings = false, bool _insertVersionPragma = true, bool _allowMultipleErrors = false) -{ - // Silence compiler version warning - string source = _insertVersionPragma ? "pragma solidity >=0.0;\n" + _source : _source; - ErrorList errors; - ErrorReporter errorReporter(errors); - Parser parser(errorReporter); - ASTPointer sourceUnit; - // catch exceptions for a transition period - try - { - sourceUnit = parser.parse(std::make_shared(CharStream(source))); - if(!sourceUnit) - BOOST_FAIL("Parsing failed in type checker test."); - - SyntaxChecker syntaxChecker(errorReporter); - if (!syntaxChecker.checkSyntax(*sourceUnit)) - return make_pair(sourceUnit, errorReporter.errors().at(0)); - - std::shared_ptr globalContext = make_shared(); - map> scopes; - NameAndTypeResolver resolver(globalContext->declarations(), scopes, errorReporter); - solAssert(Error::containsOnlyWarnings(errorReporter.errors()), ""); - resolver.registerDeclarations(*sourceUnit); - - bool success = true; - for (ASTPointer const& node: sourceUnit->nodes()) - if (ContractDefinition* contract = dynamic_cast(node.get())) - { - globalContext->setCurrentContract(*contract); - resolver.updateDeclaration(*globalContext->currentThis()); - resolver.updateDeclaration(*globalContext->currentSuper()); - if (!resolver.resolveNamesAndTypes(*contract)) - success = false; - } - if (success) - { - TypeChecker typeChecker(errorReporter); - for (ASTPointer const& node: sourceUnit->nodes()) - if (ContractDefinition* contract = dynamic_cast(node.get())) - { - bool success = typeChecker.checkTypeRequirements(*contract); - BOOST_CHECK(success || !errorReporter.errors().empty()); - } - } - if (success) - if (!PostTypeChecker(errorReporter).check(*sourceUnit)) - success = false; - if (success) - if (!StaticAnalyzer(errorReporter).analyze(*sourceUnit)) - success = false; - std::shared_ptr error; - for (auto const& currentError: errorReporter.errors()) - { - if ( - (_reportWarnings && currentError->type() == Error::Type::Warning) || - (!_reportWarnings && currentError->type() != Error::Type::Warning) - ) - { - if (error && !_allowMultipleErrors) - { - string message("Multiple errors found: "); - for (auto const& e: errorReporter.errors()) - if (string const* description = boost::get_error_info(*e)) - message += *description + ", "; - - BOOST_FAIL(message); - } - if (!error) - error = currentError; - } - } - if (error) - return make_pair(sourceUnit, error); - } - catch (InternalCompilerError const& _e) - { - string message("Internal compiler error"); - if (string const* description = boost::get_error_info(_e)) - message += ": " + *description; - BOOST_FAIL(message); - } - catch (Error const& _e) - { - return make_pair(sourceUnit, std::make_shared(_e)); - } - catch (...) - { - BOOST_FAIL("Unexpected exception."); - } - return make_pair(sourceUnit, nullptr); -} - -ASTPointer parseAndAnalyse(string const& _source) -{ - auto sourceAndError = parseAnalyseAndReturnError(_source); - BOOST_REQUIRE(!!sourceAndError.first); - BOOST_REQUIRE(!sourceAndError.second); - return sourceAndError.first; -} - -bool success(string const& _source) -{ - return !parseAnalyseAndReturnError(_source).second; -} - -Error expectError(std::string const& _source, bool _warning = false, bool _allowMultiple = false) -{ - auto sourceAndError = parseAnalyseAndReturnError(_source, _warning, true, _allowMultiple); - BOOST_REQUIRE(!!sourceAndError.second); - BOOST_REQUIRE(!!sourceAndError.first); - return *sourceAndError.second; -} - -static ContractDefinition const* retrieveContract(ASTPointer _source, unsigned index) -{ - ContractDefinition* contract; - unsigned counter = 0; - for (ASTPointer const& node: _source->nodes()) - if ((contract = dynamic_cast(node.get())) && counter == index) - return contract; - - return nullptr; -} - -static FunctionTypePointer retrieveFunctionBySignature( - ContractDefinition const& _contract, - std::string const& _signature -) -{ - FixedHash<4> hash(dev::keccak256(_signature)); - return _contract.interfaceFunctions()[hash]; -} - -} - -#define CHECK_ERROR_OR_WARNING(text, typ, substring, warning, allowMulti) \ -do \ -{ \ - Error err = expectError((text), (warning), (allowMulti)); \ - BOOST_CHECK(err.type() == (Error::Type::typ)); \ - BOOST_CHECK(searchErrorMessage(err, (substring))); \ -} while(0) - -// [checkError(text, type, substring)] asserts that the compilation down to typechecking -// emits an error of type [type] and with a message containing [substring]. -#define CHECK_ERROR(text, type, substring) \ -CHECK_ERROR_OR_WARNING(text, type, substring, false, false) - -// [checkError(text, type, substring)] asserts that the compilation down to typechecking -// emits an error of type [type] and with a message containing [substring]. -#define CHECK_ERROR_ALLOW_MULTI(text, type, substring) \ -CHECK_ERROR_OR_WARNING(text, type, substring, false, true) - -// [checkWarning(text, substring)] asserts that the compilation down to typechecking -// emits a warning and with a message containing [substring]. -#define CHECK_WARNING(text, substring) \ -CHECK_ERROR_OR_WARNING(text, Warning, substring, true, false) - -// [checkWarningAllowMulti(text, substring)] aserts that the compilation down to typechecking -// emits a warning and with a message containing [substring]. -#define CHECK_WARNING_ALLOW_MULTI(text, substring) \ -CHECK_ERROR_OR_WARNING(text, Warning, substring, true, true) - -// [checkSuccess(text)] asserts that the compilation down to typechecking succeeds. -#define CHECK_SUCCESS(text) do { BOOST_CHECK(success((text))); } while(0) - -#define CHECK_SUCCESS_NO_WARNINGS(text) \ -do \ -{ \ - auto sourceAndError = parseAnalyseAndReturnError((text), true); \ - BOOST_CHECK(sourceAndError.second == nullptr); \ -} \ -while(0) - - -BOOST_AUTO_TEST_SUITE(SolidityNameAndTypeResolution) +BOOST_FIXTURE_TEST_SUITE(SolidityNameAndTypeResolution, AnalysisFramework) BOOST_AUTO_TEST_CASE(smoke_test) { @@ -613,13 +425,13 @@ BOOST_AUTO_TEST_CASE(comparison_of_mapping_types) BOOST_AUTO_TEST_CASE(function_no_implementation) { - ASTPointer sourceUnit; + SourceUnit const* sourceUnit = nullptr; char const* text = R"( contract test { function functionName(bytes32 input) returns (bytes32 out); } )"; - ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseAndAnalyse(text), "Parsing and name Resolving failed"); + sourceUnit = parseAndAnalyse(text); std::vector> nodes = sourceUnit->nodes(); ContractDefinition* contract = dynamic_cast(nodes[1].get()); BOOST_REQUIRE(contract); @@ -629,12 +441,12 @@ BOOST_AUTO_TEST_CASE(function_no_implementation) BOOST_AUTO_TEST_CASE(abstract_contract) { - ASTPointer sourceUnit; + SourceUnit const* sourceUnit = nullptr; char const* text = R"( contract base { function foo(); } contract derived is base { function foo() {} } )"; - ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseAndAnalyse(text), "Parsing and name Resolving failed"); + sourceUnit = parseAndAnalyse(text); std::vector> nodes = sourceUnit->nodes(); ContractDefinition* base = dynamic_cast(nodes[1].get()); ContractDefinition* derived = dynamic_cast(nodes[2].get()); @@ -648,12 +460,12 @@ BOOST_AUTO_TEST_CASE(abstract_contract) BOOST_AUTO_TEST_CASE(abstract_contract_with_overload) { - ASTPointer sourceUnit; + SourceUnit const* sourceUnit = nullptr; char const* text = R"( contract base { function foo(bool); } contract derived is base { function foo(uint) {} } )"; - ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseAndAnalyse(text), "Parsing and name Resolving failed"); + sourceUnit = parseAndAnalyse(text); std::vector> nodes = sourceUnit->nodes(); ContractDefinition* base = dynamic_cast(nodes[1].get()); ContractDefinition* derived = dynamic_cast(nodes[2].get()); @@ -665,7 +477,6 @@ BOOST_AUTO_TEST_CASE(abstract_contract_with_overload) BOOST_AUTO_TEST_CASE(create_abstract_contract) { - ASTPointer sourceUnit; char const* text = R"( contract base { function foo(); } contract derived { @@ -678,7 +489,6 @@ BOOST_AUTO_TEST_CASE(create_abstract_contract) BOOST_AUTO_TEST_CASE(redeclare_implemented_abstract_function_as_abstract) { - ASTPointer sourceUnit; char const* text = R"( contract base { function foo(); } contract derived is base { function foo() {} } @@ -689,12 +499,12 @@ BOOST_AUTO_TEST_CASE(redeclare_implemented_abstract_function_as_abstract) BOOST_AUTO_TEST_CASE(implement_abstract_via_constructor) { - ASTPointer sourceUnit; + SourceUnit const* sourceUnit = nullptr; char const* text = R"( contract base { function foo(); } contract foo is base { function foo() {} } )"; - ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseAndAnalyse(text), "Parsing and name resolving failed"); + sourceUnit = parseAndAnalyse(text); std::vector> nodes = sourceUnit->nodes(); BOOST_CHECK_EQUAL(nodes.size(), 3); ContractDefinition* derived = dynamic_cast(nodes[2].get()); @@ -704,7 +514,7 @@ BOOST_AUTO_TEST_CASE(implement_abstract_via_constructor) BOOST_AUTO_TEST_CASE(function_canonical_signature) { - ASTPointer sourceUnit; + SourceUnit const* sourceUnit = nullptr; char const* text = R"( contract Test { function foo(uint256 arg1, uint64 arg2, bool arg3) returns (uint256 ret) { @@ -712,7 +522,7 @@ BOOST_AUTO_TEST_CASE(function_canonical_signature) } } )"; - ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseAndAnalyse(text), "Parsing and name Resolving failed"); + sourceUnit = parseAndAnalyse(text); for (ASTPointer const& node: sourceUnit->nodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) { @@ -723,7 +533,7 @@ BOOST_AUTO_TEST_CASE(function_canonical_signature) BOOST_AUTO_TEST_CASE(function_canonical_signature_type_aliases) { - ASTPointer sourceUnit; + SourceUnit const* sourceUnit = nullptr; char const* text = R"( contract Test { function boo(uint, bytes32, address) returns (uint ret) { @@ -731,7 +541,7 @@ BOOST_AUTO_TEST_CASE(function_canonical_signature_type_aliases) } } )"; - ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseAndAnalyse(text), "Parsing and name Resolving failed"); + sourceUnit = parseAndAnalyse(text); for (ASTPointer const& node: sourceUnit->nodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) { @@ -744,7 +554,7 @@ BOOST_AUTO_TEST_CASE(function_canonical_signature_type_aliases) BOOST_AUTO_TEST_CASE(function_external_types) { - ASTPointer sourceUnit; + SourceUnit const* sourceUnit = nullptr; char const* text = R"( contract C { uint a; @@ -755,7 +565,7 @@ BOOST_AUTO_TEST_CASE(function_external_types) } } )"; - ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseAndAnalyse(text), "Parsing and name Resolving failed"); + sourceUnit = parseAndAnalyse(text); for (ASTPointer const& node: sourceUnit->nodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) { @@ -769,7 +579,7 @@ BOOST_AUTO_TEST_CASE(function_external_types) BOOST_AUTO_TEST_CASE(enum_external_type) { // bug #1801 - ASTPointer sourceUnit; + SourceUnit const* sourceUnit = nullptr; char const* text = R"( contract Test { enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } @@ -778,7 +588,7 @@ BOOST_AUTO_TEST_CASE(enum_external_type) } } )"; - ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseAndAnalyse(text), "Parsing and name Resolving failed"); + sourceUnit = parseAndAnalyse(text); for (ASTPointer const& node: sourceUnit->nodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) { @@ -1163,10 +973,10 @@ BOOST_AUTO_TEST_CASE(state_variable_accessors) } )"; - ASTPointer source; + SourceUnit const* source; ContractDefinition const* contract; - ETH_TEST_CHECK_NO_THROW(source = parseAndAnalyse(text), "Parsing and Resolving names failed"); - BOOST_REQUIRE((contract = retrieveContract(source, 0)) != nullptr); + source = parseAndAnalyse(text); + BOOST_REQUIRE((contract = retrieveContract(*source, 0)) != nullptr); FunctionTypePointer function = retrieveFunctionBySignature(*contract, "foo()"); BOOST_REQUIRE(function && function->hasDeclaration()); auto returnParams = function->returnParameterTypes(); @@ -1217,10 +1027,9 @@ BOOST_AUTO_TEST_CASE(private_state_variable) } )"; - ASTPointer source; ContractDefinition const* contract; - ETH_TEST_CHECK_NO_THROW(source = parseAndAnalyse(text), "Parsing and Resolving names failed"); - BOOST_CHECK((contract = retrieveContract(source, 0)) != nullptr); + SourceUnit const* source = parseAndAnalyse(text); + BOOST_CHECK((contract = retrieveContract(*source, 0)) != nullptr); FunctionTypePointer function; function = retrieveFunctionBySignature(*contract, "foo()"); BOOST_CHECK_MESSAGE(function == nullptr, "Accessor function of a private variable should not exist"); @@ -1711,8 +1520,7 @@ BOOST_AUTO_TEST_CASE(overflow_caused_by_ether_units) uint256 a; } )"; - ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(sourceCodeFine), - "Parsing and Resolving names failed"); + CHECK_SUCCESS(sourceCodeFine); char const* sourceCode = R"( contract c { function c () { @@ -2091,7 +1899,7 @@ BOOST_AUTO_TEST_CASE(test_for_bug_override_function_with_bytearray_type) function f(bytes) external returns (uint256 r) {r = 42;} } )"; - ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(sourceCode), "Parsing and Name Resolving failed"); + CHECK_SUCCESS(sourceCode); } BOOST_AUTO_TEST_CASE(array_with_nonconstant_length) @@ -2311,7 +2119,7 @@ BOOST_AUTO_TEST_CASE(test_byte_is_alias_of_byte1) function f() { byte a = arr[0];} } )"; - ETH_TEST_REQUIRE_NO_THROW(parseAndAnalyse(text), "Type resolving failed"); + CHECK_SUCCESS(text); } BOOST_AUTO_TEST_CASE(warns_assigning_decimal_to_bytesxx) @@ -2498,7 +2306,7 @@ BOOST_AUTO_TEST_CASE(assignment_of_nonoverloaded_function) function g() returns(uint) { var x = f; return x(7); } } )"; - ETH_TEST_REQUIRE_NO_THROW(parseAndAnalyse(sourceCode), "Type resolving failed"); + CHECK_SUCCESS(sourceCode); } BOOST_AUTO_TEST_CASE(assignment_of_overloaded_function) @@ -4872,7 +4680,11 @@ BOOST_AUTO_TEST_CASE(unsatisfied_version) char const* text = R"( pragma solidity ^99.99.0; )"; - BOOST_CHECK(expectError(text, true).type() == Error::Type::SyntaxError); + auto sourceAndError = parseAnalyseAndReturnError(text, false, false, false); + BOOST_REQUIRE(!!sourceAndError.second); + BOOST_REQUIRE(!!sourceAndError.first); + BOOST_CHECK(sourceAndError.second->type() == Error::Type::SyntaxError); + BOOST_CHECK(searchErrorMessage(*sourceAndError.second, "Source file requires different compiler version")); } BOOST_AUTO_TEST_CASE(constant_constructor) From 3c24dcfe45f285a209a6f22ef2aff81b69049a89 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 29 Aug 2017 13:26:23 +0100 Subject: [PATCH 047/162] Document more of the codegen features --- libsolidity/codegen/Compiler.h | 5 +++++ libsolidity/codegen/CompilerContext.h | 9 ++++++++- libsolidity/codegen/CompilerUtils.h | 12 ++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/libsolidity/codegen/Compiler.h b/libsolidity/codegen/Compiler.h index 8c63ea9c5..c6ee93fbd 100644 --- a/libsolidity/codegen/Compiler.h +++ b/libsolidity/codegen/Compiler.h @@ -40,6 +40,8 @@ public: m_context(&m_runtimeContext) { } + /// Compiles a contract. + /// @arg _metadata contains the to be injected metadata CBOR void compileContract( ContractDefinition const& _contract, std::map const& _contracts, @@ -51,8 +53,11 @@ public: ContractDefinition const& _contract, std::map const& _contracts ); + /// @returns Entire assembly. eth::Assembly const& assembly() const { return m_context.assembly(); } + /// @returns The entire assembled object (with constructor). eth::LinkerObject assembledObject() const { return m_context.assembledObject(); } + /// @returns Only the runtime object (without constructor). eth::LinkerObject runtimeObject() const { return m_context.assembledRuntimeObject(m_runtimeSub); } /// @arg _sourceCodes is the map of input files to source code strings /// @arg _inJsonFromat shows whether the out should be in Json format diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index 96cbf6c11..3994b0101 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -56,7 +56,9 @@ public: m_runtimeSub = size_t(m_asm->newSub(m_runtimeContext->m_asm).data()); } + /// Update currently enabled set of experimental features. void setExperimentalFeatures(std::set const& _features) { m_experimentalFeatures = _features; } + /// @returns true if the given feature is enabled. bool experimentalFeatureActive(ExperimentalFeature _feature) const { return m_experimentalFeatures.count(_feature); } void addStateVariable(VariableDeclaration const& _declaration, u256 const& _storageOffset, unsigned _byteOffset); @@ -78,13 +80,15 @@ public: /// @returns the entry label of the given function. Might return an AssemblyItem of type /// UndefinedItem if it does not exist yet. eth::AssemblyItem functionEntryLabelIfExists(Declaration const& _declaration) const; - void setInheritanceHierarchy(std::vector const& _hierarchy) { m_inheritanceHierarchy = _hierarchy; } /// @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; } /// @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). @@ -155,6 +159,7 @@ public: /// Adds a subroutine to the code (in the data section) and pushes its size (via a tag) /// on the stack. @returns the pushsub assembly item. eth::AssemblyItem addSubroutine(eth::AssemblyPointer const& _assembly) { return m_asm->appendSubroutine(_assembly); } + /// Pushes the size of the subroutine. void pushSubroutineSize(size_t _subRoutine) { m_asm->pushSubroutineSize(_subRoutine); } /// Pushes the offset of the subroutine. void pushSubroutineOffset(size_t _subRoutine) { m_asm->pushSubroutineOffset(_subRoutine); } @@ -189,6 +194,7 @@ public: /// Appends arbitrary data to the end of the bytecode. void appendAuxiliaryData(bytes const& _data) { m_asm->appendAuxiliaryDataToEnd(_data); } + /// Run optimisation step. void optimise(bool _fullOptimsation, unsigned _runs = 200) { m_asm->optimise(_fullOptimsation, true, _runs); } /// @returns the runtime context if in creation mode and runtime context is set, nullptr otherwise. @@ -196,6 +202,7 @@ public: /// @returns the identifier of the runtime subroutine. size_t runtimeSub() const { return m_runtimeSub; } + /// @returns a const reference to the underlying assembly. eth::Assembly const& assembly() const { return *m_asm; } /// @returns non-const reference to the underlying assembly. Should be avoided in favour of /// wrappers in this class. diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index 18b702500..5e45699be 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -38,14 +38,20 @@ public: /// Stores the initial value of the free-memory-pointer at its position; void initialiseFreeMemoryPointer(); /// Copies the free memory pointer to the stack. + /// Stack pre: + /// Stack post: void fetchFreeMemoryPointer(); /// Stores the free memory pointer from the stack. + /// Stack pre: + /// Stack post: void storeFreeMemoryPointer(); /// Allocates a number of bytes in memory as given on the stack. /// Stack pre: /// Stack post: void allocateMemory(); /// Appends code that transforms memptr to (memptr - free_memptr) memptr + /// Stack pre: + /// Stack post: void toSizeAfterFreeMemoryPointer(); /// Loads data from memory to the stack. @@ -105,6 +111,8 @@ public: /// Special case of @a encodeToMemory which assumes that everything is padded to words /// and dynamic data is not copied in place (i.e. a proper ABI encoding). + /// Stack pre: ... + /// Stack post: void abiEncode( TypePointers const& _givenTypes, TypePointers const& _targetTypes, @@ -185,9 +193,13 @@ public: static unsigned sizeOnStack(std::vector> const& _variableTypes); /// Helper function to shift top value on the stack to the left. + /// Stack pre: + /// Stack post: void leftShiftNumberOnStack(unsigned _bits); /// Helper function to shift top value on the stack to the right. + /// Stack pre: + /// Stack post: void rightShiftNumberOnStack(unsigned _bits, bool _isSigned = false); /// Appends code that computes tha Keccak-256 hash of the topmost stack element of 32 byte type. From c86181787575d8cb18eebc824bf323b63b0b5484 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 29 Aug 2017 00:34:39 +0100 Subject: [PATCH 048/162] Add header for jsonCompiler --- solc/jsonCompiler.cpp | 10 +++----- solc/jsonCompiler.h | 42 +++++++++++++++++++++++++++++++ test/fuzzer.cpp | 8 +----- test/libsolidity/JSONCompiler.cpp | 11 +------- 4 files changed, 47 insertions(+), 24 deletions(-) create mode 100644 solc/jsonCompiler.h diff --git a/solc/jsonCompiler.cpp b/solc/jsonCompiler.cpp index 684d49e4b..7e797a62d 100644 --- a/solc/jsonCompiler.cpp +++ b/solc/jsonCompiler.cpp @@ -20,24 +20,20 @@ * JSON interface for the solidity compiler to be used from Javascript. */ -#include +#include #include #include #include #include +#include + #include "license.h" using namespace std; using namespace dev; using namespace solidity; -extern "C" { -/// Callback used to retrieve additional source files. "Returns" two pointers that should be -/// heap-allocated and are free'd by the caller. -typedef void (*CStyleReadFileCallback)(char const* _path, char** o_contents, char** o_error); -} - namespace { diff --git a/solc/jsonCompiler.h b/solc/jsonCompiler.h new file mode 100644 index 000000000..c392ce93a --- /dev/null +++ b/solc/jsonCompiler.h @@ -0,0 +1,42 @@ +/* + 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 . +*/ +/** + * @author Christian + * @date 2014 + * JSON interface for the solidity compiler to be used from Javascript. + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/// Callback used to retrieve additional source files. "Returns" two pointers that should be +/// heap-allocated and are free'd by the caller. +typedef void (*CStyleReadFileCallback)(char const* _path, char** o_contents, char** o_error); + +char const* license(); +char const* version(); +char const* compileJSON(char const* _input, bool _optimize); +char const* compileJSONMulti(char const* _input, bool _optimize); +char const* compileJSONCallback(char const* _input, bool _optimize, CStyleReadFileCallback _readCallback); +char const* compileStandard(char const* _input, CStyleReadFileCallback _readCallback); + +#ifdef __cplusplus +} +#endif diff --git a/test/fuzzer.cpp b/test/fuzzer.cpp index c3a321f7a..2c39dde27 100644 --- a/test/fuzzer.cpp +++ b/test/fuzzer.cpp @@ -20,6 +20,7 @@ #include #include +#include #include @@ -33,13 +34,6 @@ using namespace dev; using namespace dev::eth; namespace po = boost::program_options; -extern "C" -{ -extern char const* compileJSON(char const* _input, bool _optimize); -typedef void (*CStyleReadFileCallback)(char const* _path, char** o_contents, char** o_error); -extern char const* compileStandard(char const* _input, CStyleReadFileCallback _readCallback); -} - namespace { diff --git a/test/libsolidity/JSONCompiler.cpp b/test/libsolidity/JSONCompiler.cpp index 541cfbf0d..7dc4808bd 100644 --- a/test/libsolidity/JSONCompiler.cpp +++ b/test/libsolidity/JSONCompiler.cpp @@ -23,22 +23,13 @@ #include #include #include +#include #include "../Metadata.h" #include "../TestHelper.h" using namespace std; -extern "C" -{ -extern char const* version(); -extern char const* license(); -extern char const* compileJSON(char const* _input, bool _optimize); -extern char const* compileJSONMulti(char const* _input, bool _optimize); -extern char const* compileJSONCallback(char const* _input, bool _optimize, void* _readCallback); -extern char const* compileStandard(char const* _input, void* _readCallback); -} - namespace dev { namespace solidity From cd22da1d9e77a9a7b6fe3ecf5bd828977acc6282 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 25 Aug 2017 12:43:58 +0200 Subject: [PATCH 049/162] Remove escape function. --- libdevcore/CommonData.cpp | 28 ---------------------------- libsolidity/ast/ASTPrinter.cpp | 14 +++++++++++--- 2 files changed, 11 insertions(+), 31 deletions(-) diff --git a/libdevcore/CommonData.cpp b/libdevcore/CommonData.cpp index 14caf4947..db11e61c6 100644 --- a/libdevcore/CommonData.cpp +++ b/libdevcore/CommonData.cpp @@ -28,34 +28,6 @@ using namespace std; using namespace dev; -std::string dev::escaped(std::string const& _s, bool _all) -{ - static const map prettyEscapes{{'\r', 'r'}, {'\n', 'n'}, {'\t', 't'}, {'\v', 'v'}}; - std::string ret; - ret.reserve(_s.size() + 2); - ret.push_back('"'); - for (auto i: _s) - if (i == '"' && !_all) - ret += "\\\""; - else if (i == '\\' && !_all) - ret += "\\\\"; - else if (prettyEscapes.count(i) && !_all) - { - ret += '\\'; - ret += prettyEscapes.find(i)->second; - } - else if (i < ' ' || _all) - { - ret += "\\x"; - ret.push_back("0123456789abcdef"[(uint8_t)i / 16]); - ret.push_back("0123456789abcdef"[(uint8_t)i % 16]); - } - else - ret.push_back(i); - ret.push_back('"'); - return ret; -} - int dev::fromHex(char _i, WhenError _throw) { if (_i >= '0' && _i <= '9') diff --git a/libsolidity/ast/ASTPrinter.cpp b/libsolidity/ast/ASTPrinter.cpp index 392179ef5..091914333 100644 --- a/libsolidity/ast/ASTPrinter.cpp +++ b/libsolidity/ast/ASTPrinter.cpp @@ -21,9 +21,12 @@ */ #include -#include #include +#include + +#include + using namespace std; namespace dev @@ -579,8 +582,13 @@ void ASTPrinter::printSourcePart(ASTNode const& _node) if (!m_source.empty()) { SourceLocation const& location(_node.location()); - *m_ostream << indentation() << " Source: " - << escaped(m_source.substr(location.start, location.end - location.start), false) << endl; + *m_ostream << + indentation() << + " Source: " << + /// Note: this "abuses" the JSON library to print a string (as it is not a valid root node). + /// It also makes a copy of the string. + jsonCompactPrint(m_source.substr(location.start, location.end - location.start)) << + endl; } } From eb33e76416c7e8091bd33365b8b0917a9146868b Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 29 Aug 2017 15:51:37 +0100 Subject: [PATCH 050/162] Use Json::valueToQuotedString directly --- libsolidity/ast/ASTPrinter.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/libsolidity/ast/ASTPrinter.cpp b/libsolidity/ast/ASTPrinter.cpp index 091914333..81e6cc44f 100644 --- a/libsolidity/ast/ASTPrinter.cpp +++ b/libsolidity/ast/ASTPrinter.cpp @@ -23,7 +23,7 @@ #include #include -#include +#include #include @@ -585,9 +585,7 @@ void ASTPrinter::printSourcePart(ASTNode const& _node) *m_ostream << indentation() << " Source: " << - /// Note: this "abuses" the JSON library to print a string (as it is not a valid root node). - /// It also makes a copy of the string. - jsonCompactPrint(m_source.substr(location.start, location.end - location.start)) << + Json::valueToQuotedString(m_source.substr(location.start, location.end - location.start).c_str()) << endl; } } From 23182c7fdfd675b99ed9ea86b30e3ff1dd9516f2 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 31 Aug 2017 00:23:26 +0100 Subject: [PATCH 051/162] Add --without-optimizer option to fuzzer --- test/fuzzer.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/test/fuzzer.cpp b/test/fuzzer.cpp index 2c39dde27..53ba72017 100644 --- a/test/fuzzer.cpp +++ b/test/fuzzer.cpp @@ -121,13 +121,12 @@ void testStandardCompiler() } } -void testCompiler() +void testCompiler(bool optimize) { if (!quiet) - cout << "Testing compiler." << endl; + cout << "Testing compiler " << (optimize ? "with" : "without") << " optimizer." << endl; string input = readInput(); - bool optimize = true; string outputString(compileJSON(input.c_str(), optimize)); Json::Value outputJson; if (!Json::Reader().parse(outputString, outputJson)) @@ -191,6 +190,10 @@ Allowed options)", "const-opt", "Run the constant optimizer instead of compiling. " "Expects a binary string of up to 32 bytes on stdin." + ) + ( + "without-optimizer", + "Run without optimizations. Cannot be used together with standard-json." ); po::variables_map arguments; @@ -216,7 +219,7 @@ Allowed options)", else if (arguments.count("standard-json")) testStandardCompiler(); else - testCompiler(); + testCompiler(!arguments.count("without-optimizer")); return 0; } From b7e8d305b330f899ad0d08a4dad7851596545203 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 31 Aug 2017 00:24:25 +0100 Subject: [PATCH 052/162] Run fuzzer tests without optimizer too --- test/cmdlineTests.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index eb5c714da..f12a66865 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -147,6 +147,13 @@ TMPDIR=$(mktemp -d) cat "$f" exit 1 fi + + "$REPO_ROOT"/build/test/solfuzzer --without-optimizer --quiet < "$f" + if [ $? -ne 0 ]; then + echo "Fuzzer (without optimizer) failed on:" + cat "$f" + exit 1 + fi set -e done ) From 9e63710b8e997015bcde1e32ea661b83f5ec0d25 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 31 Aug 2017 12:16:41 +0200 Subject: [PATCH 053/162] Remove parameter names for defaulted functions. --- libsolidity/formal/SolverInterface.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libsolidity/formal/SolverInterface.h b/libsolidity/formal/SolverInterface.h index 32d92a2a6..70dc15853 100644 --- a/libsolidity/formal/SolverInterface.h +++ b/libsolidity/formal/SolverInterface.h @@ -56,10 +56,10 @@ public: Expression(u256 const& _number): name(_number.str()) {} Expression(bigint const& _number): name(_number.str()) {} - Expression(Expression const& _other) = default; - Expression(Expression&& _other) = default; - Expression& operator=(Expression const& _other) = default; - Expression& operator=(Expression&& _other) = default; + Expression(Expression const&) = default; + Expression(Expression&&) = default; + Expression& operator=(Expression const&) = default; + Expression& operator=(Expression&&) = default; static Expression ite(Expression _condition, Expression _trueValue, Expression _falseValue) { From 8743b2ceadd4a50e7137aca99b4fceeef780b55c Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 29 Aug 2017 22:48:54 +0100 Subject: [PATCH 054/162] Document call/gas modifiers properly --- docs/frequently-asked-questions.rst | 17 ----------------- docs/types.rst | 12 ++++++++++++ 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index 5f1a981ea..ed09044ef 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -503,23 +503,6 @@ Note2: Optimizing storage access can pull the gas costs down considerably, becau currently do not work across loops and also have a problem with bounds checking. You might get much better results in the future, though. -What does ``p.recipient.call.value(p.amount)(p.data)`` do? -========================================================== - -Every external function call in Solidity can be modified in two ways: - -1. You can add Ether together with the call -2. You can limit the amount of gas available to the call - -This is done by "calling a function on the function": - -``f.gas(2).value(20)()`` calls the modified function ``f`` and thereby sending 20 -Wei and limiting the gas to 2 (so this function call will most likely go out of -gas and return your 20 Wei). - -In the above example, the low-level function ``call`` is used to invoke another -contract with ``p.data`` as payload and ``p.amount`` Wei is sent with that call. - What happens to a ``struct``'s mapping when copying over a ``struct``? ====================================================================== diff --git a/docs/types.rst b/docs/types.rst index aa4589de5..098d03801 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -127,6 +127,18 @@ the function ``call`` is provided which takes an arbitrary number of arguments o ``call`` returns a boolean indicating whether the invoked function terminated (``true``) or caused an EVM exception (``false``). It is not possible to access the actual data returned (for this we would need to know the encoding and size in advance). +It is possible to adjust the supplied gas with the ``.gas()`` modifier:: + + namReg.call.gas(1000000)("register", "MyName"); + +Similarly, the supplied Ether value can be controlled too:: + + nameReg.call.value(1 ether)("register", "MyName"); + +Lastly, these modifiers can be combined. Their order does not matter:: + + nameReg.call.gas(1000000).value(1 ether)("register", "MyName"); + In a similar way, the function ``delegatecall`` can be used: the difference is that only the code of the given address is used, all other aspects (storage, balance, ...) are taken from the current contract. The purpose of ``delegatecall`` is to use library code which is stored in another contract. The user has to ensure that the layout of storage in both contracts is suitable for delegatecall to be used. Prior to homestead, only a limited variant called ``callcode`` was available that did not provide access to the original ``msg.sender`` and ``msg.value`` values. All three functions ``call``, ``delegatecall`` and ``callcode`` are very low-level functions and should only be used as a *last resort* as they break the type-safety of Solidity. From f787ecae5a0186bb19a99ac8577123700b0c93a7 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 29 Aug 2017 22:53:50 +0100 Subject: [PATCH 055/162] Document byte[] vs bytes --- docs/frequently-asked-questions.rst | 7 ------- docs/types.rst | 4 ++++ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index ed09044ef..694b28d50 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -441,13 +441,6 @@ The correct way to do this is the following:: } } -What is the difference between ``bytes`` and ``byte[]``? -======================================================== - -``bytes`` is usually more efficient: When used as arguments to functions (i.e. in -CALLDATA) or in memory, every single element of a ``byte[]`` is padded to 32 -bytes which wastes 31 bytes per element. - Is it possible to send a value while calling an overloaded function? ==================================================================== diff --git a/docs/types.rst b/docs/types.rst index 098d03801..23a70837a 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -181,6 +181,10 @@ Members: * ``.length`` yields the fixed length of the byte array (read-only). +.. note:: + It is possible to use an array of bytes as ``byte[]``, but it is wasting a lot of space, 31 bytes every element, + to be exact, when passing in calls. It is better to use ``bytes``. + Dynamically-sized byte array ---------------------------- From 8e1aae2e1a5c47fe11b5e17e670fcbb09ebda365 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 29 Aug 2017 22:56:05 +0100 Subject: [PATCH 056/162] Document gas/value modifiers with overloading --- docs/frequently-asked-questions.rst | 10 ---------- docs/types.rst | 6 ++++++ 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index 694b28d50..8fd0c041f 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -441,16 +441,6 @@ The correct way to do this is the following:: } } -Is it possible to send a value while calling an overloaded function? -==================================================================== - -It's a known missing feature. https://www.pivotaltracker.com/story/show/92020468 -as part of https://www.pivotaltracker.com/n/projects/1189488 - -Best solution currently see is to introduce a special case for gas and value and -just re-check whether they are present at the point of overload resolution. - - ****************** Advanced Questions ****************** diff --git a/docs/types.rst b/docs/types.rst index 23a70837a..d3ebfe142 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -139,6 +139,12 @@ Lastly, these modifiers can be combined. Their order does not matter:: nameReg.call.gas(1000000).value(1 ether)("register", "MyName"); +.. note:: + It is not yet possible to use the gas or value modifiers on overloaded functions. + + A workaround is to introduce a special case for gas and value and just re-check + whether they are present at the point of overload resolution. + In a similar way, the function ``delegatecall`` can be used: the difference is that only the code of the given address is used, all other aspects (storage, balance, ...) are taken from the current contract. The purpose of ``delegatecall`` is to use library code which is stored in another contract. The user has to ensure that the layout of storage in both contracts is suitable for delegatecall to be used. Prior to homestead, only a limited variant called ``callcode`` was available that did not provide access to the original ``msg.sender`` and ``msg.value`` values. All three functions ``call``, ``delegatecall`` and ``callcode`` are very low-level functions and should only be used as a *last resort* as they break the type-safety of Solidity. From f3230a41cebe4778c5c831e2bfa4fb3b7e10f4cb Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 29 Aug 2017 23:34:53 +0100 Subject: [PATCH 057/162] Document caveats about timestamp and blockhash --- docs/frequently-asked-questions.rst | 18 ------------------ docs/units-and-global-variables.rst | 12 ++++++++++++ 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index 8fd0c041f..e4eee32b2 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -125,24 +125,6 @@ Example:: } } -Are timestamps (``now,`` ``block.timestamp``) reliable? -======================================================= - -This depends on what you mean by "reliable". -In general, they are supplied by miners and are therefore vulnerable. - -Unless someone really messes up the blockchain or the clock on -your computer, you can make the following assumptions: - -You publish a transaction at a time X, this transaction contains same -code that calls ``now`` and is included in a block whose timestamp is Y -and this block is included into the canonical chain (published) at a time Z. - -The value of ``now`` will be identical to Y and X <= Y <= Z. - -Never use ``now`` or ``block.hash`` as a source of randomness, unless you know -what you are doing! - Can a contract function return a ``struct``? ============================================ diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index 647953060..887535da7 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -72,6 +72,18 @@ Block and Transaction Properties ``msg.value`` can change for every **external** function call. This includes calls to library functions. +.. note:: + Do not rely on ``block.timestamp``, ``now`` and ``block.blockhash`` as a source of randomness, + unless you know what you are doing. + + Both the timestamp and the block hash can be influenced by miners to some degree. + Bad actors in the mining community can for example run a casino payout function on a chosen hash + and just retry a different hash if they did not receive any money. + + The current block timestamp must be strictly larger than the timestamp of the last block, + but the only guarantee is that it will be somewhere between the timestamps of two + consecutive blocks in the canonical chain. + .. note:: If you want to implement access restrictions in library functions using ``msg.sender``, you have to manually supply the value of From cbd729957b60599f55dfd1f3c7b64a7e4540c6ce Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 29 Aug 2017 23:39:28 +0100 Subject: [PATCH 058/162] Document character set --- docs/frequently-asked-questions.rst | 7 ------- docs/introduction-to-smart-contracts.rst | 4 ++++ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index e4eee32b2..c4de60cd9 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -212,13 +212,6 @@ Better use ``for (uint i = 0; i < a.length...`` See `struct_and_for_loop_tester.sol `_. -What character set does Solidity use? -===================================== - -Solidity is character set agnostic concerning strings in the source code, although -UTF-8 is recommended. Identifiers (variables, functions, ...) can only use -ASCII. - What are some examples of basic string manipulation (``substring``, ``indexOf``, ``charAt``, etc)? ================================================================================================== diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index 1a3cf6387..33a8ad10a 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -57,6 +57,10 @@ and overwrite your number, but the number will still be stored in the history of the blockchain. Later, we will see how you can impose access restrictions so that only you can alter the number. +.. note:: + All identifiers (contract names, function names and variable names) are restricted to + the ASCII character set. It is possible to store UTF-8 encoded data in string variables. + .. index:: ! subcurrency Subcurrency Example From 88bce877c467982be049b4f6515974cec3ebd89e Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 30 Aug 2017 02:31:54 +0100 Subject: [PATCH 059/162] Clarify some subtleties of the fallback function --- docs/contracts.rst | 21 ++++++++++++++++--- docs/frequently-asked-questions.rst | 31 ----------------------------- 2 files changed, 18 insertions(+), 34 deletions(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index aa3f8fa6c..a1a446658 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -543,9 +543,12 @@ functions match the given function identifier (or if no data was supplied at all). Furthermore, this function is executed whenever the contract receives plain -Ether (without data). In such a context, there is usually very little gas available to -the function call (to be precise, 2300 gas), so it is important to make fallback functions as cheap as -possible. +Ether (without data). Additionally, in order to receive Ether, the fallback function +must be marked ``payable``. If no such function exists, the contract cannot receive +Ether through regular transactions. + +In such a context, there is usually very little gas available to the function call (to be precise, 2300 gas), +so it is important to make fallback functions as cheap as possible. In particular, the following operations will consume more gas than the stipend provided to a fallback function: @@ -556,6 +559,10 @@ In particular, the following operations will consume more gas than the stipend p Please ensure you test your fallback function thoroughly to ensure the execution cost is less than 2300 gas before deploying a contract. +.. note:: + Even though the fallback function cannot have arguments, one can still use ``msg.data`` to retrieve + any payload supplied with the call. + .. warning:: Contracts that receive Ether directly (without a function call, i.e. using ``send`` or ``transfer``) but do not define a fallback function @@ -563,6 +570,14 @@ Please ensure you test your fallback function thoroughly to ensure the execution before Solidity v0.4.0). So if you want your contract to receive Ether, you have to implement a fallback function. +.. warning:: + A contract without a payable fallback function can receive Ether as a recipient of a `coinbase transaction` (aka `miner block reward`) + or as a destination of a ``selfdestruct``. + + A contract cannot react to such Ether transfers and thus also cannot reject them. This is a design choice of the EVM and Solidity cannot work around it. + + It also means that ``this.balance`` can be higher than the sum of some manual accounting implemented in a contract (i.e. having a counter updated in the fallback function). + :: pragma solidity ^0.4.0; diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index c4de60cd9..9edfb4fb9 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -137,37 +137,6 @@ Enums are not supported by the ABI, they are just supported by Solidity. You have to do the mapping yourself for now, we might provide some help later. -What is the deal with ``function () { ... }`` inside Solidity contracts? How can a function not have a name? -============================================================================================================ - -This function is called "fallback function" and it -is called when someone just sent Ether to the contract without -providing any data or if someone messed up the types so that they tried to -call a function that does not exist. - -The default behaviour (if no fallback function is explicitly given) in -these situations is to throw an exception. - -If the contract is meant to receive Ether with simple transfers, you -should implement the fallback function as - -``function() payable { }`` - -Another use of the fallback function is to e.g. register that your -contract received ether by using an event. - -*Attention*: If you implement the fallback function take care that it uses as -little gas as possible, because ``send()`` will only supply a limited amount. - -Is it possible to pass arguments to the fallback function? -========================================================== - -The fallback function cannot take parameters. - -Under special circumstances, you can send data. If you take care -that none of the other functions is invoked, you can access the data -by ``msg.data``. - Can state variables be initialized in-line? =========================================== From ac5fd64c238a88493d24a33cc7bc20313799385f Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 1 Sep 2017 19:18:46 +0100 Subject: [PATCH 060/162] Mention that different Unicode codepoint can look the same --- docs/introduction-to-smart-contracts.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index 33a8ad10a..fd12e97b0 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -61,6 +61,10 @@ so that only you can alter the number. All identifiers (contract names, function names and variable names) are restricted to the ASCII character set. It is possible to store UTF-8 encoded data in string variables. +.. warning:: + Be careful with using Unicode text as similarly looking (or even identical) can have different + code points and as such will be encoded as a different byte array. + .. index:: ! subcurrency Subcurrency Example From c975bf87db9e41014af70d74dd48514b5783c30f Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 29 Aug 2017 21:26:39 +0100 Subject: [PATCH 061/162] Move fixed after int in docs --- docs/types.rst | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/types.rst b/docs/types.rst index aa4589de5..b8d387b2b 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -70,6 +70,15 @@ sign extends. Shifting by a negative amount throws a runtime exception. are going to be rounded towards zero (truncated). In other programming languages the shift right of negative values works like division with rounding down (towards negative infinity). +.. index:: ! ufixed, ! fixed, ! fixed point number + +Fixed Point Numbers +------------------- + +.. warning:: + Fixed point numbers are not fully supported by Solidity yet. They can be declared, but + cannot be assigned to or from. + .. index:: address, balance, send, call, callcode, delegatecall, transfer .. _address: @@ -181,15 +190,6 @@ As a rule of thumb, use ``bytes`` for arbitrary-length raw byte data and ``strin for arbitrary-length string (UTF-8) data. If you can limit the length to a certain number of bytes, always use one of ``bytes1`` to ``bytes32`` because they are much cheaper. -.. index:: ! ufixed, ! fixed, ! fixed point number - -Fixed Point Numbers -------------------- - -.. warning:: - Fixed point numbers are not fully supported by Solidity yet. They can be declared, but - cannot be assigned to or from. - .. index:: address, literal;address .. _address_literals: From 76bd8c57694b9335dc5ce29204f57825b4a565eb Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 29 Aug 2017 21:38:41 +0100 Subject: [PATCH 062/162] Explain fixed point types --- docs/types.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/types.rst b/docs/types.rst index b8d387b2b..d1b5a3f1b 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -79,6 +79,15 @@ Fixed Point Numbers Fixed point numbers are not fully supported by Solidity yet. They can be declared, but cannot be assigned to or from. +``fixed`` / ``ufixed``: Signed and unsigned fixed point number of various sizes. Keywords ``ufixedMxN`` and ``fixedMxN``, where ``M`` represent the number of bits taken by +the type and ``N`` represent how many decimal points are available. ``M`` must be divisible by 8 and goes from 8 to 256 bits. ``N`` must be between 0 and 80, inclusive. +``ufixed`` and ``fixed`` are aliases for ``ufixed128x19`` and ``fixed128x19``, respectively. + +Operators: + +* Comparisons: ``<=``, ``<``, ``==``, ``!=``, ``>=``, ``>`` (evaluate to ``bool``) +* Arithmetic operators: ``+``, ``-``, unary ``-``, unary ``+``, ``*``, ``/``, ``%`` (remainder) + .. index:: address, balance, send, call, callcode, delegatecall, transfer .. _address: From 435eeec5e15d94ffc2dff01dd09c8c92c697dc4e Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 29 Aug 2017 21:38:54 +0100 Subject: [PATCH 063/162] Explain the difference between floating and fixed point --- docs/frequently-asked-questions.rst | 5 ----- docs/types.rst | 6 ++++++ 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index 5f1a981ea..d9a68ed13 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -103,11 +103,6 @@ This is a limitation of the EVM and will be solved with the next protocol update Returning variably-sized data as part of an external transaction or call is fine. -How do you represent ``double``/``float`` in Solidity? -====================================================== - -This is not yet possible. - Is it possible to in-line initialize an array like so: ``string[] myarray = ["a", "b"];`` ========================================================================================= diff --git a/docs/types.rst b/docs/types.rst index d1b5a3f1b..71a4b0e66 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -88,6 +88,12 @@ Operators: * Comparisons: ``<=``, ``<``, ``==``, ``!=``, ``>=``, ``>`` (evaluate to ``bool``) * Arithmetic operators: ``+``, ``-``, unary ``-``, unary ``+``, ``*``, ``/``, ``%`` (remainder) +.. note:: + The main difference between floating point (``float`` and ``double`` in many languages, more precisely IEEE 754 numbers) and fixed point numbers is + that the number of bits used for the integer and the fractional part (the part after the decimal dot) is flexible in the former, while it is strictly + defined in the latter. Generally, in floating point almost the entire space is used to represent the number, while only a small number of bits define + where the decimal point is. + .. index:: address, balance, send, call, callcode, delegatecall, transfer .. _address: From bdafce3e4f7d4a98d09310527ff6a82ad591f709 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 5 Sep 2017 14:50:27 +0100 Subject: [PATCH 064/162] Convert ABI spec from Markdwon to RST --- docs/abi-spec.rst | 238 +++++++++++++++++++++++----------------------- 1 file changed, 119 insertions(+), 119 deletions(-) diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index ab7a8b325..fc1a3adb7 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -34,42 +34,42 @@ Types The following elementary types exist: -- `uint`: unsigned integer type of `M` bits, `0 < M <= 256`, `M % 8 == 0`. e.g. `uint32`, `uint8`, `uint256`. +- ``uint``: unsigned integer type of ``M`` bits, ``0 < M <= 256``, ``M % 8 == 0``. e.g. ``uint32``, ``uint8``, ``uint256``. -- `int`: two's complement signed integer type of `M` bits, `0 < M <= 256`, `M % 8 == 0`. +- ``int``: two's complement signed integer type of ``M`` bits, ``0 < M <= 256``, ``M % 8 == 0``. -- `address`: equivalent to `uint160`, except for the assumed interpretation and language typing. +- ``address``: equivalent to ``uint160``, except for the assumed interpretation and language typing. -- `uint`, `int`: synonyms for `uint256`, `int256` respectively (not to be used for computing the function selector). +- ``uint``, ``int``: synonyms for ``uint256``, ``int256`` respectively (not to be used for computing the function selector). -- `bool`: equivalent to `uint8` restricted to the values 0 and 1 +- ``bool``: equivalent to ``uint8`` restricted to the values 0 and 1 -- `fixedx`: signed fixed-point decimal number of `M` bits, `8 <= M <= 256`, `M % 8 ==0`, and `0 < N <= 80`, which denotes the value `v` as `v / (10 ** N)`. +- ``fixedx``: signed fixed-point decimal number of ``M`` bits, ``8 <= M <= 256``, ``M % 8 ==0``, and ``0 < N <= 80``, which denotes the value ``v`` as ``v / (10 ** N)``. -- `ufixedx`: unsigned variant of `fixedx`. +- ``ufixedx``: unsigned variant of ``fixedx``. -- `fixed`, `ufixed`: synonyms for `fixed128x19`, `ufixed128x19` respectively (not to be used for computing the function selector). +- ``fixed``, ``ufixed``: synonyms for ``fixed128x19``, ``ufixed128x19`` respectively (not to be used for computing the function selector). -- `bytes`: binary type of `M` bytes, `0 < M <= 32`. +- ``bytes``: binary type of ``M`` bytes, ``0 < M <= 32``. -- `function`: equivalent to `bytes24`: an address, followed by a function selector +- ``function``: equivalent to ``bytes24``: an address, followed by a function selector The following (fixed-size) array type exists: -- `[M]`: a fixed-length array of the given fixed-length type. +- ``[M]``: a fixed-length array of the given fixed-length type. The following non-fixed-size types exist: -- `bytes`: dynamic sized byte sequence. +- ``bytes``: dynamic sized byte sequence. -- `string`: dynamic sized unicode string assumed to be UTF-8 encoded. +- ``string``: dynamic sized unicode string assumed to be UTF-8 encoded. -- `[]`: a variable-length array of the given fixed-length type. +- ``[]``: a variable-length array of the given fixed-length type. Types can be combined to anonymous structs by enclosing a finite non-negative number of them inside parentheses, separated by commas: -- `(T1,T2,...,Tn)`: anonymous struct (ordered tuple) consisting of the types `T1`, ..., `Tn`, `n >= 0` +- ``(T1,T2,...,Tn)``: anonymous struct (ordered tuple) consisting of the types ``T1``, ..., ``Tn``, ``n >= 0`` It is possible to form structs of structs, arrays of structs and so on. @@ -82,96 +82,96 @@ properties, which are especially useful if some arguments are nested arrays: Properties: - 1. The number of reads necessary to access a value is at most the depth of the value inside the argument array structure, i.e. four reads are needed to retrieve `a_i[k][l][r]`. In a previous version of the ABI, the number of reads scaled linearly with the total number of dynamic parameters in the worst case. + 1. The number of reads necessary to access a value is at most the depth of the value inside the argument array structure, i.e. four reads are needed to retrieve ``a_i[k][l][r]``. In a previous version of the ABI, the number of reads scaled linearly with the total number of dynamic parameters in the worst case. 2. The data of a variable or array element is not interleaved with other data and it is relocatable, i.e. it only uses relative "addresses" We distinguish static and dynamic types. Static types are encoded in-place and dynamic types are encoded at a separately allocated location after the current block. **Definition:** The following types are called "dynamic": -* `bytes` -* `string` -* `T[]` for any `T` -* `T[k]` for any dynamic `T` and any `k > 0` -* `(T1,...,Tk)` if any `Ti` is dynamic for `1 <= i <= k` +* ``bytes`` +* ``string`` +* ``T[]`` for any ``T`` +* ``T[k]`` for any dynamic ``T`` and any ``k > 0`` +* ``(T1,...,Tk)`` if any ``Ti`` is dynamic for ``1 <= i <= k`` All other types are called "static". -**Definition:** `len(a)` is the number of bytes in a binary string `a`. -The type of `len(a)` is assumed to be `uint256`. +**Definition:** ``len(a)`` is the number of bytes in a binary string ``a``. +The type of ``len(a)`` is assumed to be ``uint256``. -We define `enc`, the actual encoding, as a mapping of values of the ABI types to binary strings such -that `len(enc(X))` depends on the value of `X` if and only if the type of `X` is dynamic. +We define ``enc``, the actual encoding, as a mapping of values of the ABI types to binary strings such +that ``len(enc(X))`` depends on the value of ``X`` if and only if the type of ``X`` is dynamic. -**Definition:** For any ABI value `X`, we recursively define `enc(X)`, depending -on the type of `X` being +**Definition:** For any ABI value ``X``, we recursively define ``enc(X)``, depending +on the type of ``X`` being -- `(T1,...,Tk)` for `k >= 0` and any types `T1`, ..., `Tk` +- ``(T1,...,Tk)`` for ``k >= 0`` and any types ``T1``, ..., ``Tk`` - `enc(X) = head(X(1)) ... head(X(k-1)) tail(X(0)) ... tail(X(k-1))` + ``enc(X) = head(X(1)) ... head(X(k-1)) tail(X(0)) ... tail(X(k-1))`` - where `X(i)` is the `ith` component of the value, and - `head` and `tail` are defined for `Ti` being a static type as + where ``X(i)`` is the ``ith`` component of the value, and + ``head`` and ``tail`` are defined for ``Ti`` being a static type as - `head(X(i)) = enc(X(i))` and `tail(X(i)) = ""` (the empty string) + ``head(X(i)) = enc(X(i))`` and ``tail(X(i)) = ""`` (the empty string) and as - `head(X(i)) = enc(len(head(X(0)) ... head(X(k-1)) tail(X(0)) ... tail(X(i-1))))` - `tail(X(i)) = enc(X(i))` + ``head(X(i)) = enc(len(head(X(0)) ... head(X(k-1)) tail(X(0)) ... tail(X(i-1))))`` + ``tail(X(i)) = enc(X(i))`` - otherwise, i.e. if `Ti` is a dynamic type. + otherwise, i.e. if ``Ti`` is a dynamic type. - Note that in the dynamic case, `head(X(i))` is well-defined since the lengths of + Note that in the dynamic case, ``head(X(i))`` is well-defined since the lengths of the head parts only depend on the types and not the values. Its value is the offset - of the beginning of `tail(X(i))` relative to the start of `enc(X)`. + of the beginning of ``tail(X(i))`` relative to the start of ``enc(X)``. -- `T[k]` for any `T` and `k`: +- ``T[k]`` for any ``T`` and ``k``: - `enc(X) = enc((X[0], ..., X[k-1]))` + ``enc(X) = enc((X[0], ..., X[k-1]))`` - i.e. it is encoded as if it were an anonymous struct with `k` elements + i.e. it is encoded as if it were an anonymous struct with ``k`` elements of the same type. -- `T[]` where `X` has `k` elements (`k` is assumed to be of type `uint256`): +- ``T[]`` where ``X`` has ``k`` elements (``k`` is assumed to be of type ``uint256``): - `enc(X) = enc(k) enc([X[1], ..., X[k]])` + ``enc(X) = enc(k) enc([X[1], ..., X[k]])`` - i.e. it is encoded as if it were an array of static size `k`, prefixed with + i.e. it is encoded as if it were an array of static size ``k``, prefixed with the number of elements. -- `bytes`, of length `k` (which is assumed to be of type `uint256`): +- ``bytes``, of length ``k`` (which is assumed to be of type ``uint256``): - `enc(X) = enc(k) pad_right(X)`, i.e. the number of bytes is encoded as a - `uint256` followed by the actual value of `X` as a byte sequence, followed by - the minimum number of zero-bytes such that `len(enc(X))` is a multiple of 32. + ``enc(X) = enc(k) pad_right(X)``, i.e. the number of bytes is encoded as a + ``uint256`` followed by the actual value of ``X`` as a byte sequence, followed by + the minimum number of zero-bytes such that ``len(enc(X))`` is a multiple of 32. -- `string`: +- ``string``: - `enc(X) = enc(enc_utf8(X))`, i.e. `X` is utf-8 encoded and this value is interpreted as of `bytes` type and encoded further. Note that the length used in this subsequent encoding is the number of bytes of the utf-8 encoded string, not its number of characters. + ``enc(X) = enc(enc_utf8(X))``, i.e. ``X`` is utf-8 encoded and this value is interpreted as of ``bytes`` type and encoded further. Note that the length used in this subsequent encoding is the number of bytes of the utf-8 encoded string, not its number of characters. -- `uint`: `enc(X)` is the big-endian encoding of `X`, padded on the higher-order (left) side with zero-bytes such that the length is a multiple of 32 bytes. -- `address`: as in the `uint160` case -- `int`: `enc(X)` is the big-endian two's complement encoding of `X`, padded on the higher-oder (left) side with `0xff` for negative `X` and with zero bytes for positive `X` such that the length is a multiple of 32 bytes. -- `bool`: as in the `uint8` case, where `1` is used for `true` and `0` for `false` -- `fixedx`: `enc(X)` is `enc(X * 10**N)` where `X * 10**N` is interpreted as a `int256`. -- `fixed`: as in the `fixed128x19` case -- `ufixedx`: `enc(X)` is `enc(X * 10**N)` where `X * 10**N` is interpreted as a `uint256`. -- `ufixed`: as in the `ufixed128x19` case -- `bytes`: `enc(X)` is the sequence of bytes in `X` padded with zero-bytes to a length of 32. +- ``uint``: ``enc(X)`` is the big-endian encoding of ``X``, padded on the higher-order (left) side with zero-bytes such that the length is a multiple of 32 bytes. +- ``address``: as in the ``uint160`` case +- ``int``: ``enc(X)`` is the big-endian two's complement encoding of ``X``, padded on the higher-oder (left) side with ``0xff`` for negative ``X`` and with zero bytes for positive ``X`` such that the length is a multiple of 32 bytes. +- ``bool``: as in the ``uint8`` case, where ``1`` is used for ``true`` and ``0`` for ``false`` +- ``fixedx``: ``enc(X)`` is ``enc(X * 10**N)`` where ``X * 10**N`` is interpreted as a ``int256``. +- ``fixed``: as in the ``fixed128x19`` case +- ``ufixedx``: ``enc(X)`` is ``enc(X * 10**N)`` where ``X * 10**N`` is interpreted as a ``uint256``. +- ``ufixed``: as in the ``ufixed128x19`` case +- ``bytes``: ``enc(X)`` is the sequence of bytes in ``X`` padded with zero-bytes to a length of 32. -Note that for any `X`, `len(enc(X))` is a multiple of 32. +Note that for any ``X``, ``len(enc(X))`` is a multiple of 32. Function Selector and Argument Encoding ======================================= -All in all, a call to the function `f` with parameters `a_1, ..., a_n` is encoded as +All in all, a call to the function ``f`` with parameters ``a_1, ..., a_n`` is encoded as - `function_selector(f) enc((a_1, ..., a_n))` + ``function_selector(f) enc((a_1, ..., a_n))`` -and the return values `v_1, ..., v_k` of `f` are encoded as +and the return values ``v_1, ..., v_k`` of ``f`` are encoded as - `enc((v_1, ..., v_k))` + ``enc((v_1, ..., v_k))`` i.e. the values are combined into an anonymous struct and encoded. @@ -191,40 +191,40 @@ Given the contract: } -Thus for our `Foo` example if we wanted to call `baz` with the parameters `69` and `true`, we would pass 68 bytes total, which can be broken down into: +Thus for our ``Foo`` example if we wanted to call ``baz`` with the parameters ``69`` and ``true``, we would pass 68 bytes total, which can be broken down into: -- `0xcdcd77c0`: the Method ID. This is derived as the first 4 bytes of the Keccak hash of the ASCII form of the signature `baz(uint32,bool)`. -- `0x0000000000000000000000000000000000000000000000000000000000000045`: the first parameter, a uint32 value `69` padded to 32 bytes -- `0x0000000000000000000000000000000000000000000000000000000000000001`: the second parameter - boolean `true`, padded to 32 bytes +- ``0xcdcd77c0``: the Method ID. This is derived as the first 4 bytes of the Keccak hash of the ASCII form of the signature ``baz(uint32,bool)``. +- ``0x0000000000000000000000000000000000000000000000000000000000000045``: the first parameter, a uint32 value ``69`` padded to 32 bytes +- ``0x0000000000000000000000000000000000000000000000000000000000000001``: the second parameter - boolean ``true``, padded to 32 bytes In total:: 0xcdcd77c000000000000000000000000000000000000000000000000000000000000000450000000000000000000000000000000000000000000000000000000000000001 -It returns a single `bool`. If, for example, it were to return `false`, its output would be the single byte array `0x0000000000000000000000000000000000000000000000000000000000000000`, a single bool. +It returns a single ``bool``. If, for example, it were to return ``false``, its output would be the single byte array ``0x0000000000000000000000000000000000000000000000000000000000000000``, a single bool. -If we wanted to call `bar` with the argument `["abc", "def"]`, we would pass 68 bytes total, broken down into: +If we wanted to call ``bar`` with the argument ``["abc", "def"]``, we would pass 68 bytes total, broken down into: -- `0xfce353f6`: the Method ID. This is derived from the signature `bar(bytes3[2])`. -- `0x6162630000000000000000000000000000000000000000000000000000000000`: the first part of the first parameter, a `bytes3` value `"abc"` (left-aligned). -- `0x6465660000000000000000000000000000000000000000000000000000000000`: the second part of the first parameter, a `bytes3` value `"def"` (left-aligned). +- ``0xfce353f6``: the Method ID. This is derived from the signature ``bar(bytes3[2])``. +- ``0x6162630000000000000000000000000000000000000000000000000000000000``: the first part of the first parameter, a ``bytes3`` value ``"abc"`` (left-aligned). +- ``0x6465660000000000000000000000000000000000000000000000000000000000``: the second part of the first parameter, a ``bytes3`` value ``"def"`` (left-aligned). In total:: 0xfce353f661626300000000000000000000000000000000000000000000000000000000006465660000000000000000000000000000000000000000000000000000000000 -If we wanted to call `sam` with the arguments `"dave"`, `true` and `[1,2,3]`, we would pass 292 bytes total, broken down into: +If we wanted to call ``sam`` with the arguments ``"dave"``, ``true`` and ``[1,2,3]``, we would pass 292 bytes total, broken down into: -- `0xa5643bf2`: the Method ID. This is derived from the signature `sam(bytes,bool,uint256[])`. Note that `uint` is replaced with its canonical representation `uint256`. -- `0x0000000000000000000000000000000000000000000000000000000000000060`: the location of the data part of the first parameter (dynamic type), measured in bytes from the start of the arguments block. In this case, `0x60`. -- `0x0000000000000000000000000000000000000000000000000000000000000001`: the second parameter: boolean true. -- `0x00000000000000000000000000000000000000000000000000000000000000a0`: the location of the data part of the third parameter (dynamic type), measured in bytes. In this case, `0xa0`. -- `0x0000000000000000000000000000000000000000000000000000000000000004`: the data part of the first argument, it starts with the length of the byte array in elements, in this case, 4. -- `0x6461766500000000000000000000000000000000000000000000000000000000`: the contents of the first argument: the UTF-8 (equal to ASCII in this case) encoding of `"dave"`, padded on the right to 32 bytes. -- `0x0000000000000000000000000000000000000000000000000000000000000003`: the data part of the third argument, it starts with the length of the array in elements, in this case, 3. -- `0x0000000000000000000000000000000000000000000000000000000000000001`: the first entry of the third parameter. -- `0x0000000000000000000000000000000000000000000000000000000000000002`: the second entry of the third parameter. -- `0x0000000000000000000000000000000000000000000000000000000000000003`: the third entry of the third parameter. +- ``0xa5643bf2``: the Method ID. This is derived from the signature ``sam(bytes,bool,uint256[])``. Note that ``uint`` is replaced with its canonical representation ``uint256``. +- ``0x0000000000000000000000000000000000000000000000000000000000000060``: the location of the data part of the first parameter (dynamic type), measured in bytes from the start of the arguments block. In this case, ``0x60``. +- ``0x0000000000000000000000000000000000000000000000000000000000000001``: the second parameter: boolean true. +- ``0x00000000000000000000000000000000000000000000000000000000000000a0``: the location of the data part of the third parameter (dynamic type), measured in bytes. In this case, ``0xa0``. +- ``0x0000000000000000000000000000000000000000000000000000000000000004``: the data part of the first argument, it starts with the length of the byte array in elements, in this case, 4. +- ``0x6461766500000000000000000000000000000000000000000000000000000000``: the contents of the first argument: the UTF-8 (equal to ASCII in this case) encoding of ``"dave"``, padded on the right to 32 bytes. +- ``0x0000000000000000000000000000000000000000000000000000000000000003``: the data part of the third argument, it starts with the length of the array in elements, in this case, 3. +- ``0x0000000000000000000000000000000000000000000000000000000000000001``: the first entry of the third parameter. +- ``0x0000000000000000000000000000000000000000000000000000000000000002``: the second entry of the third parameter. +- ``0x0000000000000000000000000000000000000000000000000000000000000003``: the third entry of the third parameter. In total:: @@ -233,26 +233,26 @@ In total:: Use of Dynamic Types ==================== -A call to a function with the signature `f(uint,uint32[],bytes10,bytes)` with values `(0x123, [0x456, 0x789], "1234567890", "Hello, world!")` is encoded in the following way: +A call to a function with the signature ``f(uint,uint32[],bytes10,bytes)`` with values ``(0x123, [0x456, 0x789], "1234567890", "Hello, world!")`` is encoded in the following way: -We take the first four bytes of `sha3("f(uint256,uint32[],bytes10,bytes)")`, i.e. `0x8be65246`. -Then we encode the head parts of all four arguments. For the static types `uint256` and `bytes10`, these are directly the values we want to pass, whereas for the dynamic types `uint32[]` and `bytes`, we use the offset in bytes to the start of their data area, measured from the start of the value encoding (i.e. not counting the first four bytes containing the hash of the function signature). These are: +We take the first four bytes of ``sha3("f(uint256,uint32[],bytes10,bytes)")``, i.e. ``0x8be65246``. +Then we encode the head parts of all four arguments. For the static types ``uint256`` and ``bytes10``, these are directly the values we want to pass, whereas for the dynamic types ``uint32[]`` and ``bytes``, we use the offset in bytes to the start of their data area, measured from the start of the value encoding (i.e. not counting the first four bytes containing the hash of the function signature). These are: - - `0x0000000000000000000000000000000000000000000000000000000000000123` (`0x123` padded to 32 bytes) - - `0x0000000000000000000000000000000000000000000000000000000000000080` (offset to start of data part of second parameter, 4*32 bytes, exactly the size of the head part) - - `0x3132333435363738393000000000000000000000000000000000000000000000` (`"1234567890"` padded to 32 bytes on the right) - - `0x00000000000000000000000000000000000000000000000000000000000000e0` (offset to start of data part of fourth parameter = offset to start of data part of first dynamic parameter + size of data part of first dynamic parameter = 4\*32 + 3\*32 (see below)) + - ``0x0000000000000000000000000000000000000000000000000000000000000123`` (``0x123`` padded to 32 bytes) + - ``0x0000000000000000000000000000000000000000000000000000000000000080`` (offset to start of data part of second parameter, 4*32 bytes, exactly the size of the head part) + - ``0x3132333435363738393000000000000000000000000000000000000000000000`` (``"1234567890"`` padded to 32 bytes on the right) + - ``0x00000000000000000000000000000000000000000000000000000000000000e0`` (offset to start of data part of fourth parameter = offset to start of data part of first dynamic parameter + size of data part of first dynamic parameter = 4\*32 + 3\*32 (see below)) -After this, the data part of the first dynamic argument, `[0x456, 0x789]` follows: +After this, the data part of the first dynamic argument, ``[0x456, 0x789]`` follows: - - `0x0000000000000000000000000000000000000000000000000000000000000002` (number of elements of the array, 2) - - `0x0000000000000000000000000000000000000000000000000000000000000456` (first element) - - `0x0000000000000000000000000000000000000000000000000000000000000789` (second element) + - ``0x0000000000000000000000000000000000000000000000000000000000000002`` (number of elements of the array, 2) + - ``0x0000000000000000000000000000000000000000000000000000000000000456`` (first element) + - ``0x0000000000000000000000000000000000000000000000000000000000000789`` (second element) -Finally, we encode the data part of the second dynamic argument, `"Hello, world!"`: +Finally, we encode the data part of the second dynamic argument, ``"Hello, world!"``: - - `0x000000000000000000000000000000000000000000000000000000000000000d` (number of elements (bytes in this case): 13) - - `0x48656c6c6f2c20776f726c642100000000000000000000000000000000000000` (`"Hello, world!"` padded to 32 bytes on the right) + - ``0x000000000000000000000000000000000000000000000000000000000000000d`` (number of elements (bytes in this case): 13) + - ``0x48656c6c6f2c20776f726c642100000000000000000000000000000000000000`` (``"Hello, world!"`` padded to 32 bytes on the right) All together, the encoding is (newline after function selector and each 32-bytes for clarity): @@ -278,41 +278,41 @@ Given an event name and series of event parameters, we split them into two sub-s In effect, a log entry using this ABI is described as: -- `address`: the address of the contract (intrinsically provided by Ethereum); -- `topics[0]`: `keccak(EVENT_NAME+"("+EVENT_ARGS.map(canonical_type_of).join(",")+")")` (`canonical_type_of` is a function that simply returns the canonical type of a given argument, e.g. for `uint indexed foo`, it would return `uint256`). If the event is declared as `anonymous` the `topics[0]` is not generated; -- `topics[n]`: `EVENT_INDEXED_ARGS[n - 1]` (`EVENT_INDEXED_ARGS` is the series of `EVENT_ARGS` that are indexed); -- `data`: `abi_serialise(EVENT_NON_INDEXED_ARGS)` (`EVENT_NON_INDEXED_ARGS` is the series of `EVENT_ARGS` that are not indexed, `abi_serialise` is the ABI serialisation function used for returning a series of typed values from a function, as described above). +- ``address``: the address of the contract (intrinsically provided by Ethereum); +- ``topics[0]``: ``keccak(EVENT_NAME+"("+EVENT_ARGS.map(canonical_type_of).join(",")+")")`` (``canonical_type_of`` is a function that simply returns the canonical type of a given argument, e.g. for ``uint indexed foo``, it would return ``uint256``). If the event is declared as ``anonymous`` the ``topics[0]`` is not generated; +- ``topics[n]``: ``EVENT_INDEXED_ARGS[n - 1]`` (``EVENT_INDEXED_ARGS`` is the series of ``EVENT_ARGS`` that are indexed); +- ``data``: ``abi_serialise(EVENT_NON_INDEXED_ARGS)`` (``EVENT_NON_INDEXED_ARGS`` is the series of ``EVENT_ARGS`` that are not indexed, ``abi_serialise`` is the ABI serialisation function used for returning a series of typed values from a function, as described above). JSON ==== The JSON format for a contract's interface is given by an array of function and/or event descriptions. A function description is a JSON object with the fields: -- `type`: `"function"`, `"constructor"`, or `"fallback"` (the :ref:`unnamed "default" function `); -- `name`: the name of the function; -- `inputs`: an array of objects, each of which contains: - * `name`: the name of the parameter; - * `type`: the canonical type of the parameter. -- `outputs`: an array of objects similar to `inputs`, can be omitted if function doesn't return anything; -- `payable`: `true` if function accepts ether, defaults to `false`; -- `stateMutability`: a string with one of the following values: `pure` (:ref:`specified to not read blockchain state `), `view` (:ref:`specified to not modify the blockchain state `), `nonpayable` and `payable` (same as `payable` above). -- `constant`: `true` if function is either `pure` or `view` +- ``type``: ``"function"``, ``"constructor"``, or ``"fallback"`` (the :ref:`unnamed "default" function `); +- ``name``: the name of the function; +- ``inputs``: an array of objects, each of which contains: + * ``name``: the name of the parameter; + * ``type``: the canonical type of the parameter. +- ``outputs``: an array of objects similar to ``inputs``, can be omitted if function doesn't return anything; +- ``payable``: ``true`` if function accepts ether, defaults to ``false``; +- ``stateMutability``: a string with one of the following values: ``pure`` (:ref:`specified to not read blockchain state `), ``view`` (:ref:`specified to not modify the blockchain state `), ``nonpayable`` and ``payable`` (same as ``payable`` above). +- ``constant``: ``true`` if function is either ``pure`` or ``view`` -`type` can be omitted, defaulting to `"function"`. +``type`` can be omitted, defaulting to ``"function"``. -Constructor and fallback function never have `name` or `outputs`. Fallback function doesn't have `inputs` either. +Constructor and fallback function never have ``name`` or ``outputs``. Fallback function doesn't have ``inputs`` either. Sending non-zero ether to non-payable function will throw. Don't do it. An event description is a JSON object with fairly similar fields: -- `type`: always `"event"` -- `name`: the name of the event; -- `inputs`: an array of objects, each of which contains: - * `name`: the name of the parameter; - * `type`: the canonical type of the parameter. - * `indexed`: `true` if the field is part of the log's topics, `false` if it one of the log's data segment. -- `anonymous`: `true` if the event was declared as `anonymous`. +- ``type``: always ``"event"`` +- ``name``: the name of the event; +- ``inputs``: an array of objects, each of which contains: + * ``name``: the name of the parameter; + * ``type``: the canonical type of the parameter. + * ``indexed``: ``true`` if the field is part of the log's topics, ``false`` if it one of the log's data segment. +- ``anonymous``: ``true`` if the event was declared as ``anonymous``. For example, From 818a3719296db56852da9a21ae06f305c15f8343 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 29 Aug 2017 12:06:09 +0200 Subject: [PATCH 065/162] Explain how to run the tests in more detail. --- docs/contributing.rst | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/contributing.rst b/docs/contributing.rst index 9d1b2ce32..01caa5b15 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -66,14 +66,19 @@ Running the compiler tests Solidity includes different types of tests. They are included in the application called ``soltest``. Some of them require the ``cpp-ethereum`` client in testing mode. -To run ``cpp-ethereum`` in testing mode: ``eth --test -d /tmp/testeth``. +To run a subset of the tests that do not require ``cpp-ethereum``, use ``./build/test/soltest -- --no-ipc``. -To run the tests: ``soltest -- --ipcpath /tmp/testeth/geth.ipc``. +For all other tests, you need to install `cpp-ethereum `_ and run it in testing mode: ``eth --test -d /tmp/testeth``. + +Then you run the actual tests: ``./build/test/soltest -- --ipcpath /tmp/testeth/geth.ipc``. To run a subset of tests, filters can be used: ``soltest -t TestSuite/TestName -- --ipcpath /tmp/testeth/geth.ipc``, where ``TestName`` can be a wildcard ``*``. -Alternatively, there is a testing script at ``scripts/test.sh`` which executes all tests. +Alternatively, there is a testing script at ``scripts/test.sh`` which executes all tests and runs +``cpp-ethereum`` automatically if it is in the path (but does not download it). + +Travis CI even runs some additional tests (including ``solc-js`` and testing third party Solidity frameworks) that require compiling the Emscripten target. Whiskers ======== From 3326a2282e4bf5b83b14bc775a821806a204817e Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 5 Sep 2017 22:38:45 +0100 Subject: [PATCH 066/162] Change array too large error message as it is valid for non-calldata too --- libsolidity/analysis/TypeChecker.cpp | 2 +- test/libsolidity/SolidityNameAndTypeResolution.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 7d5a0c8ce..c46485d84 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -623,7 +623,7 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) (arrayType->location() == DataLocation::CallData)) && !arrayType->validForCalldata() ) - m_errorReporter.typeError(_variable.location(), "Array is too large to be encoded as calldata."); + m_errorReporter.typeError(_variable.location(), "Array is too large to be encoded."); return false; } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index a05371035..f88638761 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -6217,21 +6217,21 @@ BOOST_AUTO_TEST_CASE(too_large_arrays_for_calldata) } } )"; - CHECK_ERROR(text, TypeError, "Array is too large to be encoded as calldata."); + CHECK_ERROR(text, TypeError, "Array is too large to be encoded."); text = R"( contract C { function f(uint[85678901234] a) internal { } } )"; - CHECK_SUCCESS_NO_WARNINGS(text); + CHECK_ERROR(text, TypeError, "Array is too large to be encoded."); text = R"( contract C { function f(uint[85678901234] a) { } } )"; - CHECK_ERROR(text, TypeError, "Array is too large to be encoded as calldata."); + CHECK_ERROR(text, TypeError, "Array is too large to be encoded."); } BOOST_AUTO_TEST_CASE(explicit_literal_to_storage_string) From 311be6b6599b363b89d34a584773bfa6c207b519 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 31 Aug 2017 00:27:25 +0200 Subject: [PATCH 067/162] Fix warning checking in test framework. --- test/libsolidity/AnalysisFramework.cpp | 2 +- test/libsolidity/SolidityNameAndTypeResolution.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/libsolidity/AnalysisFramework.cpp b/test/libsolidity/AnalysisFramework.cpp index 173d46bc1..71bb4ca7b 100644 --- a/test/libsolidity/AnalysisFramework.cpp +++ b/test/libsolidity/AnalysisFramework.cpp @@ -59,7 +59,7 @@ AnalysisFramework::parseAnalyseAndReturnError( if (currentError->comment()->find("This is a pre-release compiler version") == 0) continue; - if (_reportWarnings == (currentError->type() == Error::Type::Warning)) + if (_reportWarnings || (currentError->type() != Error::Type::Warning)) { if (firstError && !_allowMultipleErrors) { diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index f88638761..944047815 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -4528,12 +4528,12 @@ BOOST_AUTO_TEST_CASE(warn_about_callcode) CHECK_WARNING(text, "\"callcode\" has been deprecated in favour"); } -BOOST_AUTO_TEST_CASE(no_warn_about_callcode_as_local) +BOOST_AUTO_TEST_CASE(no_warn_about_callcode_as_function) { char const* text = R"( contract test { function callcode() { - var x = this.callcode; + test.callcode(); } } )"; @@ -6140,14 +6140,14 @@ BOOST_AUTO_TEST_CASE(does_not_error_transfer_regular_function) { char const* text = R"( contract A { - function transfer(uint) {} + function transfer() {} } contract B { A a; function() { - a.transfer(100); + a.transfer(); } } )"; From 5470da4d9adc8ef07aa1c2a758b7062be843cca4 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 28 Aug 2017 19:48:34 +0200 Subject: [PATCH 068/162] View-pure checker. --- libsolidity/analysis/StaticAnalyzer.h | 4 +- libsolidity/analysis/ViewPureChecker.cpp | 227 +++++++++++++++++++++++ libsolidity/analysis/ViewPureChecker.h | 79 ++++++++ libsolidity/ast/Types.h | 1 + libsolidity/interface/CompilerStack.cpp | 11 ++ 5 files changed, 320 insertions(+), 2 deletions(-) create mode 100644 libsolidity/analysis/ViewPureChecker.cpp create mode 100644 libsolidity/analysis/ViewPureChecker.h diff --git a/libsolidity/analysis/StaticAnalyzer.h b/libsolidity/analysis/StaticAnalyzer.h index a3080b428..24ed119fa 100644 --- a/libsolidity/analysis/StaticAnalyzer.h +++ b/libsolidity/analysis/StaticAnalyzer.h @@ -37,8 +37,8 @@ namespace solidity /** * The module that performs static analysis on the AST. * In this context, static analysis is anything that can produce warnings which can help - * programmers write cleaner code. For every warning generated eher, it has to be possible to write - * equivalent code that does generate the warning. + * programmers write cleaner code. For every warning generated here, it has to be possible to write + * equivalent code that does not generate the warning. */ class StaticAnalyzer: private ASTConstVisitor { diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp new file mode 100644 index 000000000..0e2cfacf1 --- /dev/null +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -0,0 +1,227 @@ +/* + 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; +using namespace dev; +using namespace dev::solidity; + +bool ViewPureChecker::check() +{ + vector contracts; + + for (auto const& node: m_ast) + { + SourceUnit const* source = dynamic_cast(node.get()); + solAssert(source, ""); + for (auto const& topLevelNode: source->nodes()) + { + ContractDefinition const* contract = dynamic_cast(topLevelNode.get()); + if (contract) + contracts.push_back(contract); + } + } + + // Check modifiers first to infer their state mutability. + for (auto const* contract: contracts) + for (ModifierDefinition const* mod: contract->functionModifiers()) + mod->accept(*this); + + for (auto const* contract: contracts) + contract->accept(*this); + + return !m_errors; +} + + + +bool ViewPureChecker::visit(FunctionDefinition const& _funDef) +{ + solAssert(!m_currentFunction, ""); + m_currentFunction = &_funDef; + m_currentBestMutability = StateMutability::Pure; + return true; +} + +void ViewPureChecker::endVisit(FunctionDefinition const& _funDef) +{ + solAssert(m_currentFunction == &_funDef, ""); + if ( + m_currentBestMutability < _funDef.stateMutability() && + _funDef.stateMutability() != StateMutability::Payable && + _funDef.isImplemented() && + !_funDef.isConstructor() && + !_funDef.isFallback() && + !_funDef.isConstructor() && + !_funDef.annotation().superFunction + ) + m_errorReporter.warning( + _funDef.location(), + "Function state mutability can be restricted to " + stateMutabilityToString(m_currentBestMutability) + ); + m_currentFunction = nullptr; +} + +bool ViewPureChecker::visit(ModifierDefinition const&) +{ + solAssert(m_currentFunction == nullptr, ""); + m_currentBestMutability = StateMutability::Pure; + return true; +} + +void ViewPureChecker::endVisit(ModifierDefinition const& _modifierDef) +{ + solAssert(m_currentFunction == nullptr, ""); + m_inferredMutability[&_modifierDef] = m_currentBestMutability; +} + +void ViewPureChecker::endVisit(Identifier const& _identifier) +{ + Declaration const* declaration = _identifier.annotation().referencedDeclaration; + solAssert(declaration, ""); + + StateMutability mutability = StateMutability::Pure; + + bool writes = _identifier.annotation().lValueRequested; + if (VariableDeclaration const* varDecl = dynamic_cast(declaration)) + { + if (varDecl->isStateVariable()) + mutability = writes ? StateMutability::NonPayable : StateMutability::View; + } + else if (MagicVariableDeclaration const* magicVar = dynamic_cast(declaration)) + { + switch (magicVar->type()->category()) + { + case Type::Category::Contract: + solAssert(_identifier.name() == "this" || _identifier.name() == "super", ""); + if (!dynamic_cast(*magicVar->type()).isSuper()) + // reads the address + mutability = StateMutability::View; + break; + case Type::Category::Integer: + solAssert(_identifier.name() == "now", ""); + mutability = StateMutability::View; + break; + default: + break; + } + } + + reportMutability(mutability, _identifier); +} + +void ViewPureChecker::endVisit(InlineAssembly const& _inlineAssembly) +{ + // @TOOD we can and should analyze it further. + reportMutability(StateMutability::NonPayable, _inlineAssembly); +} + +void ViewPureChecker::reportMutability(StateMutability _mutability, const ASTNode& _node) +{ + if (m_currentFunction && m_currentFunction->stateMutability() < _mutability) + { + m_errors = true; + if (_mutability == StateMutability::View) + m_errorReporter.typeError( + _node.location(), + "Function declared as pure, but this expression reads from the environment or state and thus " + "requires \"view\"." + ); + else if (_mutability == StateMutability::NonPayable) + m_errorReporter.typeError( + _node.location(), + "Function declared as " + + stateMutabilityToString(m_currentFunction->stateMutability()) + + ", but this expression modifies the state and thus " + "requires non-payable (the default) or payable." + ); + else + solAssert(false, ""); + } + if (_mutability >= m_currentBestMutability) + m_currentBestMutability = _mutability; +} + +void ViewPureChecker::endVisit(FunctionCall const& _functionCall) +{ + if (_functionCall.annotation().kind != FunctionCallKind::FunctionCall) + return; + + StateMutability mut = dynamic_cast(*_functionCall.expression().annotation().type).stateMutability(); + // We only require "nonpayable" to call a payble function. + if (mut == StateMutability::Payable) + mut = StateMutability::NonPayable; + reportMutability(mut, _functionCall); +} + +void ViewPureChecker::endVisit(MemberAccess const& _memberAccess) +{ + StateMutability mutability = StateMutability::Pure; + bool writes = _memberAccess.annotation().lValueRequested; + + ASTString const& member = _memberAccess.memberName(); + switch (_memberAccess.expression().annotation().type->category()) + { + case Type::Category::Contract: + case Type::Category::Integer: + if (member == "balance" && !_memberAccess.annotation().referencedDeclaration) + mutability = StateMutability::View; + break; + case Type::Category::Magic: + // we can ignore the kind of magic and only look at the name of the member + if (member != "data" && member != "sig" && member != "blockhash") + mutability = StateMutability::View; + break; + case Type::Category::Struct: + { + if (_memberAccess.expression().annotation().type->dataStoredIn(DataLocation::Storage)) + mutability = writes ? StateMutability::NonPayable : StateMutability::View; + break; + } + case Type::Category::Array: + { + auto const& type = dynamic_cast(*_memberAccess.expression().annotation().type); + if (member == "length" && type.isDynamicallySized() && type.dataStoredIn(DataLocation::Storage)) + mutability = writes ? StateMutability::NonPayable : StateMutability::View; + break; + } + default: + break; + } + reportMutability(mutability, _memberAccess); +} + +void ViewPureChecker::endVisit(IndexAccess const& _indexAccess) +{ + solAssert(_indexAccess.indexExpression(), ""); + + bool writes = _indexAccess.annotation().lValueRequested; + if (_indexAccess.baseExpression().annotation().type->dataStoredIn(DataLocation::Storage)) + reportMutability(writes ? StateMutability::NonPayable : StateMutability::View, _indexAccess); +} + +void ViewPureChecker::endVisit(ModifierInvocation const& _modifier) +{ + solAssert(_modifier.name(), ""); + ModifierDefinition const* mod = dynamic_cast(_modifier.name()->annotation().referencedDeclaration); + solAssert(mod, ""); + solAssert(m_inferredMutability.count(mod), ""); + + reportMutability(m_inferredMutability.at(mod), _modifier); +} + diff --git a/libsolidity/analysis/ViewPureChecker.h b/libsolidity/analysis/ViewPureChecker.h new file mode 100644 index 000000000..6aedfa369 --- /dev/null +++ b/libsolidity/analysis/ViewPureChecker.h @@ -0,0 +1,79 @@ +/* + 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 +#include + +namespace dev +{ +namespace solidity +{ + +class ASTNode; +class FunctionDefinition; +class ModifierDefinition; +class Identifier; +class MemberAccess; +class IndexAccess; +class ModifierInvocation; +class FunctionCall; +class InlineAssembly; + +class ViewPureChecker: private ASTConstVisitor +{ +public: + ViewPureChecker(std::vector> const& _ast, ErrorReporter& _errorReporter): + m_ast(_ast), m_errorReporter(_errorReporter) {} + + bool check(); + +private: + + virtual bool visit(FunctionDefinition const& _funDef) override; + virtual void endVisit(FunctionDefinition const& _funDef) override; + virtual bool visit(ModifierDefinition const& _modifierDef) override; + virtual void endVisit(ModifierDefinition const& _modifierDef) override; + virtual void endVisit(Identifier const& _identifier) override; + virtual void endVisit(MemberAccess const& _memberAccess) override; + virtual void endVisit(IndexAccess const& _indexAccess) override; + virtual void endVisit(ModifierInvocation const& _modifier) override; + virtual void endVisit(FunctionCall const& _functionCall) override; + virtual void endVisit(InlineAssembly const& _inlineAssembly) override; + + /// Called when an element of mutability @a _mutability is encountered. + /// Creates appropriate warnings and errors and sets @a m_currentBestMutability. + void reportMutability(StateMutability _mutability, ASTNode const& _node); + + std::vector> const& m_ast; + ErrorReporter& m_errorReporter; + + bool m_errors = false; + StateMutability m_currentBestMutability = StateMutability::Payable; + FunctionDefinition const* m_currentFunction = nullptr; + std::map m_inferredMutability; +}; + +} +} diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index de6dcee9a..d4d6da690 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -1064,6 +1064,7 @@ public: { return _inLibrary ? shared_from_this() : TypePointer(); } + virtual bool dataStoredIn(DataLocation _location) const override { return _location == DataLocation::Storage; } TypePointer const& keyType() const { return m_keyType; } TypePointer const& valueType() const { return m_valueType; } diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 259694dab..99ad061c1 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -220,6 +221,16 @@ bool CompilerStack::analyze() noErrors = false; } + if (noErrors) + { + vector> ast; + for (Source const* source: m_sourceOrder) + ast.push_back(source->ast); + + if (!ViewPureChecker(ast, m_errorReporter).check()) + noErrors = false; + } + if (noErrors) { SMTChecker smtChecker(m_errorReporter, m_smtQuery); From 50047bf82c00952942b0a47826b6bf08490b76e3 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 18 Nov 2016 03:14:13 +0000 Subject: [PATCH 069/162] Change tests to use view or pure as appropriate --- test/libsolidity/SolidityEndToEndTest.cpp | 30 ++--- .../SolidityNameAndTypeResolution.cpp | 122 +++++++++--------- 2 files changed, 76 insertions(+), 76 deletions(-) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 73dd7d22c..175a6f480 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -6525,7 +6525,7 @@ BOOST_AUTO_TEST_CASE(state_variable_under_contract_name) contract Scope { uint stateVar = 42; - function getStateVar() constant returns (uint stateVar) { + function getStateVar() view returns (uint stateVar) { stateVar = Scope.stateVar; } } @@ -6791,7 +6791,7 @@ BOOST_AUTO_TEST_CASE(fixed_arrays_as_return_type) { char const* sourceCode = R"( contract A { - function f(uint16 input) constant returns (uint16[5] arr) + function f(uint16 input) pure returns (uint16[5] arr) { arr[0] = input; arr[1] = ++input; @@ -6820,7 +6820,7 @@ BOOST_AUTO_TEST_CASE(internal_types_in_library) { char const* sourceCode = R"( library Lib { - function find(uint16[] storage _haystack, uint16 _needle) constant returns (uint) + function find(uint16[] storage _haystack, uint16 _needle) pure returns (uint) { for (uint i = 0; i < _haystack.length; ++i) if (_haystack[i] == _needle) @@ -9913,12 +9913,12 @@ BOOST_AUTO_TEST_CASE(keccak256_assembly) { char const* sourceCode = R"( contract C { - function f() returns (bytes32 ret) { + function f() pure returns (bytes32 ret) { assembly { ret := keccak256(0, 0) } } - function g() returns (bytes32 ret) { + function g() pure returns (bytes32 ret) { assembly { 0 0 @@ -9926,12 +9926,12 @@ BOOST_AUTO_TEST_CASE(keccak256_assembly) =: ret } } - function h() returns (bytes32 ret) { + function h() pure returns (bytes32 ret) { assembly { ret := sha3(0, 0) } } - function i() returns (bytes32 ret) { + function i() pure returns (bytes32 ret) { assembly { 0 0 @@ -9979,7 +9979,7 @@ BOOST_AUTO_TEST_CASE(inlineasm_empty_let) { char const* sourceCode = R"( contract C { - function f() returns (uint a, uint b) { + function f() pure returns (uint a, uint b) { assembly { let x let y, z @@ -9998,13 +9998,13 @@ BOOST_AUTO_TEST_CASE(bare_call_invalid_address) char const* sourceCode = R"( contract C { /// Calling into non-existant account is successful (creates the account) - function f() external constant returns (bool) { + function f() external view returns (bool) { return address(0x4242).call(); } - function g() external constant returns (bool) { + function g() external view returns (bool) { return address(0x4242).callcode(); } - function h() external constant returns (bool) { + function h() external view returns (bool) { return address(0x4242).delegatecall(); } } @@ -10023,16 +10023,16 @@ BOOST_AUTO_TEST_CASE(delegatecall_return_value) function set(uint _value) external { value = _value; } - function get() external constant returns (uint) { + function get() external view returns (uint) { return value; } - function get_delegated() external constant returns (bool) { + function get_delegated() external view returns (bool) { return this.delegatecall(bytes4(sha3("get()"))); } - function assert0() external constant { + function assert0() external view { assert(value == 0); } - function assert0_delegated() external constant returns (bool) { + function assert0_delegated() external view returns (bool) { return this.delegatecall(bytes4(sha3("assert0()"))); } } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 944047815..b9be4d91b 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -1615,7 +1615,7 @@ BOOST_AUTO_TEST_CASE(warn_var_from_zero) { char const* sourceCode = R"( contract test { - function f() returns (uint) { + function f() pure returns (uint) { var i = 1; return i; } @@ -1633,7 +1633,7 @@ BOOST_AUTO_TEST_CASE(warn_var_from_zero) CHECK_WARNING(sourceCode, "uint256, which can hold values between 0 and 115792089237316195423570985008687907853269984665640564039457584007913129639935"); sourceCode = R"( contract test { - function f() { + function f() pure { var i = -2; i; } @@ -1642,7 +1642,7 @@ BOOST_AUTO_TEST_CASE(warn_var_from_zero) CHECK_WARNING(sourceCode, "int8, which can hold values between -128 and 127"); sourceCode = R"( contract test { - function f() { + function f() pure { for (var i = 0; i < msg.data.length; i++) { } } } @@ -2642,7 +2642,7 @@ BOOST_AUTO_TEST_CASE(uninitialized_mapping_array_variable) { char const* sourceCode = R"( contract C { - function f() { + function f() pure { mapping(uint => uint)[] storage x; x; } @@ -3973,7 +3973,7 @@ BOOST_AUTO_TEST_CASE(rational_unary_operation) { char const* text = R"( contract test { - function f() { + function f() pure { ufixed16x2 a = 3.25; fixed16x2 b = -3.25; a; b; @@ -3983,7 +3983,7 @@ BOOST_AUTO_TEST_CASE(rational_unary_operation) CHECK_SUCCESS_NO_WARNINGS(text); text = R"( contract test { - function f() { + function f() pure { ufixed16x2 a = +3.25; fixed16x2 b = -3.25; a; b; @@ -3993,7 +3993,7 @@ BOOST_AUTO_TEST_CASE(rational_unary_operation) CHECK_WARNING(text, "Use of unary + is deprecated"); text = R"( contract test { - function f(uint x) { + function f(uint x) pure { uint y = +x; y; } @@ -4006,7 +4006,7 @@ BOOST_AUTO_TEST_CASE(leading_zero_rationals_convert) { char const* text = R"( contract A { - function f() { + function f() pure { ufixed16x2 a = 0.5; ufixed256x52 b = 0.0000000000000006661338147750939242541790008544921875; fixed16x2 c = -0.5; @@ -4519,7 +4519,7 @@ BOOST_AUTO_TEST_CASE(warn_about_callcode) { char const* text = R"( contract test { - function f() { + function f() pure { var x = address(0x12).callcode; x; } @@ -4532,7 +4532,7 @@ BOOST_AUTO_TEST_CASE(no_warn_about_callcode_as_function) { char const* text = R"( contract test { - function callcode() { + function callcode() pure { test.callcode(); } } @@ -5275,7 +5275,7 @@ BOOST_AUTO_TEST_CASE(warns_msg_value_in_non_payable_public_function) { char const* text = R"( contract C { - function f() { + function f() view { msg.value; } } @@ -5299,7 +5299,7 @@ BOOST_AUTO_TEST_CASE(does_not_warn_msg_value_in_internal_function) { char const* text = R"( contract C { - function f() internal { + function f() view internal { msg.value; } } @@ -5311,7 +5311,7 @@ BOOST_AUTO_TEST_CASE(does_not_warn_msg_value_in_library) { char const* text = R"( library C { - function f() { + function f() view { msg.value; } } @@ -5323,7 +5323,7 @@ BOOST_AUTO_TEST_CASE(does_not_warn_msg_value_in_modifier_following_non_payable_p { char const* text = R"( contract c { - function f() { } + function f() pure { } modifier m() { msg.value; _; } } )"; @@ -5402,7 +5402,7 @@ BOOST_AUTO_TEST_CASE(invalid_address_checksum) { char const* text = R"( contract C { - function f() { + function f() pure { address x = 0xFA0bFc97E48458494Ccd857e1A85DC91F7F0046E; x; } @@ -5415,7 +5415,7 @@ BOOST_AUTO_TEST_CASE(invalid_address_no_checksum) { char const* text = R"( contract C { - function f() { + function f() pure { address x = 0xfa0bfc97e48458494ccd857e1a85dc91f7f0046e; x; } @@ -5428,7 +5428,7 @@ BOOST_AUTO_TEST_CASE(invalid_address_length) { char const* text = R"( contract C { - function f() { + function f() pure { address x = 0xA0bFc97E48458494Ccd857e1A85DC91F7F0046E; x; } @@ -5678,7 +5678,7 @@ BOOST_AUTO_TEST_CASE(warn_about_throw) { char const* text = R"( contract C { - function f() { + function f() pure { throw; } } @@ -5690,7 +5690,7 @@ BOOST_AUTO_TEST_CASE(bare_revert) { char const* text = R"( contract C { - function f(uint x) { + function f(uint x) pure { if (x > 7) revert; } @@ -5701,17 +5701,17 @@ BOOST_AUTO_TEST_CASE(bare_revert) BOOST_AUTO_TEST_CASE(bare_others) { - CHECK_WARNING("contract C { function f() { selfdestruct; } }", "Statement has no effect."); - CHECK_WARNING("contract C { function f() { assert; } }", "Statement has no effect."); - CHECK_WARNING("contract C { function f() { require; } }", "Statement has no effect."); - CHECK_WARNING("contract C { function f() { suicide; } }", "Statement has no effect."); + CHECK_WARNING("contract C { function f() pure { selfdestruct; } }", "Statement has no effect."); + CHECK_WARNING("contract C { function f() pure { assert; } }", "Statement has no effect."); + CHECK_WARNING("contract C { function f() pure { require; } }", "Statement has no effect."); + CHECK_WARNING("contract C { function f() pure { suicide; } }", "Statement has no effect."); } BOOST_AUTO_TEST_CASE(pure_statement_in_for_loop) { char const* text = R"( contract C { - function f() { + function f() pure { for (uint x = 0; x < 10; true) x++; } @@ -5724,7 +5724,7 @@ BOOST_AUTO_TEST_CASE(pure_statement_check_for_regular_for_loop) { char const* text = R"( contract C { - function f() { + function f() pure { for (uint x = 0; true; x++) {} } @@ -5780,7 +5780,7 @@ BOOST_AUTO_TEST_CASE(nowarn_swap_memory) char const* text = R"( contract C { struct S { uint a; uint b; } - function f() { + function f() pure { S memory x; S memory y; (x, y) = (y, x); @@ -5811,7 +5811,7 @@ BOOST_AUTO_TEST_CASE(warn_unused_local) { char const* text = R"( contract C { - function f() { + function f() pure { uint a; } } @@ -5823,7 +5823,7 @@ BOOST_AUTO_TEST_CASE(warn_unused_local_assigned) { char const* text = R"( contract C { - function f() { + function f() pure { uint a = 1; } } @@ -5835,14 +5835,14 @@ BOOST_AUTO_TEST_CASE(warn_unused_param) { char const* text = R"( contract C { - function f(uint a) { + function f(uint a) pure { } } )"; CHECK_WARNING(text, "Unused"); text = R"( contract C { - function f(uint a) { + function f(uint a) pure { } } )"; @@ -5853,14 +5853,14 @@ BOOST_AUTO_TEST_CASE(warn_unused_return_param) { char const* text = R"( contract C { - function f() returns (uint a) { + function f() pure returns (uint a) { } } )"; CHECK_WARNING(text, "Unused"); text = R"( contract C { - function f() returns (uint a) { + function f() pure returns (uint a) { return; } } @@ -5868,14 +5868,14 @@ BOOST_AUTO_TEST_CASE(warn_unused_return_param) CHECK_WARNING(text, "Unused"); text = R"( contract C { - function f() returns (uint) { + function f() pure returns (uint) { } } )"; CHECK_SUCCESS_NO_WARNINGS(text); text = R"( contract C { - function f() returns (uint a) { + function f() pure returns (uint a) { a = 1; } } @@ -5883,7 +5883,7 @@ BOOST_AUTO_TEST_CASE(warn_unused_return_param) CHECK_SUCCESS_NO_WARNINGS(text); text = R"( contract C { - function f() returns (uint a) { + function f() pure returns (uint a) { return 1; } } @@ -5895,7 +5895,7 @@ BOOST_AUTO_TEST_CASE(no_unused_warnings) { char const* text = R"( contract C { - function f(uint a) returns (uint b) { + function f(uint a) pure returns (uint b) { uint c = 1; b = a + c; } @@ -5908,7 +5908,7 @@ BOOST_AUTO_TEST_CASE(no_unused_dec_after_use) { char const* text = R"( contract C { - function f() { + function f() pure { a = 7; uint a; } @@ -5936,7 +5936,7 @@ BOOST_AUTO_TEST_CASE(shadowing_builtins_with_functions) { char const* text = R"( contract C { - function keccak256() {} + function keccak256() pure {} } )"; CHECK_WARNING(text, "shadows a builtin symbol"); @@ -5946,7 +5946,7 @@ BOOST_AUTO_TEST_CASE(shadowing_builtins_with_variables) { char const* text = R"( contract C { - function f() { + function f() pure { uint msg; msg; } @@ -5978,7 +5978,7 @@ BOOST_AUTO_TEST_CASE(shadowing_builtins_with_parameters) { char const* text = R"( contract C { - function f(uint require) { + function f(uint require) pure { require = 2; } } @@ -5990,7 +5990,7 @@ BOOST_AUTO_TEST_CASE(shadowing_builtins_with_return_parameters) { char const* text = R"( contract C { - function f() returns (uint require) { + function f() pure returns (uint require) { require = 2; } } @@ -6034,8 +6034,8 @@ BOOST_AUTO_TEST_CASE(function_overload_is_not_shadowing) { char const* text = R"( contract C { - function f() {} - function f(uint) {} + function f() pure {} + function f(uint) pure {} } )"; CHECK_SUCCESS_NO_WARNINGS(text); @@ -6044,9 +6044,9 @@ BOOST_AUTO_TEST_CASE(function_overload_is_not_shadowing) BOOST_AUTO_TEST_CASE(function_override_is_not_shadowing) { char const* text = R"( - contract D { function f() {} } + contract D { function f() pure {} } contract C is D { - function f(uint) {} + function f(uint) pure {} } )"; CHECK_SUCCESS_NO_WARNINGS(text); @@ -6140,7 +6140,7 @@ BOOST_AUTO_TEST_CASE(does_not_error_transfer_regular_function) { char const* text = R"( contract A { - function transfer() {} + function transfer() pure {} } contract B { @@ -6176,7 +6176,7 @@ BOOST_AUTO_TEST_CASE(warn_unspecified_storage) contract C { struct S { uint a; string b; } S x; - function f() { + function f() view { S storage y = x; y; } @@ -6187,7 +6187,7 @@ BOOST_AUTO_TEST_CASE(warn_unspecified_storage) contract C { struct S { uint a; } S x; - function f() { + function f() view { S y = x; y; } @@ -6213,21 +6213,21 @@ BOOST_AUTO_TEST_CASE(too_large_arrays_for_calldata) { char const* text = R"( contract C { - function f(uint[85678901234] a) external { + function f(uint[85678901234] a) pure external { } } )"; CHECK_ERROR(text, TypeError, "Array is too large to be encoded."); text = R"( contract C { - function f(uint[85678901234] a) internal { + function f(uint[85678901234] a) pure internal { } } )"; CHECK_ERROR(text, TypeError, "Array is too large to be encoded."); text = R"( contract C { - function f(uint[85678901234] a) { + function f(uint[85678901234] a) pure { } } )"; @@ -6238,7 +6238,7 @@ BOOST_AUTO_TEST_CASE(explicit_literal_to_storage_string) { char const* text = R"( contract C { - function f() { + function f() pure { string memory x = "abc"; x; } @@ -6247,7 +6247,7 @@ BOOST_AUTO_TEST_CASE(explicit_literal_to_storage_string) CHECK_SUCCESS_NO_WARNINGS(text); text = R"( contract C { - function f() { + function f() pure { string storage x = "abc"; } } @@ -6255,7 +6255,7 @@ BOOST_AUTO_TEST_CASE(explicit_literal_to_storage_string) CHECK_ERROR(text, TypeError, "Type literal_string \"abc\" is not implicitly convertible to expected type string storage pointer."); text = R"( contract C { - function f() { + function f() pure { string x = "abc"; } } @@ -6263,7 +6263,7 @@ BOOST_AUTO_TEST_CASE(explicit_literal_to_storage_string) CHECK_ERROR(text, TypeError, "Type literal_string \"abc\" is not implicitly convertible to expected type string storage pointer."); text = R"( contract C { - function f() { + function f() pure { string("abc"); } } @@ -6292,7 +6292,7 @@ BOOST_AUTO_TEST_CASE(using_this_in_constructor) function C() { this.f(); } - function f() { + function f() pure { } } )"; @@ -6559,7 +6559,7 @@ BOOST_AUTO_TEST_CASE(tight_packing_literals) { char const* text = R"( contract C { - function f() returns (bytes32) { + function f() pure returns (bytes32) { return keccak256(1); } } @@ -6567,7 +6567,7 @@ BOOST_AUTO_TEST_CASE(tight_packing_literals) CHECK_WARNING(text, "The type of \"int_const 1\" was inferred as uint8."); text = R"( contract C { - function f() returns (bytes32) { + function f() pure returns (bytes32) { return keccak256(uint8(1)); } } @@ -6575,7 +6575,7 @@ BOOST_AUTO_TEST_CASE(tight_packing_literals) CHECK_SUCCESS_NO_WARNINGS(text); text = R"( contract C { - function f() returns (bytes32) { + function f() pure returns (bytes32) { return sha3(1); } } @@ -6583,7 +6583,7 @@ BOOST_AUTO_TEST_CASE(tight_packing_literals) CHECK_WARNING(text, "The type of \"int_const 1\" was inferred as uint8."); text = R"( contract C { - function f() returns (bytes32) { + function f() pure returns (bytes32) { return sha256(1); } } @@ -6591,7 +6591,7 @@ BOOST_AUTO_TEST_CASE(tight_packing_literals) CHECK_WARNING(text, "The type of \"int_const 1\" was inferred as uint8."); text = R"( contract C { - function f() returns (bytes32) { + function f() pure returns (bytes32) { return ripemd160(1); } } From eacee5b25c1edbcf3e32128bd28bce5e58fc2a74 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 29 Aug 2017 16:29:28 +0200 Subject: [PATCH 070/162] Remove previous warning about pureness not being enforced. --- libsolidity/analysis/StaticAnalyzer.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index 2f1304146..ab1cbb526 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -57,8 +57,6 @@ bool StaticAnalyzer::visit(FunctionDefinition const& _function) solAssert(m_localVarUseCount.empty(), ""); m_nonPayablePublic = _function.isPublic() && !_function.isPayable(); m_constructor = _function.isConstructor(); - if (_function.stateMutability() == StateMutability::Pure) - m_errorReporter.warning(_function.location(), "Function is marked pure. Be careful, pureness is not enforced yet."); return true; } From 342367d5dcb897d1ef3bbc00853538859773ed37 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 29 Aug 2017 17:07:32 +0200 Subject: [PATCH 071/162] Store super function. --- libsolidity/analysis/TypeChecker.cpp | 3 +++ libsolidity/analysis/TypeChecker.h | 1 + libsolidity/ast/ASTAnnotations.h | 3 +++ libsolidity/ast/ASTJsonConverter.cpp | 1 + 4 files changed, 8 insertions(+) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index c46485d84..966ca560f 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -315,6 +315,9 @@ void TypeChecker::checkFunctionOverride(FunctionDefinition const& function, Func if (!functionType.hasEqualArgumentTypes(superType)) return; + if (!function.annotation().superFunction) + function.annotation().superFunction = &super; + if (function.visibility() != super.visibility()) overrideError(function, super, "Overriding function visibility differs."); diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index f2e13765d..0c6f54d3f 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -63,6 +63,7 @@ private: void checkContractDuplicateFunctions(ContractDefinition const& _contract); void checkContractIllegalOverrides(ContractDefinition const& _contract); /// Reports a type error with an appropiate message if overriden function signature differs. + /// Also stores the direct super function in the AST annotations. void checkFunctionOverride(FunctionDefinition const& function, FunctionDefinition const& super); void overrideError(FunctionDefinition const& function, FunctionDefinition const& super, std::string message); void checkContractAbstractFunctions(ContractDefinition const& _contract); diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index fd9efb4d9..3d4236cce 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -94,6 +94,9 @@ struct ContractDefinitionAnnotation: TypeDeclarationAnnotation, DocumentedAnnota struct FunctionDefinitionAnnotation: ASTAnnotation, DocumentedAnnotation { + /// The function this function overrides, if any. This is always the closest + /// in the linearized inheritance hierarchy. + FunctionDefinition const* superFunction = nullptr; }; struct EventDefinitionAnnotation: ASTAnnotation, DocumentedAnnotation diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index afc53bfe7..6811d3e4e 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -328,6 +328,7 @@ bool ASTJsonConverter::visit(FunctionDefinition const& _node) make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.stateMutability() <= StateMutability::View), make_pair("payable", _node.isPayable()), make_pair("stateMutability", stateMutabilityToString(_node.stateMutability())), + make_pair("superFunction", idOrNull(_node.annotation().superFunction)), make_pair("visibility", Declaration::visibilityToString(_node.visibility())), make_pair("parameters", toJson(_node.parameterList())), make_pair("isConstructor", _node.isConstructor()), From ec27e569b0da6650238a9b48aa53d120184001ed Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 29 Aug 2017 17:08:08 +0200 Subject: [PATCH 072/162] Do not report on overriding function and only warn for view. --- libsolidity/analysis/ViewPureChecker.cpp | 31 +++++++++++++++--------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index 0e2cfacf1..b4b7f3722 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -131,29 +131,36 @@ void ViewPureChecker::endVisit(InlineAssembly const& _inlineAssembly) reportMutability(StateMutability::NonPayable, _inlineAssembly); } -void ViewPureChecker::reportMutability(StateMutability _mutability, const ASTNode& _node) +void ViewPureChecker::reportMutability(StateMutability _mutability, ASTNode const& _node) { if (m_currentFunction && m_currentFunction->stateMutability() < _mutability) { - m_errors = true; + string text; if (_mutability == StateMutability::View) - m_errorReporter.typeError( - _node.location(), - "Function declared as pure, but this expression reads from the environment or state and thus " - "requires \"view\"." - ); + text = + "Function declared as pure, but this expression reads from the " + "environment or state and thus requires \"view\"."; else if (_mutability == StateMutability::NonPayable) - m_errorReporter.typeError( - _node.location(), + text = "Function declared as " + stateMutabilityToString(m_currentFunction->stateMutability()) + ", but this expression modifies the state and thus " - "requires non-payable (the default) or payable." - ); + "requires non-payable (the default) or payable."; + else + solAssert(false, ""); + + if (m_currentFunction->stateMutability() == StateMutability::View) + // Change this to error with 0.5.0 + m_errorReporter.warning(_node.location(), text); + else if (m_currentFunction->stateMutability() == StateMutability::Pure) + { + m_errors = true; + m_errorReporter.typeError(_node.location(), text); + } else solAssert(false, ""); } - if (_mutability >= m_currentBestMutability) + if (_mutability > m_currentBestMutability) m_currentBestMutability = _mutability; } From 41d0ad7a9c3b3a3ee26db0be9392b941b0803530 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 29 Aug 2017 15:32:20 +0200 Subject: [PATCH 073/162] New tests for view and pure checker. --- .../SolidityNameAndTypeResolution.cpp | 16 +- test/libsolidity/ViewPureChecker.cpp | 323 ++++++++++++++++++ 2 files changed, 331 insertions(+), 8 deletions(-) create mode 100644 test/libsolidity/ViewPureChecker.cpp diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index b9be4d91b..800f102ba 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -1546,7 +1546,7 @@ BOOST_AUTO_TEST_CASE(exp_warn_literal_base) { char const* sourceCode = R"( contract test { - function f() returns(uint) { + function f() pure returns(uint) { uint8 x = 100; return 10**x; } @@ -1555,7 +1555,7 @@ BOOST_AUTO_TEST_CASE(exp_warn_literal_base) CHECK_WARNING(sourceCode, "might overflow"); sourceCode = R"( contract test { - function f() returns(uint) { + function f() pure returns(uint) { uint8 x = 100; return uint8(10)**x; } @@ -1564,7 +1564,7 @@ BOOST_AUTO_TEST_CASE(exp_warn_literal_base) CHECK_SUCCESS(sourceCode); sourceCode = R"( contract test { - function f() returns(uint) { + function f() pure returns(uint) { return 2**80; } } @@ -1576,7 +1576,7 @@ BOOST_AUTO_TEST_CASE(shift_warn_literal_base) { char const* sourceCode = R"( contract test { - function f() returns(uint) { + function f() pure returns(uint) { uint8 x = 100; return 10 << x; } @@ -1585,7 +1585,7 @@ BOOST_AUTO_TEST_CASE(shift_warn_literal_base) CHECK_WARNING(sourceCode, "might overflow"); sourceCode = R"( contract test { - function f() returns(uint) { + function f() pure returns(uint) { uint8 x = 100; return uint8(10) << x; } @@ -1594,7 +1594,7 @@ BOOST_AUTO_TEST_CASE(shift_warn_literal_base) CHECK_SUCCESS(sourceCode); sourceCode = R"( contract test { - function f() returns(uint) { + function f() pure returns(uint) { return 2 << 80; } } @@ -1602,7 +1602,7 @@ BOOST_AUTO_TEST_CASE(shift_warn_literal_base) CHECK_SUCCESS(sourceCode); sourceCode = R"( contract test { - function f() returns(uint) { + function f() pure returns(uint) { uint8 x = 100; return 10 >> x; } @@ -1624,7 +1624,7 @@ BOOST_AUTO_TEST_CASE(warn_var_from_zero) CHECK_WARNING(sourceCode, "uint8, which can hold values between 0 and 255"); sourceCode = R"( contract test { - function f() { + function f() pure { var i = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; i; } diff --git a/test/libsolidity/ViewPureChecker.cpp b/test/libsolidity/ViewPureChecker.cpp new file mode 100644 index 000000000..7099ffd7a --- /dev/null +++ b/test/libsolidity/ViewPureChecker.cpp @@ -0,0 +1,323 @@ +/* + 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 . +*/ +/** + * Unit tests for the view and pure checker. + */ + +#include + +#include + +#include + +using namespace std; + +namespace dev +{ +namespace solidity +{ +namespace test +{ + +BOOST_FIXTURE_TEST_SUITE(ViewPureChecker, AnalysisFramework) + +BOOST_AUTO_TEST_CASE(smoke_test) +{ + char const* text = R"( + contract C { + uint x; + function g() pure {} + function f() view returns (uint) { return now; } + function h() { x = 2; } + function i() payable { x = 2; } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(call_internal_functions_success) +{ + char const* text = R"( + contract C { + function g() pure { g(); } + function f() view returns (uint) { f(); g(); } + function h() { h(); g(); f(); } + function i() payable { i(); h(); g(); f(); } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(suggest_pure) +{ + char const* text = R"( + contract C { + function g() view { } + } + )"; + CHECK_WARNING(text, "can be restricted to pure"); +} + +BOOST_AUTO_TEST_CASE(suggest_view) +{ + char const* text = R"( + contract C { + uint x; + function g() returns (uint) { return x; } + } + )"; + CHECK_WARNING(text, "can be restricted to view"); +} + +BOOST_AUTO_TEST_CASE(call_internal_functions_fail) +{ + CHECK_ERROR( + "contract C{ function f() pure { g(); } function g() view {} }", + TypeError, + "Function declared as pure, but this expression reads from the environment or state and thus requires \"view\"" + ); +} + +BOOST_AUTO_TEST_CASE(write_storage_fail) +{ + CHECK_WARNING( + "contract C{ uint x; function f() view { x = 2; } }", + "Function declared as view, but this expression modifies the state and thus requires non-payable (the default) or payable." + ); +} + +BOOST_AUTO_TEST_CASE(environment_access) +{ + vector view{ + "block.coinbase", + "block.timestamp", + "block.blockhash(7)", + "block.difficulty", + "block.number", + "block.gaslimit", + "msg.gas", + "msg.value", + "msg.sender", + "tx.origin", + "tx.gasprice", + "this", + "address(1).balance" + }; + vector pure{ + "msg.data", + "msg.data[0]", + "msg.sig", + "block.blockhash", // Not evaluating the function + "msg", + "block", + "tx" + }; + for (string const& x: view) + { + CHECK_ERROR( + "contract C { function f() pure { var x = " + x + "; x; } }", + TypeError, + "Function declared as pure, but this expression reads from the environment or state and thus requires \"view\"" + ); + } + for (string const& x: pure) + { + CHECK_WARNING( + "contract C { function f() view { var x = " + x + "; x; } }", + "restricted to pure" + ); + } +} + +BOOST_AUTO_TEST_CASE(modifiers) +{ + string text = R"( + contract D { + uint x; + modifier purem(uint) { _; } + modifier viewm(uint) { uint a = x; _; a; } + modifier nonpayablem(uint) { x = 2; _; } + } + contract C is D { + function f() purem(0) pure {} + function g() viewm(0) view {} + function h() nonpayablem(0) {} + function i() purem(x) view {} + function j() viewm(x) view {} + function k() nonpayablem(x) {} + function l() purem(x = 2) {} + function m() viewm(x = 2) {} + function n() nonpayablem(x = 2) {} + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(interface) +{ + string text = R"( + interface D { + function f() view; + } + contract C is D { + function f() view {} + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(overriding) +{ + string text = R"( + contract D { + uint x; + function f() { x = 2; } + } + contract C is D { + function f() {} + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(returning_structs) +{ + string text = R"( + contract C { + struct S { uint x; } + S s; + function f() view internal returns (S storage) { + return s; + } + function g() + { + f().x = 2; + } + function h() view + { + f(); + f().x; + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(mappings) +{ + string text = R"( + contract C { + mapping(uint => uint) a; + function f() view { + a; + } + function g() view { + a[2]; + } + function h() { + a[2] = 3; + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(local_storage_variables) +{ + string text = R"( + contract C { + struct S { uint a; } + S s; + function f() view { + S storage x = s; + x; + } + function g() view { + S storage x = s; + x = s; + } + function i() { + s.a = 2; + } + function h() { + S storage x = s; + x.a = 2; + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(builtin_functions) +{ + string text = R"( + contract C { + function f() { + this.transfer(1); + require(this.send(2)); + selfdestruct(this); + require(this.delegatecall()); + require(this.call()); + } + function g() pure { + var x = keccak256("abc"); + var y = sha256("abc"); + var z = ecrecover(1, 2, 3, 4); + require(true); + assert(true); + x; y; z; + } + function() payable {} + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(function_types) +{ + string text = R"( + contract C { + function f() pure { + function () external nonpayFun; + function () external view viewFun; + function () external pure pureFun; + + nonpayFun; + viewFun; + pureFun; + pureFun(); + } + function g() view { + function () external view viewFun; + + viewFun(); + } + function h() { + function () external nonpayFun; + + nonpayFun(); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_SUITE_END() + +} +} +} From 7c61a6daf2052afaba86ad52623028cc8e14572b Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 31 Aug 2017 13:13:05 +0200 Subject: [PATCH 074/162] Add view/pure information about instructions. --- libevmasm/SemanticInformation.cpp | 52 +++++++++++++++++++++++++++++++ libevmasm/SemanticInformation.h | 2 ++ 2 files changed, 54 insertions(+) diff --git a/libevmasm/SemanticInformation.cpp b/libevmasm/SemanticInformation.cpp index f63f0c616..abe47e10e 100644 --- a/libevmasm/SemanticInformation.cpp +++ b/libevmasm/SemanticInformation.cpp @@ -188,3 +188,55 @@ bool SemanticInformation::invalidatesStorage(Instruction _instruction) return false; } } + +bool SemanticInformation::invalidInPureFunctions(Instruction _instruction) +{ + switch (_instruction) + { + case Instruction::ADDRESS: + case Instruction::BALANCE: + case Instruction::ORIGIN: + case Instruction::CALLER: + case Instruction::CALLVALUE: + case Instruction::GASPRICE: + case Instruction::EXTCODESIZE: + case Instruction::EXTCODECOPY: + case Instruction::BLOCKHASH: + case Instruction::COINBASE: + case Instruction::TIMESTAMP: + case Instruction::NUMBER: + case Instruction::DIFFICULTY: + case Instruction::GASLIMIT: + case Instruction::SLOAD: + return true; + default: + break; + } + return invalidInViewFunctions(_instruction); +} + +bool SemanticInformation::invalidInViewFunctions(Instruction _instruction) +{ + switch (_instruction) + { + case Instruction::SSTORE: + case Instruction::JUMP: + case Instruction::JUMPI: + case Instruction::GAS: + case Instruction::LOG0: + case Instruction::LOG1: + case Instruction::LOG2: + case Instruction::LOG3: + case Instruction::LOG4: + case Instruction::CREATE: + case Instruction::CALL: + case Instruction::CALLCODE: + case Instruction::DELEGATECALL: + case Instruction::CREATE2: + case Instruction::SELFDESTRUCT: + return true; + default: + break; + } + return false; +} diff --git a/libevmasm/SemanticInformation.h b/libevmasm/SemanticInformation.h index 5b02061fc..e5ea7c180 100644 --- a/libevmasm/SemanticInformation.h +++ b/libevmasm/SemanticInformation.h @@ -53,6 +53,8 @@ struct SemanticInformation static bool invalidatesMemory(solidity::Instruction _instruction); /// @returns true if the given instruction modifies storage (even indirectly). static bool invalidatesStorage(solidity::Instruction _instruction); + static bool invalidInPureFunctions(solidity::Instruction _instruction); + static bool invalidInViewFunctions(solidity::Instruction _instruction); }; } From d6861d909c7dabb6993cfa7a8269f76c613428f7 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 31 Aug 2017 13:13:35 +0200 Subject: [PATCH 075/162] Analyze assembly. --- libevmasm/SemanticInformation.cpp | 1 + libsolidity/analysis/ViewPureChecker.cpp | 97 +++++++++++++++++-- libsolidity/analysis/ViewPureChecker.h | 2 +- .../SolidityNameAndTypeResolution.cpp | 4 +- test/libsolidity/ViewPureChecker.cpp | 62 ++++++++++++ 5 files changed, 153 insertions(+), 13 deletions(-) diff --git a/libevmasm/SemanticInformation.cpp b/libevmasm/SemanticInformation.cpp index abe47e10e..ceb3fbdd0 100644 --- a/libevmasm/SemanticInformation.cpp +++ b/libevmasm/SemanticInformation.cpp @@ -207,6 +207,7 @@ bool SemanticInformation::invalidInPureFunctions(Instruction _instruction) case Instruction::NUMBER: case Instruction::DIFFICULTY: case Instruction::GASLIMIT: + case Instruction::STATICCALL: case Instruction::SLOAD: return true; default: diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index b4b7f3722..60b07297a 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -17,10 +17,86 @@ #include +#include + +#include + +#include + using namespace std; using namespace dev; using namespace dev::solidity; + +class AssemblyViewPureChecker: public boost::static_visitor +{ +public: + explicit AssemblyViewPureChecker(std::function _reportMutability): + m_reportMutability(_reportMutability) {} + + void operator()(assembly::Label const&) { } + void operator()(assembly::Instruction const& _instruction) + { + if (eth::SemanticInformation::invalidInViewFunctions(_instruction.instruction)) + m_reportMutability(StateMutability::NonPayable, _instruction.location); + else if (eth::SemanticInformation::invalidInPureFunctions(_instruction.instruction)) + m_reportMutability(StateMutability::View, _instruction.location); + } + void operator()(assembly::Literal const&) {} + void operator()(assembly::Identifier const&) {} + void operator()(assembly::FunctionalInstruction const& _instr) + { + (*this)(_instr.instruction); + for (auto const& arg: _instr.arguments) + boost::apply_visitor(*this, arg); + } + void operator()(assembly::StackAssignment const&) {} + void operator()(assembly::Assignment const& _assignment) + { + boost::apply_visitor(*this, *_assignment.value); + } + void operator()(assembly::VariableDeclaration const& _varDecl) + { + if (_varDecl.value) + boost::apply_visitor(*this, *_varDecl.value); + } + void operator()(assembly::FunctionDefinition const& _funDef) + { + (*this)(_funDef.body); + } + void operator()(assembly::FunctionCall const& _funCall) + { + for (auto const& arg: _funCall.arguments) + boost::apply_visitor(*this, arg); + } + void operator()(assembly::Switch const& _switch) + { + boost::apply_visitor(*this, *_switch.expression); + for (auto const& _case: _switch.cases) + { + if (_case.value) + (*this)(*_case.value); + (*this)(_case.body); + } + } + void operator()(assembly::ForLoop const& _for) + { + (*this)(_for.pre); + boost::apply_visitor(*this, *_for.condition); + (*this)(_for.body); + (*this)(_for.post); + } + void operator()(assembly::Block const& _block) + { + for (auto const& s: _block.statements) + boost::apply_visitor(*this, s); + } + +private: + std::function m_reportMutability; +}; + + bool ViewPureChecker::check() { vector contracts; @@ -122,16 +198,17 @@ void ViewPureChecker::endVisit(Identifier const& _identifier) } } - reportMutability(mutability, _identifier); + reportMutability(mutability, _identifier.location()); } void ViewPureChecker::endVisit(InlineAssembly const& _inlineAssembly) { - // @TOOD we can and should analyze it further. - reportMutability(StateMutability::NonPayable, _inlineAssembly); + AssemblyViewPureChecker{ + [=](StateMutability _mut, SourceLocation const& _loc) { reportMutability(_mut, _loc); } + }(_inlineAssembly.operations()); } -void ViewPureChecker::reportMutability(StateMutability _mutability, ASTNode const& _node) +void ViewPureChecker::reportMutability(StateMutability _mutability, SourceLocation const& _location) { if (m_currentFunction && m_currentFunction->stateMutability() < _mutability) { @@ -151,11 +228,11 @@ void ViewPureChecker::reportMutability(StateMutability _mutability, ASTNode cons if (m_currentFunction->stateMutability() == StateMutability::View) // Change this to error with 0.5.0 - m_errorReporter.warning(_node.location(), text); + m_errorReporter.warning(_location, text); else if (m_currentFunction->stateMutability() == StateMutability::Pure) { m_errors = true; - m_errorReporter.typeError(_node.location(), text); + m_errorReporter.typeError(_location, text); } else solAssert(false, ""); @@ -173,7 +250,7 @@ void ViewPureChecker::endVisit(FunctionCall const& _functionCall) // We only require "nonpayable" to call a payble function. if (mut == StateMutability::Payable) mut = StateMutability::NonPayable; - reportMutability(mut, _functionCall); + reportMutability(mut, _functionCall.location()); } void ViewPureChecker::endVisit(MemberAccess const& _memberAccess) @@ -210,7 +287,7 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess) default: break; } - reportMutability(mutability, _memberAccess); + reportMutability(mutability, _memberAccess.location()); } void ViewPureChecker::endVisit(IndexAccess const& _indexAccess) @@ -219,7 +296,7 @@ void ViewPureChecker::endVisit(IndexAccess const& _indexAccess) bool writes = _indexAccess.annotation().lValueRequested; if (_indexAccess.baseExpression().annotation().type->dataStoredIn(DataLocation::Storage)) - reportMutability(writes ? StateMutability::NonPayable : StateMutability::View, _indexAccess); + reportMutability(writes ? StateMutability::NonPayable : StateMutability::View, _indexAccess.location()); } void ViewPureChecker::endVisit(ModifierInvocation const& _modifier) @@ -229,6 +306,6 @@ void ViewPureChecker::endVisit(ModifierInvocation const& _modifier) solAssert(mod, ""); solAssert(m_inferredMutability.count(mod), ""); - reportMutability(m_inferredMutability.at(mod), _modifier); + reportMutability(m_inferredMutability.at(mod), _modifier.location()); } diff --git a/libsolidity/analysis/ViewPureChecker.h b/libsolidity/analysis/ViewPureChecker.h index 6aedfa369..ae3035336 100644 --- a/libsolidity/analysis/ViewPureChecker.h +++ b/libsolidity/analysis/ViewPureChecker.h @@ -64,7 +64,7 @@ private: /// Called when an element of mutability @a _mutability is encountered. /// Creates appropriate warnings and errors and sets @a m_currentBestMutability. - void reportMutability(StateMutability _mutability, ASTNode const& _node); + void reportMutability(StateMutability _mutability, SourceLocation const& _location); std::vector> const& m_ast; ErrorReporter& m_errorReporter; diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 800f102ba..6886fcd0b 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -5234,7 +5234,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_storage_variable_access_out_of_functions) char const* text = R"( contract test { uint a; - function f() { + function f() pure { assembly { function g() -> x { x := a_slot } } @@ -5921,7 +5921,7 @@ BOOST_AUTO_TEST_CASE(no_unused_inline_asm) { char const* text = R"( contract C { - function f() { + function f() pure { uint a; assembly { a := 1 diff --git a/test/libsolidity/ViewPureChecker.cpp b/test/libsolidity/ViewPureChecker.cpp index 7099ffd7a..fabd1bee0 100644 --- a/test/libsolidity/ViewPureChecker.cpp +++ b/test/libsolidity/ViewPureChecker.cpp @@ -316,6 +316,68 @@ BOOST_AUTO_TEST_CASE(function_types) CHECK_SUCCESS_NO_WARNINGS(text); } +BOOST_AUTO_TEST_CASE(creation) +{ + string text = R"( + contract D {} + contract C { + function f() { new D(); } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(assembly) +{ + string text = R"( + contract C { + struct S { uint x; } + S s; + function e() pure { + assembly { mstore(keccak256(0, 20), mul(s_slot, 2)) } + } + function f() pure { + uint x; + assembly { x := 7 } + } + function g() view { + assembly { for {} 1 { pop(sload(0)) } { } } + } + function h() view { + assembly { function g() { pop(blockhash(20)) } } + } + function j() { + assembly { pop(call(0, 1, 2, 3, 4, 5, 6)) } + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(assembly_staticcall) +{ + string text = R"( + contract C { + function i() view { + assembly { pop(staticcall(0, 1, 2, 3, 4, 5)) } + } + } + )"; + CHECK_WARNING(text, "only available after the Metropolis"); +} + +BOOST_AUTO_TEST_CASE(assembly_jump) +{ + string text = R"( + contract C { + function k() { + assembly { jump(2) } + } + } + )"; + CHECK_WARNING(text, "low-level EVM features"); +} + BOOST_AUTO_TEST_SUITE_END() } From 7886c24d4003beb155b6dba119b92d2df7ec0140 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 31 Aug 2017 16:03:50 +0200 Subject: [PATCH 076/162] Modifier invocation can be base constructor call --- libsolidity/analysis/ViewPureChecker.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index 60b07297a..55f391b73 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -302,10 +302,12 @@ void ViewPureChecker::endVisit(IndexAccess const& _indexAccess) void ViewPureChecker::endVisit(ModifierInvocation const& _modifier) { solAssert(_modifier.name(), ""); - ModifierDefinition const* mod = dynamic_cast(_modifier.name()->annotation().referencedDeclaration); - solAssert(mod, ""); - solAssert(m_inferredMutability.count(mod), ""); - - reportMutability(m_inferredMutability.at(mod), _modifier.location()); + if (ModifierDefinition const* mod = dynamic_cast(_modifier.name()->annotation().referencedDeclaration)) + { + solAssert(m_inferredMutability.count(mod), ""); + reportMutability(m_inferredMutability.at(mod), _modifier.location()); + } + else + solAssert(dynamic_cast(_modifier.name()->annotation().referencedDeclaration), ""); } From 1a1db1ec963935bffcabd6115cf99d04fd1d633b Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 31 Aug 2017 16:14:54 +0200 Subject: [PATCH 077/162] Tone down error message. --- libsolidity/analysis/ViewPureChecker.cpp | 4 ++-- test/libsolidity/ViewPureChecker.cpp | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index 55f391b73..e5b433c25 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -215,13 +215,13 @@ void ViewPureChecker::reportMutability(StateMutability _mutability, SourceLocati string text; if (_mutability == StateMutability::View) text = - "Function declared as pure, but this expression reads from the " + "Function declared as pure, but this expression (potentially) reads from the " "environment or state and thus requires \"view\"."; else if (_mutability == StateMutability::NonPayable) text = "Function declared as " + stateMutabilityToString(m_currentFunction->stateMutability()) + - ", but this expression modifies the state and thus " + ", but this expression (potentially) modifies the state and thus " "requires non-payable (the default) or payable."; else solAssert(false, ""); diff --git a/test/libsolidity/ViewPureChecker.cpp b/test/libsolidity/ViewPureChecker.cpp index fabd1bee0..6e99260c0 100644 --- a/test/libsolidity/ViewPureChecker.cpp +++ b/test/libsolidity/ViewPureChecker.cpp @@ -88,7 +88,7 @@ BOOST_AUTO_TEST_CASE(call_internal_functions_fail) CHECK_ERROR( "contract C{ function f() pure { g(); } function g() view {} }", TypeError, - "Function declared as pure, but this expression reads from the environment or state and thus requires \"view\"" + "Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires \"view\"" ); } @@ -96,7 +96,7 @@ BOOST_AUTO_TEST_CASE(write_storage_fail) { CHECK_WARNING( "contract C{ uint x; function f() view { x = 2; } }", - "Function declared as view, but this expression modifies the state and thus requires non-payable (the default) or payable." + "Function declared as view, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable." ); } @@ -131,7 +131,7 @@ BOOST_AUTO_TEST_CASE(environment_access) CHECK_ERROR( "contract C { function f() pure { var x = " + x + "; x; } }", TypeError, - "Function declared as pure, but this expression reads from the environment or state and thus requires \"view\"" + "Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires \"view\"" ); } for (string const& x: pure) From c83768c4260979b6d30185ef19352d27b161c3b0 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 1 Sep 2017 12:31:24 +0200 Subject: [PATCH 078/162] Fix tests --- libsolidity/analysis/ViewPureChecker.cpp | 13 ++++++++----- test/libsolidity/SolidityEndToEndTest.cpp | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index e5b433c25..705d61e88 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -292,11 +292,14 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess) void ViewPureChecker::endVisit(IndexAccess const& _indexAccess) { - solAssert(_indexAccess.indexExpression(), ""); - - bool writes = _indexAccess.annotation().lValueRequested; - if (_indexAccess.baseExpression().annotation().type->dataStoredIn(DataLocation::Storage)) - reportMutability(writes ? StateMutability::NonPayable : StateMutability::View, _indexAccess.location()); + if (!_indexAccess.indexExpression()) + solAssert(_indexAccess.annotation().type->category() == Type::Category::TypeType, ""); + else + { + bool writes = _indexAccess.annotation().lValueRequested; + if (_indexAccess.baseExpression().annotation().type->dataStoredIn(DataLocation::Storage)) + reportMutability(writes ? StateMutability::NonPayable : StateMutability::View, _indexAccess.location()); + } } void ViewPureChecker::endVisit(ModifierInvocation const& _modifier) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 175a6f480..fa4d675cc 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -6820,7 +6820,7 @@ BOOST_AUTO_TEST_CASE(internal_types_in_library) { char const* sourceCode = R"( library Lib { - function find(uint16[] storage _haystack, uint16 _needle) pure returns (uint) + function find(uint16[] storage _haystack, uint16 _needle) view returns (uint) { for (uint i = 0; i < _haystack.length; ++i) if (_haystack[i] == _needle) From 15bdc48a73d1cbebb9f75830fd1abbd29f798c12 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 1 Sep 2017 19:58:38 +0200 Subject: [PATCH 079/162] Rename and add anonymous namespace. --- libevmasm/SemanticInformation.cpp | 2 +- libevmasm/SemanticInformation.h | 2 +- libsolidity/analysis/ViewPureChecker.cpp | 5 ++++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/libevmasm/SemanticInformation.cpp b/libevmasm/SemanticInformation.cpp index ceb3fbdd0..e277a08d2 100644 --- a/libevmasm/SemanticInformation.cpp +++ b/libevmasm/SemanticInformation.cpp @@ -189,7 +189,7 @@ bool SemanticInformation::invalidatesStorage(Instruction _instruction) } } -bool SemanticInformation::invalidInPureFunctions(Instruction _instruction) +bool SemanticInformation::readsFromState(Instruction _instruction) { switch (_instruction) { diff --git a/libevmasm/SemanticInformation.h b/libevmasm/SemanticInformation.h index e5ea7c180..963088d98 100644 --- a/libevmasm/SemanticInformation.h +++ b/libevmasm/SemanticInformation.h @@ -53,7 +53,7 @@ struct SemanticInformation static bool invalidatesMemory(solidity::Instruction _instruction); /// @returns true if the given instruction modifies storage (even indirectly). static bool invalidatesStorage(solidity::Instruction _instruction); - static bool invalidInPureFunctions(solidity::Instruction _instruction); + static bool readsFromState(solidity::Instruction _instruction); static bool invalidInViewFunctions(solidity::Instruction _instruction); }; diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index 705d61e88..6621edb1d 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -27,6 +27,8 @@ using namespace std; using namespace dev; using namespace dev::solidity; +namespace +{ class AssemblyViewPureChecker: public boost::static_visitor { @@ -39,7 +41,7 @@ public: { if (eth::SemanticInformation::invalidInViewFunctions(_instruction.instruction)) m_reportMutability(StateMutability::NonPayable, _instruction.location); - else if (eth::SemanticInformation::invalidInPureFunctions(_instruction.instruction)) + else if (eth::SemanticInformation::readsFromState(_instruction.instruction)) m_reportMutability(StateMutability::View, _instruction.location); } void operator()(assembly::Literal const&) {} @@ -96,6 +98,7 @@ private: std::function m_reportMutability; }; +} bool ViewPureChecker::check() { From b756274357c1e33517d535fdd2349a88221d4780 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 5 Sep 2017 18:33:52 +0200 Subject: [PATCH 080/162] Allow constant variables in pure functions. --- libsolidity/analysis/ViewPureChecker.cpp | 2 +- test/libsolidity/ViewPureChecker.cpp | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index 6621edb1d..d4a6e96f1 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -179,7 +179,7 @@ void ViewPureChecker::endVisit(Identifier const& _identifier) bool writes = _identifier.annotation().lValueRequested; if (VariableDeclaration const* varDecl = dynamic_cast(declaration)) { - if (varDecl->isStateVariable()) + if (varDecl->isStateVariable() && !varDecl->isConstant()) mutability = writes ? StateMutability::NonPayable : StateMutability::View; } else if (MagicVariableDeclaration const* magicVar = dynamic_cast(declaration)) diff --git a/test/libsolidity/ViewPureChecker.cpp b/test/libsolidity/ViewPureChecker.cpp index 6e99260c0..9cea98505 100644 --- a/test/libsolidity/ViewPureChecker.cpp +++ b/test/libsolidity/ViewPureChecker.cpp @@ -378,6 +378,19 @@ BOOST_AUTO_TEST_CASE(assembly_jump) CHECK_WARNING(text, "low-level EVM features"); } +BOOST_AUTO_TEST_CASE(constant) +{ + string text = R"( + contract C { + uint constant x = 2; + function k() pure returns (uint) { + return x; + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + BOOST_AUTO_TEST_SUITE_END() } From 333a6e5280a6a5dba244aa7859d67325f85e07c5 Mon Sep 17 00:00:00 2001 From: Yoichi Hirai Date: Thu, 7 Sep 2017 17:56:28 +0200 Subject: [PATCH 081/162] Add the Travis build status badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1fed49fb4..cb7437296 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # The Solidity Contract-Oriented Programming Language -[![Join the chat at https://gitter.im/ethereum/solidity](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ethereum/solidity?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![Join the chat at https://gitter.im/ethereum/solidity](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ethereum/solidity?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status](https://travis-ci.org/ethereum/solidity.svg?branch=develop)](https://travis-ci.org/ethereum/solidity) ## Useful links To get started you can find an introduction to the language in the [Solidity documentation](https://solidity.readthedocs.org). In the documentation, you can find [code examples](https://solidity.readthedocs.io/en/latest/solidity-by-example.html) as well as [a reference](https://solidity.readthedocs.io/en/latest/solidity-in-depth.html) of the syntax and details on how to write smart contracts. From b1a7281ee90065d3497d87825764fc9c008698ea Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 5 Sep 2017 14:20:53 +0100 Subject: [PATCH 082/162] Promote metadata to its own chapter (split from miscellaneous) --- docs/index.rst | 1 + docs/metadata.rst | 144 +++++++++++++++++++++++++++++++++++++++++ docs/miscellaneous.rst | 144 ----------------------------------------- 3 files changed, 145 insertions(+), 144 deletions(-) create mode 100644 docs/metadata.rst diff --git a/docs/index.rst b/docs/index.rst index 8c33fb9da..cb093bd6b 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -144,6 +144,7 @@ Contents solidity-in-depth.rst security-considerations.rst using-the-compiler.rst + metadata.rst abi-spec.rst style-guide.rst common-patterns.rst diff --git a/docs/metadata.rst b/docs/metadata.rst new file mode 100644 index 000000000..dbde87e86 --- /dev/null +++ b/docs/metadata.rst @@ -0,0 +1,144 @@ +################# +Contract Metadata +################# + +.. index:: metadata, contract verification + +The Solidity compiler automatically generates a JSON file, the +contract metadata, that contains information about the current contract. +It can be used to query the compiler version, the sources used, the ABI +and NatSpec documentation in order to more safely interact with the contract +and to verify its source code. + +The compiler appends a Swarm hash of the metadata file to the end of the +bytecode (for details, see below) of each contract, so that you can retrieve +the file in an authenticated way without having to resort to a centralized +data provider. + +Of course, you have to publish the metadata file to Swarm (or some other service) +so that others can access it. The file can be output by using ``solc --metadata`` +and the file will be called ``ContractName_meta.json``. +It will contain Swarm references to the source code, so you have to upload +all source files and the metadata file. + +The metadata file has the following format. The example below is presented in a +human-readable way. Properly formatted metadata should use quotes correctly, +reduce whitespace to a minimum and sort the keys of all objects to arrive at a +unique formatting. +Comments are of course also not permitted and used here only for explanatory purposes. + +.. code-block:: none + + { + // Required: The version of the metadata format + version: "1", + // Required: Source code language, basically selects a "sub-version" + // of the specification + language: "Solidity", + // Required: Details about the compiler, contents are specific + // to the language. + compiler: { + // Required for Solidity: Version of the compiler + version: "0.4.6+commit.2dabbdf0.Emscripten.clang", + // Optional: Hash of the compiler binary which produced this output + keccak256: "0x123..." + }, + // Required: Compilation source files/source units, keys are file names + sources: + { + "myFile.sol": { + // Required: keccak256 hash of the source file + "keccak256": "0x123...", + // Required (unless "content" is used, see below): Sorted URL(s) + // to the source file, protocol is more or less arbitrary, but a + // Swarm URL is recommended + "urls": [ "bzzr://56ab..." ] + }, + "mortal": { + // Required: keccak256 hash of the source file + "keccak256": "0x234...", + // Required (unless "url" is used): literal contents of the source file + "content": "contract mortal is owned { function kill() { if (msg.sender == owner) selfdestruct(owner); } }" + } + }, + // Required: Compiler settings + settings: + { + // Required for Solidity: Sorted list of remappings + remappings: [ ":g/dir" ], + // Optional: Optimizer settings (enabled defaults to false) + optimizer: { + enabled: true, + runs: 500 + }, + // Required for Solidity: File and name of the contract or library this + // metadata is created for. + compilationTarget: { + "myFile.sol": "MyContract" + }, + // Required for Solidity: Addresses for libraries used + libraries: { + "MyLib": "0x123123..." + } + }, + // Required: Generated information about the contract. + output: + { + // Required: ABI definition of the contract + abi: [ ... ], + // Required: NatSpec user documentation of the contract + userdoc: [ ... ], + // Required: NatSpec developer documentation of the contract + devdoc: [ ... ], + } + } + +.. note:: + Note the ABI definition above has no fixed order. It can change with compiler versions. + +.. note:: + Since the bytecode of the resulting contract contains the metadata hash, any change to + the metadata will result in a change of the bytecode. Furthermore, since the metadata + includes a hash of all the sources used, a single whitespace change in any of the source + codes will result in a different metadata, and subsequently a different bytecode. + +Encoding of the Metadata Hash in the Bytecode +============================================= + +Because we might support other ways to retrieve the metadata file in the future, +the mapping ``{"bzzr0": }`` is stored +`CBOR `_-encoded. Since the beginning of that +encoding is not easy to find, its length is added in a two-byte big-endian +encoding. The current version of the Solidity compiler thus adds the following +to the end of the deployed bytecode:: + + 0xa1 0x65 'b' 'z' 'z' 'r' '0' 0x58 0x20 <32 bytes swarm hash> 0x00 0x29 + +So in order to retrieve the data, the end of the deployed bytecode can be checked +to match that pattern and use the Swarm hash to retrieve the file. + +Usage for Automatic Interface Generation and NatSpec +==================================================== + +The metadata is used in the following way: A component that wants to interact +with a contract (e.g. Mist) retrieves the code of the contract, from that +the Swarm hash of a file which is then retrieved. +That file is JSON-decoded into a structure like above. + +The component can then use the ABI to automatically generate a rudimentary +user interface for the contract. + +Furthermore, Mist can use the userdoc to display a confirmation message to the user +whenever they interact with the contract. + +Usage for Source Code Verification +================================== + +In order to verify the compilation, sources can be retrieved from Swarm +via the link in the metadata file. +The compiler of the correct version (which is checked to be part of the "official" compilers) +is invoked on that input with the specified settings. The resulting +bytecode is compared to the data of the creation transaction or ``CREATE`` opcode data. +This automatically verifies the metadata since its hash is part of the bytecode. +Excess data corresponds to the constructor input data, which should be decoded +according to the interface and presented to the user. diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index e9b01340c..6d6c25acb 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -221,150 +221,6 @@ This means the following source mappings represent the same information: ``1:2:1;:9;2::2;;`` -***************** -Contract Metadata -***************** - -The Solidity compiler automatically generates a JSON file, the -contract metadata, that contains information about the current contract. -It can be used to query the compiler version, the sources used, the ABI -and NatSpec documentation in order to more safely interact with the contract -and to verify its source code. - -The compiler appends a Swarm hash of the metadata file to the end of the -bytecode (for details, see below) of each contract, so that you can retrieve -the file in an authenticated way without having to resort to a centralized -data provider. - -Of course, you have to publish the metadata file to Swarm (or some other service) -so that others can access it. The file can be output by using ``solc --metadata`` -and the file will be called ``ContractName_meta.json``. -It will contain Swarm references to the source code, so you have to upload -all source files and the metadata file. - -The metadata file has the following format. The example below is presented in a -human-readable way. Properly formatted metadata should use quotes correctly, -reduce whitespace to a minimum and sort the keys of all objects to arrive at a -unique formatting. -Comments are of course also not permitted and used here only for explanatory purposes. - -.. code-block:: none - - { - // Required: The version of the metadata format - version: "1", - // Required: Source code language, basically selects a "sub-version" - // of the specification - language: "Solidity", - // Required: Details about the compiler, contents are specific - // to the language. - compiler: { - // Required for Solidity: Version of the compiler - version: "0.4.6+commit.2dabbdf0.Emscripten.clang", - // Optional: Hash of the compiler binary which produced this output - keccak256: "0x123..." - }, - // Required: Compilation source files/source units, keys are file names - sources: - { - "myFile.sol": { - // Required: keccak256 hash of the source file - "keccak256": "0x123...", - // Required (unless "content" is used, see below): Sorted URL(s) - // to the source file, protocol is more or less arbitrary, but a - // Swarm URL is recommended - "urls": [ "bzzr://56ab..." ] - }, - "mortal": { - // Required: keccak256 hash of the source file - "keccak256": "0x234...", - // Required (unless "url" is used): literal contents of the source file - "content": "contract mortal is owned { function kill() { if (msg.sender == owner) selfdestruct(owner); } }" - } - }, - // Required: Compiler settings - settings: - { - // Required for Solidity: Sorted list of remappings - remappings: [ ":g/dir" ], - // Optional: Optimizer settings (enabled defaults to false) - optimizer: { - enabled: true, - runs: 500 - }, - // Required for Solidity: File and name of the contract or library this - // metadata is created for. - compilationTarget: { - "myFile.sol": "MyContract" - }, - // Required for Solidity: Addresses for libraries used - libraries: { - "MyLib": "0x123123..." - } - }, - // Required: Generated information about the contract. - output: - { - // Required: ABI definition of the contract - abi: [ ... ], - // Required: NatSpec user documentation of the contract - userdoc: [ ... ], - // Required: NatSpec developer documentation of the contract - devdoc: [ ... ], - } - } - -.. note:: - Note the ABI definition above has no fixed order. It can change with compiler versions. - -.. note:: - Since the bytecode of the resulting contract contains the metadata hash, any change to - the metadata will result in a change of the bytecode. Furthermore, since the metadata - includes a hash of all the sources used, a single whitespace change in any of the source - codes will result in a different metadata, and subsequently a different bytecode. - -Encoding of the Metadata Hash in the Bytecode -============================================= - -Because we might support other ways to retrieve the metadata file in the future, -the mapping ``{"bzzr0": }`` is stored -`CBOR `_-encoded. Since the beginning of that -encoding is not easy to find, its length is added in a two-byte big-endian -encoding. The current version of the Solidity compiler thus adds the following -to the end of the deployed bytecode:: - - 0xa1 0x65 'b' 'z' 'z' 'r' '0' 0x58 0x20 <32 bytes swarm hash> 0x00 0x29 - -So in order to retrieve the data, the end of the deployed bytecode can be checked -to match that pattern and use the Swarm hash to retrieve the file. - -Usage for Automatic Interface Generation and NatSpec -==================================================== - -The metadata is used in the following way: A component that wants to interact -with a contract (e.g. Mist) retrieves the code of the contract, from that -the Swarm hash of a file which is then retrieved. -That file is JSON-decoded into a structure like above. - -The component can then use the ABI to automatically generate a rudimentary -user interface for the contract. - -Furthermore, Mist can use the userdoc to display a confirmation message to the user -whenever they interact with the contract. - -Usage for Source Code Verification -================================== - -In order to verify the compilation, sources can be retrieved from Swarm -via the link in the metadata file. -The compiler of the correct version (which is checked to be part of the "official" compilers) -is invoked on that input with the specified settings. The resulting -bytecode is compared to the data of the creation transaction or ``CREATE`` opcode data. -This automatically verifies the metadata since its hash is part of the bytecode. -Excess data corresponds to the constructor input data, which should be decoded -according to the interface and presented to the user. - - *************** Tips and Tricks *************** From 55d2a459a9193024930101c79bbf48af2eb39e2d Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 28 Aug 2017 15:30:01 +0100 Subject: [PATCH 083/162] Mark functions static in libevmasm --- libevmasm/Assembly.cpp | 4 ++-- libevmasm/Assembly.h | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index 8c1f92965..696ab0b35 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -210,7 +210,7 @@ ostream& Assembly::streamAsm(ostream& _out, string const& _prefix, StringMap con return _out; } -Json::Value Assembly::createJsonValue(string _name, int _begin, int _end, string _value, string _jumpType) const +Json::Value Assembly::createJsonValue(string _name, int _begin, int _end, string _value, string _jumpType) { Json::Value value; value["name"] = _name; @@ -223,7 +223,7 @@ Json::Value Assembly::createJsonValue(string _name, int _begin, int _end, string return value; } -string toStringInHex(u256 _value) +string Assembly::toStringInHex(u256 _value) { std::stringstream hexStr; hexStr << hex << _value; diff --git a/libevmasm/Assembly.h b/libevmasm/Assembly.h index 680cb1af2..8116c2cea 100644 --- a/libevmasm/Assembly.h +++ b/libevmasm/Assembly.h @@ -138,7 +138,8 @@ protected: private: Json::Value streamAsmJson(std::ostream& _out, StringMap const& _sourceCodes) const; std::ostream& streamAsm(std::ostream& _out, std::string const& _prefix, StringMap const& _sourceCodes) const; - Json::Value createJsonValue(std::string _name, int _begin, int _end, std::string _value = std::string(), std::string _jumpType = std::string()) const; + static Json::Value createJsonValue(std::string _name, int _begin, int _end, std::string _value = std::string(), std::string _jumpType = std::string()); + static std::string toStringInHex(u256 _value); protected: /// 0 is reserved for exception From a535a8b06ed1b9c0c5fd41805a4fe39939755f05 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 15 Jun 2017 10:22:47 +0100 Subject: [PATCH 084/162] Split out the JSON functionality from assembly.stream() --- libevmasm/Assembly.cpp | 21 ++++--------------- libevmasm/Assembly.h | 15 ++++++++------ liblll/Compiler.cpp | 2 +- libsolidity/codegen/Compiler.h | 10 ++++++--- libsolidity/codegen/CompilerContext.h | 11 +++++++--- libsolidity/interface/AssemblyStack.cpp | 2 +- libsolidity/interface/CompilerStack.cpp | 16 ++++++++++++--- libsolidity/interface/CompilerStack.h | 8 ++++++-- libsolidity/interface/StandardCompiler.cpp | 4 ++-- solc/CommandLineInterface.cpp | 24 ++++++++++++++-------- 10 files changed, 67 insertions(+), 46 deletions(-) diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index 696ab0b35..a07226ca3 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -181,7 +181,7 @@ private: } -ostream& Assembly::streamAsm(ostream& _out, string const& _prefix, StringMap const& _sourceCodes) const +ostream& Assembly::assemblyStream(ostream& _out, string const& _prefix, StringMap const& _sourceCodes) const { Functionalizer f(_out, _prefix, _sourceCodes); @@ -199,7 +199,7 @@ ostream& Assembly::streamAsm(ostream& _out, string const& _prefix, StringMap con for (size_t i = 0; i < m_subs.size(); ++i) { _out << endl << _prefix << "sub_" << i << ": assembly {\n"; - m_subs[i]->streamAsm(_out, _prefix + " ", _sourceCodes); + m_subs[i]->assemblyStream(_out, _prefix + " ", _sourceCodes); _out << _prefix << "}" << endl; } } @@ -230,7 +230,7 @@ string Assembly::toStringInHex(u256 _value) return hexStr.str(); } -Json::Value Assembly::streamAsmJson(ostream& _out, StringMap const& _sourceCodes) const +Json::Value Assembly::assemblyJSON(StringMap const& _sourceCodes) const { Json::Value root; @@ -301,29 +301,16 @@ Json::Value Assembly::streamAsmJson(ostream& _out, StringMap const& _sourceCodes { std::stringstream hexStr; hexStr << hex << i; - data[hexStr.str()] = m_subs[i]->stream(_out, "", _sourceCodes, true); + data[hexStr.str()] = m_subs[i]->assemblyJSON(_sourceCodes); } } if (m_auxiliaryData.size() > 0) root[".auxdata"] = toHex(m_auxiliaryData); - _out << root; - return root; } -Json::Value Assembly::stream(ostream& _out, string const& _prefix, StringMap const& _sourceCodes, bool _inJsonFormat) const -{ - if (_inJsonFormat) - return streamAsmJson(_out, _sourceCodes); - else - { - streamAsm(_out, _prefix, _sourceCodes); - return Json::Value(); - } -} - AssemblyItem const& Assembly::append(AssemblyItem const& _i) { assertThrow(m_deposit >= 0, AssemblyException, ""); diff --git a/libevmasm/Assembly.h b/libevmasm/Assembly.h index 8116c2cea..ab8f174d3 100644 --- a/libevmasm/Assembly.h +++ b/libevmasm/Assembly.h @@ -120,11 +120,16 @@ public: /// If @a _enable is not set, will perform some simple peephole optimizations. Assembly& optimise(bool _enable, bool _isCreation = true, size_t _runs = 200); - Json::Value stream( + /// Create a text representation of the assembly. + std::ostream& assemblyStream( std::ostream& _out, std::string const& _prefix = "", - const StringMap &_sourceCodes = StringMap(), - bool _inJsonFormat = false + StringMap const& _sourceCodes = StringMap() + ) const; + + /// Create a JSON representation of the assembly. + Json::Value assemblyJSON( + StringMap const& _sourceCodes = StringMap() ) const; protected: @@ -136,8 +141,6 @@ protected: unsigned bytesRequired(unsigned subTagSize) const; private: - Json::Value streamAsmJson(std::ostream& _out, StringMap const& _sourceCodes) const; - std::ostream& streamAsm(std::ostream& _out, std::string const& _prefix, StringMap const& _sourceCodes) const; static Json::Value createJsonValue(std::string _name, int _begin, int _end, std::string _value = std::string(), std::string _jumpType = std::string()); static std::string toStringInHex(u256 _value); @@ -162,7 +165,7 @@ protected: inline std::ostream& operator<<(std::ostream& _out, Assembly const& _a) { - _a.stream(_out); + _a.assemblyStream(_out); return _out; } diff --git a/liblll/Compiler.cpp b/liblll/Compiler.cpp index 4ec11ca93..f9bd3ab94 100644 --- a/liblll/Compiler.cpp +++ b/liblll/Compiler.cpp @@ -76,7 +76,7 @@ std::string dev::eth::compileLLLToAsm(std::string const& _src, bool _opt, std::v auto assembly = CodeFragment::compile(_src, cs).assembly(cs); if (_opt) assembly = assembly.optimise(true); - assembly.stream(ret); + assembly.assemblyStream(ret); for (auto i: cs.treesToKill) killBigints(i); return ret.str(); diff --git a/libsolidity/codegen/Compiler.h b/libsolidity/codegen/Compiler.h index c6ee93fbd..1224ff601 100644 --- a/libsolidity/codegen/Compiler.h +++ b/libsolidity/codegen/Compiler.h @@ -60,10 +60,14 @@ public: /// @returns Only the runtime object (without constructor). eth::LinkerObject runtimeObject() const { return m_context.assembledRuntimeObject(m_runtimeSub); } /// @arg _sourceCodes is the map of input files to source code strings - /// @arg _inJsonFromat shows whether the out should be in Json format - Json::Value streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap(), bool _inJsonFormat = false) const + std::ostream& assemblyStream(std::ostream& _stream, StringMap const& _sourceCodes = StringMap()) const { - return m_context.streamAssembly(_stream, _sourceCodes, _inJsonFormat); + return m_context.assemblyStream(_stream, _sourceCodes); + } + /// @arg _sourceCodes is the map of input files to source code strings + Json::Value assemblyJSON(StringMap const& _sourceCodes = StringMap()) const + { + return m_context.assemblyJSON(_sourceCodes); } /// @returns Assembly items of the normal compiler context eth::AssemblyItems const& assemblyItems() const { return m_context.assembly().items(); } diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index 3994b0101..de79aa427 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -209,10 +209,15 @@ public: eth::Assembly& nonConstAssembly() { return *m_asm; } /// @arg _sourceCodes is the map of input files to source code strings - /// @arg _inJsonFormat shows whether the out should be in Json format - Json::Value streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap(), bool _inJsonFormat = false) const + std::ostream& assemblyStream(std::ostream& _stream, StringMap const& _sourceCodes = StringMap()) const { - return m_asm->stream(_stream, "", _sourceCodes, _inJsonFormat); + return m_asm->assemblyStream(_stream, "", _sourceCodes); + } + + /// @arg _sourceCodes is the map of input files to source code strings + Json::Value assemblyJSON(StringMap const& _sourceCodes = StringMap()) const + { + return m_asm->assemblyJSON(_sourceCodes); } eth::LinkerObject const& assembledObject() const { return m_asm->assemble(); } diff --git a/libsolidity/interface/AssemblyStack.cpp b/libsolidity/interface/AssemblyStack.cpp index 23524bb3f..025a01acc 100644 --- a/libsolidity/interface/AssemblyStack.cpp +++ b/libsolidity/interface/AssemblyStack.cpp @@ -92,7 +92,7 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const assembly::CodeGenerator::assemble(*m_parserResult, *m_analysisInfo, assembly); object.bytecode = make_shared(assembly.assemble()); ostringstream tmp; - assembly.stream(tmp); + assembly.assemblyStream(tmp); object.assembly = tmp.str(); return object; } diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 259694dab..4002bac32 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -347,18 +347,28 @@ eth::LinkerObject const& CompilerStack::cloneObject(string const& _contractName) return contract(_contractName).cloneObject; } -Json::Value CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName, StringMap _sourceCodes, bool _inJsonFormat) const +ostream& CompilerStack::assemblyStream(ostream& _outStream, string const& _contractName, StringMap _sourceCodes) const { Contract const& currentContract = contract(_contractName); if (currentContract.compiler) - return currentContract.compiler->streamAssembly(_outStream, _sourceCodes, _inJsonFormat); + return currentContract.compiler->assemblyStream(_outStream, _sourceCodes); else { _outStream << "Contract not fully implemented" << endl; - return Json::Value(); + return _outStream; } } +/// FIXME: cache the JSON +Json::Value CompilerStack::assemblyJSON(string const& _contractName, StringMap _sourceCodes) const +{ + Contract const& currentContract = contract(_contractName); + if (currentContract.compiler) + return currentContract.compiler->assemblyJSON(_sourceCodes); + else + return Json::Value(); +} + vector CompilerStack::sourceNames() const { vector names; diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 2756e57d6..207778f03 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -192,9 +192,13 @@ public: /// Streams a verbose version of the assembly to @a _outStream. /// @arg _sourceCodes is the map of input files to source code strings - /// @arg _inJsonFromat shows whether the out should be in Json format /// Prerequisite: Successful compilation. - Json::Value streamAssembly(std::ostream& _outStream, std::string const& _contractName = "", StringMap _sourceCodes = StringMap(), bool _inJsonFormat = false) const; + std::ostream& assemblyStream(std::ostream& _outStream, std::string const& _contractName = "", StringMap _sourceCodes = StringMap()) const; + + /// @returns a JSON representation of the assembly. + /// @arg _sourceCodes is the map of input files to source code strings + /// Prerequisite: Successful compilation. + Json::Value assemblyJSON(std::string const& _contractName = "", StringMap _sourceCodes = StringMap()) const; /// @returns a JSON representing the contract ABI. /// Prerequisite: Successful call to parse or compile. diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index be823743f..c821a3416 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -401,9 +401,9 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) Json::Value evmData(Json::objectValue); // @TODO: add ir ostringstream tmp; - m_compilerStack.streamAssembly(tmp, contractName, createSourceList(_input), false); + m_compilerStack.assemblyStream(tmp, contractName, createSourceList(_input)); evmData["assembly"] = tmp.str(); - evmData["legacyAssembly"] = m_compilerStack.streamAssembly(tmp, contractName, createSourceList(_input), true); + evmData["legacyAssembly"] = m_compilerStack.assemblyJSON(contractName, createSourceList(_input)); evmData["methodIdentifiers"] = m_compilerStack.methodIdentifiers(contractName); evmData["gasEstimates"] = m_compilerStack.gasEstimates(contractName); diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 315f951e7..560dc98be 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -865,10 +865,7 @@ void CommandLineInterface::handleCombinedJSON() if (requests.count(g_strOpcodes)) contractData[g_strOpcodes] = solidity::disassemble(m_compiler->object(contractName).bytecode); if (requests.count(g_strAsm)) - { - ostringstream unused; - contractData[g_strAsm] = m_compiler->streamAssembly(unused, contractName, m_sourceCodes, true); - } + contractData[g_strAsm] = m_compiler->assemblyJSON(contractName, m_sourceCodes); if (requests.count(g_strSrcMap)) { auto map = m_compiler->sourceMapping(contractName); @@ -1152,14 +1149,25 @@ void CommandLineInterface::outputCompilationResults() { if (m_args.count(g_argOutputDir)) { - stringstream data; - m_compiler->streamAssembly(data, contract, m_sourceCodes, m_args.count(g_argAsmJson)); - createFile(m_compiler->filesystemFriendlyName(contract) + (m_args.count(g_argAsmJson) ? "_evm.json" : ".evm"), data.str()); + if (m_args.count(g_argAsmJson) + { + Json::Value ret = m_compiler->assemblyJSON(contract, m_sourceCodes); + createFile(m_compiler->filesystemFriendlyName(contract) + "_evm.json", dev::jsonPrettyPrint(ret)); + } + else + { + stringstream data; + m_compiler->assemblyStream(data, contract, m_sourceCodes); + createFile(m_compiler->filesystemFriendlyName(contract) + ".evm", data.str()); + } } else { cout << "EVM assembly:" << endl; - m_compiler->streamAssembly(cout, contract, m_sourceCodes, m_args.count(g_argAsmJson)); + if (m_args.count(g_argAsmJson) + cout << m_compiler->assemblyJSON(contract, m_sourceCodes); + else + m_compiler->assemblyStream(cout, contract, m_sourceCodes); } } From 50570c6c794eee01af64751c884fb6cb68f8dffc Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 30 Aug 2017 01:58:19 +0100 Subject: [PATCH 085/162] Do not return the stream in asssemblyStream --- libevmasm/Assembly.cpp | 4 +--- libevmasm/Assembly.h | 2 +- libsolidity/codegen/Compiler.h | 4 ++-- libsolidity/codegen/CompilerContext.h | 4 ++-- libsolidity/interface/CompilerStack.cpp | 5 ++--- libsolidity/interface/CompilerStack.h | 2 +- 6 files changed, 9 insertions(+), 12 deletions(-) diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index a07226ca3..2203cadf5 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -181,7 +181,7 @@ private: } -ostream& Assembly::assemblyStream(ostream& _out, string const& _prefix, StringMap const& _sourceCodes) const +void Assembly::assemblyStream(ostream& _out, string const& _prefix, StringMap const& _sourceCodes) const { Functionalizer f(_out, _prefix, _sourceCodes); @@ -206,8 +206,6 @@ ostream& Assembly::assemblyStream(ostream& _out, string const& _prefix, StringMa if (m_auxiliaryData.size() > 0) _out << endl << _prefix << "auxdata: 0x" << toHex(m_auxiliaryData) << endl; - - return _out; } Json::Value Assembly::createJsonValue(string _name, int _begin, int _end, string _value, string _jumpType) diff --git a/libevmasm/Assembly.h b/libevmasm/Assembly.h index ab8f174d3..b7e9b354b 100644 --- a/libevmasm/Assembly.h +++ b/libevmasm/Assembly.h @@ -121,7 +121,7 @@ public: Assembly& optimise(bool _enable, bool _isCreation = true, size_t _runs = 200); /// Create a text representation of the assembly. - std::ostream& assemblyStream( + void assemblyStream( std::ostream& _out, std::string const& _prefix = "", StringMap const& _sourceCodes = StringMap() diff --git a/libsolidity/codegen/Compiler.h b/libsolidity/codegen/Compiler.h index 1224ff601..5233cc91f 100644 --- a/libsolidity/codegen/Compiler.h +++ b/libsolidity/codegen/Compiler.h @@ -60,9 +60,9 @@ public: /// @returns Only the runtime object (without constructor). eth::LinkerObject runtimeObject() const { return m_context.assembledRuntimeObject(m_runtimeSub); } /// @arg _sourceCodes is the map of input files to source code strings - std::ostream& assemblyStream(std::ostream& _stream, StringMap const& _sourceCodes = StringMap()) const + void assemblyStream(std::ostream& _stream, StringMap const& _sourceCodes = StringMap()) const { - return m_context.assemblyStream(_stream, _sourceCodes); + m_context.assemblyStream(_stream, _sourceCodes); } /// @arg _sourceCodes is the map of input files to source code strings Json::Value assemblyJSON(StringMap const& _sourceCodes = StringMap()) const diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index de79aa427..47d4edde3 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -209,9 +209,9 @@ public: eth::Assembly& nonConstAssembly() { return *m_asm; } /// @arg _sourceCodes is the map of input files to source code strings - std::ostream& assemblyStream(std::ostream& _stream, StringMap const& _sourceCodes = StringMap()) const + void assemblyStream(std::ostream& _stream, StringMap const& _sourceCodes = StringMap()) const { - return m_asm->assemblyStream(_stream, "", _sourceCodes); + m_asm->assemblyStream(_stream, "", _sourceCodes); } /// @arg _sourceCodes is the map of input files to source code strings diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 4002bac32..5f62bb03f 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -347,15 +347,14 @@ eth::LinkerObject const& CompilerStack::cloneObject(string const& _contractName) return contract(_contractName).cloneObject; } -ostream& CompilerStack::assemblyStream(ostream& _outStream, string const& _contractName, StringMap _sourceCodes) const +void CompilerStack::assemblyStream(ostream& _outStream, string const& _contractName, StringMap _sourceCodes) const { Contract const& currentContract = contract(_contractName); if (currentContract.compiler) - return currentContract.compiler->assemblyStream(_outStream, _sourceCodes); + currentContract.compiler->assemblyStream(_outStream, _sourceCodes); else { _outStream << "Contract not fully implemented" << endl; - return _outStream; } } diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 207778f03..2f1b9bb36 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -193,7 +193,7 @@ public: /// Streams a verbose version of the assembly to @a _outStream. /// @arg _sourceCodes is the map of input files to source code strings /// Prerequisite: Successful compilation. - std::ostream& assemblyStream(std::ostream& _outStream, std::string const& _contractName = "", StringMap _sourceCodes = StringMap()) const; + void assemblyStream(std::ostream& _outStream, std::string const& _contractName = "", StringMap _sourceCodes = StringMap()) const; /// @returns a JSON representation of the assembly. /// @arg _sourceCodes is the map of input files to source code strings From bbfb16cf5ce903150bc3a141ac50553d8bf6d346 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 30 Aug 2017 02:17:15 +0100 Subject: [PATCH 086/162] Introduce assemblyString --- libevmasm/Assembly.cpp | 7 +++++++ libevmasm/Assembly.h | 3 +++ liblll/Compiler.cpp | 5 ++--- libsolidity/codegen/Compiler.h | 4 ++-- libsolidity/codegen/CompilerContext.h | 4 ++-- libsolidity/interface/AssemblyStack.cpp | 4 +--- libsolidity/interface/CompilerStack.cpp | 9 ++++----- libsolidity/interface/CompilerStack.h | 4 ++-- libsolidity/interface/StandardCompiler.cpp | 4 +--- solc/CommandLineInterface.cpp | 7 +++---- 10 files changed, 27 insertions(+), 24 deletions(-) diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index 2203cadf5..6b4bb52b4 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -208,6 +208,13 @@ void Assembly::assemblyStream(ostream& _out, string const& _prefix, StringMap co _out << endl << _prefix << "auxdata: 0x" << toHex(m_auxiliaryData) << endl; } +string Assembly::assemblyString(StringMap const& _sourceCodes) const +{ + ostringstream tmp; + assemblyStream(tmp, "", _sourceCodes); + return tmp.str(); +} + Json::Value Assembly::createJsonValue(string _name, int _begin, int _end, string _value, string _jumpType) { Json::Value value; diff --git a/libevmasm/Assembly.h b/libevmasm/Assembly.h index b7e9b354b..cbdd71bcf 100644 --- a/libevmasm/Assembly.h +++ b/libevmasm/Assembly.h @@ -121,6 +121,9 @@ public: Assembly& optimise(bool _enable, bool _isCreation = true, size_t _runs = 200); /// Create a text representation of the assembly. + std::string assemblyString( + StringMap const& _sourceCodes = StringMap() + ) const; void assemblyStream( std::ostream& _out, std::string const& _prefix = "", diff --git a/liblll/Compiler.cpp b/liblll/Compiler.cpp index f9bd3ab94..b69675aab 100644 --- a/liblll/Compiler.cpp +++ b/liblll/Compiler.cpp @@ -72,14 +72,13 @@ std::string dev::eth::compileLLLToAsm(std::string const& _src, bool _opt, std::v { CompilerState cs; cs.populateStandard(); - stringstream ret; auto assembly = CodeFragment::compile(_src, cs).assembly(cs); if (_opt) assembly = assembly.optimise(true); - assembly.assemblyStream(ret); + string ret = assembly.assemblyString(); for (auto i: cs.treesToKill) killBigints(i); - return ret.str(); + return ret; } catch (Exception const& _e) { diff --git a/libsolidity/codegen/Compiler.h b/libsolidity/codegen/Compiler.h index 5233cc91f..06654486a 100644 --- a/libsolidity/codegen/Compiler.h +++ b/libsolidity/codegen/Compiler.h @@ -60,9 +60,9 @@ public: /// @returns Only the runtime object (without constructor). eth::LinkerObject runtimeObject() const { return m_context.assembledRuntimeObject(m_runtimeSub); } /// @arg _sourceCodes is the map of input files to source code strings - void assemblyStream(std::ostream& _stream, StringMap const& _sourceCodes = StringMap()) const + std::string assemblyString(StringMap const& _sourceCodes = StringMap()) const { - m_context.assemblyStream(_stream, _sourceCodes); + return m_context.assemblyString(_sourceCodes); } /// @arg _sourceCodes is the map of input files to source code strings Json::Value assemblyJSON(StringMap const& _sourceCodes = StringMap()) const diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index 47d4edde3..5116585e7 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -209,9 +209,9 @@ public: eth::Assembly& nonConstAssembly() { return *m_asm; } /// @arg _sourceCodes is the map of input files to source code strings - void assemblyStream(std::ostream& _stream, StringMap const& _sourceCodes = StringMap()) const + std::string assemblyString(StringMap const& _sourceCodes = StringMap()) const { - m_asm->assemblyStream(_stream, "", _sourceCodes); + return m_asm->assemblyString(_sourceCodes); } /// @arg _sourceCodes is the map of input files to source code strings diff --git a/libsolidity/interface/AssemblyStack.cpp b/libsolidity/interface/AssemblyStack.cpp index 025a01acc..504ad92c4 100644 --- a/libsolidity/interface/AssemblyStack.cpp +++ b/libsolidity/interface/AssemblyStack.cpp @@ -91,9 +91,7 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const eth::Assembly assembly; assembly::CodeGenerator::assemble(*m_parserResult, *m_analysisInfo, assembly); object.bytecode = make_shared(assembly.assemble()); - ostringstream tmp; - assembly.assemblyStream(tmp); - object.assembly = tmp.str(); + object.assembly = assembly.assemblyString(); return object; } case Machine::EVM15: diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 5f62bb03f..41bbf687c 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -347,15 +347,14 @@ eth::LinkerObject const& CompilerStack::cloneObject(string const& _contractName) return contract(_contractName).cloneObject; } -void CompilerStack::assemblyStream(ostream& _outStream, string const& _contractName, StringMap _sourceCodes) const +/// FIXME: cache this string +string CompilerStack::assemblyString(string const& _contractName, StringMap _sourceCodes) const { Contract const& currentContract = contract(_contractName); if (currentContract.compiler) - currentContract.compiler->assemblyStream(_outStream, _sourceCodes); + return currentContract.compiler->assemblyString(_sourceCodes); else - { - _outStream << "Contract not fully implemented" << endl; - } + return string(); } /// FIXME: cache the JSON diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 2f1b9bb36..f1bbae47e 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -190,10 +190,10 @@ public: /// if the contract does not (yet) have bytecode. std::string const* runtimeSourceMapping(std::string const& _contractName = "") const; - /// Streams a verbose version of the assembly to @a _outStream. + /// @return a verbose text representation of the assembly. /// @arg _sourceCodes is the map of input files to source code strings /// Prerequisite: Successful compilation. - void assemblyStream(std::ostream& _outStream, std::string const& _contractName = "", StringMap _sourceCodes = StringMap()) const; + std::string assemblyString(std::string const& _contractName = "", StringMap _sourceCodes = StringMap()) const; /// @returns a JSON representation of the assembly. /// @arg _sourceCodes is the map of input files to source code strings diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index c821a3416..b4fbbef9b 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -400,9 +400,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) // EVM Json::Value evmData(Json::objectValue); // @TODO: add ir - ostringstream tmp; - m_compilerStack.assemblyStream(tmp, contractName, createSourceList(_input)); - evmData["assembly"] = tmp.str(); + evmData["assembly"] = m_compilerStack.assemblyString(contractName, createSourceList(_input)); evmData["legacyAssembly"] = m_compilerStack.assemblyJSON(contractName, createSourceList(_input)); evmData["methodIdentifiers"] = m_compilerStack.methodIdentifiers(contractName); evmData["gasEstimates"] = m_compilerStack.gasEstimates(contractName); diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 560dc98be..32c61585d 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -1156,9 +1156,8 @@ void CommandLineInterface::outputCompilationResults() } else { - stringstream data; - m_compiler->assemblyStream(data, contract, m_sourceCodes); - createFile(m_compiler->filesystemFriendlyName(contract) + ".evm", data.str()); + string ret = m_compiler->assemblyString(contract, m_sourceCodes); + createFile(m_compiler->filesystemFriendlyName(contract) + ".evm", ret)); } } else @@ -1167,7 +1166,7 @@ void CommandLineInterface::outputCompilationResults() if (m_args.count(g_argAsmJson) cout << m_compiler->assemblyJSON(contract, m_sourceCodes); else - m_compiler->assemblyStream(cout, contract, m_sourceCodes); + cout << m_compiler->assemblyString(contract, m_sourceCodes); } } From bd6510d99a503ddbdbfaa9bd459f020215f8a028 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 11 Sep 2017 15:18:56 +0100 Subject: [PATCH 087/162] Simplify assembly printing in CLI --- solc/CommandLineInterface.cpp | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 32c61585d..e6d8776bf 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -1147,26 +1147,19 @@ void CommandLineInterface::outputCompilationResults() // do we need EVM assembly? if (m_args.count(g_argAsm) || m_args.count(g_argAsmJson)) { + string ret; + if (m_args.count(g_argAsmJson)) + ret = dev::jsonPrettyPrint(m_compiler->assemblyJSON(contract, m_sourceCodes)); + else + ret = m_compiler->assemblyString(contract, m_sourceCodes); + if (m_args.count(g_argOutputDir)) { - if (m_args.count(g_argAsmJson) - { - Json::Value ret = m_compiler->assemblyJSON(contract, m_sourceCodes); - createFile(m_compiler->filesystemFriendlyName(contract) + "_evm.json", dev::jsonPrettyPrint(ret)); - } - else - { - string ret = m_compiler->assemblyString(contract, m_sourceCodes); - createFile(m_compiler->filesystemFriendlyName(contract) + ".evm", ret)); - } + createFile(m_compiler->filesystemFriendlyName(contract) + (m_args.count(g_argAsmJson) ? "_evm.json" : ".evm"), ret); } else { - cout << "EVM assembly:" << endl; - if (m_args.count(g_argAsmJson) - cout << m_compiler->assemblyJSON(contract, m_sourceCodes); - else - cout << m_compiler->assemblyString(contract, m_sourceCodes); + cout << "EVM assembly:" << endl << ret << endl; } } From 7ed938b95e3061ee6b1782e09abccf1b26092b53 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 6 Sep 2017 15:05:35 +0100 Subject: [PATCH 088/162] Change retrieveContract to take name and not index --- test/libsolidity/AnalysisFramework.cpp | 6 +++--- test/libsolidity/AnalysisFramework.h | 2 +- test/libsolidity/SolidityNameAndTypeResolution.cpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/libsolidity/AnalysisFramework.cpp b/test/libsolidity/AnalysisFramework.cpp index 71bb4ca7b..5f5f6411c 100644 --- a/test/libsolidity/AnalysisFramework.cpp +++ b/test/libsolidity/AnalysisFramework.cpp @@ -106,12 +106,12 @@ void AnalysisFramework::printErrors() ); } -ContractDefinition const* AnalysisFramework::retrieveContract(SourceUnit const& _source, unsigned index) +ContractDefinition const* AnalysisFramework::retrieveContractByName(SourceUnit const& _source, string const& _name) { ContractDefinition* contract = nullptr; - unsigned counter = 0; + for (shared_ptr const& node: _source.nodes()) - if ((contract = dynamic_cast(node.get())) && counter == index) + if ((contract = dynamic_cast(node.get())) && contract->name() == _name) return contract; return nullptr; diff --git a/test/libsolidity/AnalysisFramework.h b/test/libsolidity/AnalysisFramework.h index 0cdedaea4..f73f06c23 100644 --- a/test/libsolidity/AnalysisFramework.h +++ b/test/libsolidity/AnalysisFramework.h @@ -59,7 +59,7 @@ protected: void printErrors(); - ContractDefinition const* retrieveContract(SourceUnit const& _source, unsigned index); + ContractDefinition const* retrieveContractByName(SourceUnit const& _source, std::string const& _name); FunctionTypePointer retrieveFunctionBySignature( ContractDefinition const& _contract, std::string const& _signature diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 944047815..7bef8fec7 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -976,7 +976,7 @@ BOOST_AUTO_TEST_CASE(state_variable_accessors) SourceUnit const* source; ContractDefinition const* contract; source = parseAndAnalyse(text); - BOOST_REQUIRE((contract = retrieveContract(*source, 0)) != nullptr); + BOOST_REQUIRE((contract = retrieveContractByName(*source, "test")) != nullptr); FunctionTypePointer function = retrieveFunctionBySignature(*contract, "foo()"); BOOST_REQUIRE(function && function->hasDeclaration()); auto returnParams = function->returnParameterTypes(); @@ -1029,7 +1029,7 @@ BOOST_AUTO_TEST_CASE(private_state_variable) ContractDefinition const* contract; SourceUnit const* source = parseAndAnalyse(text); - BOOST_CHECK((contract = retrieveContract(*source, 0)) != nullptr); + BOOST_CHECK((contract = retrieveContractByName(*source, "test")) != nullptr); FunctionTypePointer function; function = retrieveFunctionBySignature(*contract, "foo()"); BOOST_CHECK_MESSAGE(function == nullptr, "Accessor function of a private variable should not exist"); From a52e0de67c2fad02da43ef645c739632c61c923b Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 28 Aug 2017 21:05:47 +0100 Subject: [PATCH 089/162] Do not show the same error multiple times for events --- Changelog.md | 1 + libsolidity/analysis/TypeChecker.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Changelog.md b/Changelog.md index 4af4419d7..f61bb560c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,7 @@ Features: * Optimizer: Add new optimization step to remove unused ``JUMPDEST``s. + * Type Checker: Do not show the same error multiple times for events. * Type Checker: Warn on using literals as tight packing parameters in ``keccak256``, ``sha3``, ``sha256`` and ``ripemd160``. Bugfixes: diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index c46485d84..d2151cdaa 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -698,15 +698,15 @@ bool TypeChecker::visit(EventDefinition const& _eventDef) { if (var->isIndexed()) numIndexed++; - if (_eventDef.isAnonymous() && numIndexed > 4) - m_errorReporter.typeError(_eventDef.location(), "More than 4 indexed arguments for anonymous event."); - else if (!_eventDef.isAnonymous() && numIndexed > 3) - m_errorReporter.typeError(_eventDef.location(), "More than 3 indexed arguments for event."); if (!type(*var)->canLiveOutsideStorage()) m_errorReporter.typeError(var->location(), "Type is required to live outside storage."); if (!type(*var)->interfaceType(false)) m_errorReporter.typeError(var->location(), "Internal type is not allowed as event parameter type."); } + if (_eventDef.isAnonymous() && numIndexed > 4) + m_errorReporter.typeError(_eventDef.location(), "More than 4 indexed arguments for anonymous event."); + else if (!_eventDef.isAnonymous() && numIndexed > 3) + m_errorReporter.typeError(_eventDef.location(), "More than 3 indexed arguments for event."); return false; } From 2b88eeb37039e8cb2639a3ad294739278673aefc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aaron=20Cola=C3=A7o?= Date: Sun, 3 Sep 2017 15:18:48 +0530 Subject: [PATCH 090/162] Make `createAndEndowD` payable in contract creation example --- docs/control-structures.rst | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 052549031..0497365b1 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -206,7 +206,7 @@ Those names will still be present on the stack, but they are inaccessible. return k; } } - + .. index:: ! new, contracts;creating @@ -237,16 +237,17 @@ creation-dependencies are not possible. D newD = new D(arg); } - function createAndEndowD(uint arg, uint amount) { + function createAndEndowD(uint arg, uint amount) payable { // Send ether along with the creation D newD = (new D).value(amount)(arg); } } -As seen in the example, it is possible to forward Ether to the creation using the ``.value()`` option, -but it is not possible to limit the amount of gas. If the creation fails -(due to out-of-stack, not enough balance or other problems), an exception -is thrown. +As seen in the example, it is possible to forward Ether while creating +an instance of ``D`` using the ``.value()`` option, but it is not possible +to limit the amount of gas. +If the creation fails (due to out-of-stack, not enough balance or other problems), +an exception is thrown. Order of Evaluation of Expressions ================================== @@ -382,7 +383,7 @@ Solidity uses state-reverting exceptions to handle errors. Such an exception wil state in the current call (and all its sub-calls) and also flag an error to the caller. The convenience functions ``assert`` and ``require`` can be used to check for conditions and throw an exception if the condition is not met. The ``assert`` function should only be used to test for internal errors, and to check invariants. -The ``require`` function should be used to ensure valid conditions, such as inputs, or contract state variables are met, or to validate return values from calls to external contracts. +The ``require`` function should be used to ensure valid conditions, such as inputs, or contract state variables are met, or to validate return values from calls to external contracts. If used properly, analysis tools can evaluate your contract to identify the conditions and function calls which will reach a failing ``assert``. Properly functioning code should never reach a failing assert statement; if this happens there is a bug in your contract which you should fix. There are two other ways to trigger exceptions: The ``revert`` function can be used to flag an error and @@ -392,7 +393,7 @@ in a call to ``revert``. The ``throw`` keyword can also be used as an alternativ .. note:: From version 0.4.13 the ``throw`` keyword is deprecated and will be phased out in the future. -When exceptions happen in a sub-call, they "bubble up" (i.e. exceptions are rethrown) automatically. Exceptions to this rule are ``send`` +When exceptions happen in a sub-call, they "bubble up" (i.e. exceptions are rethrown) automatically. Exceptions to this rule are ``send`` and the low-level functions ``call``, ``delegatecall`` and ``callcode`` -- those return ``false`` in case of an exception instead of "bubbling up". From a03211f3c9920499837fe11edef8c030c772181a Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 12 Sep 2017 10:59:56 +0100 Subject: [PATCH 091/162] Show each unimplemented function in secondary source location --- libsolidity/analysis/TypeChecker.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index d2151cdaa..8e1d11a14 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -458,7 +458,7 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance) " to " + parameterTypes[i]->toString() + " requested." - ); + ); } void TypeChecker::endVisit(UsingForDirective const& _usingFor) @@ -1583,14 +1583,16 @@ void TypeChecker::endVisit(NewExpression const& _newExpression) if (contract->contractKind() == ContractDefinition::ContractKind::Interface) m_errorReporter.fatalTypeError(_newExpression.location(), "Cannot instantiate an interface."); if (!contract->annotation().unimplementedFunctions.empty()) + { + SecondarySourceLocation ssl; + for (auto function: contract->annotation().unimplementedFunctions) + ssl.append("Missing implementation:", function->location()); m_errorReporter.typeError( _newExpression.location(), - SecondarySourceLocation().append( - "Missing implementation:", - contract->annotation().unimplementedFunctions.front()->location() - ), + ssl, "Trying to create an instance of an abstract contract." ); + } if (!contract->constructorIsPublic()) m_errorReporter.typeError(_newExpression.location(), "Contract with internal constructor cannot be created directly."); From da1a53e02a897117e190ef91ec8b1f63f6629a9f Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 12 Sep 2017 11:07:28 +0100 Subject: [PATCH 092/162] Use secondary source location as a vector in same declaration errors --- libsolidity/analysis/TypeChecker.cpp | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index d2151cdaa..0dfae070c 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -174,18 +174,20 @@ void TypeChecker::checkContractDuplicateFunctions(ContractDefinition const& _con { vector const& overloads = it.second; for (size_t i = 0; i < overloads.size(); ++i) + { + SecondarySourceLocation ssl; + for (size_t j = i + 1; j < overloads.size(); ++j) if (FunctionType(*overloads[i]).hasEqualArgumentTypes(FunctionType(*overloads[j]))) - { - m_errorReporter.declarationError( - overloads[j]->location(), - SecondarySourceLocation().append( - "Other declaration is here:", - overloads[i]->location() - ), - "Function with same name and arguments defined twice." - ); - } + ssl.append("Other declaration is here:", overloads[j]->location()); + + if (ssl.infos.size() > 0) + m_errorReporter.declarationError( + overloads[i]->location(), + ssl, + "Function with same name and arguments defined twice." + ); + } } } From 5ae9b8dab180b94bbbc5a7a791ddd9701f1108f0 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 12 Sep 2017 11:22:29 +0100 Subject: [PATCH 093/162] Do not report overload conflicts for every line excessively --- libsolidity/analysis/TypeChecker.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 3564ff322..87750c6e2 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -173,13 +173,17 @@ void TypeChecker::checkContractDuplicateFunctions(ContractDefinition const& _con for (auto const& it: functions) { vector const& overloads = it.second; - for (size_t i = 0; i < overloads.size(); ++i) + set reported; + for (size_t i = 0; i < overloads.size() && !reported.count(i); ++i) { SecondarySourceLocation ssl; for (size_t j = i + 1; j < overloads.size(); ++j) if (FunctionType(*overloads[i]).hasEqualArgumentTypes(FunctionType(*overloads[j]))) + { ssl.append("Other declaration is here:", overloads[j]->location()); + reported.insert(j); + } if (ssl.infos.size() > 0) m_errorReporter.declarationError( From 10d290cb9b131f620c56c0bc84a9ab5bcf50368b Mon Sep 17 00:00:00 2001 From: Suman Date: Tue, 29 Aug 2017 11:58:38 -0400 Subject: [PATCH 094/162] Display helpful warning for unused function arguments/return parameters --- Changelog.md | 1 + libsolidity/analysis/StaticAnalyzer.cpp | 11 ++++++++++- test/libsolidity/SolidityNameAndTypeResolution.cpp | 14 +++++++------- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/Changelog.md b/Changelog.md index f61bb560c..d36275652 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,7 @@ Features: * Optimizer: Add new optimization step to remove unused ``JUMPDEST``s. + * Type Checker: Display helpful warning for unused function arguments/return parameters. * Type Checker: Do not show the same error multiple times for events. * Type Checker: Warn on using literals as tight packing parameters in ``keccak256``, ``sha3``, ``sha256`` and ``ripemd160``. diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index 2f1304146..d012c25d3 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -69,7 +69,16 @@ void StaticAnalyzer::endVisit(FunctionDefinition const&) m_constructor = false; for (auto const& var: m_localVarUseCount) if (var.second == 0) - m_errorReporter.warning(var.first->location(), "Unused local variable"); + { + if (var.first->isCallableParameter()) + m_errorReporter.warning( + var.first->location(), + "Unused function parameter. Remove or comment out the variable name to silence this warning." + ); + else + m_errorReporter.warning(var.first->location(), "Unused local variable."); + } + m_localVarUseCount.clear(); } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 7bef8fec7..1fbc55a27 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -5816,7 +5816,7 @@ BOOST_AUTO_TEST_CASE(warn_unused_local) } } )"; - CHECK_WARNING(text, "Unused"); + CHECK_WARNING(text, "Unused local variable."); } BOOST_AUTO_TEST_CASE(warn_unused_local_assigned) @@ -5828,10 +5828,10 @@ BOOST_AUTO_TEST_CASE(warn_unused_local_assigned) } } )"; - CHECK_WARNING(text, "Unused"); + CHECK_WARNING(text, "Unused local variable."); } -BOOST_AUTO_TEST_CASE(warn_unused_param) +BOOST_AUTO_TEST_CASE(warn_unused_function_parameter) { char const* text = R"( contract C { @@ -5839,7 +5839,7 @@ BOOST_AUTO_TEST_CASE(warn_unused_param) } } )"; - CHECK_WARNING(text, "Unused"); + CHECK_WARNING(text, "Unused function parameter. Remove or comment out the variable name to silence this warning."); text = R"( contract C { function f(uint a) { @@ -5849,7 +5849,7 @@ BOOST_AUTO_TEST_CASE(warn_unused_param) success(text); } -BOOST_AUTO_TEST_CASE(warn_unused_return_param) +BOOST_AUTO_TEST_CASE(warn_unused_return_parameter) { char const* text = R"( contract C { @@ -5857,7 +5857,7 @@ BOOST_AUTO_TEST_CASE(warn_unused_return_param) } } )"; - CHECK_WARNING(text, "Unused"); + CHECK_WARNING(text, "Unused function parameter. Remove or comment out the variable name to silence this warning."); text = R"( contract C { function f() returns (uint a) { @@ -5865,7 +5865,7 @@ BOOST_AUTO_TEST_CASE(warn_unused_return_param) } } )"; - CHECK_WARNING(text, "Unused"); + CHECK_WARNING(text, "Unused function parameter. Remove or comment out the variable name to silence this warning."); text = R"( contract C { function f() returns (uint) { From dc8754b5955c48d0434d60382823c795b14c473e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 12 Sep 2017 13:39:19 +0200 Subject: [PATCH 095/162] Travis CI: Do not cache build dir --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 315d29bf2..5d6ce442a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -174,7 +174,6 @@ cache: ccache: true directories: - boost_1_57_0 - - build - $HOME/.local install: From 622a2d8251b467ded5a3134349198005f44faf6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 12 Sep 2017 14:39:30 +0200 Subject: [PATCH 096/162] Travis CI: Fix Emscripten build --- scripts/test_emscripten.sh | 39 +++++++++++++++++++------------------- test/externalTests.sh | 5 ++--- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/scripts/test_emscripten.sh b/scripts/test_emscripten.sh index f1d44a1fa..b01b33bb7 100755 --- a/scripts/test_emscripten.sh +++ b/scripts/test_emscripten.sh @@ -29,28 +29,29 @@ set -e REPO_ROOT=$(cd $(dirname "$0")/.. && pwd) +SOLJSON="$REPO_ROOT/build/solc/soljson.js" -cd $REPO_ROOT/build +DIR=$(mktemp -d) +( + echo "Preparing solc-js..." + git clone --depth 1 https://github.com/ethereum/solc-js "$DIR" + cd "$DIR" + npm install -echo "Preparing solc-js..." -rm -rf solc-js -git clone https://github.com/ethereum/solc-js -cd solc-js -npm install + # Replace soljson with current build + echo "Replacing soljson.js" + rm -f soljson.js + cp "$SOLJSON" soljson.js -# Replace soljson with current build -echo "Replacing soljson.js" -rm -f soljson.js -# Make a copy because paths might not be absolute -cp ../solc/soljson.js soljson.js + # Update version (needed for some tests) + VERSION=$("$REPO_ROOT/scripts/get_version.sh") + echo "Updating package.json to version $VERSION" + npm version --no-git-tag-version $VERSION -# Update version (needed for some tests) -VERSION=$(../../scripts/get_version.sh) -echo "Updating package.json to version $VERSION" -npm version $VERSION - -echo "Running solc-js tests..." -npm run test + echo "Running solc-js tests..." + npm run test +) +rm -rf "$DIR" echo "Running external tests...." -"$REPO_ROOT"/test/externalTests.sh "$REPO_ROOT"/build/solc/soljson.js +"$REPO_ROOT/test/externalTests.sh" "$SOLJSON" diff --git a/test/externalTests.sh b/test/externalTests.sh index 1b74561b7..6ff2ebc5b 100755 --- a/test/externalTests.sh +++ b/test/externalTests.sh @@ -38,10 +38,9 @@ SOLJSON="$1" DIR=$(mktemp -d) ( - cd "$DIR" echo "Running Zeppelin tests..." - git clone https://github.com/OpenZeppelin/zeppelin-solidity.git - cd zeppelin-solidity + git clone --depth 1 https://github.com/OpenZeppelin/zeppelin-solidity.git "$DIR" + cd "$DIR" npm install cp "$SOLJSON" ./node_modules/solc/soljson.js npm run test From 0259459b21f1f907380ec1f692dda19f7b2388bb Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 12 Sep 2017 12:05:20 +0100 Subject: [PATCH 097/162] Limit each duplicate declaration error to 32 references --- Changelog.md | 1 + libsolidity/analysis/TypeChecker.cpp | 22 ++++++++++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index d36275652..71f873a9c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,7 @@ Features: * Optimizer: Add new optimization step to remove unused ``JUMPDEST``s. * Type Checker: Display helpful warning for unused function arguments/return parameters. * Type Checker: Do not show the same error multiple times for events. + * Type Checker: Greatly reduce the number of duplicate errors shown for duplicate constructors and functions. * Type Checker: Warn on using literals as tight packing parameters in ``keccak256``, ``sha3``, ``sha256`` and ``ripemd160``. Bugfixes: diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 87750c6e2..c54f4c871 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -164,10 +164,18 @@ void TypeChecker::checkContractDuplicateFunctions(ContractDefinition const& _con for (; it != functions[_contract.name()].end(); ++it) ssl.append("Another declaration is here:", (*it)->location()); + string msg = "More than one constructor defined."; + size_t occurrences = ssl.infos.size(); + if (occurrences > 32) + { + ssl.infos.resize(32); + msg += " Truncated from " + boost::lexical_cast(occurrences) + " to the first 32 occurrences."; + } + m_errorReporter.declarationError( functions[_contract.name()].front()->location(), ssl, - "More than one constructor defined." + msg ); } for (auto const& it: functions) @@ -186,11 +194,21 @@ void TypeChecker::checkContractDuplicateFunctions(ContractDefinition const& _con } if (ssl.infos.size() > 0) + { + string msg = "Function with same name and arguments defined twice."; + size_t occurrences = ssl.infos.size(); + if (occurrences > 32) + { + ssl.infos.resize(32); + msg += " Truncated from " + boost::lexical_cast(occurrences) + " to the first 32 occurrences."; + } + m_errorReporter.declarationError( overloads[i]->location(), ssl, - "Function with same name and arguments defined twice." + msg ); + } } } } From 1a9c503e93547fadc0a7a2dcd6d368f456f47460 Mon Sep 17 00:00:00 2001 From: Ali92hm Date: Mon, 22 May 2017 13:39:38 -0700 Subject: [PATCH 098/162] Added createJson method to make writing json easier --- solc/CommandLineInterface.cpp | 5 +++++ solc/CommandLineInterface.h | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index e6d8776bf..9367a71c1 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -508,6 +508,11 @@ void CommandLineInterface::createFile(string const& _fileName, string const& _da BOOST_THROW_EXCEPTION(FileError() << errinfo_comment("Could not write to file: " + pathName)); } +void CommandLineInterface::createJson(string const& _fileName, Json::Value const& _json) +{ + createFile(boost::filesystem::basename(_fileName) + string(".json"), dev::jsonCompactPrint(_json)); +} + bool CommandLineInterface::parseArguments(int _argc, char** _argv) { // Declare the supported options. diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index bf9400e4c..97cf21832 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -81,6 +81,11 @@ private: /// @arg _data to be written void createFile(std::string const& _fileName, std::string const& _data); + /// Create a json file in the given directory + /// @arg _fileName the name of the file (the extension will be replaced with .json) + /// @arg _json to be written + void createJson(std::string const& _fileName, Json::Value const& _json); + bool m_error = false; ///< If true, some error occurred. bool m_onlyAssemble = false; From 487ade1635699e27cdf9700545257e14ba08cf5e Mon Sep 17 00:00:00 2001 From: Ali92hm Date: Mon, 22 May 2017 13:44:19 -0700 Subject: [PATCH 099/162] Enabling --combined-json to output to file --- solc/CommandLineInterface.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 9367a71c1..84c88b243 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -910,7 +910,11 @@ void CommandLineInterface::handleCombinedJSON() output[g_strSources][sourceCode.first]["AST"] = converter.toJson(m_compiler->ast(sourceCode.first)); } } - cout << dev::jsonCompactPrint(output) << endl; + + if (m_args.count(g_argOutputDir)) + createJson("combined", output); + else + cout << dev::jsonCompactPrint(output) << endl; } void CommandLineInterface::handleAst(string const& _argStr) From 61dabb2f29362728b0b57a73680c23463e0b73f6 Mon Sep 17 00:00:00 2001 From: Ali92hm Date: Mon, 3 Jul 2017 16:46:30 -0700 Subject: [PATCH 100/162] Added --pretty-json commandline option --- solc/CommandLineInterface.cpp | 13 +++++++++---- solc/CommandLineInterface.h | 4 ++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 84c88b243..271511d4f 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -112,10 +112,12 @@ static string const g_strSourceList = "sourceList"; static string const g_strSrcMap = "srcmap"; static string const g_strSrcMapRuntime = "srcmap-runtime"; static string const g_strStandardJSON = "standard-json"; +static string const g_strPrettyJson = "pretty-json"; static string const g_strVersion = "version"; static string const g_argAbi = g_strAbi; static string const g_argAddStandard = g_strAddStandard; +static string const g_argPrettyJson = g_strPrettyJson; static string const g_argAllowPaths = g_strAllowPaths; static string const g_argAsm = g_strAsm; static string const g_argAsmJson = g_strAsmJson; @@ -508,9 +510,9 @@ void CommandLineInterface::createFile(string const& _fileName, string const& _da BOOST_THROW_EXCEPTION(FileError() << errinfo_comment("Could not write to file: " + pathName)); } -void CommandLineInterface::createJson(string const& _fileName, Json::Value const& _json) +void CommandLineInterface::createJson(string const& _fileName, string const& _json) { - createFile(boost::filesystem::basename(_fileName) + string(".json"), dev::jsonCompactPrint(_json)); + createFile(boost::filesystem::basename(_fileName) + string(".json"), _json); } bool CommandLineInterface::parseArguments(int _argc, char** _argv) @@ -546,6 +548,7 @@ Allowed options)", "Estimated number of contract runs for optimizer tuning." ) (g_argAddStandard.c_str(), "Add standard contracts.") + (g_argPrettyJson.c_str(), "Output JSON in pretty format. Currently it only works with the combined JSON output.") ( g_argLibraries.c_str(), po::value>()->value_name("libs"), @@ -911,10 +914,12 @@ void CommandLineInterface::handleCombinedJSON() } } + string json = m_args.count(g_argPrettyJson) ? dev::jsonPrettyPrint(output) : dev::jsonCompactPrint(output); + if (m_args.count(g_argOutputDir)) - createJson("combined", output); + createJson("combined", json); else - cout << dev::jsonCompactPrint(output) << endl; + cout << json << endl; } void CommandLineInterface::handleAst(string const& _argStr) diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index 97cf21832..4768c9d88 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -83,8 +83,8 @@ private: /// Create a json file in the given directory /// @arg _fileName the name of the file (the extension will be replaced with .json) - /// @arg _json to be written - void createJson(std::string const& _fileName, Json::Value const& _json); + /// @arg _json json string to be written + void createJson(std::string const& _fileName, std::string const& _json); bool m_error = false; ///< If true, some error occurred. From 135c55c4d0e51277d56b2d23f26c91cc46e3df57 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 13 Sep 2017 09:49:19 +0100 Subject: [PATCH 101/162] Overhaul contract creation section in the documentation (add reference to remix and web3.js) --- docs/contracts.rst | 48 ++++++++++------------------------------------ 1 file changed, 10 insertions(+), 38 deletions(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index a1a446658..3bd6f6a81 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -16,51 +16,23 @@ inaccessible. Creating Contracts ****************** -Contracts can be created "from outside" or from Solidity contracts. +Contracts can be created "from outside" via Ethereum transactions or from within Solidity contracts. + +IDEs, such as `Remix `_, make the creation process seamless using UI elements. + +Creating contracts programatically on Ethereum is best done via using the JavaScript API `web3.js `_. +As of today it has a method called `web3.eth.Contract `_ +to facilitate contract creation. + When a contract is created, its constructor (a function with the same name as the contract) is executed once. - A constructor is optional. Only one constructor is allowed, and this means overloading is not supported. -From ``web3.js``, i.e. the JavaScript -API, this is done as follows:: - - // Need to specify some source including contract name for the data param below - var source = "contract CONTRACT_NAME { function CONTRACT_NAME(uint a, uint b) {} }"; - - // The json abi array generated by the compiler - var abiArray = [ - { - "inputs":[ - {"name":"x","type":"uint256"}, - {"name":"y","type":"uint256"} - ], - "type":"constructor" - }, - { - "constant":true, - "inputs":[], - "name":"x", - "outputs":[{"name":"","type":"bytes32"}], - "type":"function" - } - ]; - - var MyContract_ = web3.eth.contract(source); - MyContract = web3.eth.contract(MyContract_.CONTRACT_NAME.info.abiDefinition); - // deploy new contract - var contractInstance = MyContract.new( - 10, - 11, - {from: myAccount, gas: 1000000} - ); - .. index:: constructor;arguments -Internally, constructor arguments are passed after the code of -the contract itself, but you do not have to care about this -if you use ``web3.js``. +Internally, constructor arguments are passed :ref:`ABI encoded ` after the code of +the contract itself, but you do not have to care about this if you use ``web3.js``. If a contract wants to create another contract, the source code (and the binary) of the created contract has to be known to the creator. From 58f7a27ee0336423fa7dc76b8a160b78301bbdd2 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 27 Jun 2017 10:25:36 +0100 Subject: [PATCH 102/162] Add sig member on function type --- Changelog.md | 1 + libsolidity/ast/Types.cpp | 5 +++++ libsolidity/codegen/ExpressionCompiler.cpp | 9 ++++++++- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index d36275652..49fbf8f12 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,7 @@ ### 0.4.17 (unreleased) Features: + * Code Generator: Added ``.sig`` member on external function types to retrieve their signature. * Optimizer: Add new optimization step to remove unused ``JUMPDEST``s. * Type Checker: Display helpful warning for unused function arguments/return parameters. * Type Checker: Do not show the same error multiple times for events. diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 104248582..5aa52383b 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2436,6 +2436,11 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con case Kind::BareDelegateCall: { MemberList::MemberMap members; + if (m_kind == Kind::External) + members.push_back(MemberList::Member( + "sig", + make_shared(4) + )); if (m_kind != Kind::BareDelegateCall && m_kind != Kind::DelegateCall) { if (isPayable()) diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 5c9b743ac..b7aadc128 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1068,7 +1068,14 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) solAssert(false, "Invalid member access to integer"); break; case Type::Category::Function: - solAssert(!!_memberAccess.expression().annotation().type->memberType(member), + if (member == "sig") + { + m_context << Instruction::SWAP1 << Instruction::POP; + /// need to store store it as bytes4 + utils().leftShiftNumberOnStack(224); + } + else + solAssert(!!_memberAccess.expression().annotation().type->memberType(member), "Invalid member access to function."); break; case Type::Category::Magic: From 88946f9f03625fab345572c66b33ee3e05a07159 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 27 Jun 2017 12:15:51 +0100 Subject: [PATCH 103/162] Add tests for function type sigs --- test/libsolidity/SolidityEndToEndTest.cpp | 24 ++++++ .../SolidityNameAndTypeResolution.cpp | 81 +++++++++++++++++++ 2 files changed, 105 insertions(+) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 73dd7d22c..f3d0fde36 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -10051,6 +10051,30 @@ BOOST_AUTO_TEST_CASE(delegatecall_return_value) BOOST_CHECK(callContractFunction("get_delegated()") == encodeArgs(u256(1))); } +BOOST_AUTO_TEST_CASE(function_types_sig) +{ + char const* sourceCode = R"( + contract C { + function f() returns (bytes4) { + return this.f.sig; + } + function g() returns (bytes4) { + function () external returns (bytes4) fun = this.f; + return fun.sig; + } + function h() returns (bytes4) { + function () external returns (bytes4) fun = this.f; + var funvar = fun; + return funvar.sig; + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("f()") == fromHex("0x26121ff0")); + BOOST_CHECK(callContractFunction("g()") == fromHex("0x26121ff0")); + BOOST_CHECK(callContractFunction("h()") == fromHex("0x26121ff0")); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 1fbc55a27..6576634fe 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -6285,6 +6285,87 @@ BOOST_AUTO_TEST_CASE(modifiers_access_storage_pointer) CHECK_SUCCESS_NO_WARNINGS(text); } +BOOST_AUTO_TEST_CASE(function_types_sig) +{ + char const* text = R"( + contract C { + function f() returns (bytes4) { + return f.sig; + } + } + )"; + CHECK_ERROR(text, TypeError, "Member \"sig\" not found"); + text = R"( + contract C { + function g() internal { + } + function f() returns (bytes4) { + return g.sig; + } + } + )"; + CHECK_ERROR(text, TypeError, "Member \"sig\" not found"); + text = R"( + contract C { + function f() returns (bytes4) { + function () g; + return g.sig; + } + } + )"; + CHECK_ERROR(text, TypeError, "Member \"sig\" not found"); + text = R"( + contract C { + function f() returns (bytes4) { + return this.f.sig; + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + text = R"( + contract C { + function f() external returns (bytes4) { + return this.f.sig; + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + text = R"( + contract C { + function h() external { + } + function f() external returns (bytes4) { + var g = this.h; + return g.sig; + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + text = R"( + contract C { + function h() external { + } + function f() external returns (bytes4) { + function () external g = this.h; + return g.sig; + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + text = R"( + contract C { + function h() external { + } + function f() external returns (bytes4) { + function () external g = this.h; + var i = g; + return i.sig; + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + BOOST_AUTO_TEST_CASE(using_this_in_constructor) { char const* text = R"( From fd1f8ab38ba7f2bb04e67a44ea5e947eddcf9b13 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 28 Jun 2017 18:15:41 +0100 Subject: [PATCH 104/162] Rename .sig to .selector on function types --- Changelog.md | 2 +- libsolidity/ast/Types.cpp | 2 +- libsolidity/codegen/ExpressionCompiler.cpp | 2 +- test/libsolidity/SolidityEndToEndTest.cpp | 6 ++--- .../SolidityNameAndTypeResolution.cpp | 22 +++++++++---------- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Changelog.md b/Changelog.md index 49fbf8f12..d90cd3f65 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,7 +1,7 @@ ### 0.4.17 (unreleased) Features: - * Code Generator: Added ``.sig`` member on external function types to retrieve their signature. + * Code Generator: Added ``.selector`` member on external function types to retrieve their signature. * Optimizer: Add new optimization step to remove unused ``JUMPDEST``s. * Type Checker: Display helpful warning for unused function arguments/return parameters. * Type Checker: Do not show the same error multiple times for events. diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 5aa52383b..705d0f7f2 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2438,7 +2438,7 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con MemberList::MemberMap members; if (m_kind == Kind::External) members.push_back(MemberList::Member( - "sig", + "selector", make_shared(4) )); if (m_kind != Kind::BareDelegateCall && m_kind != Kind::DelegateCall) diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index b7aadc128..631a25ff9 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1068,7 +1068,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) solAssert(false, "Invalid member access to integer"); break; case Type::Category::Function: - if (member == "sig") + if (member == "selector") { m_context << Instruction::SWAP1 << Instruction::POP; /// need to store store it as bytes4 diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index f3d0fde36..661d184a0 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -10056,16 +10056,16 @@ BOOST_AUTO_TEST_CASE(function_types_sig) char const* sourceCode = R"( contract C { function f() returns (bytes4) { - return this.f.sig; + return this.f.selector; } function g() returns (bytes4) { function () external returns (bytes4) fun = this.f; - return fun.sig; + return fun.selector; } function h() returns (bytes4) { function () external returns (bytes4) fun = this.f; var funvar = fun; - return funvar.sig; + return funvar.selector; } } )"; diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 6576634fe..9f33c5b37 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -6290,34 +6290,34 @@ BOOST_AUTO_TEST_CASE(function_types_sig) char const* text = R"( contract C { function f() returns (bytes4) { - return f.sig; + return f.selector; } } )"; - CHECK_ERROR(text, TypeError, "Member \"sig\" not found"); + CHECK_ERROR(text, TypeError, "Member \"selector\" not found"); text = R"( contract C { function g() internal { } function f() returns (bytes4) { - return g.sig; + return g.selector; } } )"; - CHECK_ERROR(text, TypeError, "Member \"sig\" not found"); + CHECK_ERROR(text, TypeError, "Member \"selector\" not found"); text = R"( contract C { function f() returns (bytes4) { function () g; - return g.sig; + return g.selector; } } )"; - CHECK_ERROR(text, TypeError, "Member \"sig\" not found"); + CHECK_ERROR(text, TypeError, "Member \"selector\" not found"); text = R"( contract C { function f() returns (bytes4) { - return this.f.sig; + return this.f.selector; } } )"; @@ -6325,7 +6325,7 @@ BOOST_AUTO_TEST_CASE(function_types_sig) text = R"( contract C { function f() external returns (bytes4) { - return this.f.sig; + return this.f.selector; } } )"; @@ -6336,7 +6336,7 @@ BOOST_AUTO_TEST_CASE(function_types_sig) } function f() external returns (bytes4) { var g = this.h; - return g.sig; + return g.selector; } } )"; @@ -6347,7 +6347,7 @@ BOOST_AUTO_TEST_CASE(function_types_sig) } function f() external returns (bytes4) { function () external g = this.h; - return g.sig; + return g.selector; } } )"; @@ -6359,7 +6359,7 @@ BOOST_AUTO_TEST_CASE(function_types_sig) function f() external returns (bytes4) { function () external g = this.h; var i = g; - return i.sig; + return i.selector; } } )"; From c70ebfd241cc3dc6d19847d94b11784da1c79cdd Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 11 Jul 2017 18:10:42 +0100 Subject: [PATCH 105/162] Document function selectors --- docs/abi-spec.rst | 2 ++ docs/types.rst | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index fc1a3adb7..3ce7f50c5 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -17,6 +17,8 @@ We assume the interface functions of a contract are strongly typed, known at com This specification does not address contracts whose interface is dynamic or otherwise known only at run-time. Should these cases become important they can be adequately handled as facilities built within the Ethereum ecosystem. +.. _abi_function_selector: + Function Selector ================= diff --git a/docs/types.rst b/docs/types.rst index 3335655ab..5c291f35d 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -400,6 +400,17 @@ Note that public functions of the current contract can be used both as an internal and as an external function. To use ``f`` as an internal function, just use ``f``, if you want to use its external form, use ``this.f``. +Additionally, public (or external) functions also have a special member called ``selector``, +which returns the :ref:`ABI function selector `:: + + pragma solidity ^0.4.0; + + contract Selector { + function f() returns (bytes4) { + return this.f.selector; + } + } + Example that shows how to use internal function types:: pragma solidity ^0.4.5; From 8b166c36369d0d1b39a4c537bc6ee8a08f3c6e5a Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 11 Jul 2017 20:22:08 +0100 Subject: [PATCH 106/162] Use hashing function in tests --- test/libsolidity/SolidityEndToEndTest.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 661d184a0..1a2abea32 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -10070,9 +10070,9 @@ BOOST_AUTO_TEST_CASE(function_types_sig) } )"; compileAndRun(sourceCode, 0, "C"); - BOOST_CHECK(callContractFunction("f()") == fromHex("0x26121ff0")); - BOOST_CHECK(callContractFunction("g()") == fromHex("0x26121ff0")); - BOOST_CHECK(callContractFunction("h()") == fromHex("0x26121ff0")); + BOOST_CHECK(callContractFunction("f()") == encodeArgs(asString(FixedHash<4>(dev::keccak256("f()")).asBytes()))); + BOOST_CHECK(callContractFunction("g()") == encodeArgs(asString(FixedHash<4>(dev::keccak256("f()")).asBytes()))); + BOOST_CHECK(callContractFunction("h()") == encodeArgs(asString(FixedHash<4>(dev::keccak256("f()")).asBytes()))); } BOOST_AUTO_TEST_SUITE_END() From 66c01301fe5cb71a2bf66af2f7170043f088815c Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 13 Sep 2017 17:18:22 +0200 Subject: [PATCH 107/162] Rename to invalidInPureFunctions --- libevmasm/SemanticInformation.cpp | 2 +- libevmasm/SemanticInformation.h | 2 +- libsolidity/analysis/ViewPureChecker.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libevmasm/SemanticInformation.cpp b/libevmasm/SemanticInformation.cpp index e277a08d2..ceb3fbdd0 100644 --- a/libevmasm/SemanticInformation.cpp +++ b/libevmasm/SemanticInformation.cpp @@ -189,7 +189,7 @@ bool SemanticInformation::invalidatesStorage(Instruction _instruction) } } -bool SemanticInformation::readsFromState(Instruction _instruction) +bool SemanticInformation::invalidInPureFunctions(Instruction _instruction) { switch (_instruction) { diff --git a/libevmasm/SemanticInformation.h b/libevmasm/SemanticInformation.h index 963088d98..e5ea7c180 100644 --- a/libevmasm/SemanticInformation.h +++ b/libevmasm/SemanticInformation.h @@ -53,7 +53,7 @@ struct SemanticInformation static bool invalidatesMemory(solidity::Instruction _instruction); /// @returns true if the given instruction modifies storage (even indirectly). static bool invalidatesStorage(solidity::Instruction _instruction); - static bool readsFromState(solidity::Instruction _instruction); + static bool invalidInPureFunctions(solidity::Instruction _instruction); static bool invalidInViewFunctions(solidity::Instruction _instruction); }; diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index d4a6e96f1..40ad68287 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -41,7 +41,7 @@ public: { if (eth::SemanticInformation::invalidInViewFunctions(_instruction.instruction)) m_reportMutability(StateMutability::NonPayable, _instruction.location); - else if (eth::SemanticInformation::readsFromState(_instruction.instruction)) + else if (eth::SemanticInformation::invalidInPureFunctions(_instruction.instruction)) m_reportMutability(StateMutability::View, _instruction.location); } void operator()(assembly::Literal const&) {} From e2f30ce9ca3bc8c6c6e64c074b98bafec7e2e5d5 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 13 Sep 2017 17:29:27 +0200 Subject: [PATCH 108/162] Minor changes from review. --- libsolidity/analysis/ViewPureChecker.cpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index 40ad68287..8f9d41c92 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -108,12 +108,7 @@ bool ViewPureChecker::check() { SourceUnit const* source = dynamic_cast(node.get()); solAssert(source, ""); - for (auto const& topLevelNode: source->nodes()) - { - ContractDefinition const* contract = dynamic_cast(topLevelNode.get()); - if (contract) - contracts.push_back(contract); - } + contracts += source->filteredNodes(source->nodes()); } // Check modifiers first to infer their state mutability. @@ -146,7 +141,6 @@ void ViewPureChecker::endVisit(FunctionDefinition const& _funDef) _funDef.isImplemented() && !_funDef.isConstructor() && !_funDef.isFallback() && - !_funDef.isConstructor() && !_funDef.annotation().superFunction ) m_errorReporter.warning( @@ -207,7 +201,7 @@ void ViewPureChecker::endVisit(Identifier const& _identifier) void ViewPureChecker::endVisit(InlineAssembly const& _inlineAssembly) { AssemblyViewPureChecker{ - [=](StateMutability _mut, SourceLocation const& _loc) { reportMutability(_mut, _loc); } + [=](StateMutability _mutability, SourceLocation const& _location) { reportMutability(_mutability, _location); } }(_inlineAssembly.operations()); } @@ -230,7 +224,7 @@ void ViewPureChecker::reportMutability(StateMutability _mutability, SourceLocati solAssert(false, ""); if (m_currentFunction->stateMutability() == StateMutability::View) - // Change this to error with 0.5.0 + // TODO Change this to error with 0.5.0 m_errorReporter.warning(_location, text); else if (m_currentFunction->stateMutability() == StateMutability::Pure) { From 172704a58fa2b7562107b8df299c5a81ba702d12 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 13 Sep 2017 18:28:36 +0200 Subject: [PATCH 109/162] Changelog entry. --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index 4af4419d7..9ca8db0cc 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,6 +3,7 @@ Features: * Optimizer: Add new optimization step to remove unused ``JUMPDEST``s. * Type Checker: Warn on using literals as tight packing parameters in ``keccak256``, ``sha3``, ``sha256`` and ``ripemd160``. + * Type Checker: Enforce ``view`` and ``pure``. Bugfixes: * ABI JSON: Include all overloaded events. From e1f90f0ad639baf2c14b6c1bbfb479cc469d2f33 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 13 Sep 2017 17:39:48 +0100 Subject: [PATCH 110/162] Fix nested lists in the ABI documentation --- docs/abi-spec.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index 3ce7f50c5..f75a6885c 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -91,6 +91,7 @@ Properties: We distinguish static and dynamic types. Static types are encoded in-place and dynamic types are encoded at a separately allocated location after the current block. **Definition:** The following types are called "dynamic": + * ``bytes`` * ``string`` * ``T[]`` for any ``T`` @@ -293,8 +294,10 @@ The JSON format for a contract's interface is given by an array of function and/ - ``type``: ``"function"``, ``"constructor"``, or ``"fallback"`` (the :ref:`unnamed "default" function `); - ``name``: the name of the function; - ``inputs``: an array of objects, each of which contains: + * ``name``: the name of the parameter; * ``type``: the canonical type of the parameter. + - ``outputs``: an array of objects similar to ``inputs``, can be omitted if function doesn't return anything; - ``payable``: ``true`` if function accepts ether, defaults to ``false``; - ``stateMutability``: a string with one of the following values: ``pure`` (:ref:`specified to not read blockchain state `), ``view`` (:ref:`specified to not modify the blockchain state `), ``nonpayable`` and ``payable`` (same as ``payable`` above). @@ -311,9 +314,11 @@ An event description is a JSON object with fairly similar fields: - ``type``: always ``"event"`` - ``name``: the name of the event; - ``inputs``: an array of objects, each of which contains: + * ``name``: the name of the parameter; * ``type``: the canonical type of the parameter. * ``indexed``: ``true`` if the field is part of the log's topics, ``false`` if it one of the log's data segment. + - ``anonymous``: ``true`` if the event was declared as ``anonymous``. For example, From 71118e99fe33290b3ef9ee2a4e96b3184ecf52b0 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 13 Sep 2017 18:48:21 +0100 Subject: [PATCH 111/162] Fix view/pure warnings on selector tests --- .../SolidityNameAndTypeResolution.cpp | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index d3c9802be..c7ef1d46f 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -6289,7 +6289,7 @@ BOOST_AUTO_TEST_CASE(function_types_sig) { char const* text = R"( contract C { - function f() returns (bytes4) { + function f() view returns (bytes4) { return f.selector; } } @@ -6297,9 +6297,9 @@ BOOST_AUTO_TEST_CASE(function_types_sig) CHECK_ERROR(text, TypeError, "Member \"selector\" not found"); text = R"( contract C { - function g() internal { + function g() pure internal { } - function f() returns (bytes4) { + function f() view returns (bytes4) { return g.selector; } } @@ -6307,7 +6307,7 @@ BOOST_AUTO_TEST_CASE(function_types_sig) CHECK_ERROR(text, TypeError, "Member \"selector\" not found"); text = R"( contract C { - function f() returns (bytes4) { + function f() view returns (bytes4) { function () g; return g.selector; } @@ -6316,7 +6316,7 @@ BOOST_AUTO_TEST_CASE(function_types_sig) CHECK_ERROR(text, TypeError, "Member \"selector\" not found"); text = R"( contract C { - function f() returns (bytes4) { + function f() view returns (bytes4) { return this.f.selector; } } @@ -6324,7 +6324,7 @@ BOOST_AUTO_TEST_CASE(function_types_sig) CHECK_SUCCESS_NO_WARNINGS(text); text = R"( contract C { - function f() external returns (bytes4) { + function f() view external returns (bytes4) { return this.f.selector; } } @@ -6332,9 +6332,9 @@ BOOST_AUTO_TEST_CASE(function_types_sig) CHECK_SUCCESS_NO_WARNINGS(text); text = R"( contract C { - function h() external { + function h() pure external { } - function f() external returns (bytes4) { + function f() view external returns (bytes4) { var g = this.h; return g.selector; } @@ -6343,10 +6343,10 @@ BOOST_AUTO_TEST_CASE(function_types_sig) CHECK_SUCCESS_NO_WARNINGS(text); text = R"( contract C { - function h() external { + function h() pure external { } - function f() external returns (bytes4) { - function () external g = this.h; + function f() view external returns (bytes4) { + function () pure external g = this.h; return g.selector; } } @@ -6354,10 +6354,10 @@ BOOST_AUTO_TEST_CASE(function_types_sig) CHECK_SUCCESS_NO_WARNINGS(text); text = R"( contract C { - function h() external { + function h() pure external { } - function f() external returns (bytes4) { - function () external g = this.h; + function f() view external returns (bytes4) { + function () pure external g = this.h; var i = g; return i.selector; } From 06c2ddfd5017b310f5de732a0132491d236f8359 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 12 Sep 2017 00:21:54 +0100 Subject: [PATCH 112/162] Add experimental feature 'v0.5.0' --- Changelog.md | 1 + libsolidity/ast/ExperimentalFeatures.h | 2 ++ 2 files changed, 3 insertions(+) diff --git a/Changelog.md b/Changelog.md index d5f27e539..d0d4d8dfa 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,7 @@ ### 0.4.17 (unreleased) Features: + * Support ``pragma experimental v0.5.0;`` to turn on upcoming breaking changes. * Code Generator: Added ``.selector`` member on external function types to retrieve their signature. * Optimizer: Add new optimization step to remove unused ``JUMPDEST``s. * Type Checker: Display helpful warning for unused function arguments/return parameters. diff --git a/libsolidity/ast/ExperimentalFeatures.h b/libsolidity/ast/ExperimentalFeatures.h index 2c0896712..3ecfac7bf 100644 --- a/libsolidity/ast/ExperimentalFeatures.h +++ b/libsolidity/ast/ExperimentalFeatures.h @@ -31,6 +31,7 @@ enum class ExperimentalFeature { SMTChecker, ABIEncoderV2, // new ABI encoder that makes use of JULIA + V050, // v0.5.0 breaking changes Test, TestOnlyAnalysis }; @@ -45,6 +46,7 @@ static const std::map ExperimentalFeatureNames { { "SMTChecker", ExperimentalFeature::SMTChecker }, { "ABIEncoderV2", ExperimentalFeature::ABIEncoderV2 }, + { "v0.5.0", ExperimentalFeature::V050 }, { "__test", ExperimentalFeature::Test }, { "__testOnlyAnalysis", ExperimentalFeature::TestOnlyAnalysis }, }; From 5b5367dc1237634886a8ccf1029d95b83a0d8e63 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 15 Aug 2017 13:22:50 +0100 Subject: [PATCH 113/162] Warn if no visibility is specified on contract functions. --- Changelog.md | 1 + libsolidity/analysis/SyntaxChecker.cpp | 14 +++++++++++++- libsolidity/analysis/SyntaxChecker.h | 1 + libsolidity/ast/AST.h | 1 + 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index d0d4d8dfa..cbecb3cd2 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,7 @@ Features: * Support ``pragma experimental v0.5.0;`` to turn on upcoming breaking changes. * Code Generator: Added ``.selector`` member on external function types to retrieve their signature. * Optimizer: Add new optimization step to remove unused ``JUMPDEST``s. + * Syntax Checker: Warn if no visibility is specified on contract functions. * Type Checker: Display helpful warning for unused function arguments/return parameters. * Type Checker: Do not show the same error multiple times for events. * Type Checker: Greatly reduce the number of duplicate errors shown for duplicate constructors and functions. diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index d2571cd35..187eb26fa 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -138,7 +138,7 @@ bool SyntaxChecker::visit(WhileStatement const&) return true; } -void SyntaxChecker::endVisit(WhileStatement const& ) +void SyntaxChecker::endVisit(WhileStatement const&) { m_inLoopDepth--; } @@ -193,6 +193,18 @@ bool SyntaxChecker::visit(PlaceholderStatement const&) return true; } +bool SyntaxChecker::visit(FunctionDefinition const& _function) +{ + if (_function.noVisibilitySpecified()) + m_errorReporter.warning( + _function.location(), + "No visibility specified. Defaulting to \"" + + Declaration::visibilityToString(_function.visibility()) + + "\"." + ); + return true; +} + bool SyntaxChecker::visit(FunctionTypeName const& _node) { for (auto const& decl: _node.parameterTypeList()->parameters()) diff --git a/libsolidity/analysis/SyntaxChecker.h b/libsolidity/analysis/SyntaxChecker.h index fa34bab31..7fffbec00 100644 --- a/libsolidity/analysis/SyntaxChecker.h +++ b/libsolidity/analysis/SyntaxChecker.h @@ -66,6 +66,7 @@ private: virtual bool visit(PlaceholderStatement const& _placeholderStatement) override; + virtual bool visit(FunctionDefinition const& _function) override; virtual bool visit(FunctionTypeName const& _node) override; ErrorReporter& m_errorReporter; diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index cecc2f04e..75b8e9461 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -180,6 +180,7 @@ public: /// @returns the declared name. ASTString const& name() const { return *m_name; } + bool noVisibilitySpecified() const { return m_visibility == Visibility::Default; } Visibility visibility() const { return m_visibility == Visibility::Default ? defaultVisibility() : m_visibility; } bool isPublic() const { return visibility() >= Visibility::Public; } virtual bool isVisibleInContract() const { return visibility() != Visibility::External; } From a02cf83d86489f5d9c9439c79e27fcecb5c5ddb4 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 15 Aug 2017 14:36:05 +0100 Subject: [PATCH 114/162] Update std to contain visibility specifiers --- std/StandardToken.sol | 14 +++++++------- std/Token.sol | 12 ++++++------ std/mortal.sol | 2 +- std/owned.sol | 2 +- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/std/StandardToken.sol b/std/StandardToken.sol index 51f925e09..2986cb563 100644 --- a/std/StandardToken.sol +++ b/std/StandardToken.sol @@ -8,24 +8,24 @@ contract StandardToken is Token { mapping (address => mapping (address => uint256)) m_allowance; - function StandardToken(address _initialOwner, uint256 _supply) { + function StandardToken(address _initialOwner, uint256 _supply) public { supply = _supply; balance[_initialOwner] = _supply; } - function balanceOf(address _account) constant returns (uint) { + function balanceOf(address _account) constant public returns (uint) { return balance[_account]; } - function totalSupply() constant returns (uint) { + function totalSupply() constant public returns (uint) { return supply; } - function transfer(address _to, uint256 _value) returns (bool success) { + function transfer(address _to, uint256 _value) public returns (bool success) { return doTransfer(msg.sender, _to, _value); } - function transferFrom(address _from, address _to, uint256 _value) returns (bool) { + function transferFrom(address _from, address _to, uint256 _value) public returns (bool) { if (m_allowance[_from][msg.sender] >= _value) { if (doTransfer(_from, _to, _value)) { m_allowance[_from][msg.sender] -= _value; @@ -47,13 +47,13 @@ contract StandardToken is Token { } } - function approve(address _spender, uint256 _value) returns (bool success) { + function approve(address _spender, uint256 _value) public returns (bool success) { m_allowance[msg.sender][_spender] = _value; Approval(msg.sender, _spender, _value); return true; } - function allowance(address _owner, address _spender) constant returns (uint256) { + function allowance(address _owner, address _spender) constant public returns (uint256) { return m_allowance[_owner][_spender]; } } diff --git a/std/Token.sol b/std/Token.sol index 59566f26f..4b4eb71e2 100644 --- a/std/Token.sol +++ b/std/Token.sol @@ -4,10 +4,10 @@ contract Token { event Transfer(address indexed _from, address indexed _to, uint256 _value); event Approval(address indexed _owner, address indexed _spender, uint256 _value); - function totalSupply() constant returns (uint256 supply); - function balanceOf(address _owner) constant returns (uint256 balance); - function transfer(address _to, uint256 _value) returns (bool success); - function transferFrom(address _from, address _to, uint256 _value) returns (bool success); - function approve(address _spender, uint256 _value) returns (bool success); - function allowance(address _owner, address _spender) constant returns (uint256 remaining); + function totalSupply() constant public returns (uint256 supply); + function balanceOf(address _owner) constant public returns (uint256 balance); + function transfer(address _to, uint256 _value) public returns (bool success); + function transferFrom(address _from, address _to, uint256 _value) public returns (bool success); + function approve(address _spender, uint256 _value) public returns (bool success); + function allowance(address _owner, address _spender) constant public returns (uint256 remaining); } diff --git a/std/mortal.sol b/std/mortal.sol index f0a6f4ce0..c43f1e4f7 100644 --- a/std/mortal.sol +++ b/std/mortal.sol @@ -3,7 +3,7 @@ pragma solidity ^0.4.0; import "./owned.sol"; contract mortal is owned { - function kill() { + function kill() public { if (msg.sender == owner) selfdestruct(owner); } diff --git a/std/owned.sol b/std/owned.sol index bbb8d957d..ee9860d34 100644 --- a/std/owned.sol +++ b/std/owned.sol @@ -9,7 +9,7 @@ contract owned { } } - function owned() { + function owned() public { owner = msg.sender; } } From df8380193b3263b20f462b5d17a43a642b7f9585 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 5 Sep 2017 16:11:53 +0100 Subject: [PATCH 115/162] Update type tests to contain mandatory visibility specifiers --- .../SolidityNameAndTypeResolution.cpp | 1001 +++++++++-------- 1 file changed, 506 insertions(+), 495 deletions(-) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index c7ef1d46f..8c271fe10 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -46,7 +46,7 @@ BOOST_AUTO_TEST_CASE(smoke_test) char const* text = R"( contract test { uint256 stateVariable1; - function fun(uint256 arg1) { uint256 y; y = arg1; } + function fun(uint256 arg1) public { uint256 y; y = arg1; } } )"; CHECK_SUCCESS(text); @@ -67,8 +67,8 @@ BOOST_AUTO_TEST_CASE(double_function_declaration) { char const* text = R"( contract test { - function fun() { } - function fun() { } + function fun() public { } + function fun() public { } } )"; CHECK_ERROR(text, DeclarationError, "Function with same name and arguments defined twice."); @@ -78,9 +78,9 @@ BOOST_AUTO_TEST_CASE(double_variable_declaration) { char const* text = R"( contract test { - function f() { + function f() public { uint256 x; - if (true) { uint256 x; } + if (true) { uint256 x; } } } )"; @@ -92,7 +92,7 @@ BOOST_AUTO_TEST_CASE(name_shadowing) char const* text = R"( contract test { uint256 variable; - function f() { uint32 variable; variable = 2; } + function f() public { uint32 variable; variable = 2; } } )"; CHECK_SUCCESS(text); @@ -103,7 +103,7 @@ BOOST_AUTO_TEST_CASE(name_references) char const* text = R"( contract test { uint256 variable; - function f(uint256) returns (uint out) { f(variable); test; out; } + function f(uint256) public returns (uint out) { f(variable); test; out; } } )"; CHECK_SUCCESS(text); @@ -114,7 +114,7 @@ BOOST_AUTO_TEST_CASE(undeclared_name) char const* text = R"( contract test { uint256 variable; - function f(uint256 arg) { + function f(uint256 arg) public { f(notfound); } } @@ -126,8 +126,8 @@ BOOST_AUTO_TEST_CASE(reference_to_later_declaration) { char const* text = R"( contract test { - function g() { f(); } - function f() {} + function g() public { f(); } + function f() public {} } )"; CHECK_SUCCESS(text); @@ -192,7 +192,7 @@ BOOST_AUTO_TEST_CASE(type_inference_smoke_test) { char const* text = R"( contract test { - function f(uint256 arg1, uint32 arg2) returns (bool ret) { + function f(uint256 arg1, uint32 arg2) public returns (bool ret) { var x = arg1 + arg2 == 8; ret = x; } } @@ -204,7 +204,7 @@ BOOST_AUTO_TEST_CASE(type_checking_return) { char const* text = R"( contract test { - function f() returns (bool r) { return 1 >= 2; } + function f() public returns (bool r) { return 1 >= 2; } } )"; CHECK_SUCCESS(text); @@ -214,7 +214,7 @@ BOOST_AUTO_TEST_CASE(type_checking_return_wrong_number) { char const* text = R"( contract test { - function f() returns (bool r1, bool r2) { return 1 >= 2; } + function f() public returns (bool r1, bool r2) { return 1 >= 2; } } )"; CHECK_ERROR(text, TypeError, "Different number of arguments in return statement than in returns declaration."); @@ -224,7 +224,7 @@ BOOST_AUTO_TEST_CASE(type_checking_return_wrong_type) { char const* text = R"( contract test { - function f() returns (uint256 r) { return 1 >= 2; } + function f() public returns (uint256 r) { return 1 >= 2; } } )"; CHECK_ERROR(text, TypeError, "Return argument type bool is not implicitly convertible to expected type (type of first return variable) uint256."); @@ -234,8 +234,8 @@ BOOST_AUTO_TEST_CASE(type_checking_function_call) { char const* text = R"( contract test { - function f() returns (bool) { return g(12, true) == 3; } - function g(uint256, bool) returns (uint256) { } + function f() public returns (bool) { return g(12, true) == 3; } + function g(uint256, bool) public returns (uint256) { } } )"; CHECK_SUCCESS(text); @@ -245,7 +245,7 @@ BOOST_AUTO_TEST_CASE(type_conversion_for_comparison) { char const* text = R"( contract test { - function f() { uint32(2) == int64(2); } + function f() public { uint32(2) == int64(2); } } )"; CHECK_SUCCESS(text); @@ -255,7 +255,7 @@ BOOST_AUTO_TEST_CASE(type_conversion_for_comparison_invalid) { char const* text = R"( contract test { - function f() { int32(2) == uint64(2); } + function f() public { int32(2) == uint64(2); } } )"; CHECK_ERROR(text, TypeError, "Operator == not compatible with types int32 and uint64"); @@ -265,7 +265,7 @@ BOOST_AUTO_TEST_CASE(type_inference_explicit_conversion) { char const* text = R"( contract test { - function f() returns (int256 r) { var x = int256(uint32(2)); return x; } + function f() public returns (int256 r) { var x = int256(uint32(2)); return x; } } )"; CHECK_SUCCESS(text); @@ -275,7 +275,7 @@ BOOST_AUTO_TEST_CASE(large_string_literal) { char const* text = R"( contract test { - function f() { var x = "123456789012345678901234567890123"; } + function f() public { var x = "123456789012345678901234567890123"; } } )"; CHECK_SUCCESS(text); @@ -285,7 +285,7 @@ BOOST_AUTO_TEST_CASE(balance) { char const* text = R"( contract test { - function fun() { + function fun() public { uint256 x = address(0).balance; } } @@ -297,7 +297,7 @@ BOOST_AUTO_TEST_CASE(balance_invalid) { char const* text = R"( contract test { - function fun() { + function fun() public { address(0).balance = 7; } } @@ -313,7 +313,7 @@ BOOST_AUTO_TEST_CASE(assignment_to_mapping) mapping(uint=>uint) map; } str data; - function fun() { + function fun() public { var a = data.map; data.map = a; } @@ -330,7 +330,7 @@ BOOST_AUTO_TEST_CASE(assignment_to_struct) mapping(uint=>uint) map; } str data; - function fun() { + function fun() public { var a = data; data = a; } @@ -343,7 +343,7 @@ BOOST_AUTO_TEST_CASE(returns_in_constructor) { char const* text = R"( contract test { - function test() returns (uint a) { } + function test() public returns (uint a) { } } )"; CHECK_ERROR(text, TypeError, "Non-empty \"returns\" directive for constructor."); @@ -353,12 +353,12 @@ BOOST_AUTO_TEST_CASE(forward_function_reference) { char const* text = R"( contract First { - function fun() returns (bool) { + function fun() public returns (bool) { return Second(1).fun(1, true, 3) > 0; } } contract Second { - function fun(uint, bool, uint) returns (uint) { + function fun(uint, bool, uint) public returns (uint) { if (First(2).fun() == true) return 1; } } @@ -370,7 +370,7 @@ BOOST_AUTO_TEST_CASE(comparison_bitop_precedence) { char const* text = R"( contract First { - function fun() returns (bool ret) { + function fun() public returns (bool ret) { return 1 & 2 == 8 & 9 && 1 ^ 2 < 4 | 6; } } @@ -382,7 +382,7 @@ BOOST_AUTO_TEST_CASE(comparison_of_function_types) { char const* text = R"( contract C { - function f() returns (bool ret) { + function f() public returns (bool ret) { return this.f < this.f; } } @@ -390,7 +390,7 @@ BOOST_AUTO_TEST_CASE(comparison_of_function_types) CHECK_ERROR(text, TypeError, "Operator < not compatible"); text = R"( contract C { - function f() returns (bool ret) { + function f() public returns (bool ret) { return f < f; } } @@ -398,10 +398,10 @@ BOOST_AUTO_TEST_CASE(comparison_of_function_types) CHECK_ERROR(text, TypeError, "Operator < not compatible"); text = R"( contract C { - function f() returns (bool ret) { + function f() public returns (bool ret) { return f == f; } - function g() returns (bool ret) { + function g() public returns (bool ret) { return f != f; } } @@ -414,7 +414,7 @@ BOOST_AUTO_TEST_CASE(comparison_of_mapping_types) char const* text = R"( contract C { mapping(uint => uint) x; - function f() returns (bool ret) { + function f() public returns (bool ret) { var y = x; return x == y; } @@ -428,7 +428,7 @@ BOOST_AUTO_TEST_CASE(function_no_implementation) SourceUnit const* sourceUnit = nullptr; char const* text = R"( contract test { - function functionName(bytes32 input) returns (bytes32 out); + function functionName(bytes32 input) public returns (bytes32 out); } )"; sourceUnit = parseAndAnalyse(text); @@ -444,7 +444,7 @@ BOOST_AUTO_TEST_CASE(abstract_contract) SourceUnit const* sourceUnit = nullptr; char const* text = R"( contract base { function foo(); } - contract derived is base { function foo() {} } + contract derived is base { function foo() public {} } )"; sourceUnit = parseAndAnalyse(text); std::vector> nodes = sourceUnit->nodes(); @@ -463,7 +463,7 @@ BOOST_AUTO_TEST_CASE(abstract_contract_with_overload) SourceUnit const* sourceUnit = nullptr; char const* text = R"( contract base { function foo(bool); } - contract derived is base { function foo(uint) {} } + contract derived is base { function foo(uint) public {} } )"; sourceUnit = parseAndAnalyse(text); std::vector> nodes = sourceUnit->nodes(); @@ -481,7 +481,7 @@ BOOST_AUTO_TEST_CASE(create_abstract_contract) contract base { function foo(); } contract derived { base b; - function foo() { b = new base(); } + function foo() public { b = new base(); } } )"; CHECK_ERROR(text, TypeError, "Trying to create an instance of an abstract contract."); @@ -491,7 +491,7 @@ BOOST_AUTO_TEST_CASE(redeclare_implemented_abstract_function_as_abstract) { char const* text = R"( contract base { function foo(); } - contract derived is base { function foo() {} } + contract derived is base { function foo() public {} } contract wrong is derived { function foo(); } )"; CHECK_ERROR(text, TypeError, "Redeclaring an already implemented function as abstract"); @@ -502,7 +502,7 @@ BOOST_AUTO_TEST_CASE(implement_abstract_via_constructor) SourceUnit const* sourceUnit = nullptr; char const* text = R"( contract base { function foo(); } - contract foo is base { function foo() {} } + contract foo is base { function foo() public {} } )"; sourceUnit = parseAndAnalyse(text); std::vector> nodes = sourceUnit->nodes(); @@ -517,7 +517,7 @@ BOOST_AUTO_TEST_CASE(function_canonical_signature) SourceUnit const* sourceUnit = nullptr; char const* text = R"( contract Test { - function foo(uint256 arg1, uint64 arg2, bool arg3) returns (uint256 ret) { + function foo(uint256 arg1, uint64 arg2, bool arg3) public returns (uint256 ret) { ret = arg1 + arg2; } } @@ -536,7 +536,7 @@ BOOST_AUTO_TEST_CASE(function_canonical_signature_type_aliases) SourceUnit const* sourceUnit = nullptr; char const* text = R"( contract Test { - function boo(uint, bytes32, address) returns (uint ret) { + function boo(uint, bytes32, address) public returns (uint ret) { ret = 5; } } @@ -604,7 +604,7 @@ BOOST_AUTO_TEST_CASE(function_external_call_allowed_conversion) char const* text = R"( contract C {} contract Test { - function externalCall() { + function externalCall() public { C arg; this.g(arg); } @@ -619,7 +619,7 @@ BOOST_AUTO_TEST_CASE(function_external_call_not_allowed_conversion) char const* text = R"( contract C {} contract Test { - function externalCall() { + function externalCall() public { address arg; this.g(arg); } @@ -637,8 +637,8 @@ BOOST_AUTO_TEST_CASE(function_internal_allowed_conversion) } contract Test { C a; - function g (C c) {} - function internalCall() { + function g (C c) public {} + function internalCall() public { g(a); } } @@ -654,8 +654,8 @@ BOOST_AUTO_TEST_CASE(function_internal_not_allowed_conversion) } contract Test { address a; - function g (C c) {} - function internalCall() { + function g (C c) public {} + function internalCall() public { g(a); } } @@ -667,8 +667,8 @@ BOOST_AUTO_TEST_CASE(hash_collision_in_interface) { char const* text = R"( contract test { - function gsf() { } - function tgeo() { } + function gsf() public { } + function tgeo() public { } } )"; CHECK_ERROR(text, TypeError, "Function signature hash collision for tgeo()"); @@ -680,7 +680,7 @@ BOOST_AUTO_TEST_CASE(inheritance_basic) contract base { uint baseMember; struct BaseType { uint element; } } contract derived is base { BaseType data; - function f() { baseMember = 7; } + function f() public { baseMember = 7; } } )"; CHECK_SUCCESS(text); @@ -689,11 +689,11 @@ BOOST_AUTO_TEST_CASE(inheritance_basic) BOOST_AUTO_TEST_CASE(inheritance_diamond_basic) { char const* text = R"( - contract root { function rootFunction() {} } - contract inter1 is root { function f() {} } - contract inter2 is root { function f() {} } + contract root { function rootFunction() public {} } + contract inter1 is root { function f() public {} } + contract inter2 is root { function f() public {} } contract derived is root, inter2, inter1 { - function g() { f(); rootFunction(); } + function g() public { f(); rootFunction(); } } )"; CHECK_SUCCESS(text); @@ -711,8 +711,8 @@ BOOST_AUTO_TEST_CASE(cyclic_inheritance) BOOST_AUTO_TEST_CASE(legal_override_direct) { char const* text = R"( - contract B { function f() {} } - contract C is B { function f(uint i) {} } + contract B { function f() public {} } + contract C is B { function f(uint i) public {} } )"; CHECK_SUCCESS(text); } @@ -720,8 +720,8 @@ BOOST_AUTO_TEST_CASE(legal_override_direct) BOOST_AUTO_TEST_CASE(legal_override_indirect) { char const* text = R"( - contract A { function f(uint a) {} } - contract B { function f() {} } + contract A { function f(uint a) public {} } + contract B { function f() public {} } contract C is A, B { } )"; CHECK_SUCCESS(text); @@ -740,7 +740,7 @@ BOOST_AUTO_TEST_CASE(illegal_override_remove_constness) { char const* text = R"( contract B { function f() constant {} } - contract C is B { function f() {} } + contract C is B { function f() public {} } )"; CHECK_ERROR(text, TypeError, "Overriding function changes state mutability from \"view\" to \"nonpayable\"."); } @@ -748,7 +748,7 @@ BOOST_AUTO_TEST_CASE(illegal_override_remove_constness) BOOST_AUTO_TEST_CASE(illegal_override_add_constness) { char const* text = R"( - contract B { function f() {} } + contract B { function f() public {} } contract C is B { function f() constant {} } )"; CHECK_ERROR(text, TypeError, "Overriding function changes state mutability from \"nonpayable\" to \"view\"."); @@ -757,8 +757,8 @@ BOOST_AUTO_TEST_CASE(illegal_override_add_constness) BOOST_AUTO_TEST_CASE(complex_inheritance) { char const* text = R"( - contract A { function f() { uint8 x = C(0).g(); } } - contract B { function f() {} function g() returns (uint8) {} } + contract A { function f() public { uint8 x = C(0).g(); } } + contract B { function f() public {} function g() public returns (uint8) {} } contract C is A, B { } )"; CHECK_SUCCESS(text); @@ -768,8 +768,8 @@ BOOST_AUTO_TEST_CASE(constructor_visibility) { // The constructor of a base class should not be visible in the derived class char const* text = R"( - contract A { function A() { } } - contract B is A { function f() { A x = A(0); } } + contract A { function A() public { } } + contract B is A { function f() public { A x = A(0); } } )"; CHECK_SUCCESS(text); } @@ -778,8 +778,8 @@ BOOST_AUTO_TEST_CASE(overriding_constructor) { // It is fine to "override" constructor of a base class since it is invisible char const* text = R"( - contract A { function A() { } } - contract B is A { function A() returns (uint8 r) {} } + contract A { function A() public { } } + contract B is A { function A() public returns (uint8 r) {} } )"; CHECK_SUCCESS(text); } @@ -787,7 +787,7 @@ BOOST_AUTO_TEST_CASE(overriding_constructor) BOOST_AUTO_TEST_CASE(missing_base_constructor_arguments) { char const* text = R"( - contract A { function A(uint a) { } } + contract A { function A(uint a) public { } } contract B is A { } )"; CHECK_SUCCESS(text); @@ -796,7 +796,7 @@ BOOST_AUTO_TEST_CASE(missing_base_constructor_arguments) BOOST_AUTO_TEST_CASE(base_constructor_arguments_override) { char const* text = R"( - contract A { function A(uint a) { } } + contract A { function A(uint a) public { } } contract B is A { } )"; CHECK_SUCCESS(text); @@ -807,7 +807,7 @@ BOOST_AUTO_TEST_CASE(implicit_derived_to_base_conversion) char const* text = R"( contract A { } contract B is A { - function f() { A a = B(1); } + function f() public { A a = B(1); } } )"; CHECK_SUCCESS(text); @@ -818,7 +818,7 @@ BOOST_AUTO_TEST_CASE(implicit_base_to_derived_conversion) char const* text = R"( contract A { } contract B is A { - function f() { B b = A(1); } + function f() public { B b = A(1); } } )"; CHECK_ERROR(text, TypeError, "Type contract A is not implicitly convertible to expected type contract B."); @@ -828,11 +828,11 @@ BOOST_AUTO_TEST_CASE(super_excludes_current_contract) { char const* text = R"( contract A { - function b() {} + function b() public {} } contract B is A { - function f() { + function f() public { super.f(); } } @@ -845,7 +845,7 @@ BOOST_AUTO_TEST_CASE(function_modifier_invocation) { char const* text = R"( contract B { - function f() mod1(2, true) mod2("0123456") { } + function f() mod1(2, true) mod2("0123456") public { } modifier mod1(uint a, bool b) { if (b) _; } modifier mod2(bytes7 a) { while (a == "1234567") _; } } @@ -857,7 +857,7 @@ BOOST_AUTO_TEST_CASE(invalid_function_modifier_type) { char const* text = R"( contract B { - function f() mod1(true) { } + function f() mod1(true) public { } modifier mod1(uint a) { if (a > 0) _; } } )"; @@ -868,7 +868,7 @@ BOOST_AUTO_TEST_CASE(function_modifier_invocation_parameters) { char const* text = R"( contract B { - function f(uint8 a) mod1(a, true) mod2(r) returns (bytes7 r) { } + function f(uint8 a) mod1(a, true) mod2(r) public returns (bytes7 r) { } modifier mod1(uint a, bool b) { if (b) _; } modifier mod2(bytes7 a) { while (a == "1234567") _; } } @@ -880,7 +880,7 @@ BOOST_AUTO_TEST_CASE(function_modifier_invocation_local_variables) { char const* text = R"( contract B { - function f() mod(x) { uint x = 7; } + function f() mod(x) public { uint x = 7; } modifier mod(uint a) { if (a > 0) _; } } )"; @@ -891,7 +891,7 @@ BOOST_AUTO_TEST_CASE(function_modifier_double_invocation) { char const* text = R"( contract B { - function f(uint x) mod(x) mod(2) { } + function f(uint x) mod(x) mod(2) public { } modifier mod(uint a) { if (a > 0) _; } } )"; @@ -901,9 +901,9 @@ BOOST_AUTO_TEST_CASE(function_modifier_double_invocation) BOOST_AUTO_TEST_CASE(base_constructor_double_invocation) { char const* text = R"( - contract C { function C(uint a) {} } + contract C { function C(uint a) public {} } contract B is C { - function B() C(2) C(2) {} + function B() C(2) C(2) public {} } )"; CHECK_ERROR(text, DeclarationError, "Base constructor already provided"); @@ -931,7 +931,7 @@ BOOST_AUTO_TEST_CASE(modifier_overrides_function) { char const* text = R"( contract A { modifier mod(uint a) { _; } } - contract B is A { function mod(uint a) { } } + contract B is A { function mod(uint a) public { } } )"; // Error: Identifier already declared. // Error: Override changes modifier to function. @@ -941,7 +941,7 @@ BOOST_AUTO_TEST_CASE(modifier_overrides_function) BOOST_AUTO_TEST_CASE(function_overrides_modifier) { char const* text = R"( - contract A { function mod(uint a) { } } + contract A { function mod(uint a) public { } } contract B is A { modifier mod(uint a) { _; } } )"; // Error: Identifier already declared. @@ -953,7 +953,7 @@ BOOST_AUTO_TEST_CASE(modifier_returns_value) { char const* text = R"( contract A { - function f(uint a) mod(2) returns (uint r) { } + function f(uint a) mod(2) public returns (uint r) { } modifier mod(uint a) { _; return 7; } } )"; @@ -964,7 +964,7 @@ BOOST_AUTO_TEST_CASE(state_variable_accessors) { char const* text = R"( contract test { - function fun() { + function fun() public { uint64(2); } uint256 public foo; @@ -1005,11 +1005,11 @@ BOOST_AUTO_TEST_CASE(function_clash_with_state_variable_accessor) { char const* text = R"( contract test { - function fun() { + function fun() public { uint64(2); } uint256 foo; - function foo() {} + function foo() public {} } )"; CHECK_ERROR(text, DeclarationError, "Identifier already declared."); @@ -1019,7 +1019,7 @@ BOOST_AUTO_TEST_CASE(private_state_variable) { char const* text = R"( contract test { - function fun() { + function fun() public { uint64(2); } uint256 private foo; @@ -1041,7 +1041,7 @@ BOOST_AUTO_TEST_CASE(missing_state_variable) { char const* text = R"( contract Scope { - function getStateVar() constant returns (uint stateVar) { + function getStateVar() constant public returns (uint stateVar) { stateVar = Scope.stateVar; // should fail. } } @@ -1058,7 +1058,7 @@ BOOST_AUTO_TEST_CASE(base_class_state_variable_accessor) uint256 public m_aMember; } contract Child is Parent { - function foo() returns (uint256) { return Parent.m_aMember; } + function foo() public returns (uint256) { return Parent.m_aMember; } } )"; CHECK_SUCCESS(text); @@ -1081,8 +1081,8 @@ BOOST_AUTO_TEST_CASE(base_class_state_variable_internal_member) contract Parent { uint256 internal m_aMember; } - contract Child is Parent{ - function foo() returns (uint256) { return Parent.m_aMember; } + contract Child is Parent { + function foo() public returns (uint256) { return Parent.m_aMember; } } )"; CHECK_SUCCESS(text); @@ -1094,11 +1094,11 @@ BOOST_AUTO_TEST_CASE(state_variable_member_of_wrong_class1) contract Parent1 { uint256 internal m_aMember1; } - contract Parent2 is Parent1{ + contract Parent2 is Parent1 { uint256 internal m_aMember2; } - contract Child is Parent2{ - function foo() returns (uint256) { return Parent2.m_aMember1; } + contract Child is Parent2 { + function foo() public returns (uint256) { return Parent2.m_aMember1; } } )"; CHECK_ERROR(text, TypeError, "Member \"m_aMember1\" not found or not visible after argument-dependent lookup in type(contract Parent2)"); @@ -1114,7 +1114,7 @@ BOOST_AUTO_TEST_CASE(state_variable_member_of_wrong_class2) uint256 internal m_aMember2; } contract Child is Parent2 { - function foo() returns (uint256) { return Child.m_aMember2; } + function foo() public returns (uint256) { return Child.m_aMember2; } uint256 public m_aMember3; } )"; @@ -1126,7 +1126,7 @@ BOOST_AUTO_TEST_CASE(fallback_function) char const* text = R"( contract C { uint x; - function() { x = 2; } + function() public { x = 2; } } )"; CHECK_SUCCESS(text); @@ -1137,7 +1137,7 @@ BOOST_AUTO_TEST_CASE(fallback_function_with_arguments) char const* text = R"( contract C { uint x; - function(uint a) { x = 2; } + function(uint a) public { x = 2; } } )"; CHECK_ERROR(text, TypeError, "Fallback function cannot take parameters."); @@ -1147,7 +1147,7 @@ BOOST_AUTO_TEST_CASE(fallback_function_in_library) { char const* text = R"( library C { - function() {} + function() public {} } )"; CHECK_ERROR(text, TypeError, "Libraries cannot have fallback functions."); @@ -1157,7 +1157,7 @@ BOOST_AUTO_TEST_CASE(fallback_function_with_return_parameters) { char const* text = R"( contract C { - function() returns (uint) { } + function() public returns (uint) { } } )"; CHECK_ERROR(text, TypeError, "Fallback function cannot return values."); @@ -1179,8 +1179,8 @@ BOOST_AUTO_TEST_CASE(fallback_function_twice) char const* text = R"( contract C { uint x; - function() { x = 2; } - function() { x = 3; } + function() public { x = 2; } + function() public { x = 3; } } )"; CHECK_ERROR_ALLOW_MULTI(text, DeclarationError, "Function with same name and arguments defined twice."); @@ -1191,10 +1191,10 @@ BOOST_AUTO_TEST_CASE(fallback_function_inheritance) char const* text = R"( contract A { uint x; - function() { x = 1; } + function() public { x = 1; } } contract C is A { - function() { x = 2; } + function() public { x = 2; } } )"; CHECK_SUCCESS(text); @@ -1205,7 +1205,7 @@ BOOST_AUTO_TEST_CASE(event) char const* text = R"( contract c { event e(uint indexed a, bytes3 indexed s, bool indexed b); - function f() { e(2, "abc", true); } + function f() public { e(2, "abc", true); } } )"; CHECK_SUCCESS(text); @@ -1257,7 +1257,7 @@ BOOST_AUTO_TEST_CASE(event_call) char const* text = R"( contract c { event e(uint a, bytes3 indexed s, bool indexed b); - function f() { e(2, "abc", true); } + function f() public { e(2, "abc", true); } } )"; CHECK_SUCCESS(text); @@ -1267,7 +1267,7 @@ BOOST_AUTO_TEST_CASE(event_function_inheritance_clash) { char const* text = R"( contract A { - function dup() returns (uint) { + function dup() public returns (uint) { return 1; } } @@ -1287,7 +1287,7 @@ BOOST_AUTO_TEST_CASE(function_event_inheritance_clash) event dup(); } contract A { - function dup() returns (uint) { + function dup() public returns (uint) { return 1; } } @@ -1302,7 +1302,7 @@ BOOST_AUTO_TEST_CASE(function_event_in_contract_clash) char const* text = R"( contract A { event dup(); - function dup() returns (uint) { + function dup() public returns (uint) { return 1; } } @@ -1317,7 +1317,7 @@ BOOST_AUTO_TEST_CASE(event_inheritance) event e(uint a, bytes3 indexed s, bool indexed b); } contract c is base { - function f() { e(2, "abc", true); } + function f() public { e(2, "abc", true); } } )"; CHECK_SUCCESS(text); @@ -1338,10 +1338,10 @@ BOOST_AUTO_TEST_CASE(access_to_default_function_visibility) { char const* text = R"( contract c { - function f() {} + function f() public {} } contract d { - function g() { c(0).f(); } + function g() public { c(0).f(); } } )"; CHECK_SUCCESS(text); @@ -1354,7 +1354,7 @@ BOOST_AUTO_TEST_CASE(access_to_internal_function) function f() internal {} } contract d { - function g() { c(0).f(); } + function g() public { c(0).f(); } } )"; CHECK_ERROR(text, TypeError, "Member \"f\" not found or not visible after argument-dependent lookup in contract c"); @@ -1367,7 +1367,7 @@ BOOST_AUTO_TEST_CASE(access_to_default_state_variable_visibility) uint a; } contract d { - function g() { c(0).a(); } + function g() public { c(0).a(); } } )"; CHECK_ERROR(text, TypeError, "Member \"a\" not found or not visible after argument-dependent lookup in contract c"); @@ -1380,7 +1380,7 @@ BOOST_AUTO_TEST_CASE(access_to_internal_state_variable) uint public a; } contract d { - function g() { c(0).a(); } + function g() public { c(0).a(); } } )"; CHECK_SUCCESS(text); @@ -1390,10 +1390,10 @@ BOOST_AUTO_TEST_CASE(error_count_in_named_args) { char const* sourceCode = R"( contract test { - function a(uint a, uint b) returns (uint r) { + function a(uint a, uint b) public returns (uint r) { r = a + b; } - function b() returns (uint r) { + function b() public returns (uint r) { r = a({a: 1}); } } @@ -1405,10 +1405,10 @@ BOOST_AUTO_TEST_CASE(empty_in_named_args) { char const* sourceCode = R"( contract test { - function a(uint a, uint b) returns (uint r) { + function a(uint a, uint b) public returns (uint r) { r = a + b; } - function b() returns (uint r) { + function b() public returns (uint r) { r = a({}); } } @@ -1420,10 +1420,10 @@ BOOST_AUTO_TEST_CASE(duplicate_parameter_names_in_named_args) { char const* sourceCode = R"( contract test { - function a(uint a, uint b) returns (uint r) { + function a(uint a, uint b) public returns (uint r) { r = a + b; } - function b() returns (uint r) { + function b() public returns (uint r) { r = a({a: 1, a: 2}); } } @@ -1435,10 +1435,10 @@ BOOST_AUTO_TEST_CASE(invalid_parameter_names_in_named_args) { char const* sourceCode = R"( contract test { - function a(uint a, uint b) returns (uint r) { + function a(uint a, uint b) public returns (uint r) { r = a + b; } - function b() returns (uint r) { + function b() public returns (uint r) { r = a({a: 1, c: 2}); } } @@ -1450,7 +1450,7 @@ BOOST_AUTO_TEST_CASE(empty_name_input_parameter) { char const* text = R"( contract test { - function f(uint) { } + function f(uint) public { } } )"; CHECK_SUCCESS(text); @@ -1460,7 +1460,7 @@ BOOST_AUTO_TEST_CASE(constant_input_parameter) { char const* text = R"( contract test { - function f(uint[] constant a) { } + function f(uint[] constant a) public { } } )"; CHECK_ERROR_ALLOW_MULTI(text, TypeError, "Illegal use of \"constant\" specifier."); @@ -1470,7 +1470,7 @@ BOOST_AUTO_TEST_CASE(empty_name_return_parameter) { char const* text = R"( contract test { - function f() returns(bool) { } + function f() public returns (bool) { } } )"; CHECK_SUCCESS(text); @@ -1480,7 +1480,7 @@ BOOST_AUTO_TEST_CASE(empty_name_input_parameter_with_named_one) { char const* text = R"( contract test { - function f(uint, uint k) returns(uint ret_k) { + function f(uint, uint k) public returns (uint ret_k) { return k; } } @@ -1492,7 +1492,7 @@ BOOST_AUTO_TEST_CASE(empty_name_return_parameter_with_named_one) { char const* text = R"( contract test { - function f() returns(uint ret_k, uint) { + function f() public returns (uint ret_k, uint) { return 5; } } @@ -1504,7 +1504,7 @@ BOOST_AUTO_TEST_CASE(disallow_declaration_of_void_type) { char const* sourceCode = R"( contract c { - function f() { var (x) = f(); } + function f() public { var (x) = f(); } } )"; CHECK_ERROR(sourceCode, TypeError, "Not enough components (0) in value to assign all variables (1)."); @@ -1514,7 +1514,7 @@ BOOST_AUTO_TEST_CASE(overflow_caused_by_ether_units) { char const* sourceCodeFine = R"( contract c { - function c () { + function c () public { a = 115792089237316195423570985008687907853269984665640564039458; } uint256 a; @@ -1523,7 +1523,7 @@ BOOST_AUTO_TEST_CASE(overflow_caused_by_ether_units) CHECK_SUCCESS(sourceCodeFine); char const* sourceCode = R"( contract c { - function c () { + function c () public { a = 115792089237316195423570985008687907853269984665640564039458 ether; } uint256 a; @@ -1536,7 +1536,7 @@ BOOST_AUTO_TEST_CASE(exp_operator_exponent_too_big) { char const* sourceCode = R"( contract test { - function f() returns(uint d) { return 2 ** 10000000000; } + function f() public returns (uint d) { return 2 ** 10000000000; } } )"; CHECK_ERROR(sourceCode, TypeError, "Operator ** not compatible with types int_const 2 and int_const 10000000000"); @@ -1546,7 +1546,7 @@ BOOST_AUTO_TEST_CASE(exp_warn_literal_base) { char const* sourceCode = R"( contract test { - function f() pure returns(uint) { + function f() pure public returns(uint) { uint8 x = 100; return 10**x; } @@ -1555,7 +1555,7 @@ BOOST_AUTO_TEST_CASE(exp_warn_literal_base) CHECK_WARNING(sourceCode, "might overflow"); sourceCode = R"( contract test { - function f() pure returns(uint) { + function f() pure public returns(uint) { uint8 x = 100; return uint8(10)**x; } @@ -1564,7 +1564,7 @@ BOOST_AUTO_TEST_CASE(exp_warn_literal_base) CHECK_SUCCESS(sourceCode); sourceCode = R"( contract test { - function f() pure returns(uint) { + function f() pure public returns(uint) { return 2**80; } } @@ -1576,7 +1576,7 @@ BOOST_AUTO_TEST_CASE(shift_warn_literal_base) { char const* sourceCode = R"( contract test { - function f() pure returns(uint) { + function f() pure public returns(uint) { uint8 x = 100; return 10 << x; } @@ -1585,7 +1585,7 @@ BOOST_AUTO_TEST_CASE(shift_warn_literal_base) CHECK_WARNING(sourceCode, "might overflow"); sourceCode = R"( contract test { - function f() pure returns(uint) { + function f() pure public returns(uint) { uint8 x = 100; return uint8(10) << x; } @@ -1594,7 +1594,7 @@ BOOST_AUTO_TEST_CASE(shift_warn_literal_base) CHECK_SUCCESS(sourceCode); sourceCode = R"( contract test { - function f() pure returns(uint) { + function f() pure public returns(uint) { return 2 << 80; } } @@ -1602,7 +1602,7 @@ BOOST_AUTO_TEST_CASE(shift_warn_literal_base) CHECK_SUCCESS(sourceCode); sourceCode = R"( contract test { - function f() pure returns(uint) { + function f() pure public returns(uint) { uint8 x = 100; return 10 >> x; } @@ -1615,7 +1615,7 @@ BOOST_AUTO_TEST_CASE(warn_var_from_zero) { char const* sourceCode = R"( contract test { - function f() pure returns (uint) { + function f() pure public returns (uint) { var i = 1; return i; } @@ -1624,7 +1624,7 @@ BOOST_AUTO_TEST_CASE(warn_var_from_zero) CHECK_WARNING(sourceCode, "uint8, which can hold values between 0 and 255"); sourceCode = R"( contract test { - function f() pure { + function f() pure public { var i = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; i; } @@ -1633,7 +1633,7 @@ BOOST_AUTO_TEST_CASE(warn_var_from_zero) CHECK_WARNING(sourceCode, "uint256, which can hold values between 0 and 115792089237316195423570985008687907853269984665640564039457584007913129639935"); sourceCode = R"( contract test { - function f() pure { + function f() pure public { var i = -2; i; } @@ -1642,7 +1642,7 @@ BOOST_AUTO_TEST_CASE(warn_var_from_zero) CHECK_WARNING(sourceCode, "int8, which can hold values between -128 and 127"); sourceCode = R"( contract test { - function f() pure { + function f() pure public { for (var i = 0; i < msg.data.length; i++) { } } } @@ -1672,7 +1672,7 @@ BOOST_AUTO_TEST_CASE(enum_member_access_accross_contracts) enum MyEnum { One, Two } } contract Impl { - function test() returns (Interface.MyEnum) { + function test() public returns (Interface.MyEnum) { return Interface.MyEnum.One; } } @@ -1685,7 +1685,7 @@ BOOST_AUTO_TEST_CASE(enum_invalid_member_access) char const* text = R"( contract test { enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } - function test() { + function test() public { choices = ActionChoices.RunAroundWavingYourHands; } ActionChoices choices; @@ -1699,7 +1699,7 @@ BOOST_AUTO_TEST_CASE(enum_invalid_direct_member_access) char const* text = R"( contract test { enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } - function test() { + function test() public { choices = Sit; } ActionChoices choices; @@ -1713,7 +1713,7 @@ BOOST_AUTO_TEST_CASE(enum_explicit_conversion_is_okay) char const* text = R"( contract test { enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } - function test() { + function test() public { a = uint256(ActionChoices.GoStraight); b = uint64(ActionChoices.Sit); } @@ -1729,7 +1729,7 @@ BOOST_AUTO_TEST_CASE(int_to_enum_explicit_conversion_is_okay) char const* text = R"( contract test { enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } - function test() { + function test() public { a = 2; b = ActionChoices(a); } @@ -1745,7 +1745,7 @@ BOOST_AUTO_TEST_CASE(enum_implicit_conversion_is_not_okay_256) char const* text = R"( contract test { enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } - function test() { + function test() public { a = ActionChoices.GoStraight; } uint256 a; @@ -1759,7 +1759,7 @@ BOOST_AUTO_TEST_CASE(enum_implicit_conversion_is_not_okay_64) char const* text = R"( contract test { enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } - function test() { + function test() public { b = ActionChoices.Sit; } uint64 b; @@ -1774,7 +1774,7 @@ BOOST_AUTO_TEST_CASE(enum_to_enum_conversion_is_not_okay) contract test { enum Paper { Up, Down, Left, Right } enum Ground { North, South, West, East } - function test() { + function test() public { Ground(Paper.Up); } } @@ -1801,7 +1801,7 @@ BOOST_AUTO_TEST_CASE(enum_name_resolution_under_current_contract_name) Second } - function a() { + function a() public { A.Foo; } } @@ -1816,7 +1816,7 @@ BOOST_AUTO_TEST_CASE(private_visibility) function f() private {} } contract derived is base { - function g() { f(); } + function g() public { f(); } } )"; CHECK_ERROR(sourceCode, DeclarationError, "Undeclared identifier."); @@ -1829,7 +1829,7 @@ BOOST_AUTO_TEST_CASE(private_visibility_via_explicit_base_access) function f() private {} } contract derived is base { - function g() { base.f(); } + function g() public { base.f(); } } )"; CHECK_ERROR(sourceCode, TypeError, "Member \"f\" not found or not visible after argument-dependent lookup in type(contract base)"); @@ -1840,7 +1840,7 @@ BOOST_AUTO_TEST_CASE(external_visibility) char const* sourceCode = R"( contract c { function f() external {} - function g() { f(); } + function g() public { f(); } } )"; CHECK_ERROR(sourceCode, DeclarationError, "Undeclared identifier."); @@ -1853,7 +1853,7 @@ BOOST_AUTO_TEST_CASE(external_base_visibility) function f() external {} } contract derived is base { - function g() { base.f(); } + function g() public { base.f(); } } )"; CHECK_ERROR(sourceCode, TypeError, "Member \"f\" not found or not visible after argument-dependent lookup in type(contract base)"); @@ -1906,7 +1906,7 @@ BOOST_AUTO_TEST_CASE(array_with_nonconstant_length) { char const* text = R"( contract c { - function f(uint a) { uint8[a] x; } + function f(uint a) public { uint8[a] x; } } )"; CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal."); @@ -1916,7 +1916,7 @@ BOOST_AUTO_TEST_CASE(array_with_negative_length) { char const* text = R"( contract c { - function f(uint a) { uint8[-1] x; } + function f(uint a) public { uint8[-1] x; } } )"; CHECK_ERROR(text, TypeError, "Array with negative length specified"); @@ -1928,7 +1928,7 @@ BOOST_AUTO_TEST_CASE(array_copy_with_different_types1) contract c { bytes a; uint[] b; - function f() { b = a; } + function f() public { b = a; } } )"; CHECK_ERROR(text, TypeError, "Type bytes storage ref is not implicitly convertible to expected type uint256[] storage ref."); @@ -1940,7 +1940,7 @@ BOOST_AUTO_TEST_CASE(array_copy_with_different_types2) contract c { uint32[] a; uint8[] b; - function f() { b = a; } + function f() public { b = a; } } )"; CHECK_ERROR(text, TypeError, "Type uint32[] storage ref is not implicitly convertible to expected type uint8[] storage ref."); @@ -1952,7 +1952,7 @@ BOOST_AUTO_TEST_CASE(array_copy_with_different_types_conversion_possible) contract c { uint32[] a; uint8[] b; - function f() { a = b; } + function f() public { a = b; } } )"; CHECK_SUCCESS(text); @@ -1964,7 +1964,7 @@ BOOST_AUTO_TEST_CASE(array_copy_with_different_types_static_dynamic) contract c { uint32[] a; uint8[80] b; - function f() { a = b; } + function f() public { a = b; } } )"; CHECK_SUCCESS(text); @@ -1976,7 +1976,7 @@ BOOST_AUTO_TEST_CASE(array_copy_with_different_types_dynamic_static) contract c { uint[] a; uint[80] b; - function f() { b = a; } + function f() public { b = a; } } )"; CHECK_ERROR(text, TypeError, "Type uint256[] storage ref is not implicitly convertible to expected type uint256[80] storage ref."); @@ -2116,7 +2116,7 @@ BOOST_AUTO_TEST_CASE(test_byte_is_alias_of_byte1) char const* text = R"( contract c { bytes arr; - function f() { byte a = arr[0];} + function f() public { byte a = arr[0];} } )"; CHECK_SUCCESS(text); @@ -2156,7 +2156,7 @@ BOOST_AUTO_TEST_CASE(assigning_value_to_const_variable) { char const* text = R"( contract Foo { - function changeIt() { x = 9; } + function changeIt() public { x = 9; } uint constant x = 56; } )"; @@ -2179,7 +2179,7 @@ BOOST_AUTO_TEST_CASE(constant_string_literal_disallows_assignment) char const* text = R"( contract Test { string constant x = "abefghijklmnopqabcdefghijklmnopqabcdefghijklmnopqabca"; - function f() { + function f() public { x[0] = "f"; } } @@ -2277,9 +2277,9 @@ BOOST_AUTO_TEST_CASE(overloaded_function_cannot_resolve) { char const* sourceCode = R"( contract test { - function f() returns(uint) { return 1; } - function f(uint a) returns(uint) { return a; } - function g() returns(uint) { return f(3, 5); } + function f() public returns (uint) { return 1; } + function f(uint a) public returns (uint) { return a; } + function g() public returns (uint) { return f(3, 5); } } )"; CHECK_ERROR(sourceCode, TypeError, "No matching declaration found after argument-dependent lookup."); @@ -2290,9 +2290,9 @@ BOOST_AUTO_TEST_CASE(ambiguous_overloaded_function) // literal 1 can be both converted to uint and uint8, so the call is ambiguous. char const* sourceCode = R"( contract test { - function f(uint8 a) returns(uint) { return a; } - function f(uint a) returns(uint) { return 2*a; } - function g() returns(uint) { return f(1); } + function f(uint8 a) public returns (uint) { return a; } + function f(uint a) public returns (uint) { return 2*a; } + function g() public returns (uint) { return f(1); } } )"; CHECK_ERROR(sourceCode, TypeError, "No unique declaration found after argument-dependent lookup."); @@ -2302,8 +2302,8 @@ BOOST_AUTO_TEST_CASE(assignment_of_nonoverloaded_function) { char const* sourceCode = R"( contract test { - function f(uint a) returns(uint) { return 2 * a; } - function g() returns(uint) { var x = f; return x(7); } + function f(uint a) public returns (uint) { return 2 * a; } + function g() public returns (uint) { var x = f; return x(7); } } )"; CHECK_SUCCESS(sourceCode); @@ -2313,9 +2313,9 @@ BOOST_AUTO_TEST_CASE(assignment_of_overloaded_function) { char const* sourceCode = R"( contract test { - function f() returns(uint) { return 1; } - function f(uint a) returns(uint) { return 2 * a; } - function g() returns(uint) { var x = f; return x(7); } + function f() public returns (uint) { return 1; } + function f(uint a) public returns (uint) { return 2 * a; } + function g() public returns (uint) { var x = f; return x(7); } } )"; CHECK_ERROR(sourceCode, TypeError, "No matching declaration found after variable lookup."); @@ -2326,10 +2326,10 @@ BOOST_AUTO_TEST_CASE(external_types_clash) char const* sourceCode = R"( contract base { enum a { X } - function f(a) { } + function f(a) public { } } contract test is base { - function f(uint8 a) { } + function f(uint8 a) public { } } )"; CHECK_ERROR(sourceCode, TypeError, "Function overload clash during conversion to external types for arguments."); @@ -2339,10 +2339,10 @@ BOOST_AUTO_TEST_CASE(override_changes_return_types) { char const* sourceCode = R"( contract base { - function f(uint a) returns (uint) { } + function f(uint a) public returns (uint) { } } contract test is base { - function f(uint a) returns (uint8) { } + function f(uint a) public returns (uint8) { } } )"; CHECK_ERROR(sourceCode, TypeError, "Overriding function return types differ"); @@ -2352,8 +2352,8 @@ BOOST_AUTO_TEST_CASE(multiple_constructors) { char const* sourceCode = R"( contract test { - function test(uint a) { } - function test() {} + function test(uint a) public { } + function test() public {} } )"; CHECK_ERROR(sourceCode, DeclarationError, "More than one constructor defined"); @@ -2363,7 +2363,7 @@ BOOST_AUTO_TEST_CASE(equal_overload) { char const* sourceCode = R"( contract C { - function test(uint a) returns (uint b) { } + function test(uint a) public returns (uint b) { } function test(uint a) external {} } )"; @@ -2374,7 +2374,7 @@ BOOST_AUTO_TEST_CASE(uninitialized_var) { char const* sourceCode = R"( contract C { - function f() returns (uint) { var x; return 2; } + function f() public returns (uint) { var x; return 2; } } )"; CHECK_ERROR(sourceCode, TypeError, "Assignment necessary for type detection."); @@ -2426,7 +2426,7 @@ BOOST_AUTO_TEST_CASE(string_index) char const* sourceCode = R"( contract C { string s; - function f() { var a = s[2]; } + function f() public { var a = s[2]; } } )"; CHECK_ERROR(sourceCode, TypeError, "Index access for string is not possible."); @@ -2437,7 +2437,7 @@ BOOST_AUTO_TEST_CASE(string_length) char const* sourceCode = R"( contract C { string s; - function f() { var a = s.length; } + function f() public { var a = s.length; } } )"; CHECK_ERROR(sourceCode, TypeError, "Member \"length\" not found or not visible after argument-dependent lookup in string storage ref"); @@ -2506,15 +2506,15 @@ BOOST_AUTO_TEST_CASE(positive_integers_to_unsigned_out_of_bound) BOOST_AUTO_TEST_CASE(integer_boolean_operators) { char const* sourceCode1 = R"( - contract test { function() { uint x = 1; uint y = 2; x || y; } } + contract test { function() public { uint x = 1; uint y = 2; x || y; } } )"; CHECK_ERROR(sourceCode1, TypeError, "Operator || not compatible with types uint256 and uint256"); char const* sourceCode2 = R"( - contract test { function() { uint x = 1; uint y = 2; x && y; } } + contract test { function() public { uint x = 1; uint y = 2; x && y; } } )"; CHECK_ERROR(sourceCode2, TypeError, "Operator && not compatible with types uint256 and uint256"); char const* sourceCode3 = R"( - contract test { function() { uint x = 1; !x; } } + contract test { function() public { uint x = 1; !x; } } )"; CHECK_ERROR(sourceCode3, TypeError, "Unary operator ! cannot be applied to type uint256"); } @@ -2522,15 +2522,15 @@ BOOST_AUTO_TEST_CASE(integer_boolean_operators) BOOST_AUTO_TEST_CASE(exp_signed_variable) { char const* sourceCode1 = R"( - contract test { function() { uint x = 3; int y = -4; x ** y; } } + contract test { function() public { uint x = 3; int y = -4; x ** y; } } )"; CHECK_ERROR(sourceCode1, TypeError, "Operator ** not compatible with types uint256 and int256"); char const* sourceCode2 = R"( - contract test { function() { uint x = 3; int y = -4; y ** x; } } + contract test { function() public { uint x = 3; int y = -4; y ** x; } } )"; CHECK_ERROR(sourceCode2, TypeError, "Operator ** not compatible with types int256 and uint256"); char const* sourceCode3 = R"( - contract test { function() { int x = -3; int y = -4; x ** y; } } + contract test { function() public { int x = -3; int y = -4; x ** y; } } )"; CHECK_ERROR(sourceCode3, TypeError, "Operator ** not compatible with types int256 and int256"); } @@ -2538,11 +2538,11 @@ BOOST_AUTO_TEST_CASE(exp_signed_variable) BOOST_AUTO_TEST_CASE(reference_compare_operators) { char const* sourceCode1 = R"( - contract test { bytes a; bytes b; function() { a == b; } } + contract test { bytes a; bytes b; function() public { a == b; } } )"; CHECK_ERROR(sourceCode1, TypeError, "Operator == not compatible with types bytes storage ref and bytes storage ref"); char const* sourceCode2 = R"( - contract test { struct s {uint a;} s x; s y; function() { x == y; } } + contract test { struct s {uint a;} s x; s y; function() public { x == y; } } )"; CHECK_ERROR(sourceCode2, TypeError, "Operator == not compatible with types struct test.s storage ref and struct test.s storage ref"); } @@ -2571,7 +2571,7 @@ BOOST_AUTO_TEST_CASE(storage_location_local_variables) { char const* sourceCode = R"( contract C { - function f() { + function f() public { uint[] storage x; uint[] memory y; uint[] memory z; @@ -2586,7 +2586,7 @@ BOOST_AUTO_TEST_CASE(no_mappings_in_memory_array) { char const* sourceCode = R"( contract C { - function f() { + function f() public { mapping(uint=>uint)[] memory x; } } @@ -2599,7 +2599,7 @@ BOOST_AUTO_TEST_CASE(assignment_mem_to_local_storage_variable) char const* sourceCode = R"( contract C { uint[] data; - function f(uint[] x) { + function f(uint[] x) public { var dataRef = data; dataRef = x; } @@ -2614,7 +2614,7 @@ BOOST_AUTO_TEST_CASE(storage_assign_to_different_local_variable) contract C { uint[] data; uint8[] otherData; - function f() { + function f() public { uint8[] storage x = otherData; uint[] storage y = data; y = x; @@ -2629,7 +2629,7 @@ BOOST_AUTO_TEST_CASE(uninitialized_mapping_variable) { char const* sourceCode = R"( contract C { - function f() { + function f() public { mapping(uint => uint) x; x; } @@ -2642,7 +2642,7 @@ BOOST_AUTO_TEST_CASE(uninitialized_mapping_array_variable) { char const* sourceCode = R"( contract C { - function f() pure { + function f() pure public { mapping(uint => uint)[] storage x; x; } @@ -2656,7 +2656,7 @@ BOOST_AUTO_TEST_CASE(no_delete_on_storage_pointers) char const* sourceCode = R"( contract C { uint[] data; - function f() { + function f() public { var x = data; delete x; } @@ -2670,7 +2670,7 @@ BOOST_AUTO_TEST_CASE(assignment_mem_storage_variable_directly) char const* sourceCode = R"( contract C { uint[] data; - function f(uint[] x) { + function f(uint[] x) public { data = x; } } @@ -2684,7 +2684,7 @@ BOOST_AUTO_TEST_CASE(function_argument_mem_to_storage) contract C { function f(uint[] storage x) private { } - function g(uint[] x) { + function g(uint[] x) public { f(x); } } @@ -2699,7 +2699,7 @@ BOOST_AUTO_TEST_CASE(function_argument_storage_to_mem) function f(uint[] storage x) private { g(x); } - function g(uint[] x) { + function g(uint[] x) public { } } )"; @@ -2725,8 +2725,8 @@ BOOST_AUTO_TEST_CASE(dynamic_return_types_not_possible) { char const* sourceCode = R"( contract C { - function f(uint) returns (string); - function g() { + function f(uint) public returns (string); + function g() public { var (x,) = this.f(2); // we can assign to x but it is not usable. bytes(x).length; @@ -2740,7 +2740,7 @@ BOOST_AUTO_TEST_CASE(memory_arrays_not_resizeable) { char const* sourceCode = R"( contract C { - function f() { + function f() public { uint[] memory x; x.length = 2; } @@ -2754,7 +2754,7 @@ BOOST_AUTO_TEST_CASE(struct_constructor) char const* sourceCode = R"( contract C { struct S { uint a; bool x; } - function f() { + function f() public { S memory s = S(1, true); } } @@ -2768,7 +2768,7 @@ BOOST_AUTO_TEST_CASE(struct_constructor_nested) contract C { struct X { uint x1; uint x2; } struct S { uint s1; uint[3] s2; X s3; } - function f() { + function f() public { uint[3] memory s2; S memory s = S(1, s2, X(4, 5)); } @@ -2782,7 +2782,7 @@ BOOST_AUTO_TEST_CASE(struct_named_constructor) char const* sourceCode = R"( contract C { struct S { uint a; bool x; } - function f() { + function f() public { S memory s = S({a: 1, x: true}); } } @@ -2794,7 +2794,7 @@ BOOST_AUTO_TEST_CASE(literal_strings) { char const* text = R"( contract Foo { - function f() { + function f() public { string memory long = "01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"; string memory short = "123"; long; short; @@ -2810,7 +2810,7 @@ BOOST_AUTO_TEST_CASE(memory_structs_with_mappings) contract Test { struct S { uint8 a; mapping(uint => uint) b; uint8 c; } S s; - function f() { + function f() public { S memory x; x.b[1]; } @@ -2884,10 +2884,10 @@ BOOST_AUTO_TEST_CASE(call_to_library_function) { char const* text = R"( library Lib { - function min(uint, uint) returns (uint); + function min(uint, uint) public returns (uint); } contract Test { - function f() { + function f() public { uint t = Lib.min(12, 7); } } @@ -2899,7 +2899,7 @@ BOOST_AUTO_TEST_CASE(creating_contract_within_the_contract) { char const* sourceCode = R"( contract Test { - function f() { var x = new Test(); } + function f() public { var x = new Test(); } } )"; CHECK_ERROR(sourceCode, TypeError, "Circular reference for contract creation (cannot create instance of derived or same contract)."); @@ -2910,7 +2910,7 @@ BOOST_AUTO_TEST_CASE(array_out_of_bound_access) char const* text = R"( contract c { uint[2] dataArray; - function set5th() returns (bool) { + function set5th() public returns (bool) { dataArray[5] = 2; return true; } @@ -2923,7 +2923,7 @@ BOOST_AUTO_TEST_CASE(literal_string_to_storage_pointer) { char const* text = R"( contract C { - function f() { string x = "abc"; } + function f() public { string x = "abc"; } } )"; CHECK_ERROR(text, TypeError, "Type literal_string \"abc\" is not implicitly convertible to expected type string storage pointer."); @@ -2934,11 +2934,10 @@ BOOST_AUTO_TEST_CASE(non_initialized_references) char const* text = R"( contract c { - struct s{ + struct s { uint a; } - function f() - { + function f() public { s storage x; x.a = 2; } @@ -2953,7 +2952,7 @@ BOOST_AUTO_TEST_CASE(keccak256_with_large_integer_constant) char const* text = R"( contract c { - function f() { keccak256(2**500); } + function f() public { keccak256(2**500); } } )"; CHECK_ERROR(text, TypeError, "Invalid rational number (too large or division by zero)."); @@ -2962,9 +2961,9 @@ BOOST_AUTO_TEST_CASE(keccak256_with_large_integer_constant) BOOST_AUTO_TEST_CASE(cyclic_binary_dependency) { char const* text = R"( - contract A { function f() { new B(); } } - contract B { function f() { new C(); } } - contract C { function f() { new A(); } } + contract A { function f() public { new B(); } } + contract B { function f() public { new C(); } } + contract C { function f() public { new A(); } } )"; CHECK_ERROR(text, TypeError, "Circular reference for contract creation (cannot create instance of derived or same contract)."); } @@ -2973,8 +2972,8 @@ BOOST_AUTO_TEST_CASE(cyclic_binary_dependency_via_inheritance) { char const* text = R"( contract A is B { } - contract B { function f() { new C(); } } - contract C { function f() { new A(); } } + contract B { function f() public { new C(); } } + contract C { function f() public { new A(); } } )"; CHECK_ERROR(text, TypeError, "Definition of base has to precede definition of derived contract"); } @@ -2982,7 +2981,7 @@ BOOST_AUTO_TEST_CASE(cyclic_binary_dependency_via_inheritance) BOOST_AUTO_TEST_CASE(multi_variable_declaration_fail) { char const* text = R"( - contract C { function f() { var (x,y); x = 1; y = 1;} } + contract C { function f() public { var (x,y); x = 1; y = 1;} } )"; CHECK_ERROR(text, TypeError, "Assignment necessary for type detection."); } @@ -2991,10 +2990,10 @@ BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fine) { char const* text = R"( contract C { - function three() returns (uint, uint, uint); - function two() returns (uint, uint); + function three() public returns (uint, uint, uint); + function two() public returns (uint, uint); function none(); - function f() { + function f() public { var (a,) = three(); var (b,c,) = two(); var (,d) = three(); @@ -3012,8 +3011,8 @@ BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_1) { char const* text = R"( contract C { - function one() returns (uint); - function f() { var (a, b, ) = one(); } + function one() public returns (uint); + function f() public { var (a, b, ) = one(); } } )"; CHECK_ERROR(text, TypeError, "Not enough components (1) in value to assign all variables (2)."); @@ -3022,8 +3021,8 @@ BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_2) { char const* text = R"( contract C { - function one() returns (uint); - function f() { var (a, , ) = one(); } + function one() public returns (uint); + function f() public { var (a, , ) = one(); } } )"; CHECK_ERROR(text, TypeError, "Not enough components (1) in value to assign all variables (2)."); @@ -3033,8 +3032,8 @@ BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_3) { char const* text = R"( contract C { - function one() returns (uint); - function f() { var (, , a) = one(); } + function one() public returns (uint); + function f() public { var (, , a) = one(); } } )"; CHECK_ERROR(text, TypeError, "Not enough components (1) in value to assign all variables (2)."); @@ -3044,8 +3043,8 @@ BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_4) { char const* text = R"( contract C { - function one() returns (uint); - function f() { var (, a, b) = one(); } + function one() public returns (uint); + function f() public { var (, a, b) = one(); } } )"; CHECK_ERROR(text, TypeError, "Not enough components (1) in value to assign all variables (2)."); @@ -3055,7 +3054,7 @@ BOOST_AUTO_TEST_CASE(tuples) { char const* text = R"( contract C { - function f() { + function f() public { uint a = (1); var (b,) = (uint8(1),); var (c,d) = (uint32(1), 2 + a); @@ -3071,7 +3070,7 @@ BOOST_AUTO_TEST_CASE(tuples_empty_components) { char const* text = R"( contract C { - function f() { + function f() public { (1,,2); } } @@ -3083,8 +3082,8 @@ BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_5) { char const* text = R"( contract C { - function one() returns (uint); - function f() { var (,) = one(); } + function one() public returns (uint); + function f() public { var (,) = one(); } } )"; CHECK_ERROR(text, TypeError, "Wildcard both at beginning and end of variable declaration list is only allowed if the number of components is equal."); @@ -3094,8 +3093,8 @@ BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_6) { char const* text = R"( contract C { - function two() returns (uint, uint); - function f() { var (a, b, c) = two(); } + function two() public returns (uint, uint); + function f() public { var (a, b, c) = two(); } } )"; CHECK_ERROR(text, TypeError, "Not enough components (2) in value to assign all variables (3)"); @@ -3105,8 +3104,8 @@ BOOST_AUTO_TEST_CASE(tuple_assignment_from_void_function) { char const* text = R"( contract C { - function f() { } - function g() { + function f() public { } + function g() public { var (x,) = (f(), f()); } } @@ -3118,7 +3117,7 @@ BOOST_AUTO_TEST_CASE(tuple_compound_assignment) { char const* text = R"( contract C { - function f() returns (uint a, uint b) { + function f() public returns (uint a, uint b) { (a, b) += (1, 1); } } @@ -3133,7 +3132,7 @@ BOOST_AUTO_TEST_CASE(member_access_parser_ambiguity) struct R { uint[10][10] y; } struct S { uint a; uint b; uint[20][20][20] c; R d; } S data; - function f() { + function f() public { C.S x = data; C.S memory y; C.S[10] memory z; @@ -3172,10 +3171,10 @@ BOOST_AUTO_TEST_CASE(using_for_not_library) BOOST_AUTO_TEST_CASE(using_for_function_exists) { char const* text = R"( - library D { function double(uint self) returns (uint) { return 2*self; } } + library D { function double(uint self) public returns (uint) { return 2*self; } } contract C { using D for uint; - function f(uint a) { + function f(uint a) public { a.double; } } @@ -3186,10 +3185,10 @@ BOOST_AUTO_TEST_CASE(using_for_function_exists) BOOST_AUTO_TEST_CASE(using_for_function_on_int) { char const* text = R"( - library D { function double(uint self) returns (uint) { return 2*self; } } + library D { function double(uint self) public returns (uint) { return 2*self; } } contract C { using D for uint; - function f(uint a) returns (uint) { + function f(uint a) public returns (uint) { return a.double(); } } @@ -3200,11 +3199,11 @@ BOOST_AUTO_TEST_CASE(using_for_function_on_int) BOOST_AUTO_TEST_CASE(using_for_function_on_struct) { char const* text = R"( - library D { struct s { uint a; } function mul(s storage self, uint x) returns (uint) { return self.a *= x; } } + library D { struct s { uint a; } function mul(s storage self, uint x) public returns (uint) { return self.a *= x; } } contract C { using D for D.s; D.s x; - function f(uint a) returns (uint) { + function f(uint a) public returns (uint) { return x.mul(a); } } @@ -3217,13 +3216,13 @@ BOOST_AUTO_TEST_CASE(using_for_overload) char const* text = R"( library D { struct s { uint a; } - function mul(s storage self, uint x) returns (uint) { return self.a *= x; } - function mul(s storage, bytes32) returns (bytes32) { } + function mul(s storage self, uint x) public returns (uint) { return self.a *= x; } + function mul(s storage, bytes32) public returns (bytes32) { } } contract C { using D for D.s; D.s x; - function f(uint a) returns (uint) { + function f(uint a) public returns (uint) { return x.mul(a); } } @@ -3234,11 +3233,11 @@ BOOST_AUTO_TEST_CASE(using_for_overload) BOOST_AUTO_TEST_CASE(using_for_by_name) { char const* text = R"( - library D { struct s { uint a; } function mul(s storage self, uint x) returns (uint) { return self.a *= x; } } + library D { struct s { uint a; } function mul(s storage self, uint x) public returns (uint) { return self.a *= x; } } contract C { using D for D.s; D.s x; - function f(uint a) returns (uint) { + function f(uint a) public returns (uint) { return x.mul({x: a}); } } @@ -3249,10 +3248,10 @@ BOOST_AUTO_TEST_CASE(using_for_by_name) BOOST_AUTO_TEST_CASE(using_for_mismatch) { char const* text = R"( - library D { function double(bytes32 self) returns (uint) { return 2; } } + library D { function double(bytes32 self) public returns (uint) { return 2; } } contract C { using D for uint; - function f(uint a) returns (uint) { + function f(uint a) public returns (uint) { return a.double(); } } @@ -3265,10 +3264,10 @@ BOOST_AUTO_TEST_CASE(using_for_not_used) // This is an error because the function is only bound to uint. // Had it been bound to *, it would have worked. char const* text = R"( - library D { function double(uint self) returns (uint) { return 2; } } + library D { function double(uint self) public returns (uint) { return 2; } } contract C { using D for uint; - function f(uint16 a) returns (uint) { + function f(uint16 a) public returns (uint) { return a.double(); } } @@ -3281,7 +3280,7 @@ BOOST_AUTO_TEST_CASE(library_memory_struct) char const* text = R"( library c { struct S { uint x; } - function f() returns (S ) {} + function f() public returns (S ) {} } )"; CHECK_ERROR(text, TypeError, "Internal type is not allowed for public or external functions."); @@ -3291,10 +3290,10 @@ BOOST_AUTO_TEST_CASE(using_for_arbitrary_mismatch) { // Bound to a, but self type does not match. char const* text = R"( - library D { function double(bytes32 self) returns (uint) { return 2; } } + library D { function double(bytes32 self) public returns (uint) { return 2; } } contract C { using D for *; - function f(uint a) returns (uint) { + function f(uint a) public returns (uint) { return a.double(); } } @@ -3305,11 +3304,11 @@ BOOST_AUTO_TEST_CASE(using_for_arbitrary_mismatch) BOOST_AUTO_TEST_CASE(bound_function_in_var) { char const* text = R"( - library D { struct s { uint a; } function mul(s storage self, uint x) returns (uint) { return self.a *= x; } } + library D { struct s { uint a; } function mul(s storage self, uint x) public returns (uint) { return self.a *= x; } } contract C { using D for D.s; D.s x; - function f(uint a) returns (uint) { + function f(uint a) public returns (uint) { var g = x.mul; return g({x: a}); } @@ -3326,7 +3325,7 @@ BOOST_AUTO_TEST_CASE(create_memory_arrays) struct S { uint a; uint b; uint[20][20][20] c; R d; } } contract C { - function f(uint size) { + function f(uint size) public { L.S[][] memory x = new L.S[][](10); var y = new uint[](20); var z = new bytes(size); @@ -3341,7 +3340,7 @@ BOOST_AUTO_TEST_CASE(mapping_in_memory_array) { char const* text = R"( contract C { - function f(uint size) { + function f(uint size) public { var x = new mapping(uint => uint)[](4); } } @@ -3353,7 +3352,7 @@ BOOST_AUTO_TEST_CASE(new_for_non_array) { char const* text = R"( contract C { - function f(uint size) { + function f(uint size) public { var x = new uint(7); } } @@ -3365,7 +3364,7 @@ BOOST_AUTO_TEST_CASE(invalid_args_creating_memory_array) { char const* text = R"( contract C { - function f(uint size) { + function f(uint size) public { var x = new uint[](); } } @@ -3388,7 +3387,7 @@ BOOST_AUTO_TEST_CASE(inline_array_declaration_and_passing_implicit_conversion) { char const* text = R"( contract C { - function f() returns (uint) { + function f() public returns (uint) { uint8 x = 7; uint16 y = 8; uint32 z = 9; @@ -3404,7 +3403,7 @@ BOOST_AUTO_TEST_CASE(inline_array_declaration_and_passing_implicit_conversion_st { char const* text = R"( contract C { - function f() returns (string) { + function f() public returns (string) { string memory x = "Hello"; string memory y = "World"; string[2] memory z = [x, y]; @@ -3419,7 +3418,7 @@ BOOST_AUTO_TEST_CASE(inline_array_declaration_const_int_conversion) { char const* text = R"( contract C { - function f() returns (uint) { + function f() public returns (uint) { uint8[4] memory z = [1,2,3,5]; return (z[0]); } @@ -3432,7 +3431,7 @@ BOOST_AUTO_TEST_CASE(inline_array_declaration_const_string_conversion) { char const* text = R"( contract C { - function f() returns (string) { + function f() public returns (string) { string[2] memory z = ["Hello", "World"]; return (z[0]); } @@ -3445,7 +3444,7 @@ BOOST_AUTO_TEST_CASE(inline_array_declaration_no_type) { char const* text = R"( contract C { - function f() returns (uint) { + function f() public returns (uint) { return ([4,5,6][1]); } } @@ -3457,7 +3456,7 @@ BOOST_AUTO_TEST_CASE(inline_array_declaration_no_type_strings) { char const* text = R"( contract C { - function f() returns (string) { + function f() public returns (string) { return (["foo", "man", "choo"][1]); } } @@ -3485,7 +3484,7 @@ BOOST_AUTO_TEST_CASE(invalid_types_in_inline_array) { char const* text = R"( contract C { - function f() { + function f() public { uint[3] x = [45, 'foo', true]; } } @@ -3497,7 +3496,7 @@ BOOST_AUTO_TEST_CASE(dynamic_inline_array) { char const* text = R"( contract C { - function f() { + function f() public { uint8[4][4] memory dyn = [[1, 2, 3, 4], [2, 3, 4, 5], [3, 4, 5, 6], [4, 5, 6, 7]]; } } @@ -3509,7 +3508,7 @@ BOOST_AUTO_TEST_CASE(lvalues_as_inline_array) { char const* text = R"( contract C { - function f() { + function f() public { [1, 2, 3]++; [1, 2, 3] = [4, 5, 6]; } @@ -3522,7 +3521,7 @@ BOOST_AUTO_TEST_CASE(break_not_in_loop) { char const* text = R"( contract C { - function f() { + function f() public { if (true) break; } @@ -3535,7 +3534,7 @@ BOOST_AUTO_TEST_CASE(continue_not_in_loop) { char const* text = R"( contract C { - function f() { + function f() public { if (true) continue; } @@ -3548,7 +3547,7 @@ BOOST_AUTO_TEST_CASE(continue_not_in_loop_2) { char const* text = R"( contract C { - function f() { + function f() public { while (true) { } @@ -3563,7 +3562,7 @@ BOOST_AUTO_TEST_CASE(invalid_different_types_for_conditional_expression) { char const* text = R"( contract C { - function f() { + function f() public { true ? true : 2; } } @@ -3575,7 +3574,7 @@ BOOST_AUTO_TEST_CASE(left_value_in_conditional_expression_not_supported_yet) { char const* text = R"( contract C { - function f() { + function f() public { uint x; uint y; (true ? x : y) = 1; @@ -3595,7 +3594,7 @@ BOOST_AUTO_TEST_CASE(conditional_expression_with_different_struct) struct s2 { uint x; } - function f() { + function f() public { s1 memory x; s2 memory y; true ? x : y; @@ -3609,10 +3608,10 @@ BOOST_AUTO_TEST_CASE(conditional_expression_with_different_function_type) { char const* text = R"( contract C { - function x(bool) {} - function y() {} + function x(bool) public {} + function y() public {} - function f() { + function f() public { true ? x : y; } } @@ -3627,7 +3626,7 @@ BOOST_AUTO_TEST_CASE(conditional_expression_with_different_enum) enum small { A, B, C, D } enum big { A, B, C, D } - function f() { + function f() public { small x; big y; @@ -3645,7 +3644,7 @@ BOOST_AUTO_TEST_CASE(conditional_expression_with_different_mapping) mapping(uint8 => uint8) table1; mapping(uint32 => uint8) table2; - function f() { + function f() public { true ? table1 : table2; } } @@ -3663,15 +3662,15 @@ BOOST_AUTO_TEST_CASE(conditional_with_all_types) s1 struct_x; s1 struct_y; - function fun_x() {} - function fun_y() {} + function fun_x() public {} + function fun_y() public {} enum small { A, B, C, D } mapping(uint8 => uint8) table1; mapping(uint8 => uint8) table2; - function f() { + function f() public { // integers uint x; uint y; @@ -3686,7 +3685,7 @@ BOOST_AUTO_TEST_CASE(conditional_with_all_types) var i = true ? "hello" : "world"; i = "used"; //Avoid unused var warning } - function f2() { + function f2() public { // bool bool j = true ? true : false; j = j && true; // Avoid unused var warning @@ -3711,7 +3710,7 @@ BOOST_AUTO_TEST_CASE(conditional_with_all_types) m &= m; } - function f3() { + function f3() public { // contract doesn't fit in here // struct @@ -3765,7 +3764,7 @@ BOOST_AUTO_TEST_CASE(index_access_for_bytes) char const* text = R"( contract C { bytes20 x; - function f(bytes16 b) { + function f(bytes16 b) public { b[uint(x[2])]; } } @@ -3778,7 +3777,7 @@ BOOST_AUTO_TEST_CASE(uint7_and_uintM_as_identifier) char const* text = R"( contract test { string uintM = "Hello 4 you"; - function f() { + function f() public { uint8 uint7 = 3; uint7 = 5; string memory intM; @@ -3794,7 +3793,7 @@ BOOST_AUTO_TEST_CASE(varM_disqualified_as_keyword) { char const* text = R"( contract test { - function f() { + function f() public { uintM something = 3; intM should = 4; bytesM fail = "now"; @@ -3808,7 +3807,7 @@ BOOST_AUTO_TEST_CASE(long_uint_variable_fails) { char const* text = R"( contract test { - function f() { + function f() public { uint99999999999999999999999999 something = 3; } } @@ -3820,7 +3819,7 @@ BOOST_AUTO_TEST_CASE(bytes10abc_is_identifier) { char const* text = R"( contract test { - function f() { + function f() public { bytes32 bytes10abc = "abc"; } } @@ -3832,7 +3831,7 @@ BOOST_AUTO_TEST_CASE(int10abc_is_identifier) { char const* text = R"( contract test { - function f() { + function f() public { uint uint10abc = 3; int int10abc = 4; uint10abc; int10abc; @@ -3845,9 +3844,9 @@ BOOST_AUTO_TEST_CASE(int10abc_is_identifier) BOOST_AUTO_TEST_CASE(library_functions_do_not_have_value) { char const* text = R"( - library L { function l() {} } + library L { function l() public {} } contract test { - function f() { + function f() public { L.l.value; } } @@ -3888,9 +3887,9 @@ BOOST_AUTO_TEST_CASE(invalid_fixed_types_7x8_mxn) BOOST_AUTO_TEST_CASE(library_instances_cannot_be_used) { char const* text = R"( - library L { function l() {} } + library L { function l() public {} } contract test { - function f() { + function f() public { L x; x.l(); } @@ -3903,7 +3902,7 @@ BOOST_AUTO_TEST_CASE(invalid_fixed_type_long) { char const* text = R"( contract test { - function f() { + function f() public { fixed8x888888888888888888888888888888888888888888888888888 b; } } @@ -3915,7 +3914,7 @@ BOOST_AUTO_TEST_CASE(fixed_type_int_conversion) { char const* text = R"( contract test { - function f() { + function f() public { uint64 a = 3; int64 b = 4; fixed c = b; @@ -3931,7 +3930,7 @@ BOOST_AUTO_TEST_CASE(fixed_type_rational_int_conversion) { char const* text = R"( contract test { - function f() { + function f() public { fixed c = 3; ufixed d = 4; c; d; @@ -3945,7 +3944,7 @@ BOOST_AUTO_TEST_CASE(fixed_type_rational_fraction_conversion) { char const* text = R"( contract test { - function f() { + function f() public { fixed a = 4.5; ufixed d = 2.5; a; d; @@ -3959,7 +3958,7 @@ BOOST_AUTO_TEST_CASE(invalid_int_implicit_conversion_from_fixed) { char const* text = R"( contract test { - function f() { + function f() public { fixed a = 4.5; int b = a; a; b; @@ -3973,7 +3972,7 @@ BOOST_AUTO_TEST_CASE(rational_unary_operation) { char const* text = R"( contract test { - function f() pure { + function f() pure public { ufixed16x2 a = 3.25; fixed16x2 b = -3.25; a; b; @@ -3983,7 +3982,7 @@ BOOST_AUTO_TEST_CASE(rational_unary_operation) CHECK_SUCCESS_NO_WARNINGS(text); text = R"( contract test { - function f() pure { + function f() pure public { ufixed16x2 a = +3.25; fixed16x2 b = -3.25; a; b; @@ -3993,7 +3992,7 @@ BOOST_AUTO_TEST_CASE(rational_unary_operation) CHECK_WARNING(text, "Use of unary + is deprecated"); text = R"( contract test { - function f(uint x) pure { + function f(uint x) pure public { uint y = +x; y; } @@ -4006,7 +4005,7 @@ BOOST_AUTO_TEST_CASE(leading_zero_rationals_convert) { char const* text = R"( contract A { - function f() pure { + function f() pure public { ufixed16x2 a = 0.5; ufixed256x52 b = 0.0000000000000006661338147750939242541790008544921875; fixed16x2 c = -0.5; @@ -4022,7 +4021,7 @@ BOOST_AUTO_TEST_CASE(size_capabilities_of_fixed_point_types) { char const* text = R"( contract test { - function f() { + function f() public { ufixed256x1 a = 123456781234567979695948382928485849359686494864095409282048094275023098123.5; ufixed256x77 b = 0.920890746623327805482905058466021565416131529487595827354393978494366605267637; ufixed224x78 c = 0.000000000001519884736399797998492268541131529487595827354393978494366605267646; @@ -4040,7 +4039,7 @@ BOOST_AUTO_TEST_CASE(zero_handling) { char const* text = R"( contract test { - function f() { + function f() public { fixed16x2 a = 0; a; ufixed32x1 b = 0; b; } @@ -4053,7 +4052,7 @@ BOOST_AUTO_TEST_CASE(fixed_type_invalid_implicit_conversion_size) { char const* text = R"( contract test { - function f() { + function f() public { ufixed a = 11/4; ufixed248x8 b = a; b; } @@ -4066,7 +4065,7 @@ BOOST_AUTO_TEST_CASE(fixed_type_invalid_implicit_conversion_lost_data) { char const* text = R"( contract test { - function f() { + function f() public { ufixed256x1 a = 1/3; a; } } @@ -4078,7 +4077,7 @@ BOOST_AUTO_TEST_CASE(fixed_type_valid_explicit_conversions) { char const* text = R"( contract test { - function f() { + function f() public { ufixed256x80 a = ufixed256x80(1/3); a; ufixed248x80 b = ufixed248x80(1/3); b; ufixed8x1 c = ufixed8x1(1/3); c; @@ -4092,7 +4091,7 @@ BOOST_AUTO_TEST_CASE(invalid_array_declaration_with_rational) { char const* text = R"( contract test { - function f() { + function f() public { uint[3.5] a; a; } } @@ -4104,7 +4103,7 @@ BOOST_AUTO_TEST_CASE(invalid_array_declaration_with_signed_fixed_type) { char const* text = R"( contract test { - function f() { + function f() public { uint[fixed(3.5)] a; a; } } @@ -4116,7 +4115,7 @@ BOOST_AUTO_TEST_CASE(invalid_array_declaration_with_unsigned_fixed_type) { char const* text = R"( contract test { - function f() { + function f() public { uint[ufixed(3.5)] a; a; } } @@ -4128,7 +4127,7 @@ BOOST_AUTO_TEST_CASE(rational_to_bytes_implicit_conversion) { char const* text = R"( contract test { - function f() { + function f() public { bytes32 c = 3.2; c; } } @@ -4140,7 +4139,7 @@ BOOST_AUTO_TEST_CASE(fixed_to_bytes_implicit_conversion) { char const* text = R"( contract test { - function f() { + function f() public { fixed a = 3.25; bytes32 c = a; c; } @@ -4154,7 +4153,7 @@ BOOST_AUTO_TEST_CASE(mapping_with_fixed_literal) char const* text = R"( contract test { mapping(ufixed8x1 => string) fixedString; - function f() { + function f() public { fixedString[0.5] = "Half"; } } @@ -4180,7 +4179,7 @@ BOOST_AUTO_TEST_CASE(inline_array_fixed_types) { char const* text = R"( contract test { - function f() { + function f() public { fixed[3] memory a = [fixed(3.5), fixed(-4.25), fixed(967.125)]; } } @@ -4192,7 +4191,7 @@ BOOST_AUTO_TEST_CASE(inline_array_rationals) { char const* text = R"( contract test { - function f() { + function f() public { ufixed128x3[4] memory a = [ufixed128x3(3.5), 4.125, 2.5, 4.0]; } } @@ -4204,7 +4203,7 @@ BOOST_AUTO_TEST_CASE(rational_index_access) { char const* text = R"( contract test { - function f() { + function f() public { uint[] memory a; a[.5]; } @@ -4217,7 +4216,7 @@ BOOST_AUTO_TEST_CASE(rational_to_fixed_literal_expression) { char const* text = R"( contract test { - function f() { + function f() public { ufixed64x8 a = 3.5 * 3; ufixed64x8 b = 4 - 2.5; ufixed64x8 c = 11 / 4; @@ -4236,7 +4235,7 @@ BOOST_AUTO_TEST_CASE(rational_as_exponent_value_signed) { char const* text = R"( contract test { - function f() { + function f() public { fixed g = 2 ** -2.2; } } @@ -4248,7 +4247,7 @@ BOOST_AUTO_TEST_CASE(rational_as_exponent_value_unsigned) { char const* text = R"( contract test { - function f() { + function f() public { ufixed b = 3 ** 2.5; } } @@ -4260,7 +4259,7 @@ BOOST_AUTO_TEST_CASE(rational_as_exponent_half) { char const* text = R"( contract test { - function f() { + function f() public { 2 ** (1/2); } } @@ -4272,7 +4271,7 @@ BOOST_AUTO_TEST_CASE(rational_as_exponent_value_neg_quarter) { char const* text = R"( contract test { - function f() { + function f() public { 42 ** (-1/4); } } @@ -4284,7 +4283,7 @@ BOOST_AUTO_TEST_CASE(fixed_point_casting_exponents_15) { char const* text = R"( contract test { - function f() { + function f() public { var a = 3 ** ufixed(1.5); } } @@ -4296,7 +4295,7 @@ BOOST_AUTO_TEST_CASE(fixed_point_casting_exponents_neg) { char const* text = R"( contract test { - function f() { + function f() public { var c = 42 ** fixed(-1/4); } } @@ -4308,7 +4307,7 @@ BOOST_AUTO_TEST_CASE(var_capable_of_holding_constant_rationals) { char const* text = R"( contract test { - function f() { + function f() public { var a = 0.12345678; var b = 12345678.352; var c = 0.00000009; @@ -4323,7 +4322,7 @@ BOOST_AUTO_TEST_CASE(var_and_rational_with_tuple) { char const* text = R"( contract test { - function f() { + function f() public { var (a, b) = (.5, 1/3); a; b; } @@ -4336,7 +4335,7 @@ BOOST_AUTO_TEST_CASE(var_handle_divided_integers) { char const* text = R"( contract test { - function f() { + function f() public { var x = 1/3; } } @@ -4348,7 +4347,7 @@ BOOST_AUTO_TEST_CASE(rational_bitnot_unary_operation) { char const* text = R"( contract test { - function f() { + function f() public { ~fixed(3.5); } } @@ -4360,7 +4359,7 @@ BOOST_AUTO_TEST_CASE(rational_bitor_binary_operation) { char const* text = R"( contract test { - function f() { + function f() public { fixed(1.5) | 3; } } @@ -4372,7 +4371,7 @@ BOOST_AUTO_TEST_CASE(rational_bitxor_binary_operation) { char const* text = R"( contract test { - function f() { + function f() public { fixed(1.75) ^ 3; } } @@ -4384,7 +4383,7 @@ BOOST_AUTO_TEST_CASE(rational_bitand_binary_operation) { char const* text = R"( contract test { - function f() { + function f() public { fixed(1.75) & 3; } } @@ -4396,7 +4395,7 @@ BOOST_AUTO_TEST_CASE(missing_bool_conversion) { char const* text = R"( contract test { - function b(uint a) { + function b(uint a) public { bool(a == 1); } } @@ -4408,7 +4407,7 @@ BOOST_AUTO_TEST_CASE(integer_and_fixed_interaction) { char const* text = R"( contract test { - function f() { + function f() public { ufixed a = uint64(1) + ufixed(2); } } @@ -4420,7 +4419,7 @@ BOOST_AUTO_TEST_CASE(signed_rational_modulus) { char const* text = R"( contract test { - function f() { + function f() public { fixed a = 0.42578125 % -0.4271087646484375; fixed b = .5 % a; fixed c = a % b; @@ -4434,7 +4433,7 @@ BOOST_AUTO_TEST_CASE(one_divided_by_three_integer_conversion) { char const* text = R"( contract test { - function f() { + function f() public { uint a = 1/3; } } @@ -4446,8 +4445,8 @@ BOOST_AUTO_TEST_CASE(unused_return_value) { char const* text = R"( contract test { - function g() returns (uint) {} - function f() { + function g() public returns (uint) {} + function f() public { g(); } } @@ -4459,7 +4458,7 @@ BOOST_AUTO_TEST_CASE(unused_return_value_send) { char const* text = R"( contract test { - function f() { + function f() public { address(0x12).send(1); } } @@ -4471,7 +4470,7 @@ BOOST_AUTO_TEST_CASE(unused_return_value_call) { char const* text = R"( contract test { - function f() { + function f() public { address(0x12).call("abc"); } } @@ -4483,7 +4482,7 @@ BOOST_AUTO_TEST_CASE(unused_return_value_call_value) { char const* text = R"( contract test { - function f() { + function f() public { address(0x12).call.value(2)("abc"); } } @@ -4495,7 +4494,7 @@ BOOST_AUTO_TEST_CASE(unused_return_value_callcode) { char const* text = R"( contract test { - function f() { + function f() public { address(0x12).callcode("abc"); } } @@ -4507,7 +4506,7 @@ BOOST_AUTO_TEST_CASE(unused_return_value_delegatecall) { char const* text = R"( contract test { - function f() { + function f() public { address(0x12).delegatecall("abc"); } } @@ -4519,7 +4518,7 @@ BOOST_AUTO_TEST_CASE(warn_about_callcode) { char const* text = R"( contract test { - function f() pure { + function f() pure public { var x = address(0x12).callcode; x; } @@ -4532,7 +4531,7 @@ BOOST_AUTO_TEST_CASE(no_warn_about_callcode_as_function) { char const* text = R"( contract test { - function callcode() pure { + function callcode() pure public { test.callcode(); } } @@ -4554,7 +4553,7 @@ BOOST_AUTO_TEST_CASE(payable_in_library) { char const* text = R"( library test { - function f() payable {} + function f() payable public {} } )"; CHECK_ERROR(text, TypeError, "Library functions cannot be payable."); @@ -4593,8 +4592,8 @@ BOOST_AUTO_TEST_CASE(payable_private) BOOST_AUTO_TEST_CASE(illegal_override_payable) { char const* text = R"( - contract B { function f() payable {} } - contract C is B { function f() {} } + contract B { function f() payable public {} } + contract C is B { function f() public {} } )"; CHECK_ERROR(text, TypeError, "Overriding function changes state mutability from \"payable\" to \"nonpayable\"."); } @@ -4602,8 +4601,8 @@ BOOST_AUTO_TEST_CASE(illegal_override_payable) BOOST_AUTO_TEST_CASE(illegal_override_payable_nonpayable) { char const* text = R"( - contract B { function f() {} } - contract C is B { function f() payable {} } + contract B { function f() public {} } + contract C is B { function f() payable public {} } )"; CHECK_ERROR(text, TypeError, "Overriding function changes state mutability from \"nonpayable\" to \"payable\"."); } @@ -4616,11 +4615,11 @@ BOOST_AUTO_TEST_CASE(function_variable_mixin) bool ok = false; } contract func { - function ok() returns (bool) { return true; } + function ok() public returns (bool) { return true; } } contract attr_func is attribute, func { - function checkOk() returns (bool) { return ok(); } + function checkOk() public returns (bool) { return ok(); } } )"; CHECK_ERROR(text, DeclarationError, "Identifier already declared."); @@ -4629,11 +4628,11 @@ BOOST_AUTO_TEST_CASE(function_variable_mixin) BOOST_AUTO_TEST_CASE(calling_payable) { char const* text = R"( - contract receiver { function pay() payable {} } + contract receiver { function pay() payable public {} } contract test { - function f() { (new receiver()).pay.value(10)(); } + function f() public { (new receiver()).pay.value(10)(); } receiver r = new receiver(); - function g() { r.pay.value(10)(); } + function g() public { r.pay.value(10)(); } } )"; CHECK_SUCCESS(text); @@ -4642,9 +4641,9 @@ BOOST_AUTO_TEST_CASE(calling_payable) BOOST_AUTO_TEST_CASE(calling_nonpayable) { char const* text = R"( - contract receiver { function nopay() {} } + contract receiver { function nopay() public {} } contract test { - function f() { (new receiver()).nopay.value(10)(); } + function f() public { (new receiver()).nopay.value(10)(); } } )"; CHECK_ERROR(text, TypeError, "Member \"value\" not found or not visible after argument-dependent lookup in function () external - did you forget the \"payable\" modifier?"); @@ -4657,7 +4656,7 @@ BOOST_AUTO_TEST_CASE(non_payable_constructor) function C() { } } contract D { - function f() returns (uint) { + function f() public returns (uint) { (new C).value(2)(); return 2; } @@ -4687,7 +4686,7 @@ BOOST_AUTO_TEST_CASE(unsatisfied_version) BOOST_CHECK(searchErrorMessage(*sourceAndError.second, "Source file requires different compiler version")); } -BOOST_AUTO_TEST_CASE(constant_constructor) +BOOST_AUTO_TEST_CASE(invalid_constructor_statemutability) { char const* text = R"( contract test { @@ -4695,6 +4694,18 @@ BOOST_AUTO_TEST_CASE(constant_constructor) } )"; CHECK_ERROR(text, TypeError, "Constructor must be payable or non-payable"); + text = R"( + contract test { + function test() view {} + } + )"; + CHECK_ERROR(text, TypeError, "Constructor must be payable or non-payable"); + text = R"( + contract test { + function test() pure {} + } + )"; + CHECK_ERROR(text, TypeError, "Constructor must be payable or non-payable"); } BOOST_AUTO_TEST_CASE(external_constructor) @@ -4712,7 +4723,7 @@ BOOST_AUTO_TEST_CASE(invalid_array_as_statement) char const* text = R"( contract test { struct S { uint x; } - function test(uint k) { S[k]; } + function test(uint k) public { S[k]; } } )"; CHECK_ERROR(text, TypeError, "Integer constant expected."); @@ -4722,13 +4733,13 @@ BOOST_AUTO_TEST_CASE(using_directive_for_missing_selftype) { char const* text = R"( library B { - function b() {} + function b() public {} } contract A { using B for bytes; - function a() { + function a() public { bytes memory x; x.b(); } @@ -4741,7 +4752,7 @@ BOOST_AUTO_TEST_CASE(function_type) { char const* text = R"( contract C { - function f() { + function f() public { function(uint) returns (uint) x; } } @@ -4753,7 +4764,7 @@ BOOST_AUTO_TEST_CASE(function_type_parameter) { char const* text = R"( contract C { - function f(function(uint) external returns (uint) g) returns (function(uint) external returns (uint)) { + function f(function(uint) external returns (uint) g) public returns (function(uint) external returns (uint)) { return g; } } @@ -4765,7 +4776,7 @@ BOOST_AUTO_TEST_CASE(function_type_returned) { char const* text = R"( contract C { - function f() returns (function(uint) external returns (uint) g) { + function f() public returns (function(uint) external returns (uint) g) { return g; } } @@ -4777,7 +4788,7 @@ BOOST_AUTO_TEST_CASE(private_function_type) { char const* text = R"( contract C { - function f() { + function f() public { function(uint) private returns (uint) x; } } @@ -4789,7 +4800,7 @@ BOOST_AUTO_TEST_CASE(public_function_type) { char const* text = R"( contract C { - function f() { + function f() public { function(uint) public returns (uint) x; } } @@ -4812,7 +4823,7 @@ BOOST_AUTO_TEST_CASE(call_value_on_non_payable_function_type) char const* text = R"( contract C { function (uint) external returns (uint) x; - function f() { + function f() public { x.value(2)(); } } @@ -4845,7 +4856,7 @@ BOOST_AUTO_TEST_CASE(call_value_on_payable_function_type) char const* text = R"( contract C { function (uint) external payable returns (uint) x; - function f() { + function f() public { x.value(2)(1); } } @@ -4859,7 +4870,7 @@ BOOST_AUTO_TEST_CASE(internal_function_as_external_parameter) // as parameters to external functions. char const* text = R"( contract C { - function f(function(uint) internal returns (uint) x) { + function f(function(uint) internal returns (uint) x) public { } } )"; @@ -4871,7 +4882,7 @@ BOOST_AUTO_TEST_CASE(internal_function_returned_from_public_function) // It should not be possible to return internal functions from external functions. char const* text = R"( contract C { - function f() returns (function(uint) internal returns (uint) x) { + function f() public returns (function(uint) internal returns (uint) x) { } } )"; @@ -4893,7 +4904,7 @@ BOOST_AUTO_TEST_CASE(internal_function_as_external_parameter_in_library_external { char const* text = R"( library L { - function f(function(uint) internal returns (uint) x) { + function f(function(uint) internal returns (uint) x) public { } } )"; @@ -4906,7 +4917,7 @@ BOOST_AUTO_TEST_CASE(function_type_arrays) contract C { function(uint) external returns (uint)[] public x; function(uint) internal returns (uint)[10] y; - function f() { + function f() public { function(uint) returns (uint)[10] memory a; function(uint) returns (uint)[10] storage b = y; function(uint) external returns (uint)[] memory c; @@ -4924,7 +4935,7 @@ BOOST_AUTO_TEST_CASE(delete_function_type) contract C { function(uint) external returns (uint) x; function(uint) internal returns (uint) y; - function f() { + function f() public { delete x; var a = y; delete a; @@ -4943,7 +4954,7 @@ BOOST_AUTO_TEST_CASE(delete_function_type_invalid) { char const* text = R"( contract C { - function f() { + function f() public { delete f; } } @@ -4955,7 +4966,7 @@ BOOST_AUTO_TEST_CASE(delete_external_function_type_invalid) { char const* text = R"( contract C { - function f() { + function f() public { delete this.f; } } @@ -4970,9 +4981,9 @@ BOOST_AUTO_TEST_CASE(external_function_to_function_type_calldata_parameter) // when converting to a function type. char const* text = R"( contract C { - function f(function(bytes memory) external g) { } + function f(function(bytes memory) external g) public { } function callback(bytes) external {} - function g() { + function g() public { f(this.callback); } } @@ -4984,7 +4995,7 @@ BOOST_AUTO_TEST_CASE(external_function_type_to_address) { char const* text = R"( contract C { - function f() returns (address) { + function f() public returns (address) { return address(this.f); } } @@ -4996,7 +5007,7 @@ BOOST_AUTO_TEST_CASE(internal_function_type_to_address) { char const* text = R"( contract C { - function f() returns (address) { + function f() public returns (address) { return address(f); } } @@ -5008,7 +5019,7 @@ BOOST_AUTO_TEST_CASE(external_function_type_to_uint) { char const* text = R"( contract C { - function f() returns (uint) { + function f() public returns (uint) { return uint(this.f); } } @@ -5030,7 +5041,7 @@ BOOST_AUTO_TEST_CASE(warn_function_type_return_parameters_with_names) { char const* text = R"( contract C { - function(uint) returns(bool ret) f; + function(uint) returns (bool ret) f; } )"; CHECK_WARNING(text, "Naming function type return parameters is deprecated."); @@ -5080,7 +5091,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_unbalanced_positive_stack) { char const* text = R"( contract test { - function f() { + function f() public { assembly { 1 } @@ -5094,7 +5105,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_unbalanced_negative_stack) { char const* text = R"( contract test { - function f() { + function f() public { assembly { pop } @@ -5109,7 +5120,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_unbalanced_two_stack_load) char const* text = R"( contract c { uint8 x; - function f() { + function f() public { assembly { x pop } } } @@ -5140,7 +5151,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_storage) char const* text = R"( contract test { uint x = 1; - function f() { + function f() public { assembly { x := 2 } @@ -5173,7 +5184,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_constant_assign) char const* text = R"( contract test { uint constant x = 1; - function f() { + function f() public { assembly { x := 2 } @@ -5188,7 +5199,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_constant_access) char const* text = R"( contract test { uint constant x = 1; - function f() { + function f() public { assembly { let y := x } @@ -5202,7 +5213,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_local_variable_access_out_of_functions) { char const* text = R"( contract test { - function f() { + function f() public { uint a; assembly { function g() -> x { x := a } @@ -5218,7 +5229,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_local_variable_access_out_of_functions_stor char const* text = R"( contract test { uint[] r; - function f() { + function f() public { uint[] storage a = r; assembly { function g() -> x { x := a_offset } @@ -5234,7 +5245,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_storage_variable_access_out_of_functions) char const* text = R"( contract test { uint a; - function f() pure { + function f() pure public { assembly { function g() -> x { x := a_slot } } @@ -5262,7 +5273,7 @@ BOOST_AUTO_TEST_CASE(invalid_mobile_type) { char const* text = R"( contract C { - function f() { + function f() public { // Invalid number [1, 78901234567890123456789012345678901234567890123456789345678901234567890012345678012345678901234567]; } @@ -5275,7 +5286,7 @@ BOOST_AUTO_TEST_CASE(warns_msg_value_in_non_payable_public_function) { char const* text = R"( contract C { - function f() view { + function f() view public { msg.value; } } @@ -5287,7 +5298,7 @@ BOOST_AUTO_TEST_CASE(does_not_warn_msg_value_in_payable_function) { char const* text = R"( contract C { - function f() payable { + function f() payable public { msg.value; } } @@ -5311,7 +5322,7 @@ BOOST_AUTO_TEST_CASE(does_not_warn_msg_value_in_library) { char const* text = R"( library C { - function f() view { + function f() view public { msg.value; } } @@ -5323,7 +5334,7 @@ BOOST_AUTO_TEST_CASE(does_not_warn_msg_value_in_modifier_following_non_payable_p { char const* text = R"( contract c { - function f() pure { } + function f() pure public { } modifier m() { msg.value; _; } } )"; @@ -5335,7 +5346,7 @@ BOOST_AUTO_TEST_CASE(assignment_to_constant) char const* text = R"( contract c { uint constant a = 1; - function f() { a = 2; } + function f() public { a = 2; } } )"; CHECK_ERROR(text, TypeError, "Cannot assign to a constant variable."); @@ -5348,7 +5359,7 @@ BOOST_AUTO_TEST_CASE(inconstructible_internal_constructor) function C() internal {} } contract D { - function f() { var x = new C(); } + function f() public { var x = new C(); } } )"; CHECK_ERROR(text, TypeError, "Contract with internal constructor cannot be created directly."); @@ -5361,7 +5372,7 @@ BOOST_AUTO_TEST_CASE(inconstructible_internal_constructor_inverted) char const* text = R"( contract B { A a; - function B() { + function B() public { a = new A(this); } } @@ -5379,7 +5390,7 @@ BOOST_AUTO_TEST_CASE(constructible_internal_constructor) function C() internal {} } contract D is C { - function D() { } + function D() public { } } )"; success(text); @@ -5389,7 +5400,7 @@ BOOST_AUTO_TEST_CASE(address_checksum_type_deduction) { char const* text = R"( contract C { - function f() { + function f() public { var x = 0xfA0bFc97E48458494Ccd857e1A85DC91F7F0046E; x.send(2); } @@ -5402,7 +5413,7 @@ BOOST_AUTO_TEST_CASE(invalid_address_checksum) { char const* text = R"( contract C { - function f() pure { + function f() pure public { address x = 0xFA0bFc97E48458494Ccd857e1A85DC91F7F0046E; x; } @@ -5415,7 +5426,7 @@ BOOST_AUTO_TEST_CASE(invalid_address_no_checksum) { char const* text = R"( contract C { - function f() pure { + function f() pure public { address x = 0xfa0bfc97e48458494ccd857e1a85dc91f7f0046e; x; } @@ -5428,7 +5439,7 @@ BOOST_AUTO_TEST_CASE(invalid_address_length) { char const* text = R"( contract C { - function f() pure { + function f() pure public { address x = 0xA0bFc97E48458494Ccd857e1A85DC91F7F0046E; x; } @@ -5448,7 +5459,7 @@ BOOST_AUTO_TEST_CASE(address_test_for_bug_in_implementation) CHECK_ERROR(text, TypeError, "is not implicitly convertible to expected type address"); text = R"( contract AddrString { - function f() returns (address) { + function f() public returns (address) { return "0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c"; } } @@ -5476,7 +5487,7 @@ BOOST_AUTO_TEST_CASE(address_methods) { char const* text = R"( contract C { - function f() { + function f() public { address addr; uint balance = addr.balance; bool callRet = addr.call(); @@ -5553,7 +5564,7 @@ BOOST_AUTO_TEST_CASE(interface_function_bodies) { char const* text = R"( interface I { - function f() { + function f() public { } } )"; @@ -5627,7 +5638,7 @@ BOOST_AUTO_TEST_CASE(interface_function_parameters) { char const* text = R"( interface I { - function f(uint a) returns(bool); + function f(uint a) public returns (bool); } )"; success(text); @@ -5650,7 +5661,7 @@ BOOST_AUTO_TEST_CASE(using_interface) function f(); } contract C is I { - function f() { + function f() public { } } )"; @@ -5667,7 +5678,7 @@ BOOST_AUTO_TEST_CASE(using_interface_complex) function(); } contract C is I { - function f() { + function f() public { } } )"; @@ -5678,7 +5689,7 @@ BOOST_AUTO_TEST_CASE(warn_about_throw) { char const* text = R"( contract C { - function f() pure { + function f() pure public { throw; } } @@ -5690,7 +5701,7 @@ BOOST_AUTO_TEST_CASE(bare_revert) { char const* text = R"( contract C { - function f(uint x) pure { + function f(uint x) pure public { if (x > 7) revert; } @@ -5701,17 +5712,17 @@ BOOST_AUTO_TEST_CASE(bare_revert) BOOST_AUTO_TEST_CASE(bare_others) { - CHECK_WARNING("contract C { function f() pure { selfdestruct; } }", "Statement has no effect."); - CHECK_WARNING("contract C { function f() pure { assert; } }", "Statement has no effect."); - CHECK_WARNING("contract C { function f() pure { require; } }", "Statement has no effect."); - CHECK_WARNING("contract C { function f() pure { suicide; } }", "Statement has no effect."); + CHECK_WARNING("contract C { function f() pure public { selfdestruct; } }", "Statement has no effect."); + CHECK_WARNING("contract C { function f() pure public { assert; } }", "Statement has no effect."); + CHECK_WARNING("contract C { function f() pure public { require; } }", "Statement has no effect."); + CHECK_WARNING("contract C { function f() pure public { suicide; } }", "Statement has no effect."); } BOOST_AUTO_TEST_CASE(pure_statement_in_for_loop) { char const* text = R"( contract C { - function f() pure { + function f() pure public { for (uint x = 0; x < 10; true) x++; } @@ -5724,7 +5735,7 @@ BOOST_AUTO_TEST_CASE(pure_statement_check_for_regular_for_loop) { char const* text = R"( contract C { - function f() pure { + function f() pure public { for (uint x = 0; true; x++) {} } @@ -5739,7 +5750,7 @@ BOOST_AUTO_TEST_CASE(warn_multiple_storage_storage_copies) contract C { struct S { uint a; uint b; } S x; S y; - function f() { + function f() public { (x, y) = (y, x); } } @@ -5753,7 +5764,7 @@ BOOST_AUTO_TEST_CASE(warn_multiple_storage_storage_copies_fill_right) contract C { struct S { uint a; uint b; } S x; S y; - function f() { + function f() public { (x, y, ) = (y, x, 1, 2); } } @@ -5767,7 +5778,7 @@ BOOST_AUTO_TEST_CASE(warn_multiple_storage_storage_copies_fill_left) contract C { struct S { uint a; uint b; } S x; S y; - function f() { + function f() public { (,x, y) = (1, 2, y, x); } } @@ -5780,7 +5791,7 @@ BOOST_AUTO_TEST_CASE(nowarn_swap_memory) char const* text = R"( contract C { struct S { uint a; uint b; } - function f() pure { + function f() pure public { S memory x; S memory y; (x, y) = (y, x); @@ -5796,7 +5807,7 @@ BOOST_AUTO_TEST_CASE(nowarn_swap_storage_pointers) contract C { struct S { uint a; uint b; } S x; S y; - function f() { + function f() public { S storage x_local = x; S storage y_local = y; S storage z_local = x; @@ -5811,7 +5822,7 @@ BOOST_AUTO_TEST_CASE(warn_unused_local) { char const* text = R"( contract C { - function f() pure { + function f() pure public { uint a; } } @@ -5823,7 +5834,7 @@ BOOST_AUTO_TEST_CASE(warn_unused_local_assigned) { char const* text = R"( contract C { - function f() pure { + function f() pure public { uint a = 1; } } @@ -5835,14 +5846,14 @@ BOOST_AUTO_TEST_CASE(warn_unused_function_parameter) { char const* text = R"( contract C { - function f(uint a) pure { + function f(uint a) pure public { } } )"; CHECK_WARNING(text, "Unused function parameter. Remove or comment out the variable name to silence this warning."); text = R"( contract C { - function f(uint a) pure { + function f(uint a) pure public { } } )"; @@ -5853,14 +5864,14 @@ BOOST_AUTO_TEST_CASE(warn_unused_return_parameter) { char const* text = R"( contract C { - function f() pure returns (uint a) { + function f() pure public returns (uint a) { } } )"; CHECK_WARNING(text, "Unused function parameter. Remove or comment out the variable name to silence this warning."); text = R"( contract C { - function f() pure returns (uint a) { + function f() pure public returns (uint a) { return; } } @@ -5868,14 +5879,14 @@ BOOST_AUTO_TEST_CASE(warn_unused_return_parameter) CHECK_WARNING(text, "Unused function parameter. Remove or comment out the variable name to silence this warning."); text = R"( contract C { - function f() pure returns (uint) { + function f() pure public returns (uint) { } } )"; CHECK_SUCCESS_NO_WARNINGS(text); text = R"( contract C { - function f() pure returns (uint a) { + function f() pure public returns (uint a) { a = 1; } } @@ -5883,7 +5894,7 @@ BOOST_AUTO_TEST_CASE(warn_unused_return_parameter) CHECK_SUCCESS_NO_WARNINGS(text); text = R"( contract C { - function f() pure returns (uint a) { + function f() pure public returns (uint a) { return 1; } } @@ -5895,7 +5906,7 @@ BOOST_AUTO_TEST_CASE(no_unused_warnings) { char const* text = R"( contract C { - function f(uint a) pure returns (uint b) { + function f(uint a) pure public returns (uint b) { uint c = 1; b = a + c; } @@ -5908,7 +5919,7 @@ BOOST_AUTO_TEST_CASE(no_unused_dec_after_use) { char const* text = R"( contract C { - function f() pure { + function f() pure public { a = 7; uint a; } @@ -5921,7 +5932,7 @@ BOOST_AUTO_TEST_CASE(no_unused_inline_asm) { char const* text = R"( contract C { - function f() pure { + function f() pure public { uint a; assembly { a := 1 @@ -5936,7 +5947,7 @@ BOOST_AUTO_TEST_CASE(shadowing_builtins_with_functions) { char const* text = R"( contract C { - function keccak256() pure {} + function keccak256() pure public {} } )"; CHECK_WARNING(text, "shadows a builtin symbol"); @@ -5946,7 +5957,7 @@ BOOST_AUTO_TEST_CASE(shadowing_builtins_with_variables) { char const* text = R"( contract C { - function f() pure { + function f() pure public { uint msg; msg; } @@ -5978,7 +5989,7 @@ BOOST_AUTO_TEST_CASE(shadowing_builtins_with_parameters) { char const* text = R"( contract C { - function f(uint require) pure { + function f(uint require) pure public { require = 2; } } @@ -5990,7 +6001,7 @@ BOOST_AUTO_TEST_CASE(shadowing_builtins_with_return_parameters) { char const* text = R"( contract C { - function f() pure returns (uint require) { + function f() pure public returns (uint require) { require = 2; } } @@ -6024,7 +6035,7 @@ BOOST_AUTO_TEST_CASE(shadowing_builtins_ignores_constructor) { char const* text = R"( contract C { - function C() {} + function C() public {} } )"; CHECK_SUCCESS_NO_WARNINGS(text); @@ -6034,8 +6045,8 @@ BOOST_AUTO_TEST_CASE(function_overload_is_not_shadowing) { char const* text = R"( contract C { - function f() pure {} - function f(uint) pure {} + function f() pure public {} + function f(uint) pure public {} } )"; CHECK_SUCCESS_NO_WARNINGS(text); @@ -6044,9 +6055,9 @@ BOOST_AUTO_TEST_CASE(function_overload_is_not_shadowing) BOOST_AUTO_TEST_CASE(function_override_is_not_shadowing) { char const* text = R"( - contract D { function f() pure {} } + contract D { function f() pure public {} } contract C is D { - function f(uint) pure {} + function f(uint) pure public {} } )"; CHECK_SUCCESS_NO_WARNINGS(text); @@ -6058,7 +6069,7 @@ BOOST_AUTO_TEST_CASE(callable_crash) contract C { struct S { uint a; bool x; } S public s; - function C() { + function C() public { 3({a: 1, x: true}); } } @@ -6070,13 +6081,13 @@ BOOST_AUTO_TEST_CASE(error_transfer_non_payable_fallback) { char const* text = R"( contract A { - function() {} + function() public {} } contract B { A a; - function() { + function() public { a.transfer(100); } } @@ -6092,7 +6103,7 @@ BOOST_AUTO_TEST_CASE(error_transfer_no_fallback) contract B { A a; - function() { + function() public { a.transfer(100); } } @@ -6104,13 +6115,13 @@ BOOST_AUTO_TEST_CASE(error_send_non_payable_fallback) { char const* text = R"( contract A { - function() {} + function() public {} } contract B { A a; - function() { + function() public { require(a.send(100)); } } @@ -6122,13 +6133,13 @@ BOOST_AUTO_TEST_CASE(does_not_error_transfer_payable_fallback) { char const* text = R"( contract A { - function() payable {} + function() payable public {} } contract B { A a; - function() { + function() public { a.transfer(100); } } @@ -6140,13 +6151,13 @@ BOOST_AUTO_TEST_CASE(does_not_error_transfer_regular_function) { char const* text = R"( contract A { - function transfer() pure {} + function transfer() pure public {} } contract B { A a; - function() { + function() public { a.transfer(); } } @@ -6157,7 +6168,7 @@ BOOST_AUTO_TEST_CASE(does_not_error_transfer_regular_function) BOOST_AUTO_TEST_CASE(returndatacopy_as_variable) { char const* text = R"( - contract c { function f() { uint returndatasize; assembly { returndatasize }}} + contract c { function f() public { uint returndatasize; assembly { returndatasize }}} )"; CHECK_WARNING_ALLOW_MULTI(text, "Variable is shadowed in inline assembly by an instruction of the same name"); } @@ -6165,7 +6176,7 @@ BOOST_AUTO_TEST_CASE(returndatacopy_as_variable) BOOST_AUTO_TEST_CASE(create2_as_variable) { char const* text = R"( - contract c { function f() { uint create2; assembly { create2(0, 0, 0, 0) }}} + contract c { function f() public { uint create2; assembly { create2(0, 0, 0, 0) }}} )"; CHECK_WARNING_ALLOW_MULTI(text, "Variable is shadowed in inline assembly by an instruction of the same name"); } @@ -6176,7 +6187,7 @@ BOOST_AUTO_TEST_CASE(warn_unspecified_storage) contract C { struct S { uint a; string b; } S x; - function f() view { + function f() view public { S storage y = x; y; } @@ -6187,7 +6198,7 @@ BOOST_AUTO_TEST_CASE(warn_unspecified_storage) contract C { struct S { uint a; } S x; - function f() view { + function f() view public { S y = x; y; } @@ -6200,7 +6211,7 @@ BOOST_AUTO_TEST_CASE(implicit_conversion_disallowed) { char const* text = R"( contract C { - function f() returns (bytes4) { + function f() public returns (bytes4) { uint32 tmp = 1; return tmp; } @@ -6227,7 +6238,7 @@ BOOST_AUTO_TEST_CASE(too_large_arrays_for_calldata) CHECK_ERROR(text, TypeError, "Array is too large to be encoded."); text = R"( contract C { - function f(uint[85678901234] a) pure { + function f(uint[85678901234] a) pure public { } } )"; @@ -6238,7 +6249,7 @@ BOOST_AUTO_TEST_CASE(explicit_literal_to_storage_string) { char const* text = R"( contract C { - function f() pure { + function f() pure public { string memory x = "abc"; x; } @@ -6247,7 +6258,7 @@ BOOST_AUTO_TEST_CASE(explicit_literal_to_storage_string) CHECK_SUCCESS_NO_WARNINGS(text); text = R"( contract C { - function f() pure { + function f() pure public { string storage x = "abc"; } } @@ -6255,7 +6266,7 @@ BOOST_AUTO_TEST_CASE(explicit_literal_to_storage_string) CHECK_ERROR(text, TypeError, "Type literal_string \"abc\" is not implicitly convertible to expected type string storage pointer."); text = R"( contract C { - function f() pure { + function f() pure public { string x = "abc"; } } @@ -6263,7 +6274,7 @@ BOOST_AUTO_TEST_CASE(explicit_literal_to_storage_string) CHECK_ERROR(text, TypeError, "Type literal_string \"abc\" is not implicitly convertible to expected type string storage pointer."); text = R"( contract C { - function f() pure { + function f() pure public { string("abc"); } } @@ -6316,7 +6327,7 @@ BOOST_AUTO_TEST_CASE(function_types_sig) CHECK_ERROR(text, TypeError, "Member \"selector\" not found"); text = R"( contract C { - function f() view returns (bytes4) { + function f() view external returns (bytes4) { return this.f.selector; } } @@ -6370,10 +6381,10 @@ BOOST_AUTO_TEST_CASE(using_this_in_constructor) { char const* text = R"( contract C { - function C() { + function C() public { this.f(); } - function f() pure { + function f() pure public { } } )"; @@ -6386,7 +6397,7 @@ BOOST_AUTO_TEST_CASE(do_not_crash_on_not_lvalue) char const* text = R"( contract C { mapping (uint => uint) m; - function f() { + function f() public { m(1) = 2; } } @@ -6398,7 +6409,7 @@ BOOST_AUTO_TEST_CASE(builtin_reject_gas) { char const* text = R"( contract C { - function f() { + function f() public { keccak256.gas(); } } @@ -6406,7 +6417,7 @@ BOOST_AUTO_TEST_CASE(builtin_reject_gas) CHECK_ERROR(text, TypeError, "Member \"gas\" not found or not visible after argument-dependent lookup"); text = R"( contract C { - function f() { + function f() public { sha256.gas(); } } @@ -6414,7 +6425,7 @@ BOOST_AUTO_TEST_CASE(builtin_reject_gas) CHECK_ERROR(text, TypeError, "Member \"gas\" not found or not visible after argument-dependent lookup"); text = R"( contract C { - function f() { + function f() public { ripemd160.gas(); } } @@ -6422,7 +6433,7 @@ BOOST_AUTO_TEST_CASE(builtin_reject_gas) CHECK_ERROR(text, TypeError, "Member \"gas\" not found or not visible after argument-dependent lookup"); text = R"( contract C { - function f() { + function f() public { ecrecover.gas(); } } @@ -6434,7 +6445,7 @@ BOOST_AUTO_TEST_CASE(builtin_reject_value) { char const* text = R"( contract C { - function f() { + function f() public { keccak256.value(); } } @@ -6442,7 +6453,7 @@ BOOST_AUTO_TEST_CASE(builtin_reject_value) CHECK_ERROR(text, TypeError, "Member \"value\" not found or not visible after argument-dependent lookup"); text = R"( contract C { - function f() { + function f() public { sha256.value(); } } @@ -6450,7 +6461,7 @@ BOOST_AUTO_TEST_CASE(builtin_reject_value) CHECK_ERROR(text, TypeError, "Member \"value\" not found or not visible after argument-dependent lookup"); text = R"( contract C { - function f() { + function f() public { ripemd160.value(); } } @@ -6458,7 +6469,7 @@ BOOST_AUTO_TEST_CASE(builtin_reject_value) CHECK_ERROR(text, TypeError, "Member \"value\" not found or not visible after argument-dependent lookup"); text = R"( contract C { - function f() { + function f() public { ecrecover.value(); } } @@ -6531,7 +6542,7 @@ BOOST_AUTO_TEST_CASE(library_function_without_implementation) { char const* text = R"( library L { - function f(); + function f() public; } )"; CHECK_SUCCESS_NO_WARNINGS(text); @@ -6606,7 +6617,7 @@ BOOST_AUTO_TEST_CASE(reject_interface_creation) char const* text = R"( interface I {} contract C { - function f() { + function f() public { new I(); } } @@ -6619,7 +6630,7 @@ BOOST_AUTO_TEST_CASE(accept_library_creation) char const* text = R"( library L {} contract C { - function f() { + function f() public { new L(); } } @@ -6640,7 +6651,7 @@ BOOST_AUTO_TEST_CASE(tight_packing_literals) { char const* text = R"( contract C { - function f() pure returns (bytes32) { + function f() pure public returns (bytes32) { return keccak256(1); } } @@ -6648,7 +6659,7 @@ BOOST_AUTO_TEST_CASE(tight_packing_literals) CHECK_WARNING(text, "The type of \"int_const 1\" was inferred as uint8."); text = R"( contract C { - function f() pure returns (bytes32) { + function f() pure public returns (bytes32) { return keccak256(uint8(1)); } } @@ -6656,7 +6667,7 @@ BOOST_AUTO_TEST_CASE(tight_packing_literals) CHECK_SUCCESS_NO_WARNINGS(text); text = R"( contract C { - function f() pure returns (bytes32) { + function f() pure public returns (bytes32) { return sha3(1); } } @@ -6664,7 +6675,7 @@ BOOST_AUTO_TEST_CASE(tight_packing_literals) CHECK_WARNING(text, "The type of \"int_const 1\" was inferred as uint8."); text = R"( contract C { - function f() pure returns (bytes32) { + function f() pure public returns (bytes32) { return sha256(1); } } @@ -6672,7 +6683,7 @@ BOOST_AUTO_TEST_CASE(tight_packing_literals) CHECK_WARNING(text, "The type of \"int_const 1\" was inferred as uint8."); text = R"( contract C { - function f() pure returns (bytes32) { + function f() pure public returns (bytes32) { return ripemd160(1); } } From 67f96652f5701b581efcda8585ab5e765aa68344 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 14 Sep 2017 16:30:00 +0100 Subject: [PATCH 116/162] Update view/pure tests to contain mandatory visibility specifiers --- test/libsolidity/ViewPureChecker.cpp | 104 +++++++++++++-------------- 1 file changed, 51 insertions(+), 53 deletions(-) diff --git a/test/libsolidity/ViewPureChecker.cpp b/test/libsolidity/ViewPureChecker.cpp index 9cea98505..3f02564fc 100644 --- a/test/libsolidity/ViewPureChecker.cpp +++ b/test/libsolidity/ViewPureChecker.cpp @@ -40,10 +40,10 @@ BOOST_AUTO_TEST_CASE(smoke_test) char const* text = R"( contract C { uint x; - function g() pure {} - function f() view returns (uint) { return now; } - function h() { x = 2; } - function i() payable { x = 2; } + function g() pure public {} + function f() view public returns (uint) { return now; } + function h() public { x = 2; } + function i() payable public { x = 2; } } )"; CHECK_SUCCESS_NO_WARNINGS(text); @@ -53,10 +53,10 @@ BOOST_AUTO_TEST_CASE(call_internal_functions_success) { char const* text = R"( contract C { - function g() pure { g(); } - function f() view returns (uint) { f(); g(); } - function h() { h(); g(); f(); } - function i() payable { i(); h(); g(); f(); } + function g() pure public { g(); } + function f() view public returns (uint) { f(); g(); } + function h() public { h(); g(); f(); } + function i() payable public { i(); h(); g(); f(); } } )"; CHECK_SUCCESS_NO_WARNINGS(text); @@ -66,7 +66,7 @@ BOOST_AUTO_TEST_CASE(suggest_pure) { char const* text = R"( contract C { - function g() view { } + function g() view public { } } )"; CHECK_WARNING(text, "can be restricted to pure"); @@ -77,7 +77,7 @@ BOOST_AUTO_TEST_CASE(suggest_view) char const* text = R"( contract C { uint x; - function g() returns (uint) { return x; } + function g() public returns (uint) { return x; } } )"; CHECK_WARNING(text, "can be restricted to view"); @@ -86,7 +86,7 @@ BOOST_AUTO_TEST_CASE(suggest_view) BOOST_AUTO_TEST_CASE(call_internal_functions_fail) { CHECK_ERROR( - "contract C{ function f() pure { g(); } function g() view {} }", + "contract C{ function f() pure public { g(); } function g() view public {} }", TypeError, "Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires \"view\"" ); @@ -95,7 +95,7 @@ BOOST_AUTO_TEST_CASE(call_internal_functions_fail) BOOST_AUTO_TEST_CASE(write_storage_fail) { CHECK_WARNING( - "contract C{ uint x; function f() view { x = 2; } }", + "contract C{ uint x; function f() view public { x = 2; } }", "Function declared as view, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable." ); } @@ -129,7 +129,7 @@ BOOST_AUTO_TEST_CASE(environment_access) for (string const& x: view) { CHECK_ERROR( - "contract C { function f() pure { var x = " + x + "; x; } }", + "contract C { function f() pure public { var x = " + x + "; x; } }", TypeError, "Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires \"view\"" ); @@ -137,7 +137,7 @@ BOOST_AUTO_TEST_CASE(environment_access) for (string const& x: pure) { CHECK_WARNING( - "contract C { function f() view { var x = " + x + "; x; } }", + "contract C { function f() view public { var x = " + x + "; x; } }", "restricted to pure" ); } @@ -153,15 +153,15 @@ BOOST_AUTO_TEST_CASE(modifiers) modifier nonpayablem(uint) { x = 2; _; } } contract C is D { - function f() purem(0) pure {} - function g() viewm(0) view {} - function h() nonpayablem(0) {} - function i() purem(x) view {} - function j() viewm(x) view {} - function k() nonpayablem(x) {} - function l() purem(x = 2) {} - function m() viewm(x = 2) {} - function n() nonpayablem(x = 2) {} + function f() purem(0) pure public {} + function g() viewm(0) view public {} + function h() nonpayablem(0) public {} + function i() purem(x) view public {} + function j() viewm(x) view public {} + function k() nonpayablem(x) public {} + function l() purem(x = 2) public {} + function m() viewm(x = 2) public {} + function n() nonpayablem(x = 2) public {} } )"; CHECK_SUCCESS_NO_WARNINGS(text); @@ -171,10 +171,10 @@ BOOST_AUTO_TEST_CASE(interface) { string text = R"( interface D { - function f() view; + function f() view public; } contract C is D { - function f() view {} + function f() view public {} } )"; CHECK_SUCCESS_NO_WARNINGS(text); @@ -185,10 +185,10 @@ BOOST_AUTO_TEST_CASE(overriding) string text = R"( contract D { uint x; - function f() { x = 2; } + function f() public { x = 2; } } contract C is D { - function f() {} + function f() public {} } )"; CHECK_SUCCESS_NO_WARNINGS(text); @@ -203,12 +203,10 @@ BOOST_AUTO_TEST_CASE(returning_structs) function f() view internal returns (S storage) { return s; } - function g() - { + function g() public { f().x = 2; } - function h() view - { + function h() view public { f(); f().x; } @@ -222,13 +220,13 @@ BOOST_AUTO_TEST_CASE(mappings) string text = R"( contract C { mapping(uint => uint) a; - function f() view { + function f() view public { a; } - function g() view { + function g() view public { a[2]; } - function h() { + function h() public { a[2] = 3; } } @@ -242,18 +240,18 @@ BOOST_AUTO_TEST_CASE(local_storage_variables) contract C { struct S { uint a; } S s; - function f() view { + function f() view public { S storage x = s; x; } - function g() view { + function g() view public { S storage x = s; x = s; } - function i() { + function i() public { s.a = 2; } - function h() { + function h() public { S storage x = s; x.a = 2; } @@ -266,14 +264,14 @@ BOOST_AUTO_TEST_CASE(builtin_functions) { string text = R"( contract C { - function f() { + function f() public { this.transfer(1); require(this.send(2)); selfdestruct(this); require(this.delegatecall()); require(this.call()); } - function g() pure { + function g() pure public { var x = keccak256("abc"); var y = sha256("abc"); var z = ecrecover(1, 2, 3, 4); @@ -281,7 +279,7 @@ BOOST_AUTO_TEST_CASE(builtin_functions) assert(true); x; y; z; } - function() payable {} + function() payable public {} } )"; CHECK_SUCCESS_NO_WARNINGS(text); @@ -291,7 +289,7 @@ BOOST_AUTO_TEST_CASE(function_types) { string text = R"( contract C { - function f() pure { + function f() pure public { function () external nonpayFun; function () external view viewFun; function () external pure pureFun; @@ -301,12 +299,12 @@ BOOST_AUTO_TEST_CASE(function_types) pureFun; pureFun(); } - function g() view { + function g() view public { function () external view viewFun; viewFun(); } - function h() { + function h() public { function () external nonpayFun; nonpayFun(); @@ -321,7 +319,7 @@ BOOST_AUTO_TEST_CASE(creation) string text = R"( contract D {} contract C { - function f() { new D(); } + function f() public { new D(); } } )"; CHECK_SUCCESS_NO_WARNINGS(text); @@ -333,20 +331,20 @@ BOOST_AUTO_TEST_CASE(assembly) contract C { struct S { uint x; } S s; - function e() pure { + function e() pure public { assembly { mstore(keccak256(0, 20), mul(s_slot, 2)) } } - function f() pure { + function f() pure public { uint x; assembly { x := 7 } } - function g() view { + function g() view public { assembly { for {} 1 { pop(sload(0)) } { } } } - function h() view { + function h() view public { assembly { function g() { pop(blockhash(20)) } } } - function j() { + function j() public { assembly { pop(call(0, 1, 2, 3, 4, 5, 6)) } } } @@ -358,7 +356,7 @@ BOOST_AUTO_TEST_CASE(assembly_staticcall) { string text = R"( contract C { - function i() view { + function i() view public { assembly { pop(staticcall(0, 1, 2, 3, 4, 5)) } } } @@ -370,7 +368,7 @@ BOOST_AUTO_TEST_CASE(assembly_jump) { string text = R"( contract C { - function k() { + function k() public { assembly { jump(2) } } } @@ -383,7 +381,7 @@ BOOST_AUTO_TEST_CASE(constant) string text = R"( contract C { uint constant x = 2; - function k() pure returns (uint) { + function k() pure public returns (uint) { return x; } } From 24fd67b7db26f44ddc6b6db0b6f685d887762df0 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 14 Sep 2017 17:53:43 +0200 Subject: [PATCH 117/162] Enforce view with error for experimental 0.5.0. --- Changelog.md | 1 + libsolidity/analysis/ViewPureChecker.cpp | 34 ++++++++++++++++-------- libsolidity/analysis/ViewPureChecker.h | 1 + test/libsolidity/ViewPureChecker.cpp | 10 +++++++ 4 files changed, 35 insertions(+), 11 deletions(-) diff --git a/Changelog.md b/Changelog.md index d0d4d8dfa..18253a325 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,6 +9,7 @@ Features: * Type Checker: Greatly reduce the number of duplicate errors shown for duplicate constructors and functions. * Type Checker: Warn on using literals as tight packing parameters in ``keccak256``, ``sha3``, ``sha256`` and ``ripemd160``. * Type Checker: Enforce ``view`` and ``pure``. + * Type Checker: Enforce ``view`` / ``constant`` with error as experimental 0.5.0 feature. Bugfixes: * ABI JSON: Include all overloaded events. diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index 8f9d41c92..7f28c7d23 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -20,6 +20,7 @@ #include #include +#include #include @@ -102,22 +103,31 @@ private: bool ViewPureChecker::check() { - vector contracts; + // The bool means "enforce view with errors". + map contracts; for (auto const& node: m_ast) { SourceUnit const* source = dynamic_cast(node.get()); solAssert(source, ""); - contracts += source->filteredNodes(source->nodes()); + bool enforceView = source->annotation().experimentalFeatures.count(ExperimentalFeature::V050); + for (ContractDefinition const* c: source->filteredNodes(source->nodes())) + contracts[c] = enforceView; } // Check modifiers first to infer their state mutability. - for (auto const* contract: contracts) - for (ModifierDefinition const* mod: contract->functionModifiers()) + for (auto const& contract: contracts) + { + m_enforceViewWithError = contract.second; + for (ModifierDefinition const* mod: contract.first->functionModifiers()) mod->accept(*this); + } - for (auto const* contract: contracts) - contract->accept(*this); + for (auto const& contract: contracts) + { + m_enforceViewWithError = contract.second; + contract.first->accept(*this); + } return !m_errors; } @@ -223,16 +233,18 @@ void ViewPureChecker::reportMutability(StateMutability _mutability, SourceLocati else solAssert(false, ""); - if (m_currentFunction->stateMutability() == StateMutability::View) - // TODO Change this to error with 0.5.0 + solAssert( + m_currentFunction->stateMutability() == StateMutability::View || + m_currentFunction->stateMutability() == StateMutability::Pure, + "" + ); + if (!m_enforceViewWithError && m_currentFunction->stateMutability() == StateMutability::View) m_errorReporter.warning(_location, text); - else if (m_currentFunction->stateMutability() == StateMutability::Pure) + else { m_errors = true; m_errorReporter.typeError(_location, text); } - else - solAssert(false, ""); } if (_mutability > m_currentBestMutability) m_currentBestMutability = _mutability; diff --git a/libsolidity/analysis/ViewPureChecker.h b/libsolidity/analysis/ViewPureChecker.h index ae3035336..fec060b6a 100644 --- a/libsolidity/analysis/ViewPureChecker.h +++ b/libsolidity/analysis/ViewPureChecker.h @@ -70,6 +70,7 @@ private: ErrorReporter& m_errorReporter; bool m_errors = false; + bool m_enforceViewWithError = false; StateMutability m_currentBestMutability = StateMutability::Payable; FunctionDefinition const* m_currentFunction = nullptr; std::map m_inferredMutability; diff --git a/test/libsolidity/ViewPureChecker.cpp b/test/libsolidity/ViewPureChecker.cpp index 9cea98505..cdb752cdc 100644 --- a/test/libsolidity/ViewPureChecker.cpp +++ b/test/libsolidity/ViewPureChecker.cpp @@ -143,6 +143,16 @@ BOOST_AUTO_TEST_CASE(environment_access) } } +BOOST_AUTO_TEST_CASE(view_error_for_050) +{ + CHECK_ERROR( + "pragma experimental \"v0.5.0\"; contract C { uint x; function f() view { x = 2; } }", + TypeError, + "Function declared as view, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable." + ); + +} + BOOST_AUTO_TEST_CASE(modifiers) { string text = R"( From 48e0e3b97faaafff4da7687af1d5a25d2d7d1363 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 14 Sep 2017 23:13:37 +0200 Subject: [PATCH 118/162] Circle CI: Emscripten build --- circle.yml | 48 +++++++++++++++++-- scripts/build_emscripten.sh | 2 +- scripts/travis-emscripten/build_emscripten.sh | 16 ++++--- scripts/travis-emscripten/install_deps.sh | 6 +-- 4 files changed, 56 insertions(+), 16 deletions(-) diff --git a/circle.yml b/circle.yml index fd506ba55..db685da19 100644 --- a/circle.yml +++ b/circle.yml @@ -1,10 +1,50 @@ version: 2 jobs: build: - branches: - ignore: - - /.*/ docker: - - image: trzeci/emscripten:sdk-tag-1.37.18-64bit + - image: trzeci/emscripten:sdk-tag-1.37.21-64bit steps: - checkout + - run: + name: Install external tests deps + command: | + apt-get -qq update + apt-get -qy install netcat curl + curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.4/install.sh | NVM_DIR=/usr/local/nvm bash + - run: + name: Test external tests deps + command: | + export NVM_DIR="/usr/local/nvm" + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm + nvm --version + nvm install 6 + node --version + npm --version + - run: + name: Init submodules + command: | + git submodule update --init + - restore_cache: + name: Restore Boost build + key: &boost-cache-key emscripten-boost-{{ checksum "scripts/travis-emscripten/install_deps.sh" }}{{ checksum "scripts/travis-emscripten/build_emscripten.sh" }} + - run: + name: Bootstrap Boost + command: | + scripts/travis-emscripten/install_deps.sh + - run: + name: Build + command: | + scripts/travis-emscripten/build_emscripten.sh + - save_cache: + name: Save Boost build + key: *boost-cache-key + paths: + - boost_1_57_0 + - run: + name: Test + command: | + . /usr/local/nvm/nvm.sh + scripts/test_emscripten.sh + - store_artifacts: + path: build/solc/soljson.js + destination: soljson.js diff --git a/scripts/build_emscripten.sh b/scripts/build_emscripten.sh index 6046978e5..cddcd4f86 100755 --- a/scripts/build_emscripten.sh +++ b/scripts/build_emscripten.sh @@ -30,5 +30,5 @@ set -e if [[ "$OSTYPE" != "darwin"* ]]; then ./scripts/travis-emscripten/install_deps.sh - docker run -v $(pwd):/src trzeci/emscripten:sdk-tag-1.35.4-64bit ./scripts/travis-emscripten/build_emscripten.sh + docker run -v $(pwd):/root/project -w /root/project trzeci/emscripten:sdk-tag-1.35.4-64bit ./scripts/travis-emscripten/build_emscripten.sh fi diff --git a/scripts/travis-emscripten/build_emscripten.sh b/scripts/travis-emscripten/build_emscripten.sh index 5259dc7f5..bf460e8e9 100755 --- a/scripts/travis-emscripten/build_emscripten.sh +++ b/scripts/travis-emscripten/build_emscripten.sh @@ -34,11 +34,13 @@ set -ev -# We need git for extracting the commit hash -apt-get update -apt-get -y install git-core +if ! type git &>/dev/null; then + # We need git for extracting the commit hash + apt-get update + apt-get -y install git-core +fi -export WORKSPACE=/src +WORKSPACE=/root/project # Boost echo -en 'travis_fold:start:compiling_boost\\r' @@ -46,9 +48,9 @@ cd "$WORKSPACE"/boost_1_57_0 # if b2 exists, it is a fresh checkout, otherwise it comes from the cache # and is already compiled test -e b2 && ( -sed -i 's|using gcc ;|using gcc : : /usr/local/bin/em++ ;|g' ./project-config.jam -sed -i 's|$(archiver\[1\])|/usr/local/bin/emar|g' ./tools/build/src/tools/gcc.jam -sed -i 's|$(ranlib\[1\])|/usr/local/bin/emranlib|g' ./tools/build/src/tools/gcc.jam +sed -i 's|using gcc ;|using gcc : : em++ ;|g' ./project-config.jam +sed -i 's|$(archiver\[1\])|emar|g' ./tools/build/src/tools/gcc.jam +sed -i 's|$(ranlib\[1\])|emranlib|g' ./tools/build/src/tools/gcc.jam ./b2 link=static variant=release threading=single runtime-link=static \ system regex filesystem unit_test_framework program_options find . -name 'libboost*.a' -exec cp {} . \; diff --git a/scripts/travis-emscripten/install_deps.sh b/scripts/travis-emscripten/install_deps.sh index 252c74b07..45c16a9f8 100755 --- a/scripts/travis-emscripten/install_deps.sh +++ b/scripts/travis-emscripten/install_deps.sh @@ -31,10 +31,8 @@ set -ev echo -en 'travis_fold:start:installing_dependencies\\r' test -e boost_1_57_0 -a -e boost_1_57_0/boost || ( -wget 'http://downloads.sourceforge.net/project/boost/boost/'\ -'1.57.0/boost_1_57_0.tar.bz2?r=http%3A%2F%2Fsourceforge.net%2F'\ -'projects%2Fboost%2Ffiles%2Fboost%2F1.57.0%2F&ts=1421887207'\ - -O - | tar xj +wget 'https://sourceforge.net/projects/boost/files/boost/1.57.0/boost_1_57_0.tar.gz/download'\ + -O - | tar xz cd boost_1_57_0 ./bootstrap.sh --with-toolset=gcc --with-libraries=thread,system,regex,date_time,chrono,filesystem,program_options,random ) From 73771f5bb2d8aee1b71dfcc909a60aa47c591dec Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 25 Aug 2017 17:04:31 +0200 Subject: [PATCH 119/162] Named assembly labels. --- libevmasm/Assembly.cpp | 11 ++++++++ libevmasm/Assembly.h | 3 +++ libjulia/backends/evm/AbstractAssembly.h | 2 ++ libjulia/backends/evm/EVMAssembly.cpp | 8 ++++++ libjulia/backends/evm/EVMAssembly.h | 3 +++ libjulia/backends/evm/EVMCodeTransform.cpp | 30 ++++++++++++++++------ libjulia/backends/evm/EVMCodeTransform.h | 9 +++++-- libsolidity/codegen/CompilerContext.cpp | 16 +++--------- libsolidity/codegen/CompilerContext.h | 5 +++- libsolidity/codegen/CompilerUtils.cpp | 2 +- libsolidity/inlineasm/AsmCodeGen.cpp | 16 ++++++++++-- libsolidity/inlineasm/AsmCodeGen.h | 3 ++- test/libsolidity/ABIEncoderTests.cpp | 28 ++++++++++++++++++++ 13 files changed, 108 insertions(+), 28 deletions(-) diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index 6b4bb52b4..31857c09a 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -49,6 +49,8 @@ void Assembly::append(Assembly const& _a) } m_deposit = newDeposit; m_usedTags += _a.m_usedTags; + // This does not transfer the names of named tags on purpose. The tags themselves are + // transferred, but their names are only available inside the assembly. for (auto const& i: _a.m_data) m_data.insert(i); for (auto const& i: _a.m_strings) @@ -326,6 +328,14 @@ AssemblyItem const& Assembly::append(AssemblyItem const& _i) return back(); } +AssemblyItem Assembly::namedTag(string const& _name) +{ + assertThrow(!_name.empty(), AssemblyException, ""); + if (!m_namedTags.count(_name)) + m_namedTags[_name] = size_t(newTag().data()); + return AssemblyItem(Tag, m_namedTags.at(_name)); +} + AssemblyItem Assembly::newPushLibraryAddress(string const& _identifier) { h256 h(dev::keccak256(_identifier)); @@ -581,6 +591,7 @@ LinkerObject const& Assembly::assemble() const assertThrow(i.data() != 0, AssemblyException, ""); assertThrow(i.splitForeignPushTag().first == size_t(-1), AssemblyException, "Foreign tag."); assertThrow(ret.bytecode.size() < 0xffffffffL, AssemblyException, "Tag too large."); + assertThrow(m_tagPositionsInBytecode[size_t(i.data())] == size_t(-1), AssemblyException, "Duplicate tag position."); m_tagPositionsInBytecode[size_t(i.data())] = ret.bytecode.size(); ret.bytecode.push_back((byte)Instruction::JUMPDEST); break; diff --git a/libevmasm/Assembly.h b/libevmasm/Assembly.h index cbdd71bcf..885192e49 100644 --- a/libevmasm/Assembly.h +++ b/libevmasm/Assembly.h @@ -47,6 +47,8 @@ public: AssemblyItem newTag() { return AssemblyItem(Tag, m_usedTags++); } AssemblyItem newPushTag() { return AssemblyItem(PushTag, m_usedTags++); } + /// Returns a tag identified by the given name. Creates it if it does not yet exist. + AssemblyItem namedTag(std::string const& _name); AssemblyItem newData(bytes const& _data) { h256 h(dev::keccak256(asString(_data))); m_data[h] = _data; return AssemblyItem(PushData, h); } AssemblyItem newSub(AssemblyPointer const& _sub) { m_subs.push_back(_sub); return AssemblyItem(PushSub, m_subs.size() - 1); } Assembly const& sub(size_t _sub) const { return *m_subs.at(_sub); } @@ -150,6 +152,7 @@ private: protected: /// 0 is reserved for exception unsigned m_usedTags = 1; + std::map m_namedTags; AssemblyItems m_items; std::map m_data; /// Data that is appended to the very end of the contract. diff --git a/libjulia/backends/evm/AbstractAssembly.h b/libjulia/backends/evm/AbstractAssembly.h index cfc9b8a55..8e90a912b 100644 --- a/libjulia/backends/evm/AbstractAssembly.h +++ b/libjulia/backends/evm/AbstractAssembly.h @@ -66,6 +66,8 @@ public: virtual void appendLabelReference(LabelID _labelId) = 0; /// Generate a new unique label. virtual LabelID newLabelId() = 0; + /// Returns a label identified by the given name. Creates it if it does not yet exist. + virtual LabelID namedLabel(std::string const& _name) = 0; /// Append a reference to a to-be-linked symobl. /// Currently, we assume that the value is always a 20 byte number. virtual void appendLinkerSymbol(std::string const& _name) = 0; diff --git a/libjulia/backends/evm/EVMAssembly.cpp b/libjulia/backends/evm/EVMAssembly.cpp index 173d5e934..1d499b200 100644 --- a/libjulia/backends/evm/EVMAssembly.cpp +++ b/libjulia/backends/evm/EVMAssembly.cpp @@ -77,6 +77,14 @@ EVMAssembly::LabelID EVMAssembly::newLabelId() return m_nextLabelId++; } +AbstractAssembly::LabelID EVMAssembly::namedLabel(string const& _name) +{ + solAssert(!_name.empty(), ""); + if (!m_namedLabels.count(_name)) + m_namedLabels[_name] = newLabelId(); + return m_namedLabels[_name]; +} + void EVMAssembly::appendLinkerSymbol(string const&) { solAssert(false, "Linker symbols not yet implemented."); diff --git a/libjulia/backends/evm/EVMAssembly.h b/libjulia/backends/evm/EVMAssembly.h index 695858226..593cee6a2 100644 --- a/libjulia/backends/evm/EVMAssembly.h +++ b/libjulia/backends/evm/EVMAssembly.h @@ -52,6 +52,8 @@ public: virtual void appendLabelReference(LabelID _labelId) override; /// Generate a new unique label. virtual LabelID newLabelId() override; + /// Returns a label identified by the given name. Creates it if it does not yet exist. + virtual LabelID namedLabel(std::string const& _name) override; /// Append a reference to a to-be-linked symobl. /// Currently, we assume that the value is always a 20 byte number. virtual void appendLinkerSymbol(std::string const& _name) override; @@ -85,6 +87,7 @@ private: LabelID m_nextLabelId = 0; int m_stackHeight = 0; bytes m_bytecode; + std::map m_namedLabels; std::map m_labelPositions; std::map m_labelReferences; std::vector m_assemblySizePositions; diff --git a/libjulia/backends/evm/EVMCodeTransform.cpp b/libjulia/backends/evm/EVMCodeTransform.cpp index 704aa3c10..e0b11cf33 100644 --- a/libjulia/backends/evm/EVMCodeTransform.cpp +++ b/libjulia/backends/evm/EVMCodeTransform.cpp @@ -108,10 +108,10 @@ void CodeTransform::operator()(FunctionCall const& _call) visitExpression(arg); m_assembly.setSourceLocation(_call.location); if (m_evm15) - m_assembly.appendJumpsub(functionEntryID(*function), function->arguments.size(), function->returns.size()); + m_assembly.appendJumpsub(functionEntryID(_call.functionName.name, *function), function->arguments.size(), function->returns.size()); else { - m_assembly.appendJumpTo(functionEntryID(*function), function->returns.size() - function->arguments.size() - 1); + m_assembly.appendJumpTo(functionEntryID(_call.functionName.name, *function), function->returns.size() - function->arguments.size() - 1); m_assembly.appendLabel(returnLabel); m_stackAdjustment--; } @@ -286,12 +286,12 @@ void CodeTransform::operator()(FunctionDefinition const& _function) if (m_evm15) { m_assembly.appendJumpTo(afterFunction, -stackHeightBefore); - m_assembly.appendBeginsub(functionEntryID(function), _function.arguments.size()); + m_assembly.appendBeginsub(functionEntryID(_function.name, function), _function.arguments.size()); } else { m_assembly.appendJumpTo(afterFunction, -stackHeightBefore + height); - m_assembly.appendLabel(functionEntryID(function)); + m_assembly.appendLabel(functionEntryID(_function.name, function)); } m_stackAdjustment += localStackAdjustment; @@ -303,8 +303,16 @@ void CodeTransform::operator()(FunctionDefinition const& _function) m_assembly.appendConstant(u256(0)); } - CodeTransform(m_assembly, m_info, m_julia, m_evm15, m_identifierAccess, localStackAdjustment, m_context) - (_function.body); + CodeTransform( + m_assembly, + m_info, + m_julia, + m_evm15, + m_identifierAccess, + m_useNamedLabelsForFunctions, + localStackAdjustment, + m_context + )(_function.body); { // The stack layout here is: @@ -421,10 +429,16 @@ AbstractAssembly::LabelID CodeTransform::labelID(Scope::Label const& _label) return m_context->labelIDs[&_label]; } -AbstractAssembly::LabelID CodeTransform::functionEntryID(Scope::Function const& _function) +AbstractAssembly::LabelID CodeTransform::functionEntryID(string const& _name, Scope::Function const& _function) { if (!m_context->functionEntryIDs.count(&_function)) - m_context->functionEntryIDs[&_function] = m_assembly.newLabelId(); + { + AbstractAssembly::LabelID id = + m_useNamedLabelsForFunctions ? + m_assembly.namedLabel(_name) : + m_assembly.newLabelId(); + m_context->functionEntryIDs[&_function] = id; + } return m_context->functionEntryIDs[&_function]; } diff --git a/libjulia/backends/evm/EVMCodeTransform.h b/libjulia/backends/evm/EVMCodeTransform.h index cd452c5b4..2c0fd10cc 100644 --- a/libjulia/backends/evm/EVMCodeTransform.h +++ b/libjulia/backends/evm/EVMCodeTransform.h @@ -50,13 +50,15 @@ public: solidity::assembly::AsmAnalysisInfo& _analysisInfo, bool _julia = false, bool _evm15 = false, - ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess() + ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess(), + bool _useNamedLabelsForFunctions = false ): CodeTransform( _assembly, _analysisInfo, _julia, _evm15, _identifierAccess, + _useNamedLabelsForFunctions, _assembly.stackHeight(), std::make_shared() ) @@ -78,6 +80,7 @@ protected: bool _julia, bool _evm15, ExternalIdentifierAccess const& _identifierAccess, + bool _useNamedLabelsForFunctions, int _stackAdjustment, std::shared_ptr _context ): @@ -85,6 +88,7 @@ protected: m_info(_analysisInfo), m_julia(_julia), m_evm15(_evm15), + m_useNamedLabelsForFunctions(_useNamedLabelsForFunctions), m_identifierAccess(_identifierAccess), m_stackAdjustment(_stackAdjustment), m_context(_context) @@ -110,7 +114,7 @@ private: /// @returns the label ID corresponding to the given label, allocating a new one if /// necessary. AbstractAssembly::LabelID labelID(solidity::assembly::Scope::Label const& _label); - AbstractAssembly::LabelID functionEntryID(solidity::assembly::Scope::Function const& _function); + AbstractAssembly::LabelID functionEntryID(std::string const& _name, solidity::assembly::Scope::Function const& _function); /// Generates code for an expression that is supposed to return a single value. void visitExpression(solidity::assembly::Statement const& _expression); @@ -136,6 +140,7 @@ private: solidity::assembly::Scope* m_scope = nullptr; bool m_julia = false; bool m_evm15 = false; + bool m_useNamedLabelsForFunctions = false; ExternalIdentifierAccess m_identifierAccess; /// Adjustment between the stack height as determined during the analysis phase /// and the stack height in the assembly. This is caused by an initial stack being present diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index ed780d0bd..5a77162ee 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -266,19 +266,9 @@ void CompilerContext::resetVisitedNodes(ASTNode const* _node) void CompilerContext::appendInlineAssembly( string const& _assembly, vector const& _localVariables, - map const& _replacements + bool _system ) { - string replacedAssembly; - string const* assembly = &_assembly; - if (!_replacements.empty()) - { - replacedAssembly = _assembly; - for (auto const& replacement: _replacements) - replacedAssembly = boost::algorithm::replace_all_copy(replacedAssembly, replacement.first, replacement.second); - assembly = &replacedAssembly; - } - int startStackHeight = stackHeight(); julia::ExternalIdentifierAccess identifierAccess; @@ -320,7 +310,7 @@ void CompilerContext::appendInlineAssembly( ErrorList errors; ErrorReporter errorReporter(errors); - auto scanner = make_shared(CharStream(*assembly), "--CODEGEN--"); + auto scanner = make_shared(CharStream(_assembly), "--CODEGEN--"); auto parserResult = assembly::Parser(errorReporter).parse(scanner); solAssert(parserResult, "Failed to parse inline assembly block."); solAssert(errorReporter.errors().empty(), "Failed to parse inline assembly block."); @@ -329,7 +319,7 @@ void CompilerContext::appendInlineAssembly( assembly::AsmAnalyzer analyzer(analysisInfo, errorReporter, false, identifierAccess.resolve); solAssert(analyzer.analyze(*parserResult), "Failed to analyze inline assembly block."); solAssert(errorReporter.errors().empty(), "Failed to analyze inline assembly block."); - assembly::CodeGenerator::assemble(*parserResult, analysisInfo, *m_asm, identifierAccess); + assembly::CodeGenerator::assemble(*parserResult, analysisInfo, *m_asm, identifierAccess, _system); } FunctionDefinition const& CompilerContext::resolveVirtualFunction( diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index 5116585e7..dd36bba08 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -156,6 +156,8 @@ public: eth::AssemblyItem pushNewTag() { return m_asm->append(m_asm->newPushTag()).tag(); } /// @returns a new tag without pushing any opcodes or data eth::AssemblyItem newTag() { return m_asm->newTag(); } + /// @returns a new tag identified by name. + eth::AssemblyItem namedTag(std::string const& _name) { return m_asm->namedTag(_name); } /// Adds a subroutine to the code (in the data section) and pushes its size (via a tag) /// on the stack. @returns the pushsub assembly item. eth::AssemblyItem addSubroutine(eth::AssemblyPointer const& _assembly) { return m_asm->appendSubroutine(_assembly); } @@ -185,10 +187,11 @@ public: /// Appends inline assembly. @a _replacements are string-matching replacements that are performed /// prior to parsing the inline assembly. /// @param _localVariables assigns stack positions to variables with the last one being the stack top + /// @param _system if true, this is a "system-level" assembly where all functions use named labels. void appendInlineAssembly( std::string const& _assembly, std::vector const& _localVariables = std::vector(), - std::map const& _replacements = std::map{} + bool _system = false ); /// Appends arbitrary data to the end of the bytecode. diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 146472f92..3662478db 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -319,7 +319,7 @@ void CompilerUtils::abiEncode( ABIFunctions funs; string routine = funs.tupleEncoder(_givenTypes, _targetTypes, _encodeAsLibraryTypes); routine += funs.requestedFunctions(); - m_context.appendInlineAssembly("{" + routine + "}", variables); + m_context.appendInlineAssembly("{" + routine + "}", variables, true); // Remove everyhing except for "value0" / the final memory pointer. popStackSlots(numValues); } diff --git a/libsolidity/inlineasm/AsmCodeGen.cpp b/libsolidity/inlineasm/AsmCodeGen.cpp index 6d0c02558..dded9f768 100644 --- a/libsolidity/inlineasm/AsmCodeGen.cpp +++ b/libsolidity/inlineasm/AsmCodeGen.cpp @@ -83,6 +83,10 @@ public: { return assemblyTagToIdentifier(m_assembly.newTag()); } + virtual size_t namedLabel(std::string const& _name) override + { + return assemblyTagToIdentifier(m_assembly.namedTag(_name)); + } virtual void appendLinkerSymbol(std::string const& _linkerSymbol) override { m_assembly.appendLibraryAddress(_linkerSymbol); @@ -141,9 +145,17 @@ void assembly::CodeGenerator::assemble( Block const& _parsedData, AsmAnalysisInfo& _analysisInfo, eth::Assembly& _assembly, - julia::ExternalIdentifierAccess const& _identifierAccess + julia::ExternalIdentifierAccess const& _identifierAccess, + bool _useNamedLabelsForFunctions ) { EthAssemblyAdapter assemblyAdapter(_assembly); - julia::CodeTransform(assemblyAdapter, _analysisInfo, false, false, _identifierAccess)(_parsedData); + julia::CodeTransform( + assemblyAdapter, + _analysisInfo, + false, + false, + _identifierAccess, + _useNamedLabelsForFunctions + )(_parsedData); } diff --git a/libsolidity/inlineasm/AsmCodeGen.h b/libsolidity/inlineasm/AsmCodeGen.h index 2a36a590e..a7d7ead1a 100644 --- a/libsolidity/inlineasm/AsmCodeGen.h +++ b/libsolidity/inlineasm/AsmCodeGen.h @@ -46,7 +46,8 @@ public: Block const& _parsedData, AsmAnalysisInfo& _analysisInfo, eth::Assembly& _assembly, - julia::ExternalIdentifierAccess const& _identifierAccess = julia::ExternalIdentifierAccess() + julia::ExternalIdentifierAccess const& _identifierAccess = julia::ExternalIdentifierAccess(), + bool _useNamedLabelsForFunctions = false ); }; diff --git a/test/libsolidity/ABIEncoderTests.cpp b/test/libsolidity/ABIEncoderTests.cpp index 297c4ef08..4ddf17ce7 100644 --- a/test/libsolidity/ABIEncoderTests.cpp +++ b/test/libsolidity/ABIEncoderTests.cpp @@ -398,6 +398,34 @@ BOOST_AUTO_TEST_CASE(calldata) ) } +BOOST_AUTO_TEST_CASE(function_name_collision) +{ + // This tests a collision between a function name used by inline assembly + // and by the ABI encoder + string sourceCode = R"( + contract C { + function f(uint x) returns (uint) { + assembly { + function abi_encode_t_uint256_to_t_uint256() { + mstore(0, 7) + return(0, 0x20) + } + switch x + case 0 { abi_encode_t_uint256_to_t_uint256() } + } + return 1; + } + } + )"; + BOTH_ENCODERS( + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("f(uint256)", encodeArgs(0)) == encodeArgs(7)); + BOOST_CHECK(callContractFunction("f(uint256)", encodeArgs(1)) == encodeArgs(1)); + ) +} + + + BOOST_AUTO_TEST_SUITE_END() } From 80ce3ca66f063d8d87c2393e689f92d8608b4e0a Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 25 Aug 2017 18:58:12 +0200 Subject: [PATCH 120/162] Move ABI encoder into its own function. --- Changelog.md | 1 + libsolidity/codegen/ABIFunctions.cpp | 104 ++++++++++++----------- libsolidity/codegen/ABIFunctions.h | 15 ++-- libsolidity/codegen/CompilerContext.h | 5 ++ libsolidity/codegen/CompilerUtils.cpp | 17 ++-- libsolidity/codegen/ContractCompiler.cpp | 5 ++ 6 files changed, 82 insertions(+), 65 deletions(-) diff --git a/Changelog.md b/Changelog.md index e46a7c2f4..0b9295d06 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,6 +3,7 @@ Features: * Support ``pragma experimental v0.5.0;`` to turn on upcoming breaking changes. * Code Generator: Added ``.selector`` member on external function types to retrieve their signature. + * Code Generator: Keep a single copy of encoding functions when using the experimental "ABIEncoderV2". * Optimizer: Add new optimization step to remove unused ``JUMPDEST``s. * Syntax Checker: Warn if no visibility is specified on contract functions. * Type Checker: Display helpful warning for unused function arguments/return parameters. diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index a2938ed79..3a9f1a486 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -30,65 +30,73 @@ using namespace std; using namespace dev; using namespace dev::solidity; -ABIFunctions::~ABIFunctions() -{ - // This throws an exception and thus might cause immediate termination, but hey, - // it's a failed assertion anyway :-) - solAssert(m_requestedFunctions.empty(), "Forgot to call ``requestedFunctions()``."); -} - string ABIFunctions::tupleEncoder( TypePointers const& _givenTypes, TypePointers const& _targetTypes, bool _encodeAsLibraryTypes ) { - // stack: <$value0> <$value1> ... <$value(n-1)> <$headStart> + string functionName = string("abi_encode_tuple_"); + for (auto const& t: _givenTypes) + functionName += t->identifier() + "_"; + functionName += "_to_"; + for (auto const& t: _targetTypes) + functionName += t->identifier() + "_"; + if (_encodeAsLibraryTypes) + functionName += "_library"; - solAssert(!_givenTypes.empty(), ""); - size_t const headSize_ = headSize(_targetTypes); + return createFunction(functionName, [&]() { + solAssert(!_givenTypes.empty(), ""); - Whiskers encoder(R"( + // Note that the values are in reverse due to the difference in calling semantics. + Whiskers templ(R"( + function (headStart ) -> tail { + tail := add(headStart, ) + + } + )"); + templ("functionName", functionName); + size_t const headSize_ = headSize(_targetTypes); + templ("headSize", to_string(headSize_)); + string valueParams; + string encodeElements; + size_t headPos = 0; + size_t stackPos = 0; + for (size_t i = 0; i < _givenTypes.size(); ++i) { - let tail := add($headStart, ) - - := tail + solAssert(_givenTypes[i], ""); + solAssert(_targetTypes[i], ""); + size_t sizeOnStack = _givenTypes[i]->sizeOnStack(); + string valueNames = ""; + for (size_t j = 0; j < sizeOnStack; j++) + { + valueNames += "value" + to_string(stackPos) + ", "; + valueParams = ", value" + to_string(stackPos) + valueParams; + stackPos++; + } + bool dynamic = _targetTypes[i]->isDynamicallyEncoded(); + Whiskers elementTempl( + dynamic ? + string(R"( + mstore(add(headStart, ), sub(tail, headStart)) + tail := ( tail) + )") : + string(R"( + ( add(headStart, )) + )") + ); + elementTempl("values", valueNames); + elementTempl("pos", to_string(headPos)); + elementTempl("abiEncode", abiEncodingFunction(*_givenTypes[i], *_targetTypes[i], _encodeAsLibraryTypes, false)); + encodeElements += elementTempl.render(); + headPos += dynamic ? 0x20 : _targetTypes[i]->calldataEncodedSize(); } - )"); - encoder("headSize", to_string(headSize_)); - string encodeElements; - size_t headPos = 0; - size_t stackPos = 0; - for (size_t i = 0; i < _givenTypes.size(); ++i) - { - solAssert(_givenTypes[i], ""); - solAssert(_targetTypes[i], ""); - size_t sizeOnStack = _givenTypes[i]->sizeOnStack(); - string valueNames = ""; - for (size_t j = 0; j < sizeOnStack; j++) - valueNames += "$value" + to_string(stackPos++) + ", "; - bool dynamic = _targetTypes[i]->isDynamicallyEncoded(); - Whiskers elementTempl( - dynamic ? - string(R"( - mstore(add($headStart, ), sub(tail, $headStart)) - tail := ( tail) - )") : - string(R"( - ( add($headStart, )) - )") - ); - elementTempl("values", valueNames); - elementTempl("pos", to_string(headPos)); - elementTempl("abiEncode", abiEncodingFunction(*_givenTypes[i], *_targetTypes[i], _encodeAsLibraryTypes, false)); - encodeElements += elementTempl.render(); - headPos += dynamic ? 0x20 : _targetTypes[i]->calldataEncodedSize(); - } - solAssert(headPos == headSize_, ""); - encoder("encodeElements", encodeElements); - encoder("deepestStackElement", stackPos > 0 ? "$value0" : "$headStart"); + solAssert(headPos == headSize_, ""); + templ("valueParams", valueParams); + templ("encodeElements", encodeElements); - return encoder.render(); + return templ.render(); + }); } string ABIFunctions::requestedFunctions() diff --git a/libsolidity/codegen/ABIFunctions.h b/libsolidity/codegen/ABIFunctions.h index e43e2323a..5bbd842f8 100644 --- a/libsolidity/codegen/ABIFunctions.h +++ b/libsolidity/codegen/ABIFunctions.h @@ -44,15 +44,18 @@ using TypePointers = std::vector; /// multiple times. /// /// Make sure to include the result of ``requestedFunctions()`` to a block that -/// is visible from the code that was generated here. +/// is visible from the code that was generated here, or use named labels. class ABIFunctions { public: - ~ABIFunctions(); - - /// @returns assembly code block to ABI-encode values of @a _givenTypes residing on the stack + /// @returns name of an assembly function to ABI-encode values of @a _givenTypes /// into memory, converting the types to @a _targetTypes on the fly. - /// Assumed variables to be present: <$value0> <$value1> ... <$value(n-1)> <$headStart> + /// Parameters are: ... , i.e. + /// the layout on the stack is ... with + /// the top of the stack on the right. + /// The values represent stack slots. If a type occupies more or less than one + /// stack slot, it takes exactly that number of values. + /// Returns a pointer to the end of the area written in memory. /// Does not allocate memory (does not change the memory head pointer), but writes /// to memory starting at $headStart and an unrestricted amount after that. /// Assigns the end of encoded memory either to $value0 or (if that is not present) @@ -63,7 +66,7 @@ public: bool _encodeAsLibraryTypes = false ); - /// @returns auxiliary functions referenced from the block generated in @a tupleEncoder + /// @returns concatenation of all generated functions. std::string requestedFunctions(); private: diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index dd36bba08..7743fd3f0 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -22,6 +22,8 @@ #pragma once +#include + #include #include #include @@ -121,6 +123,7 @@ public: ); /// Generates the code for missing low-level functions, i.e. calls the generators passed above. void appendMissingLowLevelFunctions(); + ABIFunctions& abiFunctions() { return m_abiFunctions; } ModifierDefinition const& functionModifier(std::string const& _name) const; /// Returns the distance of the given local variable from the bottom of the stack (of the current function). @@ -302,6 +305,8 @@ private: size_t m_runtimeSub = -1; /// An index of low-level function labels by name. std::map m_lowLevelFunctions; + /// Container for ABI functions to be generated. + ABIFunctions m_abiFunctions; /// The queue of low-level functions to generate. std::queue>> m_lowLevelFunctionGenerationQueue; }; diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 3662478db..1e623357a 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -310,18 +310,13 @@ void CompilerUtils::abiEncode( { // stack: <$value0> <$value1> ... <$value(n-1)> <$headStart> - vector variables; - size_t numValues = sizeOnStack(_givenTypes); - for (size_t i = 0; i < numValues; ++i) - variables.push_back("$value" + to_string(i)); - variables.push_back("$headStart"); + auto ret = m_context.pushNewTag(); + moveIntoStack(sizeOnStack(_givenTypes) + 1); - ABIFunctions funs; - string routine = funs.tupleEncoder(_givenTypes, _targetTypes, _encodeAsLibraryTypes); - routine += funs.requestedFunctions(); - m_context.appendInlineAssembly("{" + routine + "}", variables, true); - // Remove everyhing except for "value0" / the final memory pointer. - popStackSlots(numValues); + string encoderName = m_context.abiFunctions().tupleEncoder(_givenTypes, _targetTypes, _encodeAsLibraryTypes); + m_context.appendJumpTo(m_context.namedTag(encoderName)); + m_context.adjustStackOffset(-int(sizeOnStack(_givenTypes)) - 1); + m_context << ret.tag(); } void CompilerUtils::zeroInitialiseMemoryArray(ArrayType const& _type) diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 24d3d959f..514963681 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -122,6 +122,7 @@ void ContractCompiler::appendCallValueCheck() void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _contract) { + CompilerContext::LocationSetter locationSetter(m_context, _contract); // Determine the arguments that are used for the base constructors. std::vector const& bases = _contract.annotation().linearizedBaseContracts; for (ContractDefinition const* contract: bases) @@ -174,6 +175,7 @@ size_t ContractCompiler::packIntoContractCreator(ContractDefinition const& _cont appendMissingFunctions(); m_runtimeCompiler->appendMissingFunctions(); + CompilerContext::LocationSetter locationSetter(m_context, _contract); m_context << deployRoutine; solAssert(m_context.runtimeSub() != size_t(-1), "Runtime sub not registered"); @@ -892,6 +894,9 @@ void ContractCompiler::appendMissingFunctions() solAssert(m_context.nextFunctionToCompile() != function, "Compiled the wrong function?"); } m_context.appendMissingLowLevelFunctions(); + string abiFunctions = m_context.abiFunctions().requestedFunctions(); + if (!abiFunctions.empty()) + m_context.appendInlineAssembly("{" + move(abiFunctions) + "}", {}, true); } void ContractCompiler::appendModifierOrFunctionCode() From 2e72bd163a149183c119ca9664b98b0c5473da41 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 24 Jan 2017 12:44:49 +0100 Subject: [PATCH 121/162] Allow structs as part of function interfaces. --- libsolidity/ast/Types.cpp | 2 +- libsolidity/interface/ABI.cpp | 58 ++++++-- libsolidity/interface/ABI.h | 4 + test/libsolidity/SolidityABIJSON.cpp | 136 +++++++++++++++++- test/libsolidity/SolidityEndToEndTest.cpp | 41 ++++++ .../SolidityNameAndTypeResolution.cpp | 50 +++++++ 6 files changed, 280 insertions(+), 11 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 705d0f7f2..3a93b74e2 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -1770,7 +1770,7 @@ TypePointer StructType::interfaceType(bool _inLibrary) const if (_inLibrary && location() == DataLocation::Storage) return shared_from_this(); else - return TypePointer(); + return copyForLocation(DataLocation::Memory, true); } TypePointer StructType::copyForLocation(DataLocation _location, bool _isPointer) const diff --git a/libsolidity/interface/ABI.cpp b/libsolidity/interface/ABI.cpp index 49df843d8..7c7496cdd 100644 --- a/libsolidity/interface/ABI.cpp +++ b/libsolidity/interface/ABI.cpp @@ -86,12 +86,12 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef) Json::Value params(Json::arrayValue); for (auto const& p: it->parameters()) { - solAssert(!!p->annotation().type->interfaceType(false), ""); + auto type = p->annotation().type->interfaceType(false); + solAssert(type, ""); Json::Value input; - input["name"] = p->name(); - input["type"] = p->annotation().type->interfaceType(false)->canonicalName(false); - input["indexed"] = p->isIndexed(); - params.append(input); + auto param = formatType(p->name(), *type, false); + param["indexed"] = p->isIndexed(); + params.append(param); } event["inputs"] = params; abi.append(event); @@ -111,10 +111,50 @@ Json::Value ABI::formatTypeList( for (unsigned i = 0; i < _names.size(); ++i) { solAssert(_types[i], ""); - Json::Value param; - param["name"] = _names[i]; - param["type"] = _types[i]->canonicalName(_forLibrary); - params.append(param); + params.append(formatType(_names[i], *_types[i], _forLibrary)); } return params; } + +Json::Value ABI::formatType(string const& _name, Type const& _type, bool _forLibrary) +{ + Json::Value ret; + ret["name"] = _name; + if (_type.isValueType() || (_forLibrary && _type.dataStoredIn(DataLocation::Storage))) + ret["type"] = _type.canonicalName(_forLibrary); + else if (ArrayType const* arrayType = dynamic_cast(&_type)) + { + if (arrayType->isByteArray()) + ret["type"] = _type.canonicalName(_forLibrary); + else + { + string suffix; + if (arrayType->isDynamicallySized()) + suffix = "[]"; + else + suffix = string("[") + arrayType->length().str() + "]"; + solAssert(arrayType->baseType(), ""); + Json::Value subtype = formatType("", *arrayType->baseType(), _forLibrary); + if (subtype["type"].isString() && !subtype.isMember("subtype")) + ret["type"] = subtype["type"].asString() + suffix; + else + { + ret["type"] = suffix; + solAssert(!subtype.isMember("subtype"), ""); + ret["subtype"] = subtype["type"]; + } + } + } + else if (StructType const* structType = dynamic_cast(&_type)) + { + ret["type"] = Json::arrayValue; + for (auto const& member: structType->members(nullptr)) + { + solAssert(member.type, ""); + ret["type"].append(formatType(member.name, *member.type, _forLibrary)); + } + } + else + solAssert(false, "Invalid type."); + return ret; +} diff --git a/libsolidity/interface/ABI.h b/libsolidity/interface/ABI.h index 95b162a95..7e42909b6 100644 --- a/libsolidity/interface/ABI.h +++ b/libsolidity/interface/ABI.h @@ -50,6 +50,10 @@ private: std::vector const& _types, bool _forLibrary ); + /// @returns a Json object with "name", "type" and potentially "subtype" keys, according + /// to the ABI specification. + /// If it is possible to express the type as a single string, it is allowed to return a single string. + static Json::Value formatType(std::string const& _name, Type const& _type, bool _forLibrary); }; } diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp index 4b9223de3..7f03285d3 100644 --- a/test/libsolidity/SolidityABIJSON.cpp +++ b/test/libsolidity/SolidityABIJSON.cpp @@ -48,7 +48,7 @@ public: Json::Value generatedInterface = m_compilerStack.contractABI(""); Json::Value expectedInterface; - m_reader.parse(_expectedInterfaceString, expectedInterface); + BOOST_REQUIRE(m_reader.parse(_expectedInterfaceString, expectedInterface)); BOOST_CHECK_MESSAGE( expectedInterface == generatedInterface, "Expected:\n" << expectedInterface.toStyledString() << @@ -939,6 +939,140 @@ BOOST_AUTO_TEST_CASE(function_type) checkInterface(sourceCode, interface); } +BOOST_AUTO_TEST_CASE(return_structs) +{ + char const* text = R"( + contract C { + struct S { uint a; T[] sub; } + struct T { uint[2] x; } + function f() returns (uint x, S s) { + } + } + )"; + char const* interface = R"( + [ + { + "constant" : false, + "payable": false, + "inputs": [], + "name": "f", + "outputs": [{ + "name": "x", + "type": "uint256" + }, { + "name": "s", + "type": [{ + "name": "a", + "type": "uint256" + }, { + "name": "sub", + "subtype": [{ + "name": "x", + "type": "uint256[2]" + }], + "type": "[]" + }] + }], + "type" : "function" + } + ] + )"; + checkInterface(text, interface); +} + +BOOST_AUTO_TEST_CASE(event_structs) +{ + char const* text = R"( + contract C { + struct S { uint a; T[] sub; bytes b; } + struct T { uint[2] x; } + event E(T t, S s); + } + )"; + char const* interface = R"( + [{ + "anonymous" : false, + "inputs" : [{ + "indexed" : false, + "name" : "t", + "type" : [{ + "name" : "x", + "type" : "uint256[2]" + }] + }, { + "indexed" : false, + "name" : "s", + "type" : [{ + "name" : "a", + "type" : "uint256" + }, { + "name" : "sub", + "subtype" : [{ + "name" : "x", + "type" : "uint256[2]" + }], + "type" : "[]" + }, { + "name" : "b", + "type" : "bytes" + }] + }], + "name" : "E", + "type" : "event" + }] + )"; + checkInterface(text, interface); +} + +BOOST_AUTO_TEST_CASE(structs_in_libraries) +{ + char const* text = R"( + library L { + struct S { uint a; T[] sub; bytes b; } + struct T { uint[2] x; } + function f(L.S storage s) {} + function g(L.S s) {} + } + )"; + char const* interface = R"( + [{ + "constant" : false, + "inputs" : [{ + "name" : "s", + "type" : [{ + "name" : "a", + "type" : "uint256" + }, { + "name" : "sub", + "subtype" : [{ + "name" : "x", + "type" : "uint256[2]" + }], + "type" : "[]" + }, { + "name" : "b", + "type" : "bytes" + }] + }], + "name" : "g", + "outputs" : [], + "payable" : false, + "type" : "function" + }, { + "constant" : false, + "inputs" : [{ + "name" : "s", + "type" : "L.S storage" + }], + "name" : "f", + "outputs" : [], + "payable" : false, + "type" : "function" + }] + )"; + checkInterface(text, interface); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index ea924fcb9..6ea026738 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -9682,6 +9682,47 @@ BOOST_AUTO_TEST_CASE(contracts_separated_with_comment) compileAndRun(sourceCode, 0, "C2"); } +BOOST_AUTO_TEST_CASE(return_structs) +{ + char const* sourceCode = R"( + contract C { + struct S { uint a; T[] sub; } + struct T { uint[2] x; } + function f() returns (uint x, S s) { + x = 7; + s.a = 8; + s.sub = new S[](3); + s.sub[0][0] = 9; + s.sub[1][0] = 10; + s.sub[2][1] = 11; + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + +// Will calculate the exact encoding later. +// BOOST_CHECK(callContractFunction("f()") == encodeArgs( +// u256(7), u256(0x40), +// u256(8), u256(0x40), +// u256(3), +// // s.sub[0] +// u256(9), u256(0xc0), +// // s.sub[1] +// u256(10), u256(0xe0), +// // s.sub[2] +// u256(11), u256(0x100), +// // s.sub[0].sub +// u256(2) +// // s.sub[0].sub[0].a +// u256(8), +// u256() +// // s.sub[1].sub +// u256(0) +// // s.sub[2].sub +// u256(2) +// )); +} + BOOST_AUTO_TEST_CASE(include_creation_bytecode_only_once) { char const* sourceCode = R"( diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 8c271fe10..037bc9a0c 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -5396,6 +5396,56 @@ BOOST_AUTO_TEST_CASE(constructible_internal_constructor) success(text); } +BOOST_AUTO_TEST_CASE(return_structs) +{ + char const* text = R"( + contract C { + struct S { uint a; T[] sub; } + struct T { uint[] x; } + function f() returns (uint x, S s) { + } + } + )"; + success(text); +} + +BOOST_AUTO_TEST_CASE(return_recursive_structs) +{ + char const* text = R"( + contract C { + struct S { uint a; S[] sub; } + function f() returns (uint x, S s) { + } + } + )"; + success(text); +} + +BOOST_AUTO_TEST_CASE(return_recursive_structs2) +{ + char const* text = R"( + contract C { + struct S { uint a; S[2] sub; } + function f() returns (uint x, S s) { + } + } + )"; + CHECK_ERROR(text, TypeError, "recursive data types in external functions."); +} + +BOOST_AUTO_TEST_CASE(return_recursive_structs3) +{ + char const* text = R"( + contract C { + struct S { uint a; S sub; } + struct T { S s; } + function f() returns (uint x, T t) { + } + } + )"; + CHECK_ERROR(text, TypeError, "recursive data types in external functions."); +} + BOOST_AUTO_TEST_CASE(address_checksum_type_deduction) { char const* text = R"( From 59ea19b3b957949fc53bfb5dc4e199d2196f8d18 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 1 Jun 2017 11:48:38 +0200 Subject: [PATCH 122/162] Check for recursive structs. --- libsolidity/analysis/TypeChecker.cpp | 6 +++--- libsolidity/ast/Types.cpp | 27 ++++++++++++++++++++++++++- libsolidity/ast/Types.h | 4 ++++ 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 26529c229..fe4207a3a 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -546,7 +546,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function) if (!type(*var)->canLiveOutsideStorage()) m_errorReporter.typeError(var->location(), "Type is required to live outside storage."); if (_function.visibility() >= FunctionDefinition::Visibility::Public && !(type(*var)->interfaceType(isLibraryFunction))) - m_errorReporter.fatalTypeError(var->location(), "Internal type is not allowed for public or external functions."); + m_errorReporter.fatalTypeError(var->location(), "Internal or recursive type is not allowed for public or external functions."); var->accept(*this); } @@ -641,7 +641,7 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) _variable.visibility() >= VariableDeclaration::Visibility::Public && !FunctionType(_variable).interfaceFunctionType() ) - m_errorReporter.typeError(_variable.location(), "Internal type is not allowed for public state variables."); + m_errorReporter.typeError(_variable.location(), "Internal or recursive type is not allowed for public state variables."); if (varType->category() == Type::Category::Array) if (auto arrayType = dynamic_cast(varType.get())) @@ -728,7 +728,7 @@ bool TypeChecker::visit(EventDefinition const& _eventDef) if (!type(*var)->canLiveOutsideStorage()) m_errorReporter.typeError(var->location(), "Type is required to live outside storage."); if (!type(*var)->interfaceType(false)) - m_errorReporter.typeError(var->location(), "Internal type is not allowed as event parameter type."); + m_errorReporter.typeError(var->location(), "Internal or recursive type is not allowed as event parameter type."); } if (_eventDef.isAnonymous() && numIndexed > 4) m_errorReporter.typeError(_eventDef.location(), "More than 4 indexed arguments for anonymous event."); diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 3a93b74e2..443164030 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -1769,8 +1769,10 @@ TypePointer StructType::interfaceType(bool _inLibrary) const { if (_inLibrary && location() == DataLocation::Storage) return shared_from_this(); - else + else if (!recursive()) return copyForLocation(DataLocation::Memory, true); + else + return TypePointer(); } TypePointer StructType::copyForLocation(DataLocation _location, bool _isPointer) const @@ -1836,6 +1838,29 @@ set StructType::membersMissingInMemory() const return missing; } +bool StructType::recursive() const +{ + set structsSeen; + function check = [&](StructType const* t) -> bool + { + StructDefinition const* str = &t->structDefinition(); + if (structsSeen.count(str)) + return true; + structsSeen.insert(str); + for (ASTPointer const& variable: str->members()) + { + Type const* memberType = variable->annotation().type.get(); + while (dynamic_cast(memberType)) + memberType = dynamic_cast(memberType)->baseType().get(); + if (StructType const* innerStruct = dynamic_cast(memberType)) + if (check(innerStruct)) + return true; + } + return false; + }; + return check(this); +} + TypePointer EnumType::unaryOperatorResult(Token::Value _operator) const { return _operator == Token::Delete ? make_shared() : TypePointer(); diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index d4d6da690..e6d3a7b1c 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -758,6 +758,10 @@ public: /// @returns the set of all members that are removed in the memory version (typically mappings). std::set membersMissingInMemory() const; + /// @returns true if the same struct is used recursively in one of its members. Only + /// analyses the "memory" representation, i.e. mappings are ignored in all structs. + bool recursive() const; + private: StructDefinition const& m_struct; }; From 22f85d5af371bb621f8735957b7519d14e3b40c9 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 1 Jun 2017 12:26:13 +0200 Subject: [PATCH 123/162] Update tests and error messages. --- libsolidity/codegen/ContractCompiler.cpp | 2 +- test/libsolidity/SolidityEndToEndTest.cpp | 8 ++++---- .../SolidityNameAndTypeResolution.cpp | 18 +++++++++--------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 514963681..92782b8d7 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -333,7 +333,7 @@ void ContractCompiler::appendCalldataUnpacker(TypePointers const& _typeParameter { // stack: v1 v2 ... v(k-1) base_offset current_offset TypePointer type = parameterType->decodingType(); - solAssert(type, "No decoding type found."); + solUnimplementedAssert(type, "No decoding type found."); if (type->category() == Type::Category::Array) { auto const& arrayType = dynamic_cast(*type); diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 6ea026738..3b657e39d 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -9691,10 +9691,10 @@ BOOST_AUTO_TEST_CASE(return_structs) function f() returns (uint x, S s) { x = 7; s.a = 8; - s.sub = new S[](3); - s.sub[0][0] = 9; - s.sub[1][0] = 10; - s.sub[2][1] = 11; + s.sub = new T[](3); + s.sub[0].x[0] = 9; + s.sub[1].x[0] = 10; + s.sub[2].x[1] = 11; } } )"; diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 037bc9a0c..162190bf7 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -3283,7 +3283,7 @@ BOOST_AUTO_TEST_CASE(library_memory_struct) function f() public returns (S ) {} } )"; - CHECK_ERROR(text, TypeError, "Internal type is not allowed for public or external functions."); + CHECK_SUCCESS(text); } BOOST_AUTO_TEST_CASE(using_for_arbitrary_mismatch) @@ -5402,7 +5402,7 @@ BOOST_AUTO_TEST_CASE(return_structs) contract C { struct S { uint a; T[] sub; } struct T { uint[] x; } - function f() returns (uint x, S s) { + function f() returns (uint, S) { } } )"; @@ -5414,36 +5414,36 @@ BOOST_AUTO_TEST_CASE(return_recursive_structs) char const* text = R"( contract C { struct S { uint a; S[] sub; } - function f() returns (uint x, S s) { + function f() returns (uint, S) { } } )"; - success(text); + CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions."); } BOOST_AUTO_TEST_CASE(return_recursive_structs2) { char const* text = R"( contract C { - struct S { uint a; S[2] sub; } - function f() returns (uint x, S s) { + struct S { uint a; S[2][] sub; } + function f() returns (uint, S) { } } )"; - CHECK_ERROR(text, TypeError, "recursive data types in external functions."); + CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions."); } BOOST_AUTO_TEST_CASE(return_recursive_structs3) { char const* text = R"( contract C { - struct S { uint a; S sub; } + struct S { uint a; S[][][] sub; } struct T { S s; } function f() returns (uint x, T t) { } } )"; - CHECK_ERROR(text, TypeError, "recursive data types in external functions."); + CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions."); } BOOST_AUTO_TEST_CASE(address_checksum_type_deduction) From 080be885f835462f0074b05c617fa670402b067a Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 8 Jun 2017 11:14:58 +0200 Subject: [PATCH 124/162] Function signatures containing structs. --- libsolidity/ast/Types.cpp | 90 +++++++++++++------ libsolidity/ast/Types.h | 28 ++++-- libsolidity/interface/ABI.cpp | 5 +- .../SolidityNameAndTypeResolution.cpp | 20 +++-- 4 files changed, 98 insertions(+), 45 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 443164030..9fdfe6320 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -1470,7 +1471,7 @@ string ArrayType::toString(bool _short) const return ret; } -string ArrayType::canonicalName(bool _addDataLocation) const +string ArrayType::canonicalName() const { string ret; if (isString()) @@ -1479,16 +1480,29 @@ string ArrayType::canonicalName(bool _addDataLocation) const ret = "bytes"; else { - ret = baseType()->canonicalName(false) + "["; + ret = baseType()->canonicalName() + "["; if (!isDynamicallySized()) ret += length().str(); ret += "]"; } - if (_addDataLocation && location() == DataLocation::Storage) - ret += " storage"; return ret; } +string ArrayType::signatureInExternalFunction(bool _structsByName) const +{ + if (isByteArray()) + return canonicalName(); + else + { + solAssert(baseType(), ""); + return + baseType()->signatureInExternalFunction(_structsByName) + + "[" + + (isDynamicallySized() ? "" : length().str()) + + "]"; + } +} + MemberList::MemberMap ArrayType::nativeMembers(ContractDefinition const*) const { MemberList::MemberMap members; @@ -1597,7 +1611,7 @@ string ContractType::toString(bool) const m_contract.name(); } -string ContractType::canonicalName(bool) const +string ContractType::canonicalName() const { return m_contract.annotation().canonicalName; } @@ -1727,9 +1741,8 @@ bool StructType::isDynamicallyEncoded() const u256 StructType::memorySize() const { u256 size; - for (auto const& member: members(nullptr)) - if (member.type->canLiveOutsideStorage()) - size += member.type->memoryHeadSize(); + for (auto const& t: memoryMemberTypes()) + size += t->memoryHeadSize(); return size; } @@ -1782,12 +1795,25 @@ TypePointer StructType::copyForLocation(DataLocation _location, bool _isPointer) return copy; } -string StructType::canonicalName(bool _addDataLocation) const +string StructType::signatureInExternalFunction(bool _structsByName) const { - string ret = m_struct.annotation().canonicalName; - if (_addDataLocation && location() == DataLocation::Storage) - ret += " storage"; - return ret; + if (_structsByName) + return canonicalName(); + else + { + TypePointers memberTypes = memoryMemberTypes(); + auto memberTypeStrings = memberTypes | boost::adaptors::transformed([&](TypePointer _t) -> string + { + solAssert(_t, "Parameter should have external type."); + return _t->signatureInExternalFunction(_structsByName); + }); + return "(" + boost::algorithm::join(memberTypeStrings, ",") + ")"; + } +} + +string StructType::canonicalName() const +{ + return m_struct.annotation().canonicalName; } FunctionTypePointer StructType::constructorType() const @@ -1829,6 +1855,15 @@ u256 StructType::memoryOffsetOfMember(string const& _name) const return 0; } +TypePointers StructType::memoryMemberTypes() const +{ + TypePointers types; + for (ASTPointer const& variable: m_struct.members()) + if (variable->annotation().type->canLiveOutsideStorage()) + types.push_back(variable->annotation().type); + return types; +} + set StructType::membersMissingInMemory() const { set missing; @@ -1893,7 +1928,7 @@ string EnumType::toString(bool) const return string("enum ") + m_enum.annotation().canonicalName; } -string EnumType::canonicalName(bool) const +string EnumType::canonicalName() const { return m_enum.annotation().canonicalName; } @@ -2320,7 +2355,7 @@ TypePointer FunctionType::binaryOperatorResult(Token::Value _operator, TypePoint return TypePointer(); } -string FunctionType::canonicalName(bool) const +string FunctionType::canonicalName() const { solAssert(m_kind == Kind::External, ""); return "function"; @@ -2580,20 +2615,19 @@ string FunctionType::externalSignature() const solAssert(m_declaration != nullptr, "External signature of function needs declaration"); solAssert(!m_declaration->name().empty(), "Fallback function has no signature."); - bool _inLibrary = dynamic_cast(*m_declaration->scope()).isLibrary(); - - string ret = m_declaration->name() + "("; - + bool const inLibrary = dynamic_cast(*m_declaration->scope()).isLibrary(); FunctionTypePointer external = interfaceFunctionType(); solAssert(!!external, "External function type requested."); - TypePointers externalParameterTypes = external->parameterTypes(); - for (auto it = externalParameterTypes.cbegin(); it != externalParameterTypes.cend(); ++it) + auto parameterTypes = external->parameterTypes(); + auto typeStrings = parameterTypes | boost::adaptors::transformed([&](TypePointer _t) -> string { - solAssert(!!(*it), "Parameter should have external type"); - ret += (*it)->canonicalName(_inLibrary) + (it + 1 == externalParameterTypes.cend() ? "" : ","); - } - - return ret + ")"; + solAssert(_t, "Parameter should have external type."); + string typeName = _t->signatureInExternalFunction(inLibrary); + if (inLibrary && _t->dataStoredIn(DataLocation::Storage)) + typeName += " storage"; + return typeName; + }); + return m_declaration->name() + "(" + boost::algorithm::join(typeStrings, ",") + ")"; } u256 FunctionType::externalIdentifier() const @@ -2724,9 +2758,9 @@ string MappingType::toString(bool _short) const return "mapping(" + keyType()->toString(_short) + " => " + valueType()->toString(_short) + ")"; } -string MappingType::canonicalName(bool) const +string MappingType::canonicalName() const { - return "mapping(" + keyType()->canonicalName(false) + " => " + valueType()->canonicalName(false) + ")"; + return "mapping(" + keyType()->canonicalName() + " => " + valueType()->canonicalName() + ")"; } string TypeType::identifier() const diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index e6d3a7b1c..0713f5276 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -245,9 +245,15 @@ public: virtual std::string toString(bool _short) const = 0; std::string toString() const { return toString(false); } - /// @returns the canonical name of this type for use in function signatures. - /// @param _addDataLocation if true, includes data location for reference types if it is "storage". - virtual std::string canonicalName(bool /*_addDataLocation*/) const { return toString(true); } + /// @returns the canonical name of this type for use in library function signatures. + virtual std::string canonicalName() const { return toString(true); } + /// @returns the signature of this type in external functions, i.e. `uint256` for integers + /// or `(uint256, bytes8)[2]` for an array of structs. If @a _structsByName, + /// structs are given by canonical name like `ContractName.StructName[2]`. + virtual std::string signatureInExternalFunction(bool /*_structsByName*/) const + { + return canonicalName(); + } virtual u256 literalValue(Literal const*) const { solAssert(false, "Literal value requested for type without literals."); @@ -619,7 +625,8 @@ public: virtual bool canLiveOutsideStorage() const override { return m_baseType->canLiveOutsideStorage(); } virtual unsigned sizeOnStack() const override; virtual std::string toString(bool _short) const override; - virtual std::string canonicalName(bool _addDataLocation) const override; + virtual std::string canonicalName() const override; + virtual std::string signatureInExternalFunction(bool _structsByName) const override; virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override; virtual TypePointer encodingType() const override; virtual TypePointer decodingType() const override; @@ -677,7 +684,7 @@ public: virtual unsigned sizeOnStack() const override { return m_super ? 0 : 1; } virtual bool isValueType() const override { return true; } virtual std::string toString(bool _short) const override; - virtual std::string canonicalName(bool _addDataLocation) const override; + virtual std::string canonicalName() const override; virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override; virtual TypePointer encodingType() const override @@ -744,7 +751,8 @@ public: TypePointer copyForLocation(DataLocation _location, bool _isPointer) const override; - virtual std::string canonicalName(bool _addDataLocation) const override; + virtual std::string canonicalName() const override; + virtual std::string signatureInExternalFunction(bool _structsByName) const override; /// @returns a function that peforms the type conversion between a list of struct members /// and a memory struct of this type. @@ -755,6 +763,8 @@ public: StructDefinition const& structDefinition() const { return m_struct; } + /// @returns the vector of types of members available in memory. + TypePointers memoryMemberTypes() const; /// @returns the set of all members that are removed in the memory version (typically mappings). std::set membersMissingInMemory() const; @@ -784,7 +794,7 @@ public: virtual unsigned storageBytes() const override; virtual bool canLiveOutsideStorage() const override { return true; } virtual std::string toString(bool _short) const override; - virtual std::string canonicalName(bool _addDataLocation) const override; + virtual std::string canonicalName() const override; virtual bool isValueType() const override { return true; } virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; @@ -955,7 +965,7 @@ public: virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override; - virtual std::string canonicalName(bool /*_addDataLocation*/) const override; + virtual std::string canonicalName() const override; virtual std::string toString(bool _short) const override; virtual unsigned calldataEncodedSize(bool _padded) const override; virtual bool canBeStored() const override { return m_kind == Kind::Internal || m_kind == Kind::External; } @@ -1057,7 +1067,7 @@ public: virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; virtual std::string toString(bool _short) const override; - virtual std::string canonicalName(bool _addDataLocation) const override; + virtual std::string canonicalName() const override; virtual bool canLiveOutsideStorage() const override { return false; } virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } virtual TypePointer encodingType() const override diff --git a/libsolidity/interface/ABI.cpp b/libsolidity/interface/ABI.cpp index 7c7496cdd..c04de57ec 100644 --- a/libsolidity/interface/ABI.cpp +++ b/libsolidity/interface/ABI.cpp @@ -120,12 +120,13 @@ Json::Value ABI::formatType(string const& _name, Type const& _type, bool _forLib { Json::Value ret; ret["name"] = _name; + string suffix = (_forLibrary && _type.dataStoredIn(DataLocation::Storage)) ? " storage" : ""; if (_type.isValueType() || (_forLibrary && _type.dataStoredIn(DataLocation::Storage))) - ret["type"] = _type.canonicalName(_forLibrary); + ret["type"] = _type.canonicalName() + suffix; else if (ArrayType const* arrayType = dynamic_cast(&_type)) { if (arrayType->isByteArray()) - ret["type"] = _type.canonicalName(_forLibrary); + ret["type"] = _type.canonicalName() + suffix; else { string suffix; diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 162190bf7..3cea8d603 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -599,6 +599,14 @@ BOOST_AUTO_TEST_CASE(enum_external_type) } } +BOOST_AUTO_TEST_CASE(external_structs) +{ + BOOST_FAIL("This should test external structs"); + // More test ideas: + // external function type as part of a struct and array + // external function type taking structs and arrays... +} + BOOST_AUTO_TEST_CASE(function_external_call_allowed_conversion) { char const* text = R"( @@ -980,24 +988,24 @@ BOOST_AUTO_TEST_CASE(state_variable_accessors) FunctionTypePointer function = retrieveFunctionBySignature(*contract, "foo()"); BOOST_REQUIRE(function && function->hasDeclaration()); auto returnParams = function->returnParameterTypes(); - BOOST_CHECK_EQUAL(returnParams.at(0)->canonicalName(false), "uint256"); + BOOST_CHECK_EQUAL(returnParams.at(0)->canonicalName(), "uint256"); BOOST_CHECK(function->stateMutability() == StateMutability::View); function = retrieveFunctionBySignature(*contract, "map(uint256)"); BOOST_REQUIRE(function && function->hasDeclaration()); auto params = function->parameterTypes(); - BOOST_CHECK_EQUAL(params.at(0)->canonicalName(false), "uint256"); + BOOST_CHECK_EQUAL(params.at(0)->canonicalName(), "uint256"); returnParams = function->returnParameterTypes(); - BOOST_CHECK_EQUAL(returnParams.at(0)->canonicalName(false), "bytes4"); + BOOST_CHECK_EQUAL(returnParams.at(0)->canonicalName(), "bytes4"); BOOST_CHECK(function->stateMutability() == StateMutability::View); function = retrieveFunctionBySignature(*contract, "multiple_map(uint256,uint256)"); BOOST_REQUIRE(function && function->hasDeclaration()); params = function->parameterTypes(); - BOOST_CHECK_EQUAL(params.at(0)->canonicalName(false), "uint256"); - BOOST_CHECK_EQUAL(params.at(1)->canonicalName(false), "uint256"); + BOOST_CHECK_EQUAL(params.at(0)->canonicalName(), "uint256"); + BOOST_CHECK_EQUAL(params.at(1)->canonicalName(), "uint256"); returnParams = function->returnParameterTypes(); - BOOST_CHECK_EQUAL(returnParams.at(0)->canonicalName(false), "bytes4"); + BOOST_CHECK_EQUAL(returnParams.at(0)->canonicalName(), "bytes4"); BOOST_CHECK(function->stateMutability() == StateMutability::View); } From 823e67bf4014d20c6c83d509264e1464d9578f99 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 9 Jun 2017 18:28:13 +0200 Subject: [PATCH 125/162] Tests for external signatures. --- .../SolidityNameAndTypeResolution.cpp | 32 ++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 3cea8d603..9dfbea210 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -601,12 +601,36 @@ BOOST_AUTO_TEST_CASE(enum_external_type) BOOST_AUTO_TEST_CASE(external_structs) { - BOOST_FAIL("This should test external structs"); - // More test ideas: - // external function type as part of a struct and array - // external function type taking structs and arrays... + ASTPointer sourceUnit; + char const* text = R"( + contract Test { + enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } + struct Empty {} + struct Nested { X[2][] a; mapping(uint => uint) m; uint y; } + struct X { bytes32 x; Test t; Empty[] e; } + function f(ActionChoices, uint, Empty) external {} + function g(Nested) external {} + function h(function(Nested) external returns (uint)[]) external {} + function i(Nested[]) external {} + } + )"; + ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseAndAnalyse(text), "Parsing and name Resolving failed"); + for (ASTPointer const& node: sourceUnit->nodes()) + if (ContractDefinition* contract = dynamic_cast(node.get())) + { + auto functions = contract->definedFunctions(); + BOOST_REQUIRE(!functions.empty()); + for (auto const& f: functions) + cout << f->externalSignature() << endl; + BOOST_CHECK_EQUAL("f(uint8,uint256,())", functions[0]->externalSignature()); + BOOST_CHECK_EQUAL("g(((bytes32,address,()[])[2][],uint256))", functions[1]->externalSignature()); + BOOST_CHECK_EQUAL("h(function[])", functions[2]->externalSignature()); + BOOST_CHECK_EQUAL("i(((bytes32,address,()[])[2][],uint256)[])", functions[3]->externalSignature()); + } } +// TODO: Add a test that checks the signature of library functions taking structs + BOOST_AUTO_TEST_CASE(function_external_call_allowed_conversion) { char const* text = R"( From 36a90289e65b06c54326a1c254baa5fa6029f766 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 13 Jun 2017 10:51:49 +0200 Subject: [PATCH 126/162] Fix interface type conversion internal to structs. --- libsolidity/ast/Types.cpp | 6 +++- libsolidity/interface/ABI.cpp | 4 ++- test/libsolidity/SolidityABIJSON.cpp | 36 +++++++++++++++++++ .../SolidityNameAndTypeResolution.cpp | 6 ++-- 4 files changed, 46 insertions(+), 6 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 9fdfe6320..14b30df82 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -1783,6 +1783,8 @@ TypePointer StructType::interfaceType(bool _inLibrary) const if (_inLibrary && location() == DataLocation::Storage) return shared_from_this(); else if (!recursive()) + // TODO this might not be enough, we have to convert all members to + // their interfaceType return copyForLocation(DataLocation::Memory, true); else return TypePointer(); @@ -1805,7 +1807,9 @@ string StructType::signatureInExternalFunction(bool _structsByName) const auto memberTypeStrings = memberTypes | boost::adaptors::transformed([&](TypePointer _t) -> string { solAssert(_t, "Parameter should have external type."); - return _t->signatureInExternalFunction(_structsByName); + auto t = _t->interfaceType(_structsByName); + solAssert(t, ""); + return t->signatureInExternalFunction(_structsByName); }); return "(" + boost::algorithm::join(memberTypeStrings, ",") + ")"; } diff --git a/libsolidity/interface/ABI.cpp b/libsolidity/interface/ABI.cpp index c04de57ec..0e28d0102 100644 --- a/libsolidity/interface/ABI.cpp +++ b/libsolidity/interface/ABI.cpp @@ -152,7 +152,9 @@ Json::Value ABI::formatType(string const& _name, Type const& _type, bool _forLib for (auto const& member: structType->members(nullptr)) { solAssert(member.type, ""); - ret["type"].append(formatType(member.name, *member.type, _forLibrary)); + auto t = member.type->interfaceType(_forLibrary); + solAssert(t, ""); + ret["type"].append(formatType(member.name, *t, _forLibrary)); } } else diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp index 7f03285d3..3de8b732c 100644 --- a/test/libsolidity/SolidityABIJSON.cpp +++ b/test/libsolidity/SolidityABIJSON.cpp @@ -980,6 +980,42 @@ BOOST_AUTO_TEST_CASE(return_structs) checkInterface(text, interface); } +BOOST_AUTO_TEST_CASE(return_structs_with_contracts) +{ + char const* text = R"( + contract C { + struct S { C[] x; C y; } + function f() returns (S s, C c) { + } + } + )"; + char const* interface = R"( + [ + { + "constant" : false, + "payable": false, + "inputs": [], + "name": "f", + "outputs" : [{ + "name" : "s", + "type" : [{ + "name" : "x", + "type" : "address[]" + }, { + "name" : "y", + "type" : "address" + }] + }, { + "name" : "c", + "type" : "address" + }], + "type" : "function" + } + ] + )"; + checkInterface(text, interface); +} + BOOST_AUTO_TEST_CASE(event_structs) { char const* text = R"( diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 9dfbea210..dcab8fb00 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -609,7 +609,7 @@ BOOST_AUTO_TEST_CASE(external_structs) struct Nested { X[2][] a; mapping(uint => uint) m; uint y; } struct X { bytes32 x; Test t; Empty[] e; } function f(ActionChoices, uint, Empty) external {} - function g(Nested) external {} + function g(Test, Nested) external {} function h(function(Nested) external returns (uint)[]) external {} function i(Nested[]) external {} } @@ -620,10 +620,8 @@ BOOST_AUTO_TEST_CASE(external_structs) { auto functions = contract->definedFunctions(); BOOST_REQUIRE(!functions.empty()); - for (auto const& f: functions) - cout << f->externalSignature() << endl; BOOST_CHECK_EQUAL("f(uint8,uint256,())", functions[0]->externalSignature()); - BOOST_CHECK_EQUAL("g(((bytes32,address,()[])[2][],uint256))", functions[1]->externalSignature()); + BOOST_CHECK_EQUAL("g(address,((bytes32,address,()[])[2][],uint256))", functions[1]->externalSignature()); BOOST_CHECK_EQUAL("h(function[])", functions[2]->externalSignature()); BOOST_CHECK_EQUAL("i(((bytes32,address,()[])[2][],uint256)[])", functions[3]->externalSignature()); } From 7e1b9c16528b08a8924f85c57f3b08b6cec70584 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 30 Jun 2017 10:22:35 +0200 Subject: [PATCH 127/162] Structure type json using "components". --- libsolidity/interface/ABI.cpp | 16 +- test/libsolidity/SolidityABIJSON.cpp | 249 +++++++++++++++------------ 2 files changed, 151 insertions(+), 114 deletions(-) diff --git a/libsolidity/interface/ABI.cpp b/libsolidity/interface/ABI.cpp index 0e28d0102..9af7cdc37 100644 --- a/libsolidity/interface/ABI.cpp +++ b/libsolidity/interface/ABI.cpp @@ -136,25 +136,25 @@ Json::Value ABI::formatType(string const& _name, Type const& _type, bool _forLib suffix = string("[") + arrayType->length().str() + "]"; solAssert(arrayType->baseType(), ""); Json::Value subtype = formatType("", *arrayType->baseType(), _forLibrary); - if (subtype["type"].isString() && !subtype.isMember("subtype")) - ret["type"] = subtype["type"].asString() + suffix; - else + if (subtype.isMember("components")) { - ret["type"] = suffix; - solAssert(!subtype.isMember("subtype"), ""); - ret["subtype"] = subtype["type"]; + ret["type"] = subtype["type"].asString() + suffix; + ret["components"] = subtype["components"]; } + else + ret["type"] = subtype["type"].asString() + suffix; } } else if (StructType const* structType = dynamic_cast(&_type)) { - ret["type"] = Json::arrayValue; + ret["type"] = string(); + ret["components"] = Json::arrayValue; for (auto const& member: structType->members(nullptr)) { solAssert(member.type, ""); auto t = member.type->interfaceType(_forLibrary); solAssert(t, ""); - ret["type"].append(formatType(member.name, *t, _forLibrary)); + ret["components"].append(formatType(member.name, *t, _forLibrary)); } } else diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp index 3de8b732c..5ddd28ad2 100644 --- a/test/libsolidity/SolidityABIJSON.cpp +++ b/test/libsolidity/SolidityABIJSON.cpp @@ -950,32 +950,39 @@ BOOST_AUTO_TEST_CASE(return_structs) } )"; char const* interface = R"( - [ - { + [{ "constant" : false, - "payable": false, - "inputs": [], - "name": "f", - "outputs": [{ - "name": "x", - "type": "uint256" - }, { - "name": "s", - "type": [{ - "name": "a", - "type": "uint256" - }, { - "name": "sub", - "subtype": [{ - "name": "x", - "type": "uint256[2]" - }], - "type": "[]" - }] - }], + "inputs" : [], + "name" : "f", + "outputs" : [ + { + "name" : "x", + "type" : "uint256" + }, + { + "components" : [ + { + "name" : "a", + "type" : "uint256" + }, + { + "components" : [ + { + "name" : "x", + "type" : "uint256[2]" + } + ], + "name" : "sub", + "type" : "[]" + } + ], + "name" : "s", + "type" : "" + } + ], + "payable" : false, "type" : "function" - } - ] + }] )"; checkInterface(text, interface); } @@ -990,28 +997,33 @@ BOOST_AUTO_TEST_CASE(return_structs_with_contracts) } )"; char const* interface = R"( - [ - { - "constant" : false, - "payable": false, + [{ + "constant": false, "inputs": [], "name": "f", - "outputs" : [{ - "name" : "s", - "type" : [{ - "name" : "x", - "type" : "address[]" - }, { - "name" : "y", - "type" : "address" - }] - }, { - "name" : "c", - "type" : "address" - }], - "type" : "function" - } - ] + "outputs": [ + { + "components": [ + { + "name": "x", + "type": "address[]" + }, + { + "name": "y", + "type": "address" + } + ], + "name": "s", + "type": "" + }, + { + "name": "c", + "type": "address" + } + ], + "payable": false, + "type": "function" + }] )"; checkInterface(text, interface); } @@ -1025,36 +1037,49 @@ BOOST_AUTO_TEST_CASE(event_structs) event E(T t, S s); } )"; - char const* interface = R"( - [{ - "anonymous" : false, - "inputs" : [{ - "indexed" : false, - "name" : "t", - "type" : [{ - "name" : "x", - "type" : "uint256[2]" - }] - }, { - "indexed" : false, - "name" : "s", - "type" : [{ - "name" : "a", - "type" : "uint256" - }, { - "name" : "sub", - "subtype" : [{ - "name" : "x", - "type" : "uint256[2]" - }], - "type" : "[]" - }, { - "name" : "b", - "type" : "bytes" - }] - }], - "name" : "E", - "type" : "event" + char const *interface = R"( + [{ + "anonymous": false, + "inputs": [ + { + "components": [ + { + "name": "x", + "type": "uint256[2]" + } + ], + "indexed": false, + "name": "t", + "type": "" + }, + { + "components": [ + { + "name": "a", + "type": "uint256" + }, + { + "components": [ + { + "name": "x", + "type": "uint256[2]" + } + ], + "name": "sub", + "type": "[]" + }, + { + "name": "b", + "type": "bytes" + } + ], + "indexed": false, + "name": "s", + "type": "" + } + ], + "name": "E", + "type": "event" }] )"; checkInterface(text, interface); @@ -1072,38 +1097,50 @@ BOOST_AUTO_TEST_CASE(structs_in_libraries) )"; char const* interface = R"( [{ - "constant" : false, - "inputs" : [{ - "name" : "s", - "type" : [{ - "name" : "a", - "type" : "uint256" - }, { - "name" : "sub", - "subtype" : [{ - "name" : "x", - "type" : "uint256[2]" - }], - "type" : "[]" - }, { - "name" : "b", - "type" : "bytes" - }] - }], - "name" : "g", - "outputs" : [], - "payable" : false, - "type" : "function" - }, { - "constant" : false, - "inputs" : [{ - "name" : "s", - "type" : "L.S storage" - }], - "name" : "f", - "outputs" : [], - "payable" : false, - "type" : "function" + "constant": false, + "inputs": [ + { + "components": [ + { + "name": "a", + "type": "uint256" + }, + { + "components": [ + { + "name": "x", + "type": "uint256[2]" + } + ], + "name": "sub", + "type": "[]" + }, + { + "name": "b", + "type": "bytes" + } + ], + "name": "s", + "type": "" + } + ], + "name": "g", + "outputs": [], + "payable": false, + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "s", + "type": "L.S storage" + } + ], + "name": "f", + "outputs": [], + "payable": false, + "type": "function" }] )"; checkInterface(text, interface); From e4bb767dcdab6121f00933b5d6af64e029a9e93c Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 30 Jun 2017 10:49:19 +0200 Subject: [PATCH 128/162] Document structs in ABI --- docs/abi-spec.rst | 92 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 89 insertions(+), 3 deletions(-) diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index f75a6885c..6df9f1d53 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -289,14 +289,16 @@ In effect, a log entry using this ABI is described as: JSON ==== -The JSON format for a contract's interface is given by an array of function and/or event descriptions. A function description is a JSON object with the fields: +The JSON format for a contract's interface is given by an array of function and/or event descriptions. +A function description is a JSON object with the fields: - ``type``: ``"function"``, ``"constructor"``, or ``"fallback"`` (the :ref:`unnamed "default" function `); - ``name``: the name of the function; - ``inputs``: an array of objects, each of which contains: * ``name``: the name of the parameter; - * ``type``: the canonical type of the parameter. + * ``type``: the canonical type of the parameter (more below). + * ``components``: used for tuple types (more below). - ``outputs``: an array of objects similar to ``inputs``, can be omitted if function doesn't return anything; - ``payable``: ``true`` if function accepts ether, defaults to ``false``; @@ -316,7 +318,8 @@ An event description is a JSON object with fairly similar fields: - ``inputs``: an array of objects, each of which contains: * ``name``: the name of the parameter; - * ``type``: the canonical type of the parameter. + * ``type``: the canonical type of the parameter (more below). + * ``components``: used for tuple types (more below). * ``indexed``: ``true`` if the field is part of the log's topics, ``false`` if it one of the log's data segment. - ``anonymous``: ``true`` if the event was declared as ``anonymous``. @@ -353,3 +356,86 @@ would result in the JSON: "name":"foo", "outputs": [] }] + +Use of Structs in Types +----------------------- + +If structs are part of the type, we still want to know the name of the components. Because of that, +the json structure gets arbitrarily nested in the following way: + +An object with members ``name``, ``type`` and potentially ``components`` describes a typed variable. +The canonical type is determined until a struct type is reached and the string description up +to that point is stored in ``type``, i.e. it will be a sequence of ``[]`` and ``[k]`` with +integers ``k``. The components of the struct are then stored in the member ``components``, +which is of array type and has the same structure as the top-level object except that +``indexed`` is not allowed there. + +As an example, the code + +:: + + contract Test { + struct S { uint a; uint[] b; T[] c; } + struct T { uint x; uint y; } + function f(S s, T t, uint a) { } + } + +would result in the JSON: + +.. code:: json + + [ + { + "name": "f", + "type": "function", + "inputs": [ + { + "name": "s", + "type": "", + "components": [ + { + "name": "a", + "type": "uint256" + }, + { + "name": "b", + "type": "uint256[]" + }, + { + "name": "c", + "type": "[]", + "components": [ + { + "name": "x", + "type": "uint256" + }, + { + "name": "y", + "type": "uint256" + } + ] + } + ] + }, + { + "name": "t", + "type": "", + "components": [ + { + "name": "x", + "type": "uint256" + }, + { + "name": "y", + "type": "uint256" + } + ] + }, + { + "name": "a", + "type": "uint256" + } + ], + "outputs": [] + } + ] From 44825d1c1ecef6ea3fa27da6330c12dcf4279fb3 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 30 Jun 2017 14:30:30 +0200 Subject: [PATCH 129/162] Expect test to fail until implemented. --- test/libsolidity/SolidityEndToEndTest.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 3b657e39d..393d3c645 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -9698,7 +9698,10 @@ BOOST_AUTO_TEST_CASE(return_structs) } } )"; - compileAndRun(sourceCode, 0, "C"); + // This will throw "unimplemented" until it is implemented. + BOOST_CHECK_THROW( + compileAndRun(sourceCode, 0, "C"), + Exception); // Will calculate the exact encoding later. // BOOST_CHECK(callContractFunction("f()") == encodeArgs( From 6385641f6e461a1964da793956fbe0ef8cddadd3 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 24 Aug 2017 15:08:31 +0200 Subject: [PATCH 130/162] Fix tests. --- libsolidity/ast/Types.h | 2 +- libsolidity/interface/ABI.h | 2 +- test/libsolidity/SolidityABIJSON.cpp | 4 ++++ test/libsolidity/SolidityEndToEndTest.cpp | 1 + test/libsolidity/SolidityNameAndTypeResolution.cpp | 8 ++++---- 5 files changed, 11 insertions(+), 6 deletions(-) diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 0713f5276..dd50c5735 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -248,7 +248,7 @@ public: /// @returns the canonical name of this type for use in library function signatures. virtual std::string canonicalName() const { return toString(true); } /// @returns the signature of this type in external functions, i.e. `uint256` for integers - /// or `(uint256, bytes8)[2]` for an array of structs. If @a _structsByName, + /// or `(uint256,bytes8)[2]` for an array of structs. If @a _structsByName, /// structs are given by canonical name like `ContractName.StructName[2]`. virtual std::string signatureInExternalFunction(bool /*_structsByName*/) const { diff --git a/libsolidity/interface/ABI.h b/libsolidity/interface/ABI.h index 7e42909b6..db70729db 100644 --- a/libsolidity/interface/ABI.h +++ b/libsolidity/interface/ABI.h @@ -50,7 +50,7 @@ private: std::vector const& _types, bool _forLibrary ); - /// @returns a Json object with "name", "type" and potentially "subtype" keys, according + /// @returns a Json object with "name", "type" and potentially "components" keys, according /// to the ABI specification. /// If it is possible to express the type as a single string, it is allowed to return a single string. static Json::Value formatType(std::string const& _name, Type const& _type, bool _forLibrary); diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp index 5ddd28ad2..e4ab54ec1 100644 --- a/test/libsolidity/SolidityABIJSON.cpp +++ b/test/libsolidity/SolidityABIJSON.cpp @@ -981,6 +981,7 @@ BOOST_AUTO_TEST_CASE(return_structs) } ], "payable" : false, + "stateMutability" : "nonpayable", "type" : "function" }] )"; @@ -1022,6 +1023,7 @@ BOOST_AUTO_TEST_CASE(return_structs_with_contracts) } ], "payable": false, + "stateMutability" : "nonpayable", "type": "function" }] )"; @@ -1127,6 +1129,7 @@ BOOST_AUTO_TEST_CASE(structs_in_libraries) "name": "g", "outputs": [], "payable": false, + "stateMutability": "nonpayable", "type": "function" }, { @@ -1140,6 +1143,7 @@ BOOST_AUTO_TEST_CASE(structs_in_libraries) "name": "f", "outputs": [], "payable": false, + "stateMutability": "nonpayable", "type": "function" }] )"; diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 393d3c645..2bdcbd483 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -9685,6 +9685,7 @@ BOOST_AUTO_TEST_CASE(contracts_separated_with_comment) BOOST_AUTO_TEST_CASE(return_structs) { char const* sourceCode = R"( + pragma experimental ABIEncoderV2; contract C { struct S { uint a; T[] sub; } struct T { uint[2] x; } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index dcab8fb00..778c12c5c 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -1102,7 +1102,7 @@ BOOST_AUTO_TEST_CASE(struct_accessor_one_array_only) Data public data; } )"; - CHECK_ERROR(sourceCode, TypeError, "Internal type is not allowed for public state variables."); + CHECK_ERROR(sourceCode, TypeError, "Internal or recursive type is not allowed for public state variables."); } BOOST_AUTO_TEST_CASE(base_class_state_variable_internal_member) @@ -4904,7 +4904,7 @@ BOOST_AUTO_TEST_CASE(internal_function_as_external_parameter) } } )"; - CHECK_ERROR(text, TypeError, "Internal type is not allowed for public or external functions."); + CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions."); } BOOST_AUTO_TEST_CASE(internal_function_returned_from_public_function) @@ -4916,7 +4916,7 @@ BOOST_AUTO_TEST_CASE(internal_function_returned_from_public_function) } } )"; - CHECK_ERROR(text, TypeError, "Internal type is not allowed for public or external functions."); + CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions."); } BOOST_AUTO_TEST_CASE(internal_function_as_external_parameter_in_library_internal) @@ -4938,7 +4938,7 @@ BOOST_AUTO_TEST_CASE(internal_function_as_external_parameter_in_library_external } } )"; - CHECK_ERROR(text, TypeError, "Internal type is not allowed for public or external functions."); + CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions."); } BOOST_AUTO_TEST_CASE(function_type_arrays) From 70d70e78160069d28a6b4931c995d0b24c2b09d5 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 24 Aug 2017 15:20:49 +0200 Subject: [PATCH 131/162] Implement struct encoder. --- libsolidity/ast/Types.cpp | 10 +- libsolidity/ast/Types.h | 2 +- libsolidity/codegen/ABIFunctions.cpp | 128 +++++++++++++++++++++- libsolidity/codegen/ABIFunctions.h | 7 ++ libsolidity/codegen/CompilerUtils.cpp | 2 +- test/libsolidity/ABIEncoderTests.cpp | 36 ++++++ test/libsolidity/SolidityEndToEndTest.cpp | 44 -------- 7 files changed, 177 insertions(+), 52 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 14b30df82..c7843b903 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -1735,7 +1735,15 @@ unsigned StructType::calldataEncodedSize(bool _padded) const bool StructType::isDynamicallyEncoded() const { - solAssert(false, "Structs are not yet supported in the ABI."); + solAssert(!recursive(), ""); + for (auto t: memoryMemberTypes()) + { + solAssert(t, "Parameter should have external type."); + t = t->interfaceType(false); + if (t->isDynamicallyEncoded()) + return true; + } + return false; } u256 StructType::memorySize() const diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index dd50c5735..cb4396939 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -745,7 +745,7 @@ public: virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override; virtual TypePointer encodingType() const override { - return location() == DataLocation::Storage ? std::make_shared(256) : TypePointer(); + return location() == DataLocation::Storage ? std::make_shared(256) : shared_from_this(); } virtual TypePointer interfaceType(bool _inLibrary) const override; diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index 3a9f1a486..9f6c55ba6 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -404,9 +404,11 @@ string ABIFunctions::abiEncodingFunction( else solAssert(false, ""); } - else if (dynamic_cast(&to)) + else if (auto const* toStruct = dynamic_cast(&to)) { - solUnimplementedAssert(false, "Structs not yet implemented."); + StructType const* fromStruct = dynamic_cast(&_from); + solAssert(fromStruct, ""); + return abiEncodingFunctionStruct(*fromStruct, *toStruct, _encodeAsLibraryTypes); } else if (_from.category() == Type::Category::Function) return abiEncodingFunctionFunctionType( @@ -534,7 +536,7 @@ string ABIFunctions::abiEncodingFunctionSimpleArray( for { let i := 0 } lt(i, length) { i := add(i, 1) } { mstore(pos, sub(tail, headStart)) - tail := ((srcPtr), tail) + tail := (, tail) srcPtr := (srcPtr) pos := add(pos, ) } @@ -549,7 +551,7 @@ string ABIFunctions::abiEncodingFunctionSimpleArray( let srcPtr := (value) for { let i := 0 } lt(i, length) { i := add(i, 1) } { - ((srcPtr), pos) + (, pos) srcPtr := (srcPtr) pos := add(pos, ) } @@ -573,7 +575,7 @@ string ABIFunctions::abiEncodingFunctionSimpleArray( _encodeAsLibraryTypes, true )); - templ("arrayElementAccess", inMemory ? "mload" : "sload"); + templ("arrayElementAccess", inMemory ? "mload(srcPtr)" : _from.baseType()->isValueType() ? "sload(srcPtr)" : "srcPtr" ); templ("nextArrayElement", nextArrayElementFunction(_from)); return templ.render(); }); @@ -726,6 +728,122 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray( }); } +string ABIFunctions::abiEncodingFunctionStruct( + StructType const& _from, + StructType const& _to, + bool _encodeAsLibraryTypes +) +{ + string functionName = + "abi_encode_" + + _from.identifier() + + "_to_" + + _to.identifier() + + (_encodeAsLibraryTypes ? "_library" : ""); + + solUnimplementedAssert(!_from.dataStoredIn(DataLocation::CallData), ""); + solAssert(&_from.structDefinition() == &_to.structDefinition(), ""); + + return createFunction(functionName, [&]() { + bool fromStorage = _from.location() == DataLocation::Storage; + bool dynamic = _to.isDynamicallyEncoded(); + Whiskers templ(R"( + function (value, pos) { + let tail := add(pos, ) + + <#members> + { + // + + } + + + } + )"); + templ("functionName", functionName); + templ("return", dynamic ? " -> end " : ""); + templ("assignEnd", dynamic ? "end := tail" : ""); + // to avoid multiple loads from the same slot for subsequent members + templ("init", fromStorage ? "let slotValue := 0" : ""); + u256 previousSlotOffset(-1); + u256 encodingOffset = 0; + vector> members; + for (auto const& member: _to.members(nullptr)) + { + solAssert(member.type, ""); + if (!member.type->canLiveOutsideStorage()) + continue; + solUnimplementedAssert( + member.type->mobileType() && + member.type->mobileType()->interfaceType(_encodeAsLibraryTypes) && + member.type->mobileType()->interfaceType(_encodeAsLibraryTypes)->encodingType(), + "Encoding type \"" + member.type->toString() + "\" not yet implemented." + ); + auto memberTypeTo = member.type->mobileType()->interfaceType(_encodeAsLibraryTypes)->encodingType(); + auto memberTypeFrom = _from.memberType(member.name); + solAssert(memberTypeFrom, ""); + bool dynamicMember = memberTypeTo->isDynamicallyEncoded(); + if (dynamicMember) + solAssert(dynamic, ""); + Whiskers memberTempl(R"( + + let memberValue := + )" + ( + dynamicMember ? + string(R"( + mstore(add(pos, ), sub(tail, pos)) + tail := (memberValue, tail) + )") : + string(R"( + (memberValue, add(pos, )) + )") + ) + ); + if (fromStorage) + { + solAssert(memberTypeFrom->isValueType() == memberTypeTo->isValueType(), ""); + u256 storageSlotOffset; + size_t intraSlotOffset; + tie(storageSlotOffset, intraSlotOffset) = _from.storageOffsetsOfMember(member.name); + if (memberTypeFrom->isValueType()) + { + if (storageSlotOffset != previousSlotOffset) + { + memberTempl("preprocess", "slotValue := sload(add(value, " + toCompactHexWithPrefix(storageSlotOffset) + "))"); + previousSlotOffset = storageSlotOffset; + } + else + memberTempl("preprocess", ""); + memberTempl("retrieveValue", shiftRightFunction(intraSlotOffset * 8, false) + "(slotValue)"); + } + else + { + solAssert(memberTypeFrom->dataStoredIn(DataLocation::Storage), ""); + solAssert(intraSlotOffset == 0, ""); + memberTempl("preprocess", ""); + memberTempl("retrieveValue", "add(value, " + toCompactHexWithPrefix(storageSlotOffset) + ")"); + } + } + else + { + memberTempl("preprocess", ""); + string sourceOffset = toCompactHexWithPrefix(_from.memoryOffsetOfMember(member.name)); + memberTempl("retrieveValue", "mload(add(value, " + sourceOffset + "))"); + } + memberTempl("encodingOffset", toCompactHexWithPrefix(encodingOffset)); + encodingOffset += dynamicMember ? 0x20 : memberTypeTo->calldataEncodedSize(); + memberTempl("abiEncode", abiEncodingFunction(*memberTypeFrom, *memberTypeTo, _encodeAsLibraryTypes, false)); + + members.push_back({}); + members.back()["encode"] = memberTempl.render(); + members.back()["memberName"] = member.name; + } + templ("members", members); + templ("headSize", toCompactHexWithPrefix(encodingOffset)); + return templ.render(); + }); +} + string ABIFunctions::abiEncodingFunctionStringLiteral( Type const& _from, Type const& _to, diff --git a/libsolidity/codegen/ABIFunctions.h b/libsolidity/codegen/ABIFunctions.h index 5bbd842f8..de2a140ae 100644 --- a/libsolidity/codegen/ABIFunctions.h +++ b/libsolidity/codegen/ABIFunctions.h @@ -123,6 +123,13 @@ private: bool _encodeAsLibraryTypes ); + /// Part of @a abiEncodingFunction for struct types. + std::string abiEncodingFunctionStruct( + StructType const& _givenType, + StructType const& _targetType, + bool _encodeAsLibraryTypes + ); + // @returns the name of the ABI encoding function with the given type // and queues the generation of the function to the requested functions. // Case for _givenType being a string literal diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 1e623357a..37aa1aea6 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -121,7 +121,7 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound { if (auto ref = dynamic_cast(&_type)) { - solAssert(ref->location() == DataLocation::Memory, ""); + solUnimplementedAssert(ref->location() == DataLocation::Memory, ""); storeInMemoryDynamic(IntegerType(256), _padToWordBoundaries); } else if (auto str = dynamic_cast(&_type)) diff --git a/test/libsolidity/ABIEncoderTests.cpp b/test/libsolidity/ABIEncoderTests.cpp index 4ddf17ce7..05158601f 100644 --- a/test/libsolidity/ABIEncoderTests.cpp +++ b/test/libsolidity/ABIEncoderTests.cpp @@ -424,7 +424,43 @@ BOOST_AUTO_TEST_CASE(function_name_collision) ) } +BOOST_AUTO_TEST_CASE(structs) +{ + string sourceCode = R"( + contract C { + struct S { uint16 a; uint16 b; T[] sub; uint16 c; } + struct T { uint64[2] x; } + S s; + event e(uint16, S); + function f() returns (uint, S) { + uint16 x = 7; + s.a = 8; + s.b = 9; + s.c = 10; + s.sub.length = 3; + s.sub[0].x[0] = 11; + s.sub[1].x[0] = 12; + s.sub[2].x[1] = 13; + e(x, s); + return (x, s); + } + } + )"; + NEW_ENCODER( + compileAndRun(sourceCode, 0, "C"); + bytes encoded = encodeArgs( + u256(7), 0x40, + 8, 9, 0x80, 10, + 3, + 11, 0, + 12, 0, + 0, 13 + ); + BOOST_CHECK(callContractFunction("f()") == encoded); + REQUIRE_LOG_DATA(encoded); + ) +} BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 2bdcbd483..f885b0d31 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -9682,50 +9682,6 @@ BOOST_AUTO_TEST_CASE(contracts_separated_with_comment) compileAndRun(sourceCode, 0, "C2"); } -BOOST_AUTO_TEST_CASE(return_structs) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - struct S { uint a; T[] sub; } - struct T { uint[2] x; } - function f() returns (uint x, S s) { - x = 7; - s.a = 8; - s.sub = new T[](3); - s.sub[0].x[0] = 9; - s.sub[1].x[0] = 10; - s.sub[2].x[1] = 11; - } - } - )"; - // This will throw "unimplemented" until it is implemented. - BOOST_CHECK_THROW( - compileAndRun(sourceCode, 0, "C"), - Exception); - -// Will calculate the exact encoding later. -// BOOST_CHECK(callContractFunction("f()") == encodeArgs( -// u256(7), u256(0x40), -// u256(8), u256(0x40), -// u256(3), -// // s.sub[0] -// u256(9), u256(0xc0), -// // s.sub[1] -// u256(10), u256(0xe0), -// // s.sub[2] -// u256(11), u256(0x100), -// // s.sub[0].sub -// u256(2) -// // s.sub[0].sub[0].a -// u256(8), -// u256() -// // s.sub[1].sub -// u256(0) -// // s.sub[2].sub -// u256(2) -// )); -} BOOST_AUTO_TEST_CASE(include_creation_bytecode_only_once) { From c5063d315583270e88a01a0a82a84a68190f6ba1 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 1 Sep 2017 13:37:40 +0200 Subject: [PATCH 132/162] Use "tuple" for struct types in ABI JSON. Only use tuple as a type in the ABI (and remove all "anonymous struct" references too) --- docs/abi-spec.rst | 30 +++++++++--------- libsolidity/interface/ABI.cpp | 2 +- test/libsolidity/SolidityABIJSON.cpp | 16 +++++----- .../SolidityNameAndTypeResolution.cpp | 31 +++++++++++++++++-- 4 files changed, 52 insertions(+), 27 deletions(-) diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index 6df9f1d53..f4822be7d 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -68,13 +68,12 @@ The following non-fixed-size types exist: - ``[]``: a variable-length array of the given fixed-length type. -Types can be combined to anonymous structs by enclosing a finite non-negative number +Types can be combined to a tuple by enclosing a finite non-negative number of them inside parentheses, separated by commas: -- ``(T1,T2,...,Tn)``: anonymous struct (ordered tuple) consisting of the types ``T1``, ..., ``Tn``, ``n >= 0`` - -It is possible to form structs of structs, arrays of structs and so on. +- ``(T1,T2,...,Tn)``: tuple consisting of the types ``T1``, ..., ``Tn``, ``n >= 0`` +It is possible to form tuples of tuples, arrays of tuples and so on. Formal Specification of the Encoding ==================================== @@ -133,7 +132,7 @@ on the type of ``X`` being ``enc(X) = enc((X[0], ..., X[k-1]))`` - i.e. it is encoded as if it were an anonymous struct with ``k`` elements + i.e. it is encoded as if it were a tuple with ``k`` elements of the same type. - ``T[]`` where ``X`` has ``k`` elements (``k`` is assumed to be of type ``uint256``): @@ -176,7 +175,7 @@ and the return values ``v_1, ..., v_k`` of ``f`` are encoded as ``enc((v_1, ..., v_k))`` -i.e. the values are combined into an anonymous struct and encoded. +i.e. the values are combined into a tuple and encoded. Examples ======== @@ -357,16 +356,17 @@ would result in the JSON: "outputs": [] }] -Use of Structs in Types ------------------------ +Handling tuple types +-------------------- -If structs are part of the type, we still want to know the name of the components. Because of that, +If tuples are part of the type, we still want to know the name of the components. Because of that, the json structure gets arbitrarily nested in the following way: An object with members ``name``, ``type`` and potentially ``components`` describes a typed variable. -The canonical type is determined until a struct type is reached and the string description up -to that point is stored in ``type``, i.e. it will be a sequence of ``[]`` and ``[k]`` with -integers ``k``. The components of the struct are then stored in the member ``components``, +The canonical type is determined until a tuple type is reached and the string description up +to that point is stored in ``type`` prefix with the word ``tuple``, i.e. it will be ``tuple`` followed by +a sequence of ``[]`` and ``[k]`` with +integers ``k``. The components of the tuple are then stored in the member ``components``, which is of array type and has the same structure as the top-level object except that ``indexed`` is not allowed there. @@ -391,7 +391,7 @@ would result in the JSON: "inputs": [ { "name": "s", - "type": "", + "type": "tuple", "components": [ { "name": "a", @@ -403,7 +403,7 @@ would result in the JSON: }, { "name": "c", - "type": "[]", + "type": "tuple[]", "components": [ { "name": "x", @@ -419,7 +419,7 @@ would result in the JSON: }, { "name": "t", - "type": "", + "type": "tuple", "components": [ { "name": "x", diff --git a/libsolidity/interface/ABI.cpp b/libsolidity/interface/ABI.cpp index 9af7cdc37..aefb34afa 100644 --- a/libsolidity/interface/ABI.cpp +++ b/libsolidity/interface/ABI.cpp @@ -147,7 +147,7 @@ Json::Value ABI::formatType(string const& _name, Type const& _type, bool _forLib } else if (StructType const* structType = dynamic_cast(&_type)) { - ret["type"] = string(); + ret["type"] = "tuple"; ret["components"] = Json::arrayValue; for (auto const& member: structType->members(nullptr)) { diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp index e4ab54ec1..e5d9e99c1 100644 --- a/test/libsolidity/SolidityABIJSON.cpp +++ b/test/libsolidity/SolidityABIJSON.cpp @@ -973,11 +973,11 @@ BOOST_AUTO_TEST_CASE(return_structs) } ], "name" : "sub", - "type" : "[]" + "type" : "tuple[]" } ], "name" : "s", - "type" : "" + "type" : "tuple" } ], "payable" : false, @@ -1015,7 +1015,7 @@ BOOST_AUTO_TEST_CASE(return_structs_with_contracts) } ], "name": "s", - "type": "" + "type": "tuple" }, { "name": "c", @@ -1052,7 +1052,7 @@ BOOST_AUTO_TEST_CASE(event_structs) ], "indexed": false, "name": "t", - "type": "" + "type": "tuple" }, { "components": [ @@ -1068,7 +1068,7 @@ BOOST_AUTO_TEST_CASE(event_structs) } ], "name": "sub", - "type": "[]" + "type": "tuple[]" }, { "name": "b", @@ -1077,7 +1077,7 @@ BOOST_AUTO_TEST_CASE(event_structs) ], "indexed": false, "name": "s", - "type": "" + "type": "tuple" } ], "name": "E", @@ -1115,7 +1115,7 @@ BOOST_AUTO_TEST_CASE(structs_in_libraries) } ], "name": "sub", - "type": "[]" + "type": "tuple[]" }, { "name": "b", @@ -1123,7 +1123,7 @@ BOOST_AUTO_TEST_CASE(structs_in_libraries) } ], "name": "s", - "type": "" + "type": "tuple" } ], "name": "g", diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 778c12c5c..5ab824d3e 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -601,7 +601,6 @@ BOOST_AUTO_TEST_CASE(enum_external_type) BOOST_AUTO_TEST_CASE(external_structs) { - ASTPointer sourceUnit; char const* text = R"( contract Test { enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } @@ -614,7 +613,7 @@ BOOST_AUTO_TEST_CASE(external_structs) function i(Nested[]) external {} } )"; - ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseAndAnalyse(text), "Parsing and name Resolving failed"); + SourceUnit const* sourceUnit = parseAndAnalyse(text); for (ASTPointer const& node: sourceUnit->nodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) { @@ -627,7 +626,33 @@ BOOST_AUTO_TEST_CASE(external_structs) } } -// TODO: Add a test that checks the signature of library functions taking structs +BOOST_AUTO_TEST_CASE(external_structs_in_libraries) +{ + char const* text = R"( + library Test { + enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } + struct Empty {} + struct Nested { X[2][] a; mapping(uint => uint) m; uint y; } + struct X { bytes32 x; Test t; Empty[] e; } + function f(ActionChoices, uint, Empty) external {} + function g(Test, Nested) external {} + function h(function(Nested) external returns (uint)[]) external {} + function i(Nested[]) external {} + } + )"; + SourceUnit const* sourceUnit = parseAndAnalyse(text); + for (ASTPointer const& node: sourceUnit->nodes()) + if (ContractDefinition* contract = dynamic_cast(node.get())) + { + auto functions = contract->definedFunctions(); + BOOST_REQUIRE(!functions.empty()); + BOOST_CHECK_EQUAL("f(Test.ActionChoices,uint256,Test.Empty)", functions[0]->externalSignature()); + BOOST_CHECK_EQUAL("g(Test,Test.Nested)", functions[1]->externalSignature()); + BOOST_CHECK_EQUAL("h(function[])", functions[2]->externalSignature()); + BOOST_CHECK_EQUAL("i(Test.Nested[])", functions[3]->externalSignature()); + } +} + BOOST_AUTO_TEST_CASE(function_external_call_allowed_conversion) { From 923373b41efed30839cfc26e903e32b0dddd9cb5 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 11 Sep 2017 15:03:49 +0100 Subject: [PATCH 133/162] Clarify ABI & Solidity types --- docs/abi-spec.rst | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index f4822be7d..97320c7f5 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -75,6 +75,9 @@ of them inside parentheses, separated by commas: It is possible to form tuples of tuples, arrays of tuples and so on. +.. note:: + Solidity supports all the types presented above with the same names with the exception of tuples. The ABI tuple type is utilised for encoding Solidity ``structs``. + Formal Specification of the Encoding ==================================== @@ -359,8 +362,8 @@ would result in the JSON: Handling tuple types -------------------- -If tuples are part of the type, we still want to know the name of the components. Because of that, -the json structure gets arbitrarily nested in the following way: +Despite that names are intentionally not part of the ABI encoding they do make a lot of sense to be included +in the JSON to enable displaying it to the end user. The structure is nested in the following way: An object with members ``name``, ``type`` and potentially ``components`` describes a typed variable. The canonical type is determined until a tuple type is reached and the string description up From b687d74c47c5814ce159bf16a2da2158c1a15879 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 13 Sep 2017 19:09:31 +0100 Subject: [PATCH 134/162] Add changelog --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index 0b9295d06..376494e0d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,7 @@ Features: * Code Generator: Added ``.selector`` member on external function types to retrieve their signature. * Code Generator: Keep a single copy of encoding functions when using the experimental "ABIEncoderV2". * Optimizer: Add new optimization step to remove unused ``JUMPDEST``s. + * Code Generator: Support passing ``structs`` as arguments and return parameters (requires ``pragma experimental ABIEncoderV2`` for now). * Syntax Checker: Warn if no visibility is specified on contract functions. * Type Checker: Display helpful warning for unused function arguments/return parameters. * Type Checker: Do not show the same error multiple times for events. From 06965458080dcaff6a8c486acc500b64b26a078f Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 14 Sep 2017 14:51:14 +0200 Subject: [PATCH 135/162] Check for interface types of members and cache recursion check. --- libsolidity/ast/Types.cpp | 67 ++++++++++++++++++++++++++------------- libsolidity/ast/Types.h | 4 +++ 2 files changed, 49 insertions(+), 22 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index c7843b903..83a5b465a 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -1788,14 +1788,33 @@ MemberList::MemberMap StructType::nativeMembers(ContractDefinition const*) const TypePointer StructType::interfaceType(bool _inLibrary) const { + if (!canBeUsedExternally(_inLibrary)) + return TypePointer(); + + // Has to fulfill canBeUsedExternally(_inLibrary) == !!interfaceType(_inLibrary) if (_inLibrary && location() == DataLocation::Storage) return shared_from_this(); - else if (!recursive()) - // TODO this might not be enough, we have to convert all members to - // their interfaceType - return copyForLocation(DataLocation::Memory, true); else - return TypePointer(); + return copyForLocation(DataLocation::Memory, true); +} + +bool StructType::canBeUsedExternally(bool _inLibrary) const +{ + if (_inLibrary && location() == DataLocation::Storage) + return true; + else if (recursive()) + return false; + else + { + // Check that all members have interface types. + // We pass "false" to canBeUsedExternally (_inLibrary), because this struct will be + // passed by value and thus the encoding does not differ, but it will disallow + // mappings. + for (auto const& var: m_struct.members()) + if (!var->annotation().type->canBeUsedExternally(false)) + return false; + } + return true; } TypePointer StructType::copyForLocation(DataLocation _location, bool _isPointer) const @@ -1887,25 +1906,29 @@ set StructType::membersMissingInMemory() const bool StructType::recursive() const { - set structsSeen; - function check = [&](StructType const* t) -> bool + if (!m_recursive.is_initialized()) { - StructDefinition const* str = &t->structDefinition(); - if (structsSeen.count(str)) - return true; - structsSeen.insert(str); - for (ASTPointer const& variable: str->members()) + set structsSeen; + function check = [&](StructType const* t) -> bool { - Type const* memberType = variable->annotation().type.get(); - while (dynamic_cast(memberType)) - memberType = dynamic_cast(memberType)->baseType().get(); - if (StructType const* innerStruct = dynamic_cast(memberType)) - if (check(innerStruct)) - return true; - } - return false; - }; - return check(this); + StructDefinition const* str = &t->structDefinition(); + if (structsSeen.count(str)) + return true; + structsSeen.insert(str); + for (ASTPointer const& variable: str->members()) + { + Type const* memberType = variable->annotation().type.get(); + while (dynamic_cast(memberType)) + memberType = dynamic_cast(memberType)->baseType().get(); + if (StructType const* innerStruct = dynamic_cast(memberType)) + if (check(innerStruct)) + return true; + } + return false; + }; + m_recursive = check(this); + } + return *m_recursive; } TypePointer EnumType::unaryOperatorResult(Token::Value _operator) const diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index cb4396939..8ba555215 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -32,6 +32,7 @@ #include #include +#include #include #include @@ -748,6 +749,7 @@ public: return location() == DataLocation::Storage ? std::make_shared(256) : shared_from_this(); } virtual TypePointer interfaceType(bool _inLibrary) const override; + virtual bool canBeUsedExternally(bool _inLibrary) const override; TypePointer copyForLocation(DataLocation _location, bool _isPointer) const override; @@ -774,6 +776,8 @@ public: private: StructDefinition const& m_struct; + /// Cache for the recursive() function. + mutable boost::optional m_recursive; }; /** From c001903cdc6db97f437150375a9b5343c70c3656 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 14 Sep 2017 17:03:59 +0200 Subject: [PATCH 136/162] Fixed tests with mappings in structs and added some more. --- .../SolidityNameAndTypeResolution.cpp | 92 ++++++++++++++++++- 1 file changed, 90 insertions(+), 2 deletions(-) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 5ab824d3e..d9e6a63d0 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -605,7 +605,7 @@ BOOST_AUTO_TEST_CASE(external_structs) contract Test { enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } struct Empty {} - struct Nested { X[2][] a; mapping(uint => uint) m; uint y; } + struct Nested { X[2][] a; uint y; } struct X { bytes32 x; Test t; Empty[] e; } function f(ActionChoices, uint, Empty) external {} function g(Test, Nested) external {} @@ -632,7 +632,7 @@ BOOST_AUTO_TEST_CASE(external_structs_in_libraries) library Test { enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } struct Empty {} - struct Nested { X[2][] a; mapping(uint => uint) m; uint y; } + struct Nested { X[2][] a; uint y; } struct X { bytes32 x; Test t; Empty[] e; } function f(ActionChoices, uint, Empty) external {} function g(Test, Nested) external {} @@ -653,6 +653,94 @@ BOOST_AUTO_TEST_CASE(external_structs_in_libraries) } } +BOOST_AUTO_TEST_CASE(struct_with_mapping_in_library) +{ + char const* text = R"( + library Test { + struct Nested { mapping(uint => uint)[2][] a; uint y; } + struct X { Nested n; } + function f(X storage x) external {} + } + )"; + SourceUnit const* sourceUnit = parseAndAnalyse(text); + for (ASTPointer const& node: sourceUnit->nodes()) + if (ContractDefinition* contract = dynamic_cast(node.get())) + { + auto functions = contract->definedFunctions(); + BOOST_REQUIRE(!functions.empty()); + BOOST_CHECK_EQUAL("f(Test.X storage)", functions[0]->externalSignature()); + } +} + +BOOST_AUTO_TEST_CASE(functions_with_identical_structs_in_interface) +{ + char const* text = R"( + pragma experimental ABIEncoderV2; + + contract C { + struct S1 { } + struct S2 { } + function f(S1) pure {} + function f(S2) pure {} + } + )"; + CHECK_ERROR(text, TypeError, "Function overload clash during conversion to external types for arguments"); +} + +BOOST_AUTO_TEST_CASE(functions_with_different_structs_in_interface) +{ + char const* text = R"( + pragma experimental ABIEncoderV2; + + contract C { + struct S1 { function() external a; } + struct S2 { bytes24 a; } + function f(S1) pure {} + function f(S2) pure {} + } + )"; + CHECK_SUCCESS(text); +} + +BOOST_AUTO_TEST_CASE(functions_with_stucts_of_non_external_types_in_interface) +{ + char const* text = R"( + pragma experimental ABIEncoderV2; + + contract C { + struct S { function() internal a; } + function f(S) {} + } + )"; + CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions."); +} + +BOOST_AUTO_TEST_CASE(functions_with_stucts_of_non_external_types_in_interface_2) +{ + char const* text = R"( + pragma experimental ABIEncoderV2; + + contract C { + struct S { mapping(uint => uint) a; } + function f(S) {} + } + )"; + CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions."); +} + +BOOST_AUTO_TEST_CASE(functions_with_stucts_of_non_external_types_in_interface_nested) +{ + char const* text = R"( + pragma experimental ABIEncoderV2; + + contract C { + struct T { mapping(uint => uint) a; } + struct S { T[][2] b; } + function f(S) {} + } + )"; + CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions."); +} BOOST_AUTO_TEST_CASE(function_external_call_allowed_conversion) { From f4b87548c8eac136945795d88b76b10ec87d2ff0 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 18 Sep 2017 11:04:09 +0100 Subject: [PATCH 137/162] Add useful messages to exceptions in the assembler --- libevmasm/Assembly.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index 31857c09a..df691e7dd 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -320,7 +320,7 @@ Json::Value Assembly::assemblyJSON(StringMap const& _sourceCodes) const AssemblyItem const& Assembly::append(AssemblyItem const& _i) { - assertThrow(m_deposit >= 0, AssemblyException, ""); + assertThrow(m_deposit >= 0, AssemblyException, "Stack underflow."); m_deposit += _i.deposit(); m_items.push_back(_i); if (m_items.back().location().isEmpty() && !m_currentSourceLocation.isEmpty()) @@ -330,7 +330,7 @@ AssemblyItem const& Assembly::append(AssemblyItem const& _i) AssemblyItem Assembly::namedTag(string const& _name) { - assertThrow(!_name.empty(), AssemblyException, ""); + assertThrow(!_name.empty(), AssemblyException, "Empty named tag."); if (!m_namedTags.count(_name)) m_namedTags[_name] = size_t(newTag().data()); return AssemblyItem(Tag, m_namedTags.at(_name)); @@ -588,7 +588,7 @@ LinkerObject const& Assembly::assemble() const ret.bytecode.resize(ret.bytecode.size() + 20); break; case Tag: - assertThrow(i.data() != 0, AssemblyException, ""); + assertThrow(i.data() != 0, AssemblyException, "Invalid tag position."); assertThrow(i.splitForeignPushTag().first == size_t(-1), AssemblyException, "Foreign tag."); assertThrow(ret.bytecode.size() < 0xffffffffL, AssemblyException, "Tag too large."); assertThrow(m_tagPositionsInBytecode[size_t(i.data())] == size_t(-1), AssemblyException, "Duplicate tag position."); From 20ffa1db43c84168ba887bb3460959dcb2584278 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 18 Sep 2017 11:04:24 +0100 Subject: [PATCH 138/162] Properly calculate bytesRequired for subs --- libevmasm/AssemblyItem.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libevmasm/AssemblyItem.cpp b/libevmasm/AssemblyItem.cpp index 1af266b6a..cfe91be09 100644 --- a/libevmasm/AssemblyItem.cpp +++ b/libevmasm/AssemblyItem.cpp @@ -59,18 +59,18 @@ unsigned AssemblyItem::bytesRequired(unsigned _addressLength) const case Tag: // 1 byte for the JUMPDEST return 1; case PushString: - return 33; + return 1 + 32; case Push: return 1 + max(1, dev::bytesRequired(data())); case PushSubSize: case PushProgramSize: - return 4; // worst case: a 16MB program + return 1 + 4; // worst case: a 16MB program case PushTag: case PushData: case PushSub: return 1 + _addressLength; case PushLibraryAddress: - return 21; + return 1 + 20; default: break; } From a3380ea8d0e02da1eb68eb15906015faf4e8bc3c Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sat, 16 Sep 2017 16:10:23 +0100 Subject: [PATCH 139/162] Force fallback to be external (experimental 0.5.0 change) --- Changelog.md | 3 +- libsolidity/analysis/TypeChecker.cpp | 5 +++ .../SolidityNameAndTypeResolution.cpp | 32 +++++++++++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 0b9295d06..26a4862ac 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,7 +1,7 @@ ### 0.4.17 (unreleased) Features: - * Support ``pragma experimental v0.5.0;`` to turn on upcoming breaking changes. + * Support ``pragma experimental "v0.5.0";`` to turn on upcoming breaking changes. * Code Generator: Added ``.selector`` member on external function types to retrieve their signature. * Code Generator: Keep a single copy of encoding functions when using the experimental "ABIEncoderV2". * Optimizer: Add new optimization step to remove unused ``JUMPDEST``s. @@ -12,6 +12,7 @@ Features: * Type Checker: Warn on using literals as tight packing parameters in ``keccak256``, ``sha3``, ``sha256`` and ``ripemd160``. * Type Checker: Enforce ``view`` and ``pure``. * Type Checker: Enforce ``view`` / ``constant`` with error as experimental 0.5.0 feature. + * Type Checker: Enforce fallback functions to be ``external`` as experimental 0.5.0 feature. Bugfixes: * ABI JSON: Include all overloaded events. diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 26529c229..529a4f8d7 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -120,6 +120,11 @@ bool TypeChecker::visit(ContractDefinition const& _contract) m_errorReporter.typeError(fallbackFunction->parameterList().location(), "Fallback function cannot take parameters."); if (!fallbackFunction->returnParameters().empty()) m_errorReporter.typeError(fallbackFunction->returnParameterList()->location(), "Fallback function cannot return values."); + if ( + _contract.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050) && + fallbackFunction->visibility() != FunctionDefinition::Visibility::External + ) + m_errorReporter.typeError(fallbackFunction->location(), "Fallback function must be defined as \"external\"."); } } } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 8c271fe10..be3b56f3f 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -6691,6 +6691,38 @@ BOOST_AUTO_TEST_CASE(tight_packing_literals) CHECK_WARNING(text, "The type of \"int_const 1\" was inferred as uint8."); } +BOOST_AUTO_TEST_CASE(non_external_fallback) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + contract C { + function () external { } + } + )"; + CHECK_WARNING(text, "Experimental features are turned on."); + text = R"( + pragma experimental "v0.5.0"; + contract C { + function () internal { } + } + )"; + CHECK_ERROR(text, TypeError, "Fallback function must be defined as \"external\"."); + text = R"( + pragma experimental "v0.5.0"; + contract C { + function () private { } + } + )"; + CHECK_ERROR(text, TypeError, "Fallback function must be defined as \"external\"."); + text = R"( + pragma experimental "v0.5.0"; + contract C { + function () public { } + } + )"; + CHECK_ERROR(text, TypeError, "Fallback function must be defined as \"external\"."); +} + BOOST_AUTO_TEST_SUITE_END() } From ff275e369c9f587d462481d49a7dec963d2f8d1d Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 18 Sep 2017 12:31:55 +0200 Subject: [PATCH 140/162] Allow constant byte arrays. --- Changelog.md | 1 + libsolidity/analysis/TypeChecker.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 206303839..f1b1a19cf 100644 --- a/Changelog.md +++ b/Changelog.md @@ -18,6 +18,7 @@ Features: Bugfixes: * ABI JSON: Include all overloaded events. * Parser: Crash fix related to parseTypeName. + * Type Checker: Allow constant byte arrays. ### 0.4.16 (2017-08-24) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 030c8f6be..40add37ee 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -623,7 +623,7 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) { bool allowed = false; if (auto arrayType = dynamic_cast(_variable.type().get())) - allowed = arrayType->isString(); + allowed = arrayType->isByteArray(); if (!allowed) m_errorReporter.typeError(_variable.location(), "Constants of non-value type not yet implemented."); } From ada68bcee66690f3511db14306d6bdff6e1f7dc5 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 18 Sep 2017 22:25:50 +0100 Subject: [PATCH 141/162] Add tests for constant bytes/strings --- test/libsolidity/SolidityEndToEndTest.cpp | 24 +++++++++++++++++++ .../SolidityNameAndTypeResolution.cpp | 12 ++++++++++ 2 files changed, 36 insertions(+) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index f885b0d31..bdac82783 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -10076,6 +10076,30 @@ BOOST_AUTO_TEST_CASE(function_types_sig) BOOST_CHECK(callContractFunction("h()") == encodeArgs(asString(FixedHash<4>(dev::keccak256("f()")).asBytes()))); } +BOOST_AUTO_TEST_CASE(constant_string) +{ + char const* sourceCode = R"( + contract C { + bytes constant a = "\x03\x01\x02"; + bytes constant b = hex"030102"; + string constant c = "hello"; + function f() returns (bytes) { + return a; + } + function g() returns (bytes) { + return b; + } + function h() returns (bytes) { + return bytes(c); + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("f()") == encodeDyn(string("\x03\x01\x02"))); + BOOST_CHECK(callContractFunction("g()") == encodeDyn(string("\x03\x01\x02"))); + BOOST_CHECK(callContractFunction("h()") == encodeDyn(string("hello"))); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 85acd8bf5..2ca3a5624 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -2385,6 +2385,18 @@ BOOST_AUTO_TEST_CASE(assignment_to_const_array_vars) CHECK_ERROR(text, TypeError, "implemented"); } +BOOST_AUTO_TEST_CASE(assignment_to_const_string_bytes) +{ + char const* text = R"( + contract C { + bytes constant a = "\x00\x01\x02"; + bytes constant b = hex"000102"; + string constant c = "hello"; + } + )"; + CHECK_SUCCESS(text); +} + BOOST_AUTO_TEST_CASE(constant_struct) { char const* text = R"( From 5706508332f9caffa077c0c313b82f538a13c5c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 18 Sep 2017 17:33:32 +0200 Subject: [PATCH 142/162] deps: Move and fix jsoncpp.cmake from deps submodule This copies jsoncpp.cmake from ethereum/cpp-dependencies repo and adds BUILD_BYPRODUCT information to ExternalProject configuration (required by Ninja). --- CMakeLists.txt | 2 +- cmake/jsoncpp.cmake | 50 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 cmake/jsoncpp.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 0a65071dc..89d627dc2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,7 @@ option(SOLC_LINK_STATIC "Link solc executable statically on supported platforms" # Let's find our dependencies include(EthDependencies) -include(deps/jsoncpp.cmake) +include(jsoncpp) find_package(Threads) diff --git a/cmake/jsoncpp.cmake b/cmake/jsoncpp.cmake new file mode 100644 index 000000000..79ce4c5f8 --- /dev/null +++ b/cmake/jsoncpp.cmake @@ -0,0 +1,50 @@ +include(ExternalProject) + +if (${CMAKE_SYSTEM_NAME} STREQUAL "Emscripten") + set(JSONCPP_CMAKE_COMMAND emcmake cmake) +else() + set(JSONCPP_CMAKE_COMMAND ${CMAKE_COMMAND}) +endif() + +# Disable implicit fallthrough warning in jsoncpp for gcc >= 7 until the upstream handles it properly +if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0) + set(JSONCCP_EXTRA_FLAGS -Wno-implicit-fallthrough) +else() + set(JSONCCP_EXTRA_FLAGS "") +endif() + +set(prefix "${CMAKE_BINARY_DIR}/deps") +set(JSONCPP_LIBRARY "${prefix}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}jsoncpp${CMAKE_STATIC_LIBRARY_SUFFIX}") +set(JSONCPP_INCLUDE_DIR "${prefix}/include") + +set(byproducts "") +if(CMAKE_VERSION VERSION_GREATER 3.1) + set(byproducts BUILD_BYPRODUCTS "${JSONCPP_LIBRARY}") +endif() + +ExternalProject_Add(jsoncpp-project + PREFIX "${prefix}" + DOWNLOAD_NAME jsoncpp-1.7.7.tar.gz + URL https://github.com/open-source-parsers/jsoncpp/archive/1.7.7.tar.gz + URL_HASH SHA256=087640ebcf7fbcfe8e2717a0b9528fff89c52fcf69fa2a18cc2b538008098f97 + CMAKE_COMMAND ${JSONCPP_CMAKE_COMMAND} + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX= + -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} + -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} + # Build static lib but suitable to be included in a shared lib. + -DCMAKE_POSITION_INDEPENDENT_CODE=${BUILD_SHARED_LIBS} + -DJSONCPP_WITH_TESTS=OFF + -DJSONCPP_WITH_PKGCONFIG_SUPPORT=OFF + -DCMAKE_CXX_FLAGS=${JSONCCP_EXTRA_FLAGS} + # Overwrite build and install commands to force Release build on MSVC. + BUILD_COMMAND cmake --build --config Release + INSTALL_COMMAND cmake --build --config Release --target install + ${byproducts} +) + +# Create jsoncpp imported library +add_library(jsoncpp STATIC IMPORTED) +file(MAKE_DIRECTORY ${JSONCPP_INCLUDE_DIR}) # Must exist. +set_property(TARGET jsoncpp PROPERTY IMPORTED_LOCATION ${JSONCPP_LIBRARY}) +set_property(TARGET jsoncpp PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${JSONCPP_INCLUDE_DIR}) +add_dependencies(jsoncpp jsoncpp-project) From 32965807a7f4914e184d7093048beb791cb32376 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 19 Sep 2017 10:22:28 +0200 Subject: [PATCH 143/162] Fix ABI spec regarding arrays. --- docs/abi-spec.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index 97320c7f5..b750deeec 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -58,7 +58,7 @@ The following elementary types exist: The following (fixed-size) array type exists: -- ``[M]``: a fixed-length array of the given fixed-length type. +- ``[M]``: a fixed-length array of ``M`` elements, ``M > 0``, of the given type. The following non-fixed-size types exist: @@ -66,7 +66,7 @@ The following non-fixed-size types exist: - ``string``: dynamic sized unicode string assumed to be UTF-8 encoded. -- ``[]``: a variable-length array of the given fixed-length type. +- ``[]``: a variable-length array of elements of the given type. Types can be combined to a tuple by enclosing a finite non-negative number of them inside parentheses, separated by commas: From a061e8db3463114171f3bd26831b9ac17019080a Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 19 Sep 2017 11:02:14 +0100 Subject: [PATCH 144/162] Clarify shorthand notation in ABI --- docs/abi-spec.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index b750deeec..29d98645e 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -42,7 +42,7 @@ The following elementary types exist: - ``address``: equivalent to ``uint160``, except for the assumed interpretation and language typing. -- ``uint``, ``int``: synonyms for ``uint256``, ``int256`` respectively (not to be used for computing the function selector). +- ``uint``, ``int``: synonyms for ``uint256``, ``int256`` respectively (this shorthand not to be used for computing the function selector). - ``bool``: equivalent to ``uint8`` restricted to the values 0 and 1 @@ -50,7 +50,7 @@ The following elementary types exist: - ``ufixedx``: unsigned variant of ``fixedx``. -- ``fixed``, ``ufixed``: synonyms for ``fixed128x19``, ``ufixed128x19`` respectively (not to be used for computing the function selector). +- ``fixed``, ``ufixed``: synonyms for ``fixed128x19``, ``ufixed128x19`` respectively (this shorthand not to be used for computing the function selector). - ``bytes``: binary type of ``M`` bytes, ``0 < M <= 32``. From ed1fd49ab07de1ddad9f3bc2864e17fe21ed993d Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 31 Jul 2017 20:31:12 +0100 Subject: [PATCH 145/162] Warn about obsolete sha3/suicide calls --- Changelog.md | 4 ++- libsolidity/analysis/TypeChecker.cpp | 8 +++++ .../SolidityNameAndTypeResolution.cpp | 29 +++++++++++++++++-- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index f1b1a19cf..c4918013d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,7 +5,9 @@ Features: * Code Generator: Added ``.selector`` member on external function types to retrieve their signature. * Code Generator: Keep a single copy of encoding functions when using the experimental "ABIEncoderV2". * Optimizer: Add new optimization step to remove unused ``JUMPDEST``s. - * Code Generator: Support passing ``structs`` as arguments and return parameters (requires ``pragma experimental ABIEncoderV2`` for now). + * Code Generator: Support passing ``structs`` as arguments and return parameters (requires ``pragma experimental ABIEncoderV2;`` for now). + * Static Analyzer: Warn for using deprecated builtins ``sha3`` and ``suicide`` + (replaced by ``keccak256`` and ``selfdestruct``, introduced in 0.4.2 and 0.2.0, respectively). * Syntax Checker: Warn if no visibility is specified on contract functions. * Type Checker: Display helpful warning for unused function arguments/return parameters. * Type Checker: Do not show the same error multiple times for events. diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 40add37ee..4b2ec8d64 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1477,6 +1477,14 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) else _functionCall.annotation().type = make_shared(functionType->returnParameterTypes()); + if (auto functionName = dynamic_cast(&_functionCall.expression())) + { + if (functionName->name() == "sha3" && functionType->kind() == FunctionType::Kind::SHA3) + m_errorReporter.warning(_functionCall.location(), "\"sha3\" has been deprecated in favour of \"keccak256\""); + else if (functionName->name() == "suicide" && functionType->kind() == FunctionType::Kind::Selfdestruct) + m_errorReporter.warning(_functionCall.location(), "\"suicide\" has been deprecated in favour of \"selfdestruct\""); + } + TypePointers parameterTypes = functionType->parameterTypes(); if (!functionType->padArguments()) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 2ca3a5624..39c47f9c5 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -4679,7 +4679,7 @@ BOOST_AUTO_TEST_CASE(warn_about_callcode) } } )"; - CHECK_WARNING(text, "\"callcode\" has been deprecated in favour"); + CHECK_WARNING(text, "\"callcode\" has been deprecated in favour of \"delegatecall\""); } BOOST_AUTO_TEST_CASE(no_warn_about_callcode_as_function) @@ -6877,7 +6877,7 @@ BOOST_AUTO_TEST_CASE(tight_packing_literals) } } )"; - CHECK_WARNING(text, "The type of \"int_const 1\" was inferred as uint8."); +// CHECK_WARNING(text, "The type of \"int_const 1\" was inferred as uint8."); text = R"( contract C { function f() pure public returns (bytes32) { @@ -6928,6 +6928,31 @@ BOOST_AUTO_TEST_CASE(non_external_fallback) CHECK_ERROR(text, TypeError, "Fallback function must be defined as \"external\"."); } +BOOST_AUTO_TEST_CASE(warn_about_sha3) +{ + char const* text = R"( + contract test { + function f() pure public { + var x = sha3(uint8(1)); + x; + } + } + )"; + CHECK_WARNING(text, "\"sha3\" has been deprecated in favour of \"keccak256\""); +} + +BOOST_AUTO_TEST_CASE(warn_about_suicide) +{ + char const* text = R"( + contract test { + function f() public { + suicide(1); + } + } + )"; + CHECK_WARNING(text, "\"suicide\" has been deprecated in favour of \"selfdestruct\""); +} + BOOST_AUTO_TEST_SUITE_END() } From d59ec0b9ab54593526a4badd81ecc7fb3cecb152 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 19 Sep 2017 15:23:40 +0200 Subject: [PATCH 146/162] Remove deps git submodule --- .gitmodules | 3 --- deps | 1 - 2 files changed, 4 deletions(-) delete mode 100644 .gitmodules delete mode 160000 deps diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index ba66d79ff..000000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "deps"] - path = deps - url = https://github.com/ethereum/cpp-dependencies diff --git a/deps b/deps deleted file mode 160000 index e5c8316db..000000000 --- a/deps +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e5c8316db8d3daa0abc3b5af8545ce330057608c From 4a499a38ea4e95ad5e4176c4586cb50bb633fdeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 19 Sep 2017 20:41:28 +0200 Subject: [PATCH 147/162] jsoncpp: Restore download dir in source tree --- cmake/jsoncpp.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/jsoncpp.cmake b/cmake/jsoncpp.cmake index 79ce4c5f8..6ddf4c74c 100644 --- a/cmake/jsoncpp.cmake +++ b/cmake/jsoncpp.cmake @@ -24,6 +24,7 @@ endif() ExternalProject_Add(jsoncpp-project PREFIX "${prefix}" + DOWNLOAD_DIR "${CMAKE_SOURCE_DIR}/deps/downloads" DOWNLOAD_NAME jsoncpp-1.7.7.tar.gz URL https://github.com/open-source-parsers/jsoncpp/archive/1.7.7.tar.gz URL_HASH SHA256=087640ebcf7fbcfe8e2717a0b9528fff89c52fcf69fa2a18cc2b538008098f97 From b9e4d5b6c87bd2b787f296e01602ee69d5846ce4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 19 Sep 2017 20:46:32 +0200 Subject: [PATCH 148/162] Windows: Copy install_deps.cmake from cpp-dependencies repo --- scripts/install_deps.bat | 2 +- scripts/install_deps.cmake | 99 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 scripts/install_deps.cmake diff --git a/scripts/install_deps.bat b/scripts/install_deps.bat index 512a28dfa..d02005ccd 100644 --- a/scripts/install_deps.bat +++ b/scripts/install_deps.bat @@ -58,4 +58,4 @@ REM REM Copyright (c) 2016 solidity contributors. REM --------------------------------------------------------------------------- -cmake -P deps\install_deps.cmake +cmake -P scripts\install_deps.cmake diff --git a/scripts/install_deps.cmake b/scripts/install_deps.cmake new file mode 100644 index 000000000..d1284b9e5 --- /dev/null +++ b/scripts/install_deps.cmake @@ -0,0 +1,99 @@ +get_filename_component(ROOT_DIR "${CMAKE_CURRENT_LIST_DIR}/../deps" ABSOLUTE) + +set(CACHE_DIR "${ROOT_DIR}/cache") +set(PACKAGES_DIR "${ROOT_DIR}/packages") + +function(download URL DST_FILE STATUS) + set(TMP_FILE "${DST_FILE}.part") + + get_filename_component(FILE_NAME ${DST_FILE} NAME) + if (NOT EXISTS ${DST_FILE}) + message("Downloading ${FILE_NAME}") + file(DOWNLOAD ${URL} ${TMP_FILE} SHOW_PROGRESS STATUS DOWNLOAD_STATUS) + list(GET DOWNLOAD_STATUS 0 STATUS_CODE) + if (STATUS_CODE EQUAL 0) + file(RENAME ${TMP_FILE} ${DST_FILE}) + else() + file(REMOVE ${TMP_FILE}) + list(GET DOWNLOAD_STATUS 1 ERROR_MSG) + + message("ERROR! Downloading '${FILE_NAME}' failed.") + message(STATUS "URL: ${URL}") + message(STATUS "Error: ${STATUS_CODE} ${ERROR_MSG}") + set(STATUS FALSE PARENT_SCOPE) + return() + endif() + else() + message("Using cached ${FILE_NAME}") + endif() + set(STATUS TRUE PARENT_SCOPE) +endfunction(download) + +function(download_and_unpack PACKAGE_URL DST_DIR) + get_filename_component(FILE_NAME ${PACKAGE_URL} NAME) + + set(DST_FILE "${CACHE_DIR}/${FILE_NAME}") + set(TMP_FILE "${DST_FILE}.part") + + file(MAKE_DIRECTORY ${CACHE_DIR}) + file(MAKE_DIRECTORY ${DST_DIR}) + + download(${PACKAGE_URL} ${DST_FILE} STATUS) + + if (STATUS) + message("Unpacking ${FILE_NAME} to ${DST_DIR}") + execute_process(COMMAND ${CMAKE_COMMAND} -E tar -xf ${DST_FILE} + WORKING_DIRECTORY ${DST_DIR}) + endif() +endfunction(download_and_unpack) + +# Packs installed package binaries and headers into an archive. +function(create_package NAME DIR) + message("Creating package ${NAME}") + file(MAKE_DIRECTORY ${PACKAGES_DIR}) + + # To create an archive without addicional top level directory + # (like package-X.Y.Z) we need to know all top level files/dirs. + # Usually it is just "win64" dir. + file(GLOB TOP_FILES RELATIVE ${DIR} "${DIR}/*") + + set(PACKAGE_FILE "${PACKAGES_DIR}/${NAME}.tar.gz") + execute_process(COMMAND ${CMAKE_COMMAND} -E + tar -czf ${PACKAGE_FILE} ${TOP_FILES} + WORKING_DIRECTORY ${DIR}) +endfunction(create_package) + +# Downloads the source code of the package and unpacks it to dedicated 'src' +# dir. Also creates 'build' and 'install' dir to be used by a build script. +function(prepare_package_source NAME VERSION URL) + set(PACKAGE_NAME "${NAME}-${VERSION}") + + set(PACKAGE_DIR "${CACHE_DIR}/${PACKAGE_NAME}") + set(SOURCE_DIR "${PACKAGE_DIR}/src") + set(BUILD_DIR "${PACKAGE_DIR}/build") + set(INSTALL_DIR "${PACKAGE_DIR}/install") + + if (NOT EXISTS ${SOURCE_DIR}) + download_and_unpack(${URL} ${PACKAGE_DIR} STATUS) + file(GLOB ORIG_SOURCE_DIR_NAME "${PACKAGE_DIR}/*") + file(RENAME ${ORIG_SOURCE_DIR_NAME} ${SOURCE_DIR}) + endif() + + file(MAKE_DIRECTORY ${BUILD_DIR}) + file(MAKE_DIRECTORY ${INSTALL_DIR}) + + # Export names and dirs to be used by a package-specific build script. + set(PACKAGE_NAME ${PACKAGE_NAME} PARENT_SCOPE) + set(SOURCE_DIR ${SOURCE_DIR} PARENT_SCOPE) + set(BUILD_DIR ${BUILD_DIR} PARENT_SCOPE) + set(INSTALL_DIR ${INSTALL_DIR} PARENT_SCOPE) +endfunction() + +set(INSTALL_DIR "${ROOT_DIR}/install") +set(SERVER "https://github.com/ethereum/cpp-dependencies/releases/download/vc140/") + +function(download_and_install PACKAGE_NAME) + download_and_unpack("${SERVER}${PACKAGE_NAME}.tar.gz" ${INSTALL_DIR}) +endfunction(download_and_install) + +download_and_install("boost-1.61") From aad829948a7b21d7664a43c1127679b53ae40bdc Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 22 Aug 2017 18:24:02 +0100 Subject: [PATCH 149/162] Ensure parameter names match between headers and implementation --- libdevcore/SwarmHash.h | 4 ++-- libevmasm/ConstantOptimiser.h | 2 +- libevmasm/GasMeter.cpp | 4 ++-- libsolidity/analysis/DocStringAnalyser.cpp | 16 ++++++++-------- libsolidity/analysis/NameAndTypeResolver.h | 2 +- libsolidity/analysis/PostTypeChecker.h | 4 ++-- libsolidity/codegen/ContractCompiler.h | 4 ++-- libsolidity/interface/ErrorReporter.h | 2 +- test/ExecutionFramework.h | 2 +- test/RPCSession.h | 2 +- 10 files changed, 21 insertions(+), 21 deletions(-) diff --git a/libdevcore/SwarmHash.h b/libdevcore/SwarmHash.h index a5da96f53..a06f7bda6 100644 --- a/libdevcore/SwarmHash.h +++ b/libdevcore/SwarmHash.h @@ -26,7 +26,7 @@ namespace dev { -/// Compute the "swarm hash" of @a _data -h256 swarmHash(std::string const& _data); +/// Compute the "swarm hash" of @a _input +h256 swarmHash(std::string const& _input); } diff --git a/libevmasm/ConstantOptimiser.h b/libevmasm/ConstantOptimiser.h index 82982e258..c450b0b4c 100644 --- a/libevmasm/ConstantOptimiser.h +++ b/libevmasm/ConstantOptimiser.h @@ -91,7 +91,7 @@ protected: } /// Replaces all constants i by the code given in @a _replacement[i]. - static void replaceConstants(AssemblyItems& _items, std::map const& _replacement); + static void replaceConstants(AssemblyItems& _items, std::map const& _replacements); Params m_params; u256 const& m_value; diff --git a/libevmasm/GasMeter.cpp b/libevmasm/GasMeter.cpp index 6a7c80e00..dad952bc3 100644 --- a/libevmasm/GasMeter.cpp +++ b/libevmasm/GasMeter.cpp @@ -189,9 +189,9 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _ return gas; } -GasMeter::GasConsumption GasMeter::wordGas(u256 const& _multiplier, ExpressionClasses::Id _position) +GasMeter::GasConsumption GasMeter::wordGas(u256 const& _multiplier, ExpressionClasses::Id _value) { - u256 const* value = m_state->expressionClasses().knownConstant(_position); + u256 const* value = m_state->expressionClasses().knownConstant(_value); if (!value) return GasConsumption::infinite(); return GasConsumption(_multiplier * ((*value + 31) / 32)); diff --git a/libsolidity/analysis/DocStringAnalyser.cpp b/libsolidity/analysis/DocStringAnalyser.cpp index d08c4eb5c..b3fb52583 100644 --- a/libsolidity/analysis/DocStringAnalyser.cpp +++ b/libsolidity/analysis/DocStringAnalyser.cpp @@ -38,30 +38,30 @@ bool DocStringAnalyser::analyseDocStrings(SourceUnit const& _sourceUnit) return !m_errorOccured; } -bool DocStringAnalyser::visit(ContractDefinition const& _node) +bool DocStringAnalyser::visit(ContractDefinition const& _contract) { static const set validTags = set{"author", "title", "dev", "notice"}; - parseDocStrings(_node, _node.annotation(), validTags, "contracts"); + parseDocStrings(_contract, _contract.annotation(), validTags, "contracts"); return true; } -bool DocStringAnalyser::visit(FunctionDefinition const& _node) +bool DocStringAnalyser::visit(FunctionDefinition const& _function) { - handleCallable(_node, _node, _node.annotation()); + handleCallable(_function, _function, _function.annotation()); return true; } -bool DocStringAnalyser::visit(ModifierDefinition const& _node) +bool DocStringAnalyser::visit(ModifierDefinition const& _modifier) { - handleCallable(_node, _node, _node.annotation()); + handleCallable(_modifier, _modifier, _modifier.annotation()); return true; } -bool DocStringAnalyser::visit(EventDefinition const& _node) +bool DocStringAnalyser::visit(EventDefinition const& _event) { - handleCallable(_node, _node, _node.annotation()); + handleCallable(_event, _event, _event.annotation()); return true; } diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h index 59bd3b1f4..d83697cda 100644 --- a/libsolidity/analysis/NameAndTypeResolver.h +++ b/libsolidity/analysis/NameAndTypeResolver.h @@ -148,7 +148,7 @@ public: private: bool visit(SourceUnit& _sourceUnit) override; void endVisit(SourceUnit& _sourceUnit) override; - bool visit(ImportDirective& _declaration) override; + bool visit(ImportDirective& _import) override; bool visit(ContractDefinition& _contract) override; void endVisit(ContractDefinition& _contract) override; bool visit(StructDefinition& _struct) override; diff --git a/libsolidity/analysis/PostTypeChecker.h b/libsolidity/analysis/PostTypeChecker.h index dbdf50e06..91d2b0b93 100644 --- a/libsolidity/analysis/PostTypeChecker.h +++ b/libsolidity/analysis/PostTypeChecker.h @@ -50,8 +50,8 @@ private: virtual bool visit(ContractDefinition const& _contract) override; virtual void endVisit(ContractDefinition const& _contract) override; - virtual bool visit(VariableDeclaration const& _declaration) override; - virtual void endVisit(VariableDeclaration const& _declaration) override; + virtual bool visit(VariableDeclaration const& _variable) override; + virtual void endVisit(VariableDeclaration const& _variable) override; virtual bool visit(Identifier const& _identifier) override; diff --git a/libsolidity/codegen/ContractCompiler.h b/libsolidity/codegen/ContractCompiler.h index 38c1e0454..7c5ee59f7 100644 --- a/libsolidity/codegen/ContractCompiler.h +++ b/libsolidity/codegen/ContractCompiler.h @@ -96,8 +96,8 @@ private: virtual bool visit(IfStatement const& _ifStatement) override; virtual bool visit(WhileStatement const& _whileStatement) override; virtual bool visit(ForStatement const& _forStatement) override; - virtual bool visit(Continue const& _continue) override; - virtual bool visit(Break const& _break) override; + virtual bool visit(Continue const& _continueStatement) override; + virtual bool visit(Break const& _breakStatement) override; virtual bool visit(Return const& _return) override; virtual bool visit(Throw const& _throw) override; virtual bool visit(VariableDeclarationStatement const& _variableDeclarationStatement) override; diff --git a/libsolidity/interface/ErrorReporter.h b/libsolidity/interface/ErrorReporter.h index 6f7ef51dd..a87db21d3 100644 --- a/libsolidity/interface/ErrorReporter.h +++ b/libsolidity/interface/ErrorReporter.h @@ -86,7 +86,7 @@ public: void fatalTypeError(SourceLocation const& _location, std::string const& _description); - void docstringParsingError(std::string const& _location); + void docstringParsingError(std::string const& _description); ErrorList const& errors() const; diff --git a/test/ExecutionFramework.h b/test/ExecutionFramework.h index ba385deee..e8d8d111c 100644 --- a/test/ExecutionFramework.h +++ b/test/ExecutionFramework.h @@ -262,7 +262,7 @@ protected: void sendMessage(bytes const& _data, bool _isCreation, u256 const& _value = 0); void sendEther(Address const& _to, u256 const& _value); size_t currentTimestamp(); - size_t blockTimestamp(u256 number); + size_t blockTimestamp(u256 _number); /// @returns the (potentially newly created) _ith address. Address account(size_t _i); diff --git a/test/RPCSession.h b/test/RPCSession.h index 558cb99f2..24bed7b1a 100644 --- a/test/RPCSession.h +++ b/test/RPCSession.h @@ -107,7 +107,7 @@ public: Json::Value eth_getBlockByNumber(std::string const& _blockNumber, bool _fullObjects); std::string eth_call(TransactionData const& _td, std::string const& _blockNumber); TransactionReceipt eth_getTransactionReceipt(std::string const& _transactionHash); - std::string eth_sendTransaction(TransactionData const& _transactionData); + std::string eth_sendTransaction(TransactionData const& _td); std::string eth_sendTransaction(std::string const& _transaction); std::string eth_getBalance(std::string const& _address, std::string const& _blockNumber); std::string eth_getStorageRoot(std::string const& _address, std::string const& _blockNumber); From 1c0c5d923a73cf3542ae41791aa7061922b9db5a Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 30 Aug 2017 22:43:01 +0100 Subject: [PATCH 150/162] Mark constructors explicit --- libsolidity/formal/SMTLib2Interface.h | 2 +- test/RPCSession.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libsolidity/formal/SMTLib2Interface.h b/libsolidity/formal/SMTLib2Interface.h index b8dac3662..63188acd5 100644 --- a/libsolidity/formal/SMTLib2Interface.h +++ b/libsolidity/formal/SMTLib2Interface.h @@ -41,7 +41,7 @@ namespace smt class SMTLib2Interface: public SolverInterface, public boost::noncopyable { public: - SMTLib2Interface(ReadCallback::Callback const& _queryCallback); + explicit SMTLib2Interface(ReadCallback::Callback const& _queryCallback); void reset() override; diff --git a/test/RPCSession.h b/test/RPCSession.h index 24bed7b1a..eae6a09c3 100644 --- a/test/RPCSession.h +++ b/test/RPCSession.h @@ -40,7 +40,7 @@ class IPCSocket : public boost::noncopyable { public: - IPCSocket(std::string const& _path); + explicit IPCSocket(std::string const& _path); std::string sendRequest(std::string const& _req); ~IPCSocket() { CloseHandle(m_socket); } @@ -55,7 +55,7 @@ private: class IPCSocket: public boost::noncopyable { public: - IPCSocket(std::string const& _path); + explicit IPCSocket(std::string const& _path); std::string sendRequest(std::string const& _req); ~IPCSocket() { close(m_socket); } From 6cec0789b56add0dddb3c478111fb713c6d432c8 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 30 Aug 2017 22:44:38 +0100 Subject: [PATCH 151/162] Limit of scope of variables in SHA3 --- libdevcore/SHA3.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/libdevcore/SHA3.cpp b/libdevcore/SHA3.cpp index 4d82ec85b..b0e40ccb8 100644 --- a/libdevcore/SHA3.cpp +++ b/libdevcore/SHA3.cpp @@ -97,10 +97,9 @@ static const uint64_t RC[24] = \ static inline void keccakf(void* state) { uint64_t* a = (uint64_t*)state; uint64_t b[5] = {0}; - uint64_t t = 0; - uint8_t x, y; for (int i = 0; i < 24; i++) { + uint8_t x, y; // Theta FOR5(x, 1, b[x] = 0; @@ -110,7 +109,7 @@ static inline void keccakf(void* state) { FOR5(y, 5, a[y + x] ^= b[(x + 4) % 5] ^ rol(b[(x + 1) % 5], 1); )) // Rho and pi - t = a[1]; + uint64_t t = a[1]; x = 0; REPEAT24(b[0] = a[pi[x]]; a[pi[x]] = rol(t, rho[x]); From efa4598c2305250ac048fad88951008c1cb55227 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 30 Aug 2017 22:44:46 +0100 Subject: [PATCH 152/162] Mark functions static --- libsolidity/ast/ASTJsonConverter.cpp | 4 ++-- libsolidity/ast/ASTJsonConverter.h | 20 ++++++++++---------- libsolidity/codegen/ExpressionCompiler.cpp | 2 +- libsolidity/codegen/ExpressionCompiler.h | 2 +- test/libsolidity/AnalysisFramework.h | 4 ++-- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index 6811d3e4e..51249f20d 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -129,7 +129,7 @@ string ASTJsonConverter::sourceLocationToString(SourceLocation const& _location) return std::to_string(_location.start) + ":" + std::to_string(length) + ":" + std::to_string(sourceIndex); } -string ASTJsonConverter::namePathToString(std::vector const& _namePath) const +string ASTJsonConverter::namePathToString(std::vector const& _namePath) { return boost::algorithm::join(_namePath, "."); } @@ -171,7 +171,7 @@ void ASTJsonConverter::appendExpressionAttributes( _attributes += exprAttributes; } -Json::Value ASTJsonConverter::inlineAssemblyIdentifierToJson(pair _info) +Json::Value ASTJsonConverter::inlineAssemblyIdentifierToJson(pair _info) const { Json::Value tuple(Json::objectValue); tuple["src"] = sourceLocationToString(_info.first->location); diff --git a/libsolidity/ast/ASTJsonConverter.h b/libsolidity/ast/ASTJsonConverter.h index 60c660c1f..9a886220f 100644 --- a/libsolidity/ast/ASTJsonConverter.h +++ b/libsolidity/ast/ASTJsonConverter.h @@ -120,7 +120,7 @@ private: std::vector>&& _attributes ); std::string sourceLocationToString(SourceLocation const& _location) const; - std::string namePathToString(std::vector const& _namePath) const; + static std::string namePathToString(std::vector const& _namePath); static Json::Value idOrNull(ASTNode const* _pt) { return _pt ? Json::Value(nodeId(*_pt)) : Json::nullValue; @@ -129,13 +129,13 @@ private: { return _node ? toJson(*_node) : Json::nullValue; } - Json::Value inlineAssemblyIdentifierToJson(std::pair _info); - std::string location(VariableDeclaration::Location _location); - std::string contractKind(ContractDefinition::ContractKind _kind); - std::string functionCallKind(FunctionCallKind _kind); - std::string literalTokenKind(Token::Value _token); - std::string type(Expression const& _expression); - std::string type(VariableDeclaration const& _varDecl); + Json::Value inlineAssemblyIdentifierToJson(std::pair _info) const; + static std::string location(VariableDeclaration::Location _location); + static std::string contractKind(ContractDefinition::ContractKind _kind); + static std::string functionCallKind(FunctionCallKind _kind); + static std::string literalTokenKind(Token::Value _token); + static std::string type(Expression const& _expression); + static std::string type(VariableDeclaration const& _varDecl); static int nodeId(ASTNode const& _node) { return _node.id(); @@ -151,8 +151,8 @@ private: } return tmp; } - Json::Value typePointerToJson(TypePointer _tp); - Json::Value typePointerToJson(std::shared_ptr> _tps); + static Json::Value typePointerToJson(TypePointer _tp); + static Json::Value typePointerToJson(std::shared_ptr> _tps); void appendExpressionAttributes( std::vector> &_attributes, ExpressionAnnotation const& _annotation diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 631a25ff9..c94baa107 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1819,7 +1819,7 @@ void ExpressionCompiler::setLValueToStorageItem(Expression const& _expression) setLValue(_expression, *_expression.annotation().type); } -bool ExpressionCompiler::cleanupNeededForOp(Type::Category _type, Token::Value _op) const +bool ExpressionCompiler::cleanupNeededForOp(Type::Category _type, Token::Value _op) { if (Token::isCompareOp(_op) || Token::isShiftOp(_op)) return true; diff --git a/libsolidity/codegen/ExpressionCompiler.h b/libsolidity/codegen/ExpressionCompiler.h index 5f6c3d64d..cdfa096ea 100644 --- a/libsolidity/codegen/ExpressionCompiler.h +++ b/libsolidity/codegen/ExpressionCompiler.h @@ -119,7 +119,7 @@ private: /// @returns true if the operator applied to the given type requires a cleanup prior to the /// operation. - bool cleanupNeededForOp(Type::Category _type, Token::Value _op) const; + static bool cleanupNeededForOp(Type::Category _type, Token::Value _op); /// @returns the CompilerUtils object containing the current context. CompilerUtils utils(); diff --git a/test/libsolidity/AnalysisFramework.h b/test/libsolidity/AnalysisFramework.h index f73f06c23..172ae01b9 100644 --- a/test/libsolidity/AnalysisFramework.h +++ b/test/libsolidity/AnalysisFramework.h @@ -59,8 +59,8 @@ protected: void printErrors(); - ContractDefinition const* retrieveContractByName(SourceUnit const& _source, std::string const& _name); - FunctionTypePointer retrieveFunctionBySignature( + static ContractDefinition const* retrieveContractByName(SourceUnit const& _source, std::string const& _name); + static FunctionTypePointer retrieveFunctionBySignature( ContractDefinition const& _contract, std::string const& _signature ); From f958463416e88a769d0904ca3da48dac8a9c1c5a Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 12 Sep 2017 16:08:15 +0100 Subject: [PATCH 153/162] Rename MiniMoustache to Whiskers in tests --- test/libdevcore/{MiniMoustache.cpp => Whiskers.cpp} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/libdevcore/{MiniMoustache.cpp => Whiskers.cpp} (100%) diff --git a/test/libdevcore/MiniMoustache.cpp b/test/libdevcore/Whiskers.cpp similarity index 100% rename from test/libdevcore/MiniMoustache.cpp rename to test/libdevcore/Whiskers.cpp From 3b813ed29569dde02b965c97c9fdd60469876f66 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 19 May 2017 17:06:26 +0100 Subject: [PATCH 154/162] Support multiple assignment in inline assembly --- Changelog.md | 1 + libjulia/backends/evm/EVMCodeTransform.cpp | 42 ++++++++++++---------- libjulia/backends/evm/EVMCodeTransform.h | 2 +- libsolidity/inlineasm/AsmAnalysis.cpp | 20 +++++++++-- libsolidity/inlineasm/AsmData.h | 6 +++- libsolidity/inlineasm/AsmParser.cpp | 30 +++++++++++++++- libsolidity/inlineasm/AsmPrinter.cpp | 6 +++- test/libjulia/Parser.cpp | 20 +++++++++++ test/libsolidity/InlineAssembly.cpp | 18 ++++++++++ test/libsolidity/SolidityEndToEndTest.cpp | 25 +++++++++++++ 10 files changed, 145 insertions(+), 25 deletions(-) diff --git a/Changelog.md b/Changelog.md index f1b1a19cf..9c623805c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,7 @@ Features: * Support ``pragma experimental "v0.5.0";`` to turn on upcoming breaking changes. + * Assembly Parser: Support multiple assignment (``x, y := f()``). * Code Generator: Added ``.selector`` member on external function types to retrieve their signature. * Code Generator: Keep a single copy of encoding functions when using the experimental "ABIEncoderV2". * Optimizer: Add new optimization step to remove unused ``JUMPDEST``s. diff --git a/libjulia/backends/evm/EVMCodeTransform.cpp b/libjulia/backends/evm/EVMCodeTransform.cpp index e0b11cf33..e80903d59 100644 --- a/libjulia/backends/evm/EVMCodeTransform.cpp +++ b/libjulia/backends/evm/EVMCodeTransform.cpp @@ -60,16 +60,19 @@ void CodeTransform::operator()(VariableDeclaration const& _varDecl) void CodeTransform::operator()(Assignment const& _assignment) { - visitExpression(*_assignment.value); + int height = m_assembly.stackHeight(); + boost::apply_visitor(*this, *_assignment.value); + expectDeposit(_assignment.variableNames.size(), height); + m_assembly.setSourceLocation(_assignment.location); - generateAssignment(_assignment.variableName); + generateAssignment(_assignment.variableNames); checkStackHeight(&_assignment); } void CodeTransform::operator()(StackAssignment const& _assignment) { m_assembly.setSourceLocation(_assignment.location); - generateAssignment(_assignment.variableName); + generateAssignment({_assignment.variableName}); checkStackHeight(&_assignment); } @@ -469,24 +472,27 @@ void CodeTransform::finalizeBlock(Block const& _block, int blockStartStackHeight checkStackHeight(&_block); } -void CodeTransform::generateAssignment(Identifier const& _variableName) +void CodeTransform::generateAssignment(vector const& _variableNames) { solAssert(m_scope, ""); - auto var = m_scope->lookup(_variableName.name); - if (var) + for (auto const& variableName: _variableNames | boost::adaptors::reversed) { - Scope::Variable const& _var = boost::get(*var); - if (int heightDiff = variableHeightDiff(_var, true)) - m_assembly.appendInstruction(solidity::swapInstruction(heightDiff - 1)); - m_assembly.appendInstruction(solidity::Instruction::POP); - } - else - { - solAssert( - m_identifierAccess.generateCode, - "Identifier not found and no external access available." - ); - m_identifierAccess.generateCode(_variableName, IdentifierContext::LValue, m_assembly); + auto var = m_scope->lookup(variableName.name); + if (var) + { + Scope::Variable const& _var = boost::get(*var); + if (int heightDiff = variableHeightDiff(_var, true)) + m_assembly.appendInstruction(solidity::swapInstruction(heightDiff - 1)); + m_assembly.appendInstruction(solidity::Instruction::POP); + } + else + { + solAssert( + m_identifierAccess.generateCode, + "Identifier not found and no external access available." + ); + m_identifierAccess.generateCode(variableName, IdentifierContext::LValue, m_assembly); + } } } diff --git a/libjulia/backends/evm/EVMCodeTransform.h b/libjulia/backends/evm/EVMCodeTransform.h index 2c0fd10cc..bb2be7864 100644 --- a/libjulia/backends/evm/EVMCodeTransform.h +++ b/libjulia/backends/evm/EVMCodeTransform.h @@ -124,7 +124,7 @@ private: /// to @a _blackStartStackHeight. void finalizeBlock(solidity::assembly::Block const& _block, int _blockStartStackHeight); - void generateAssignment(solidity::assembly::Identifier const& _variableName); + void generateAssignment(std::vector const& _variableNames); /// Determines the stack height difference to the given variables. Throws /// if it is not yet in scope or the height difference is too large. Returns diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index 76b0bbd54..e5bdc90f9 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -163,11 +163,25 @@ bool AsmAnalyzer::operator()(assembly::StackAssignment const& _assignment) bool AsmAnalyzer::operator()(assembly::Assignment const& _assignment) { + int const expectedItems = _assignment.variableNames.size(); + solAssert(expectedItems >= 1, ""); int const stackHeight = m_stackHeight; bool success = boost::apply_visitor(*this, *_assignment.value); - solAssert(m_stackHeight >= stackHeight, "Negative value size."); - if (!checkAssignment(_assignment.variableName, m_stackHeight - stackHeight)) - success = false; + if ((m_stackHeight - stackHeight) != expectedItems) + { + m_errorReporter.declarationError( + _assignment.location, + "Variable count does not match number of values (" + + to_string(expectedItems) + + " vs. " + + to_string(m_stackHeight - stackHeight) + + ")" + ); + return false; + } + for (auto const& variableName: _assignment.variableNames) + if (!checkAssignment(variableName, 1)) + success = false; m_info.stackHeightInfo[&_assignment] = m_stackHeight; return success; } diff --git a/libsolidity/inlineasm/AsmData.h b/libsolidity/inlineasm/AsmData.h index db5840bc9..b0dd85ca8 100644 --- a/libsolidity/inlineasm/AsmData.h +++ b/libsolidity/inlineasm/AsmData.h @@ -54,7 +54,11 @@ struct Label { SourceLocation location; std::string name; }; struct StackAssignment { SourceLocation location; Identifier variableName; }; /// Assignment ("x := mload(20:u256)", expects push-1-expression on the right hand /// side and requires x to occupy exactly one stack slot. -struct Assignment { SourceLocation location; Identifier variableName; std::shared_ptr value; }; +/// +/// Multiple assignment ("x, y := f()"), where the left hand side variables each occupy +/// a single stack slot and expects a single expression on the right hand returning +/// the same amount of items as the number of variables. +struct Assignment { SourceLocation location; std::vector variableNames; std::shared_ptr value; }; /// Functional instruction, e.g. "mul(mload(20:u256), add(2:u256, x))" struct FunctionalInstruction { SourceLocation location; Instruction instruction; std::vector arguments; }; struct FunctionCall { SourceLocation location; Identifier functionName; std::vector arguments; }; diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index d84fe999c..3087ad864 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -122,6 +122,34 @@ assembly::Statement Parser::parseStatement() { case Token::LParen: return parseCall(std::move(statement)); + case Token::Comma: + { + // if a comma follows, a multiple assignment is assumed + + if (statement.type() != typeid(assembly::Identifier)) + fatalParserError("Label name / variable name must precede \",\" (multiple assignment)."); + assembly::Identifier const& identifier = boost::get(statement); + + Assignment assignment = createWithLocation(identifier.location); + assignment.variableNames.emplace_back(identifier); + + do + { + expectToken(Token::Comma); + statement = parseElementaryOperation(false); + if (statement.type() != typeid(assembly::Identifier)) + fatalParserError("Variable name expected in multiple assignemnt."); + assignment.variableNames.emplace_back(boost::get(statement)); + } + while (currentToken() == Token::Comma); + + expectToken(Token::Colon); + expectToken(Token::Assign); + + assignment.value.reset(new Statement(parseExpression())); + assignment.location.end = locationOf(*assignment.value).end; + return assignment; + } case Token::Colon: { if (statement.type() != typeid(assembly::Identifier)) @@ -136,7 +164,7 @@ assembly::Statement Parser::parseStatement() if (!m_julia && instructions().count(identifier.name)) fatalParserError("Cannot use instruction names for identifier names."); advance(); - assignment.variableName = identifier; + assignment.variableNames.emplace_back(identifier); assignment.value.reset(new Statement(parseExpression())); assignment.location.end = locationOf(*assignment.value).end; return assignment; diff --git a/libsolidity/inlineasm/AsmPrinter.cpp b/libsolidity/inlineasm/AsmPrinter.cpp index 47ede91d4..a52728084 100644 --- a/libsolidity/inlineasm/AsmPrinter.cpp +++ b/libsolidity/inlineasm/AsmPrinter.cpp @@ -116,7 +116,11 @@ string AsmPrinter::operator()(assembly::StackAssignment const& _assignment) string AsmPrinter::operator()(assembly::Assignment const& _assignment) { - return (*this)(_assignment.variableName) + " := " + boost::apply_visitor(*this, *_assignment.value); + solAssert(_assignment.variableNames.size() >= 1, ""); + string variables = (*this)(_assignment.variableNames.front()); + for (size_t i = 1; i < _assignment.variableNames.size(); ++i) + variables += ", " + (*this)(_assignment.variableNames[i]); + return variables + " := " + boost::apply_visitor(*this, *_assignment.value); } string AsmPrinter::operator()(assembly::VariableDeclaration const& _variableDeclaration) diff --git a/test/libjulia/Parser.cpp b/test/libjulia/Parser.cpp index 510703708..f8c1aa4de 100644 --- a/test/libjulia/Parser.cpp +++ b/test/libjulia/Parser.cpp @@ -249,6 +249,26 @@ BOOST_AUTO_TEST_CASE(recursion_depth) CHECK_ERROR(input, ParserError, "recursion"); } +BOOST_AUTO_TEST_CASE(multiple_assignment) +{ + CHECK_ERROR("{ let x:u256 function f() -> a:u256, b:u256 {} 123:u256, x := f() }", ParserError, "Label name / variable name must precede \",\" (multiple assignment)."); + CHECK_ERROR("{ let x:u256 function f() -> a:u256, b:u256 {} x, 123:u256 := f() }", ParserError, "Variable name expected in multiple assignemnt."); + + /// NOTE: Travis hiccups if not having a variable + char const* text = R"( + { + function f(a:u256) -> r1:u256, r2:u256 { + r1 := a + r2 := 7:u256 + } + let x:u256 := 9:u256 + let y:u256 := 2:u256 + x, y := f(x) + } + )"; + BOOST_CHECK(successParse(text)); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index 0debc66d8..da3522b41 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -412,7 +412,25 @@ BOOST_AUTO_TEST_CASE(recursion_depth) CHECK_PARSE_ERROR(input, ParserError, "recursion"); } +BOOST_AUTO_TEST_CASE(multiple_assignment) +{ + CHECK_PARSE_ERROR("{ let x function f() -> a, b {} 123, x := f() }", ParserError, "Label name / variable name must precede \",\" (multiple assignment)."); + CHECK_PARSE_ERROR("{ let x function f() -> a, b {} x, 123 := f() }", ParserError, "Variable name expected in multiple assignemnt."); + /// NOTE: Travis hiccups if not having a variable + char const* text = R"( + { + function f(a) -> r1, r2 { + r1 := a + r2 := 7 + } + let x := 9 + let y := 2 + x, y := f(x) + } + )"; + BOOST_CHECK(successParse(text)); +} BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index bdac82783..458b64f40 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -7867,6 +7867,31 @@ BOOST_AUTO_TEST_CASE(inline_assembly_function_call) BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(1), u256(2), u256(7))); } +BOOST_AUTO_TEST_CASE(inline_assembly_function_call_assignment) +{ + char const* sourceCode = R"( + contract C { + function f() { + assembly { + let a1, b1, c1 + function asmfun(a, b, c) -> x, y, z { + x := a + y := b + z := 7 + } + a1, b1, c1 := asmfun(1, 2, 3) + mstore(0x00, a1) + mstore(0x20, b1) + mstore(0x40, c1) + return(0, 0x60) + } + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(1), u256(2), u256(7))); +} + BOOST_AUTO_TEST_CASE(inline_assembly_function_call2) { char const* sourceCode = R"( From 6948758156ba31b22fb74a3cd3e7cec0b925208b Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 19 Sep 2017 12:03:45 +0100 Subject: [PATCH 155/162] Limit parser recursion depth further (needed by increased assembly data structure size) --- libsolidity/parsing/ParserBase.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsolidity/parsing/ParserBase.cpp b/libsolidity/parsing/ParserBase.cpp index fe95b0fee..5b83c5bdc 100644 --- a/libsolidity/parsing/ParserBase.cpp +++ b/libsolidity/parsing/ParserBase.cpp @@ -104,7 +104,7 @@ void ParserBase::expectToken(Token::Value _value) void ParserBase::increaseRecursionDepth() { m_recursionDepth++; - if (m_recursionDepth >= 3000) + if (m_recursionDepth >= 2560) fatalParserError("Maximum recursion depth reached during parsing."); } From e14ab959f928c0a058b7b46d6ba4ee30e7ec08b7 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 20 Sep 2017 11:16:07 +0200 Subject: [PATCH 156/162] Remove unintentional copy in assignment operation. --- libjulia/backends/evm/EVMCodeTransform.cpp | 42 ++++++++++++---------- libjulia/backends/evm/EVMCodeTransform.h | 3 +- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/libjulia/backends/evm/EVMCodeTransform.cpp b/libjulia/backends/evm/EVMCodeTransform.cpp index e80903d59..66f593e80 100644 --- a/libjulia/backends/evm/EVMCodeTransform.cpp +++ b/libjulia/backends/evm/EVMCodeTransform.cpp @@ -65,14 +65,14 @@ void CodeTransform::operator()(Assignment const& _assignment) expectDeposit(_assignment.variableNames.size(), height); m_assembly.setSourceLocation(_assignment.location); - generateAssignment(_assignment.variableNames); + generateMultiAssignment(_assignment.variableNames); checkStackHeight(&_assignment); } void CodeTransform::operator()(StackAssignment const& _assignment) { m_assembly.setSourceLocation(_assignment.location); - generateAssignment({_assignment.variableName}); + generateAssignment(_assignment.variableName); checkStackHeight(&_assignment); } @@ -472,27 +472,31 @@ void CodeTransform::finalizeBlock(Block const& _block, int blockStartStackHeight checkStackHeight(&_block); } -void CodeTransform::generateAssignment(vector const& _variableNames) +void CodeTransform::generateMultiAssignment(vector const& _variableNames) { solAssert(m_scope, ""); for (auto const& variableName: _variableNames | boost::adaptors::reversed) + generateAssignment(variableName); +} + +void CodeTransform::generateAssignment(Identifier const& _variableName) +{ + solAssert(m_scope, ""); + auto var = m_scope->lookup(_variableName.name); + if (var) { - auto var = m_scope->lookup(variableName.name); - if (var) - { - Scope::Variable const& _var = boost::get(*var); - if (int heightDiff = variableHeightDiff(_var, true)) - m_assembly.appendInstruction(solidity::swapInstruction(heightDiff - 1)); - m_assembly.appendInstruction(solidity::Instruction::POP); - } - else - { - solAssert( - m_identifierAccess.generateCode, - "Identifier not found and no external access available." - ); - m_identifierAccess.generateCode(variableName, IdentifierContext::LValue, m_assembly); - } + Scope::Variable const& _var = boost::get(*var); + if (int heightDiff = variableHeightDiff(_var, true)) + m_assembly.appendInstruction(solidity::swapInstruction(heightDiff - 1)); + m_assembly.appendInstruction(solidity::Instruction::POP); + } + else + { + solAssert( + m_identifierAccess.generateCode, + "Identifier not found and no external access available." + ); + m_identifierAccess.generateCode(_variableName, IdentifierContext::LValue, m_assembly); } } diff --git a/libjulia/backends/evm/EVMCodeTransform.h b/libjulia/backends/evm/EVMCodeTransform.h index bb2be7864..951c8a50e 100644 --- a/libjulia/backends/evm/EVMCodeTransform.h +++ b/libjulia/backends/evm/EVMCodeTransform.h @@ -124,7 +124,8 @@ private: /// to @a _blackStartStackHeight. void finalizeBlock(solidity::assembly::Block const& _block, int _blockStartStackHeight); - void generateAssignment(std::vector const& _variableNames); + void generateMultiAssignment(std::vector const& _variableNames); + void generateAssignment(solidity::assembly::Identifier const& _variableName); /// Determines the stack height difference to the given variables. Throws /// if it is not yet in scope or the height difference is too large. Returns From 49c98bbc34902d2f5d66518341cfd41ac2d7bef3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 20 Sep 2017 16:39:41 +0200 Subject: [PATCH 157/162] CMake: Better ccache configuration EthCcache module taken from cpp-ethereum. --- CMakeLists.txt | 3 +++ cmake/EthCcache.cmake | 15 +++++++++++++++ cmake/EthCompilerSettings.cmake | 8 -------- 3 files changed, 18 insertions(+), 8 deletions(-) create mode 100644 cmake/EthCcache.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 89d627dc2..139d4fd5d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,9 @@ project(solidity VERSION ${PROJECT_VERSION}) option(SOLC_LINK_STATIC "Link solc executable statically on supported platforms" OFF) +# Setup cccache. +include(EthCcache) + # Let's find our dependencies include(EthDependencies) include(jsoncpp) diff --git a/cmake/EthCcache.cmake b/cmake/EthCcache.cmake new file mode 100644 index 000000000..9410cbcde --- /dev/null +++ b/cmake/EthCcache.cmake @@ -0,0 +1,15 @@ +# Setup ccache. +# +# The ccache is auto-enabled if the tool is found. +# To disable set -DCCACHE=OFF option. +if(NOT DEFINED CMAKE_CXX_COMPILER_LAUNCHER) + find_program(CCACHE ccache DOC "ccache tool path; set to OFF to disable") + if(CCACHE) + set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE}) + if(COMMAND cotire) + # Change ccache config to meet cotire requirements. + set(ENV{CCACHE_SLOPPINESS} pch_defines,time_macros) + endif() + message(STATUS "[ccache] Enabled: ${CCACHE}") + endif() +endif() diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index 117dd319e..1a00ae706 100644 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -14,14 +14,6 @@ # # These settings then end up spanning all POSIX platforms (Linux, OS X, BSD, etc) -# Use ccache if available -find_program(CCACHE_FOUND ccache) -if(CCACHE_FOUND) - set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache) - set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache) - message("Using ccache") -endif(CCACHE_FOUND) - include(CheckCXXCompilerFlag) check_cxx_compiler_flag(-fstack-protector-strong have_stack_protector_strong) From 7b7cf8faea296226143380b9a6219899f1151611 Mon Sep 17 00:00:00 2001 From: Matthieu Caneill Date: Thu, 21 Sep 2017 01:14:38 +0200 Subject: [PATCH 158/162] fix typo: missing word in documentation --- docs/introduction-to-smart-contracts.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index fd12e97b0..aedc0c09f 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -62,8 +62,8 @@ so that only you can alter the number. the ASCII character set. It is possible to store UTF-8 encoded data in string variables. .. warning:: - Be careful with using Unicode text as similarly looking (or even identical) can have different - code points and as such will be encoded as a different byte array. + Be careful with using Unicode text as similarly looking (or even identical) characters can + have different code points and as such will be encoded as a different byte array. .. index:: ! subcurrency From cb0be0f6a8af475d9aa0c99ae7548a5bacd4fc2b Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 21 Sep 2017 10:51:36 +0200 Subject: [PATCH 159/162] Try to fix docker release deployment. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 5d6ce442a..c30e3e0f1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -220,7 +220,7 @@ deploy: branch: - develop - release - - /^v[0-9]/ + - /^v\d/ # This is the deploy target for the native build (Linux and macOS) # which generates ZIPs per commit and the source tarball. # From 7f88d439375ad97269b7de231cf3c3f22c36c6c8 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 21 Sep 2017 14:15:13 +0200 Subject: [PATCH 160/162] docs: Specify gas fallback function from call Specify that also 21k gas is needed for fallback functions originating from external function calls. --- docs/contracts.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index 3bd6f6a81..69600fc16 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -519,8 +519,7 @@ Ether (without data). Additionally, in order to receive Ether, the fallback func must be marked ``payable``. If no such function exists, the contract cannot receive Ether through regular transactions. -In such a context, there is usually very little gas available to the function call (to be precise, 2300 gas), -so it is important to make fallback functions as cheap as possible. +In such a context, there is usually very little gas available to the function call (to be precise, 2300 gas), so it is important to make fallback functions as cheap as possible. Note that the gas required by a transaction (as opposed to an internal call) that invokes the fallback function is much higher, because each transaction charges an additional amount of 21000 gas or more for things like signature checking. In particular, the following operations will consume more gas than the stipend provided to a fallback function: From 22a58ad2d8bdde4ebc017b9f218e6eafc15e39c1 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 21 Sep 2017 10:09:36 +0200 Subject: [PATCH 161/162] Changelog cleanup in preparation for 0.4.17. --- Changelog.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Changelog.md b/Changelog.md index 458a9543a..bdd6ac468 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,13 +1,13 @@ -### 0.4.17 (unreleased) +### 0.4.17 (2017-09-21) Features: - * Support ``pragma experimental "v0.5.0";`` to turn on upcoming breaking changes. * Assembly Parser: Support multiple assignment (``x, y := f()``). - * Code Generator: Added ``.selector`` member on external function types to retrieve their signature. * Code Generator: Keep a single copy of encoding functions when using the experimental "ABIEncoderV2". + * Code Generator: Partial support for passing ``structs`` as arguments and return parameters (requires ``pragma experimental ABIEncoderV2;`` for now). + * General: Support ``pragma experimental "v0.5.0";`` to activate upcoming breaking changes. + * General: Added ``.selector`` member on external function types to retrieve their signature. * Optimizer: Add new optimization step to remove unused ``JUMPDEST``s. - * Code Generator: Support passing ``structs`` as arguments and return parameters (requires ``pragma experimental ABIEncoderV2;`` for now). - * Static Analyzer: Warn for using deprecated builtins ``sha3`` and ``suicide`` + * Static Analyzer: Warn when using deprecated builtins ``sha3`` and ``suicide`` (replaced by ``keccak256`` and ``selfdestruct``, introduced in 0.4.2 and 0.2.0, respectively). * Syntax Checker: Warn if no visibility is specified on contract functions. * Type Checker: Display helpful warning for unused function arguments/return parameters. From 3dc7c3807b403d861a956a31e49ad13de8cb0b3f Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 21 Sep 2017 15:26:17 +0200 Subject: [PATCH 162/162] Update bug list. --- 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 ea2420855..c3686ebfd 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -358,6 +358,10 @@ "bugs": [], "released": "2017-08-24" }, + "0.4.17": { + "bugs": [], + "released": "2017-09-21" + }, "0.4.2": { "bugs": [ "DelegateCallReturnValue",