diff options
51 files changed, 1065 insertions, 207 deletions
diff --git a/.travis.yml b/.travis.yml index c30e3e0f..708d3620 100644 --- a/.travis.yml +++ b/.travis.yml @@ -49,13 +49,6 @@ env: matrix: include: - # Ubuntu 14.04 LTS "Trusty Tahr" - # https://en.wikipedia.org/wiki/List_of_Ubuntu_releases#Ubuntu_14.04_LTS_.28Trusty_Tahr.29 - # - # TravisCI doesn't directly support any new Ubuntu releases. These is - # some Docker support, which we should probably investigate, at least for - # Ubuntu 16.04 LTS "Xenial Xerus" - # See https://en.wikipedia.org/wiki/List_of_Ubuntu_releases#Ubuntu_16.04_LTS_.28Xenial_Xerus.29. - os: linux dist: trusty sudo: required diff --git a/Changelog.md b/Changelog.md index 9755d2d3..8f97b0e8 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,8 +2,20 @@ Features: * Parser: Better error message for unexpected trailing comma in parameter lists. + * Standard JSON: Support the ``outputSelection`` field for selective compilation of supplied sources. + * Syntax Checker: Unary ``+`` is now a syntax error as experimental 0.5.0 feature. + * Type Checker: Disallow non-pure constant state variables as experimental 0.5.0 feature. + * Code Generator: Always use all available gas for calls as experimental 0.5.0 feature + (previously, some amount was retained in order to work in pre-tangerine whistle + EVM versions) Bugfixes: + * Parser: Fix source location of VariableDeclarationStatement. + * Type Checker: Properly check array length and don't rely on an assertion in code generation. + * Type Checker: Properly support overwriting members inherited from ``address`` in a contract + (such as ``balance``, ``transfer``, etc.) + * Type Checker: Prevent duplicate event declarations. + * Type Checker: Do not mark event parameters as shadowing state variables. ### 0.4.17 (2017-09-21) diff --git a/appveyor.yml b/appveyor.yml index 3d4d65bb..b50681b9 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -71,7 +71,7 @@ build_script: test_script: - cd %APPVEYOR_BUILD_FOLDER% - cd %APPVEYOR_BUILD_FOLDER%\build\test\%CONFIGURATION% - - soltest.exe --show-progress -- --no-ipc + - soltest.exe --show-progress -- --no-ipc --no-smt artifacts: - path: solidity-windows.zip diff --git a/docs/common-patterns.rst b/docs/common-patterns.rst index acef13b7..52319be0 100644 --- a/docs/common-patterns.rst +++ b/docs/common-patterns.rst @@ -93,7 +93,7 @@ Notice that, in this example, an attacker could trap the contract into an unusable state by causing ``richest`` to be the address of a contract that has a fallback function which fails (e.g. by using ``revert()`` or by just -conssuming more than the 2300 gas stipend). That way, +consuming more than the 2300 gas stipend). That way, whenever ``transfer`` is called to deliver funds to the "poisoned" contract, it will fail and thus also ``becomeRichest`` will fail, with the contract being stuck forever. @@ -121,7 +121,7 @@ unless you declare make your state variables ``public``. Furthermore, you can restrict who can make modifications to your contract's state or call your contract's -functions and this is what this page is about. +functions and this is what this section is about. .. index:: function;modifier diff --git a/docs/contracts.rst b/docs/contracts.rst index 69600fc1..cdc92315 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -20,7 +20,7 @@ Contracts can be created "from outside" via Ethereum transactions or from within IDEs, such as `Remix <https://remix.ethereum.org/>`_, make the creation process seamless using UI elements. -Creating contracts programatically on Ethereum is best done via using the JavaScript API `web3.js <https://github.com/etherem/web3.js>`_. +Creating contracts programatically on Ethereum is best done via using the JavaScript API `web3.js <https://github.com/ethereum/web3.js>`_. As of today it has a method called `web3.eth.Contract <https://web3js.readthedocs.io/en/1.0/web3-eth-contract.html#new-contract>`_ to facilitate contract creation. diff --git a/docs/contributing.rst b/docs/contributing.rst index 01caa5b1..0f7c3e72 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -64,9 +64,11 @@ 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. +called ``soltest``. Some of them require the ``cpp-ethereum`` client in testing mode, +some others require ``libz3`` to be installed. -To run a subset of the tests that do not require ``cpp-ethereum``, use ``./build/test/soltest -- --no-ipc``. +To disable the z3 tests, use ``./build/test/soltest -- --no-smt`` and +to run a subset of the tests that do not require ``cpp-ethereum``, use ``./build/test/soltest -- --no-ipc``. For all other tests, you need to install `cpp-ethereum <https://github.com/ethereum/cpp-ethereum/releases/download/solidityTester/eth>`_ and run it in testing mode: ``eth --test -d /tmp/testeth``. diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index 93dcfbb8..5c427c69 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -432,12 +432,12 @@ What happens to a ``struct``'s mapping when copying over a ``struct``? This is a very interesting question. Suppose that we have a contract field set up like such:: struct user { - mapping(string => address) usedContracts; + mapping(string => string) comments; } function somefunction { user user1; - user1.usedContracts["Hello"] = "World"; + user1.comments["Hello"] = "World"; user user2 = user1; } diff --git a/libdevcore/Exceptions.cpp b/libdevcore/Exceptions.cpp index f422d926..f204dbc2 100644 --- a/libdevcore/Exceptions.cpp +++ b/libdevcore/Exceptions.cpp @@ -24,10 +24,14 @@ using namespace dev; char const* Exception::what() const noexcept { + // Return the comment if available. if (string const* cmt = comment()) - return cmt->c_str(); - else - return nullptr; + return cmt->data(); + + // Fallback to base what(). + // Boost accepts nullptr, but the C++ standard doesn't + // and crashes on some platforms. + return std::exception::what(); } string Exception::lineInfo() const diff --git a/libevmasm/AssemblyItem.cpp b/libevmasm/AssemblyItem.cpp index cfe91be0..64963021 100644 --- a/libevmasm/AssemblyItem.cpp +++ b/libevmasm/AssemblyItem.cpp @@ -17,8 +17,6 @@ #include <libevmasm/AssemblyItem.h> -#include <libevmasm/SemanticInformation.h> - #include <libdevcore/CommonData.h> #include <libdevcore/FixedHash.h> @@ -112,7 +110,7 @@ bool AssemblyItem::canBeFunctional() const switch (m_type) { case Operation: - return !SemanticInformation::isDupInstruction(*this) && !SemanticInformation::isSwapInstruction(*this); + return !isDupInstruction(instruction()) && !isSwapInstruction(instruction()); case Push: case PushString: case PushTag: diff --git a/libevmasm/EVMSchedule.h b/libevmasm/EVMSchedule.h deleted file mode 100644 index 1695a59c..00000000 --- a/libevmasm/EVMSchedule.h +++ /dev/null @@ -1,62 +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 <http://www.gnu.org/licenses/>. -*/ -/** @file EVMSchedule.h - * @author Gav <i@gavwood.com> - * @author Christian <c@ethdev.com> - * @date 2015 - */ - -#pragma once - -namespace dev -{ -namespace solidity -{ - -struct EVMSchedule -{ - unsigned stackLimit = 1024; - unsigned expGas = 10; - unsigned expByteGas = 10; - unsigned keccak256Gas = 30; - unsigned keccak256WordGas = 6; - unsigned sloadGas = 200; - unsigned sstoreSetGas = 20000; - unsigned sstoreResetGas = 5000; - unsigned sstoreRefundGas = 15000; - unsigned jumpdestGas = 1; - unsigned logGas = 375; - unsigned logDataGas = 8; - unsigned logTopicGas = 375; - unsigned createGas = 32000; - unsigned callGas = 40; - unsigned callStipend = 2300; - unsigned callValueTransferGas = 9000; - unsigned callNewAccountGas = 25000; - unsigned selfdestructRefundGas = 24000; - unsigned memoryGas = 3; - unsigned quadCoeffDiv = 512; - unsigned createDataGas = 200; - unsigned txGas = 21000; - unsigned txCreateGas = 53000; - unsigned txDataZeroGas = 4; - unsigned txDataNonZeroGas = 68; - unsigned copyGas = 3; -}; - -} -} diff --git a/libevmasm/Instruction.h b/libevmasm/Instruction.h index afbef71d..d9c53900 100644 --- a/libevmasm/Instruction.h +++ b/libevmasm/Instruction.h @@ -197,6 +197,24 @@ enum class Instruction: uint8_t SELFDESTRUCT = 0xff ///< halt execution and register account for later deletion }; +/// @returns true if the instruction is a PUSH +inline bool isPushInstruction(Instruction _inst) +{ + return Instruction::PUSH1 <= _inst && _inst <= Instruction::PUSH32; +} + +/// @returns true if the instruction is a DUP +inline bool isDupInstruction(Instruction _inst) +{ + return Instruction::DUP1 <= _inst && _inst <= Instruction::DUP16; +} + +/// @returns true if the instruction is a SWAP +inline bool isSwapInstruction(Instruction _inst) +{ + return Instruction::SWAP1 <= _inst && _inst <= Instruction::SWAP16; +} + /// @returns the number of PUSH Instruction _inst inline unsigned getPushNumber(Instruction _inst) { diff --git a/libevmasm/SemanticInformation.cpp b/libevmasm/SemanticInformation.cpp index ceb3fbdd..83cfe2c6 100644 --- a/libevmasm/SemanticInformation.cpp +++ b/libevmasm/SemanticInformation.cpp @@ -90,14 +90,14 @@ bool SemanticInformation::isDupInstruction(AssemblyItem const& _item) { if (_item.type() != Operation) return false; - return Instruction::DUP1 <= _item.instruction() && _item.instruction() <= Instruction::DUP16; + return solidity::isDupInstruction(_item.instruction()); } bool SemanticInformation::isSwapInstruction(AssemblyItem const& _item) { if (_item.type() != Operation) return false; - return Instruction::SWAP1 <= _item.instruction() && _item.instruction() <= Instruction::SWAP16; + return solidity::isSwapInstruction(_item.instruction()); } bool SemanticInformation::isJumpInstruction(AssemblyItem const& _item) diff --git a/liblll/CodeFragment.cpp b/liblll/CodeFragment.cpp index 254f436f..d4ef3888 100644 --- a/liblll/CodeFragment.cpp +++ b/liblll/CodeFragment.cpp @@ -47,7 +47,8 @@ void CodeFragment::finalise(CompilerState const& _cs) } } -CodeFragment::CodeFragment(sp::utree const& _t, CompilerState& _s, bool _allowASM) +CodeFragment::CodeFragment(sp::utree const& _t, CompilerState& _s, ReadCallback const& _readFile, bool _allowASM): + m_readFile(_readFile) { /* std::cout << "CodeFragment. Locals:"; @@ -103,7 +104,7 @@ CodeFragment::CodeFragment(sp::utree const& _t, CompilerState& _s, bool _allowAS { bigint i = *_t.get<bigint*>(); if (i < 0 || i > bigint(u256(0) - 1)) - error<IntegerOutOfRange>(); + error<IntegerOutOfRange>(toString(i)); m_asm.append((u256)i); break; } @@ -113,6 +114,22 @@ CodeFragment::CodeFragment(sp::utree const& _t, CompilerState& _s, bool _allowAS } } +namespace +{ +/// Returns true iff the instruction is valid as a function. +bool validFunctionalInstruction(string us) +{ + auto it = c_instructions.find(us); + return !( + it == c_instructions.end() || + solidity::isPushInstruction(it->second) || + solidity::isDupInstruction(it->second) || + solidity::isSwapInstruction(it->second) || + it->second == solidity::Instruction::JUMPDEST + ); +} +} + void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) { if (_t.tag() == 0 && _t.empty()) @@ -157,7 +174,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) { auto i = *++_t.begin(); if (i.tag()) - error<InvalidName>(); + error<InvalidName>(toString(i)); if (i.which() == sp::utree_type::string_type) { auto sr = i.get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::string_type>>(); @@ -198,7 +215,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) int c = 0; for (auto const& i: _t) if (c++) - m_asm.append(CodeFragment(i, _s, true).m_asm); + m_asm.append(CodeFragment(i, _s, m_readFile, true).m_asm); } else if (us == "INCLUDE") { @@ -207,10 +224,12 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) string fileName = firstAsString(); if (fileName.empty()) error<InvalidName>("Empty file name provided"); - string contents = contentsString(fileName); + if (!m_readFile) + error<InvalidName>("Import callback not present"); + string contents = m_readFile(fileName); if (contents.empty()) error<InvalidName>(std::string("File not found (or empty): ") + fileName); - m_asm.append(CodeFragment::compile(contents, _s).m_asm); + m_asm.append(CodeFragment::compile(contents, _s, m_readFile).m_asm); } else if (us == "SET") { @@ -219,7 +238,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) int c = 0; for (auto const& i: _t) if (c++ == 2) - m_asm.append(CodeFragment(i, _s, false).m_asm); + m_asm.append(CodeFragment(i, _s, m_readFile, false).m_asm); m_asm.append((u256)varAddress(firstAsString(), true)); m_asm.append(Instruction::MSTORE); } @@ -244,7 +263,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) if (ii == 1) { if (i.tag()) - error<InvalidName>(); + error<InvalidName>(toString(i)); if (i.which() == sp::utree_type::string_type) { auto sr = i.get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::string_type>>(); @@ -260,7 +279,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) if (_t.size() == 3) { /// NOTE: some compilers could do the assignment first if this is done in a single line - CodeFragment code = CodeFragment(i, _s); + CodeFragment code = CodeFragment(i, _s, m_readFile); _s.defs[n] = code; } else @@ -301,13 +320,13 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) } else if (ii == 1) { - pos = CodeFragment(i, _s); + pos = CodeFragment(i, _s, m_readFile); if (pos.m_asm.deposit() != 1) - error<InvalidDeposit>(us); + error<InvalidDeposit>(toString(i)); } else if (i.tag() != 0) { - error<InvalidLiteral>(); + error<InvalidLiteral>(toString(i)); } else if (i.which() == sp::utree_type::string_type) { @@ -318,7 +337,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) { bigint bi = *i.get<bigint*>(); if (bi < 0) - error<IntegerOutOfRange>(); + error<IntegerOutOfRange>(toString(i)); else { bytes tmp = toCompactBigEndian(bi); @@ -327,7 +346,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) } else { - error<InvalidLiteral>(); + error<InvalidLiteral>(toString(i)); } ii++; @@ -380,9 +399,9 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) if (c++) { if (us == "LLL" && c == 1) - code.push_back(CodeFragment(i, ns)); + code.push_back(CodeFragment(i, ns, m_readFile)); else - code.push_back(CodeFragment(i, _s)); + code.push_back(CodeFragment(i, _s, m_readFile)); } auto requireSize = [&](unsigned s) { if (code.size() != s) error<IncorrectParameterCount>(us); }; auto requireMinSize = [&](unsigned s) { if (code.size() < s) error<IncorrectParameterCount>(us); }; @@ -403,13 +422,13 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) //requireDeposit(i, 1); cs.args[m.args[i]] = code[i]; } - m_asm.append(CodeFragment(m.code, cs).m_asm); + m_asm.append(CodeFragment(m.code, cs, m_readFile).m_asm); for (auto const& i: cs.defs) _s.defs[i.first] = i.second; for (auto const& i: cs.macros) _s.macros.insert(i); } - else if (c_instructions.count(us)) + else if (c_instructions.count(us) && validFunctionalInstruction(us)) { auto it = c_instructions.find(us); requireSize(instructionInfo(it->second).args); @@ -514,6 +533,44 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) m_asm.appendJump(begin); m_asm << end.tag(); } + else if (us == "SWITCH") + { + requireMinSize(1); + + bool hasDefault = (code.size() % 2 == 1); + int startDeposit = m_asm.deposit(); + int targetDeposit = hasDefault ? code[code.size() - 1].m_asm.deposit() : 0; + + // The conditions + AssemblyItems jumpTags; + for (unsigned i = 0; i < code.size() - 1; i += 2) + { + requireDeposit(i, 1); + m_asm.append(code[i].m_asm); + jumpTags.push_back(m_asm.appendJumpI()); + } + + // The default, if present + if (hasDefault) + m_asm.append(code[code.size() - 1].m_asm); + + // The targets - appending in reverse makes the top case the most efficient. + if (code.size() > 1) + { + auto end = m_asm.appendJump(); + for (int i = 2 * (code.size() / 2 - 1); i >= 0; i -= 2) + { + m_asm << jumpTags[i / 2].tag(); + requireDeposit(i + 1, targetDeposit); + m_asm.append(code[i + 1].m_asm); + if (i != 0) + m_asm.appendJump(end); + } + m_asm << end.tag(); + } + + m_asm.setDeposit(startDeposit + targetDeposit); + } else if (us == "ALLOC") { requireSize(1); @@ -622,13 +679,13 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) } } -CodeFragment CodeFragment::compile(string const& _src, CompilerState& _s) +CodeFragment CodeFragment::compile(string const& _src, CompilerState& _s, ReadCallback const& _readFile) { CodeFragment ret; sp::utree o; parseTreeLLL(_src, o); if (!o.empty()) - ret = CodeFragment(o, _s); + ret = CodeFragment(o, _s, _readFile); _s.treesToKill.push_back(o); return ret; } diff --git a/liblll/CodeFragment.h b/liblll/CodeFragment.h index 95d21563..e5cac34e 100644 --- a/liblll/CodeFragment.h +++ b/liblll/CodeFragment.h @@ -39,10 +39,12 @@ struct CompilerState; class CodeFragment { public: + using ReadCallback = std::function<std::string(std::string const&)>; + CodeFragment() {} - CodeFragment(sp::utree const& _t, CompilerState& _s, bool _allowASM = false); + CodeFragment(sp::utree const& _t, CompilerState& _s, ReadCallback const& _readFile, bool _allowASM = false); - static CodeFragment compile(std::string const& _src, CompilerState& _s); + static CodeFragment compile(std::string const& _src, CompilerState& _s, ReadCallback const& _readFile); /// Consolidates data and compiles code. Assembly& assembly(CompilerState const& _cs) { finalise(_cs); return m_asm; } @@ -60,6 +62,7 @@ private: bool m_finalised = false; Assembly m_asm; + ReadCallback m_readFile; }; static const CodeFragment NullCodeFragment; diff --git a/liblll/Compiler.cpp b/liblll/Compiler.cpp index b69675aa..1638f69e 100644 --- a/liblll/Compiler.cpp +++ b/liblll/Compiler.cpp @@ -28,13 +28,14 @@ using namespace std; using namespace dev; using namespace dev::eth; -bytes dev::eth::compileLLL(string const& _src, bool _opt, vector<string>* _errors) + +bytes dev::eth::compileLLL(string const& _src, bool _opt, vector<string>* _errors, ReadCallback const& _readFile) { try { CompilerState cs; cs.populateStandard(); - auto assembly = CodeFragment::compile(_src, cs).assembly(cs); + auto assembly = CodeFragment::compile(_src, cs, _readFile).assembly(cs); if (_opt) assembly = assembly.optimise(true); bytes ret = assembly.assemble().bytecode; @@ -66,13 +67,13 @@ bytes dev::eth::compileLLL(string const& _src, bool _opt, vector<string>* _error return bytes(); } -std::string dev::eth::compileLLLToAsm(std::string const& _src, bool _opt, std::vector<std::string>* _errors) +std::string dev::eth::compileLLLToAsm(std::string const& _src, bool _opt, std::vector<std::string>* _errors, ReadCallback const& _readFile) { try { CompilerState cs; cs.populateStandard(); - auto assembly = CodeFragment::compile(_src, cs).assembly(cs); + auto assembly = CodeFragment::compile(_src, cs, _readFile).assembly(cs); if (_opt) assembly = assembly.optimise(true); string ret = assembly.assemblyString(); diff --git a/liblll/Compiler.h b/liblll/Compiler.h index 04aa1e26..c3395b66 100644 --- a/liblll/Compiler.h +++ b/liblll/Compiler.h @@ -30,9 +30,11 @@ namespace dev namespace eth { +using ReadCallback = std::function<std::string(std::string const&)>; + std::string parseLLL(std::string const& _src); -std::string compileLLLToAsm(std::string const& _src, bool _opt = true, std::vector<std::string>* _errors = nullptr); -bytes compileLLL(std::string const& _src, bool _opt = true, std::vector<std::string>* _errors = nullptr); +std::string compileLLLToAsm(std::string const& _src, bool _opt = true, std::vector<std::string>* _errors = nullptr, ReadCallback const& _readFile = ReadCallback()); +bytes compileLLL(std::string const& _src, bool _opt = true, std::vector<std::string>* _errors = nullptr, ReadCallback const& _readFile = ReadCallback()); } } diff --git a/liblll/CompilerState.cpp b/liblll/CompilerState.cpp index d53dec7e..c0e344b2 100644 --- a/liblll/CompilerState.cpp +++ b/liblll/CompilerState.cpp @@ -82,5 +82,5 @@ void CompilerState::populateStandard() "(def 'shl (val shift) (mul val (exp 2 shift)))" "(def 'shr (val shift) (div val (exp 2 shift)))" "}"; - CodeFragment::compile(s, *this); + CodeFragment::compile(s, *this, CodeFragment::ReadCallback()); } diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 523e7176..5d010693 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -647,10 +647,12 @@ void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaratio bool warnAboutShadowing = true; // Do not warn about shadowing for structs and enums because their members are - // not accessible without prefixes. + // not accessible without prefixes. Also do not warn about event parameters + // because they don't participate in any proper scope. if ( dynamic_cast<StructDefinition const*>(m_currentScope) || - dynamic_cast<EnumDefinition const*>(m_currentScope) + dynamic_cast<EnumDefinition const*>(m_currentScope) || + dynamic_cast<EventDefinition const*>(m_currentScope) ) warnAboutShadowing = false; // Do not warn about the constructor shadowing the contract. diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 8f07d43a..20016112 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -149,8 +149,10 @@ void ReferencesResolver::endVisit(ArrayTypeName const& _typeName) if (!length->annotation().type) ConstantEvaluator e(*length); auto const* lengthType = dynamic_cast<RationalNumberType const*>(length->annotation().type.get()); - if (!lengthType || lengthType->isFractional()) + if (!lengthType || !lengthType->mobileType()) fatalTypeError(length->location(), "Invalid array length, expected integer literal."); + else if (lengthType->isFractional()) + fatalTypeError(length->location(), "Array with fractional length specified."); else if (lengthType->isNegative()) fatalTypeError(length->location(), "Array with negative length specified."); else @@ -347,4 +349,3 @@ void ReferencesResolver::fatalDeclarationError(SourceLocation const& _location, m_errorOccurred = true; m_errorReporter.fatalDeclarationError(_location, _description); } - diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index 187eb26f..0ca4b86c 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -182,8 +182,15 @@ bool SyntaxChecker::visit(Throw const& _throwStatement) bool SyntaxChecker::visit(UnaryOperation const& _operation) { + bool const v050 = m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeature::V050); + if (_operation.getOperator() == Token::Add) - m_errorReporter.warning(_operation.location(), "Use of unary + is deprecated."); + { + if (v050) + m_errorReporter.syntaxError(_operation.location(), "Use of unary + is deprecated."); + else + m_errorReporter.warning(_operation.location(), "Use of unary + is deprecated."); + } return true; } diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 43930125..b2a88059 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -75,6 +75,7 @@ bool TypeChecker::visit(ContractDefinition const& _contract) ASTNode::listAccept(_contract.baseContracts(), *this); checkContractDuplicateFunctions(_contract); + checkContractDuplicateEvents(_contract); checkContractIllegalOverrides(_contract); checkContractAbstractFunctions(_contract); checkContractAbstractConstructors(_contract); @@ -183,9 +184,27 @@ void TypeChecker::checkContractDuplicateFunctions(ContractDefinition const& _con msg ); } - for (auto const& it: functions) + + findDuplicateDefinitions(functions, "Function with same name and arguments defined twice."); +} + +void TypeChecker::checkContractDuplicateEvents(ContractDefinition const& _contract) +{ + /// Checks that two events with the same name defined in this contract have different + /// argument types + map<string, vector<EventDefinition const*>> events; + for (EventDefinition const* event: _contract.events()) + events[event->name()].push_back(event); + + findDuplicateDefinitions(events, "Event with same name and arguments defined twice."); +} + +template <class T> +void TypeChecker::findDuplicateDefinitions(map<string, vector<T>> const& _definitions, string _message) +{ + for (auto const& it: _definitions) { - vector<FunctionDefinition const*> const& overloads = it.second; + vector<T> const& overloads = it.second; set<size_t> reported; for (size_t i = 0; i < overloads.size() && !reported.count(i); ++i) { @@ -200,18 +219,17 @@ 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<string>(occurrences) + " to the first 32 occurrences."; + _message += " Truncated from " + boost::lexical_cast<string>(occurrences) + " to the first 32 occurrences."; } m_errorReporter.declarationError( overloads[i]->location(), ssl, - msg + _message ); } } @@ -627,14 +645,23 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) if (!allowed) m_errorReporter.typeError(_variable.location(), "Constants of non-value type not yet implemented."); } + if (!_variable.value()) m_errorReporter.typeError(_variable.location(), "Uninitialized \"constant\" variable."); else if (!_variable.value()->annotation().isPure) - m_errorReporter.warning( - _variable.value()->location(), - "Initial value for constant variable has to be compile-time constant. " - "This will fail to compile with the next breaking version change." - ); + { + if (_variable.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050)) + m_errorReporter.typeError( + _variable.value()->location(), + "Initial value for constant variable has to be compile-time constant." + ); + else + m_errorReporter.warning( + _variable.value()->location(), + "Initial value for constant variable has to be compile-time constant. " + "This will fail to compile with the next breaking version change." + ); + } } if (!_variable.isStateVariable()) { diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index 0c6f54d3..abe6dac1 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -61,6 +61,7 @@ private: /// Checks that two functions defined in this contract with the same name have different /// arguments and that there is at most one constructor. void checkContractDuplicateFunctions(ContractDefinition const& _contract); + void checkContractDuplicateEvents(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. @@ -108,6 +109,9 @@ private: virtual void endVisit(ElementaryTypeNameExpression const& _expr) override; virtual void endVisit(Literal const& _literal) override; + template <class T> + void findDuplicateDefinitions(std::map<std::string, std::vector<T>> const& _definitions, std::string _message); + bool contractDependenciesAreCyclic( ContractDefinition const& _contract, std::set<ContractDefinition const*> const& _seenContracts = std::set<ContractDefinition const*>() diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index ebf2cd8b..a3cbe50a 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -89,7 +89,7 @@ pair<u256, unsigned> const* StorageOffsets::offset(size_t _index) const MemberList& MemberList::operator=(MemberList&& _other) { - assert(&_other != this); + solAssert(&_other != this, ""); m_memberTypes = move(_other.m_memberTypes); m_storageOffsets = move(_other.m_storageOffsets); @@ -1618,8 +1618,7 @@ string ContractType::canonicalName() const MemberList::MemberMap ContractType::nativeMembers(ContractDefinition const*) const { - // All address members and all interface functions - MemberList::MemberMap members(IntegerType(160, IntegerType::Modifier::Address).nativeMembers(nullptr)); + MemberList::MemberMap members; if (m_super) { // add the most derived of all functions which are visible in derived contracts @@ -1661,9 +1660,45 @@ MemberList::MemberMap ContractType::nativeMembers(ContractDefinition const*) con &it.second->declaration() )); } + addNonConflictingAddressMembers(members); return members; } +void ContractType::addNonConflictingAddressMembers(MemberList::MemberMap& _members) +{ + MemberList::MemberMap addressMembers = IntegerType(160, IntegerType::Modifier::Address).nativeMembers(nullptr); + for (auto const& addressMember: addressMembers) + { + bool clash = false; + for (auto const& member: _members) + { + if ( + member.name == addressMember.name && + ( + // Members with different types are not allowed + member.type->category() != addressMember.type->category() || + // Members must overload functions without clash + ( + member.type->category() == Type::Category::Function && + dynamic_cast<FunctionType const&>(*member.type).hasEqualArgumentTypes(dynamic_cast<FunctionType const&>(*addressMember.type)) + ) + ) + ) + { + clash = true; + break; + } + } + + if (!clash) + _members.push_back(MemberList::Member( + addressMember.name, + addressMember.type, + addressMember.declaration + )); + } +} + shared_ptr<FunctionType const> const& ContractType::newExpressionType() const { if (!m_constructorType) diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 8ba55521..ce29975e 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -716,6 +716,8 @@ public: std::vector<std::tuple<VariableDeclaration const*, u256, unsigned>> stateVariables() const; private: + static void addNonConflictingAddressMembers(MemberList::MemberMap& _members); + ContractDefinition const& m_contract; /// If true, it is the "super" type of the current contract, i.e. it contains only inherited /// members. diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index 25886844..080be359 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -487,6 +487,7 @@ string ABIFunctions::abiEncodingFunctionCalldataArray( // TODO if this is not a byte array, we might just copy byte-by-byte anyway, // because the encoding is position-independent, but we have to check that. Whiskers templ(R"( + // <readableTypeNameFrom> -> <readableTypeNameTo> function <functionName>(start, length, pos) -> end { <storeLength> // might update pos <copyFun>(start, pos, length) @@ -495,6 +496,8 @@ string ABIFunctions::abiEncodingFunctionCalldataArray( )"); templ("storeLength", _to.isDynamicallySized() ? "mstore(pos, length) pos := add(pos, 0x20)" : ""); templ("functionName", functionName); + templ("readableTypeNameFrom", _from.toString(true)); + templ("readableTypeNameTo", _to.toString(true)); templ("copyFun", copyToMemoryFunction(true)); templ("roundUpFun", roundUpFunction()); return templ.render(); @@ -527,6 +530,7 @@ string ABIFunctions::abiEncodingFunctionSimpleArray( Whiskers templ( dynamicBase ? R"( + // <readableTypeNameFrom> -> <readableTypeNameTo> function <functionName>(value, pos) <return> { let length := <lengthFun>(value) <storeLength> // might update pos @@ -545,6 +549,7 @@ string ABIFunctions::abiEncodingFunctionSimpleArray( } )" : R"( + // <readableTypeNameFrom> -> <readableTypeNameTo> function <functionName>(value, pos) <return> { let length := <lengthFun>(value) <storeLength> // might update pos @@ -560,6 +565,8 @@ string ABIFunctions::abiEncodingFunctionSimpleArray( )" ); templ("functionName", functionName); + templ("readableTypeNameFrom", _from.toString(true)); + templ("readableTypeNameTo", _to.toString(true)); templ("return", dynamic ? " -> end " : ""); templ("assignEnd", dynamic ? "end := pos" : ""); templ("lengthFun", arrayLengthFunction(_from)); @@ -639,6 +646,7 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray( { solAssert(_to.isByteArray(), ""); Whiskers templ(R"( + // <readableTypeNameFrom> -> <readableTypeNameTo> function <functionName>(value, pos) -> ret { let slotValue := sload(value) switch and(slotValue, 1) @@ -665,6 +673,8 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray( } )"); templ("functionName", functionName); + templ("readableTypeNameFrom", _from.toString(true)); + templ("readableTypeNameTo", _to.toString(true)); templ("arrayDataSlot", arrayDataAreaFunction(_from)); return templ.render(); } @@ -681,6 +691,7 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray( // more than desired, i.e. it writes beyond the end of memory. Whiskers templ( R"( + // <readableTypeNameFrom> -> <readableTypeNameTo> function <functionName>(value, pos) <return> { let length := <lengthFun>(value) <storeLength> // might update pos @@ -701,6 +712,8 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray( )" ); templ("functionName", functionName); + templ("readableTypeNameFrom", _from.toString(true)); + templ("readableTypeNameTo", _to.toString(true)); templ("return", dynamic ? " -> end " : ""); templ("assignEnd", dynamic ? "end := pos" : ""); templ("lengthFun", arrayLengthFunction(_from)); @@ -748,6 +761,7 @@ string ABIFunctions::abiEncodingFunctionStruct( bool fromStorage = _from.location() == DataLocation::Storage; bool dynamic = _to.isDynamicallyEncoded(); Whiskers templ(R"( + // <readableTypeNameFrom> -> <readableTypeNameTo> function <functionName>(value, pos) <return> { let tail := add(pos, <headSize>) <init> @@ -761,6 +775,8 @@ string ABIFunctions::abiEncodingFunctionStruct( } )"); templ("functionName", functionName); + templ("readableTypeNameFrom", _from.toString(true)); + templ("readableTypeNameTo", _to.toString(true)); templ("return", dynamic ? " -> end " : ""); templ("assignEnd", dynamic ? "end := tail" : ""); // to avoid multiple loads from the same slot for subsequent members @@ -991,9 +1007,11 @@ string ABIFunctions::shiftLeftFunction(size_t _numBits) return createFunction(functionName, [&]() { solAssert(_numBits < 256, ""); return - Whiskers(R"(function <functionName>(value) -> newValue { + Whiskers(R"( + function <functionName>(value) -> newValue { newValue := mul(value, <multiplier>) - })") + } + )") ("functionName", functionName) ("multiplier", toCompactHexWithPrefix(u256(1) << _numBits)) .render(); @@ -1006,9 +1024,11 @@ string ABIFunctions::shiftRightFunction(size_t _numBits, bool _signed) return createFunction(functionName, [&]() { solAssert(_numBits < 256, ""); return - Whiskers(R"(function <functionName>(value) -> newValue { + Whiskers(R"( + function <functionName>(value) -> newValue { newValue := <div>(value, <multiplier>) - })") + } + )") ("functionName", functionName) ("div", _signed ? "sdiv" : "div") ("multiplier", toCompactHexWithPrefix(u256(1) << _numBits)) @@ -1021,9 +1041,11 @@ string ABIFunctions::roundUpFunction() string functionName = "round_up_to_mul_of_32"; return createFunction(functionName, [&]() { return - Whiskers(R"(function <functionName>(value) -> result { + Whiskers(R"( + function <functionName>(value) -> result { result := and(add(value, 31), not(31)) - })") + } + )") ("functionName", functionName) .render(); }); diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index e17188c2..ce8cbb5f 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -291,8 +291,11 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord CompilerUtils utils(m_context); unsigned baseSize = 1; if (!_sourceType.isByteArray()) + { // We always pad the elements, regardless of _padToWordBoundaries. baseSize = _sourceType.baseType()->calldataEncodedSize(); + solAssert(baseSize >= 0x20, ""); + } if (_sourceType.location() == DataLocation::CallData) { diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index d87c7be5..ce9c3b7f 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -38,6 +38,13 @@ #include <utility> #include <numeric> +// Change to "define" to output all intermediate code +#undef SOL_OUTPUT_ASM +#ifdef SOL_OUTPUT_ASM +#include <libsolidity/inlineasm/AsmPrinter.h> +#endif + + using namespace std; namespace dev @@ -313,10 +320,17 @@ void CompilerContext::appendInlineAssembly( ErrorReporter errorReporter(errors); auto scanner = make_shared<Scanner>(CharStream(_assembly), "--CODEGEN--"); auto parserResult = assembly::Parser(errorReporter).parse(scanner); - if (!parserResult || !errorReporter.errors().empty()) +#ifdef SOL_OUTPUT_ASM + cout << assembly::AsmPrinter()(*parserResult) << endl; +#endif + assembly::AsmAnalysisInfo analysisInfo; + bool analyzerResult = false; + if (parserResult) + analyzerResult = assembly::AsmAnalyzer(analysisInfo, errorReporter, false, identifierAccess.resolve).analyze(*parserResult); + if (!parserResult || !errorReporter.errors().empty() || !analyzerResult) { string message = - "Error parsing inline assembly block:\n" + "Error parsing/analyzing inline assembly block:\n" "------------------ Input: -----------------\n" + _assembly + "\n" "------------------ Errors: ----------------\n"; @@ -331,9 +345,6 @@ void CompilerContext::appendInlineAssembly( solAssert(false, message); } - assembly::AsmAnalysisInfo analysisInfo; - 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, _system); } diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index c2bf0f5c..fe37baac 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1714,6 +1714,9 @@ void ExpressionCompiler::appendExternalFunctionCall( if (_functionType.gasSet()) m_context << dupInstruction(m_context.baseToCurrentStackOffset(gasStackPos)); + else if (m_context.experimentalFeatureActive(ExperimentalFeature::V050)) + // Send all gas (requires tangerine whistle EVM) + m_context << Instruction::GAS; else { // send all gas except the amount needed to execute "SUB" and "CALL" diff --git a/libsolidity/formal/Z3Interface.cpp b/libsolidity/formal/Z3Interface.cpp index 522928f0..0ceed3a7 100644 --- a/libsolidity/formal/Z3Interface.cpp +++ b/libsolidity/formal/Z3Interface.cpp @@ -72,28 +72,21 @@ void Z3Interface::addAssertion(Expression const& _expr) pair<CheckResult, vector<string>> Z3Interface::check(vector<Expression> const& _expressionsToEvaluate) { -// cout << "---------------------------------" << endl; -// cout << m_solver << endl; CheckResult result; switch (m_solver.check()) { case z3::check_result::sat: result = CheckResult::SATISFIABLE; - cout << "sat" << endl; break; case z3::check_result::unsat: result = CheckResult::UNSATISFIABLE; - cout << "unsat" << endl; break; case z3::check_result::unknown: result = CheckResult::UNKNOWN; - cout << "unknown" << endl; break; default: solAssert(false, ""); } -// cout << "---------------------------------" << endl; - vector<string> values; if (result != CheckResult::UNSATISFIABLE) @@ -142,7 +135,7 @@ z3::expr Z3Interface::toZ3Expr(Expression const& _expr) return m_context.int_val(n.c_str()); } - assert(arity.count(n) && arity.at(n) == arguments.size()); + solAssert(arity.count(n) && arity.at(n) == arguments.size(), ""); if (n == "ite") return z3::ite(arguments[0], arguments[1], arguments[2]); else if (n == "not") diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index 3087ad86..1f4df75b 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -256,7 +256,7 @@ std::map<string, dev::solidity::Instruction> const& Parser::instructions() { if ( instruction.second == solidity::Instruction::JUMPDEST || - (solidity::Instruction::PUSH1 <= instruction.second && instruction.second <= solidity::Instruction::PUSH32) + solidity::isPushInstruction(instruction.second) ) continue; string name = instruction.first; @@ -443,9 +443,9 @@ assembly::Statement Parser::parseCall(assembly::Statement&& _instruction) ret.location = ret.instruction.location; solidity::Instruction instr = ret.instruction.instruction; InstructionInfo instrInfo = instructionInfo(instr); - if (solidity::Instruction::DUP1 <= instr && instr <= solidity::Instruction::DUP16) + if (solidity::isDupInstruction(instr)) fatalParserError("DUPi instructions not allowed for functional notation"); - if (solidity::Instruction::SWAP1 <= instr && instr <= solidity::Instruction::SWAP16) + if (solidity::isSwapInstruction(instr)) fatalParserError("SWAPi instructions not allowed for functional notation"); expectToken(Token::LParen); unsigned args = unsigned(instrInfo.args); diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 51544f8a..b99fe4ee 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -252,6 +252,14 @@ bool CompilerStack::parseAndAnalyze() return parse() && analyze(); } +bool CompilerStack::isRequestedContract(ContractDefinition const& _contract) const +{ + return + m_requestedContractNames.empty() || + m_requestedContractNames.count(_contract.fullyQualifiedName()) || + m_requestedContractNames.count(_contract.name()); +} + bool CompilerStack::compile() { if (m_stackState < AnalysisSuccessful) @@ -262,7 +270,8 @@ bool CompilerStack::compile() for (Source const* source: m_sourceOrder) for (ASTPointer<ASTNode> const& node: source->ast->nodes()) if (auto contract = dynamic_cast<ContractDefinition const*>(node.get())) - compileContract(*contract, compiledContracts); + if (isRequestedContract(*contract)) + compileContract(*contract, compiledContracts); this->link(); m_stackState = CompilationSuccessful; return true; diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index f1bbae47..c567ac2c 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -116,6 +116,13 @@ public: m_optimizeRuns = _runs; } + /// Sets the list of requested contract names. If empty, no filtering is performed and every contract + /// found in the supplied sources is compiled. Names are cleared iff @a _contractNames is missing. + void setRequestedContractNames(std::set<std::string> const& _contractNames = std::set<std::string>{}) + { + m_requestedContractNames = _contractNames; + } + /// @arg _metadataLiteralSources When true, store sources as literals in the contract metadata. void useMetadataLiteralSources(bool _metadataLiteralSources) { m_metadataLiteralSources = _metadataLiteralSources; } @@ -259,6 +266,9 @@ private: /// Helper function to return path converted strings. std::string sanitizePath(std::string const& _path) const { return boost::filesystem::path(_path).generic_string(); } + /// @returns true if the contract is requested to be compiled. + bool isRequestedContract(ContractDefinition const& _contract) const; + /// Compile a single contract and put the result in @a _compiledContracts. void compileContract( ContractDefinition const& _contract, @@ -297,6 +307,7 @@ private: ReadCallback::Callback m_smtQuery; bool m_optimize = false; unsigned m_optimizeRuns = 200; + std::set<std::string> m_requestedContractNames; std::map<std::string, h160> m_libraries; /// list of path prefix remappings, e.g. mylibrary: github.com/ethereum = /usr/local/ethereum /// "context:prefix=target" diff --git a/libsolidity/interface/GasEstimator.cpp b/libsolidity/interface/GasEstimator.cpp index 852b392c..22cc0266 100644 --- a/libsolidity/interface/GasEstimator.cpp +++ b/libsolidity/interface/GasEstimator.cpp @@ -48,7 +48,7 @@ GasEstimator::ASTGasConsumptionSelfAccumulated GasEstimator::structuralEstimatio ControlFlowGraph cfg(_items); for (BasicBlock const& block: cfg.optimisedBlocks()) { - assertThrow(!!block.startState, OptimizerException, ""); + solAssert(!!block.startState, ""); GasMeter meter(block.startState->copy()); auto const end = _items.begin() + block.end; for (auto iter = _items.begin() + block.begin; iter != end; ++iter) diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index b4fbbef9..430739ac 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -92,6 +92,22 @@ Json::Value formatErrorWithException( return formatError(_warning, _type, _component, message, formattedMessage, location); } +set<string> requestedContractNames(Json::Value const& _outputSelection) +{ + set<string> names; + for (auto const& sourceName: _outputSelection.getMemberNames()) + { + for (auto const& contractName: _outputSelection[sourceName].getMemberNames()) + { + /// Consider the "all sources" shortcuts as requesting everything. + if (contractName == "*" || contractName == "") + return set<string>(); + names.insert((sourceName == "*" ? "" : sourceName) + ":" + contractName); + } + } + return names; +} + /// Returns true iff @a _hash (hex with 0x prefix) is the Keccak256 hash of the binary data in @a _content. bool hashMatchesContent(string const& _hash, string const& _content) { @@ -265,6 +281,9 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) Json::Value metadataSettings = settings.get("metadata", Json::Value()); m_compilerStack.useMetadataLiteralSources(metadataSettings.get("useLiteralContent", Json::Value(false)).asBool()); + Json::Value outputSelection = settings.get("outputSelection", Json::Value()); + m_compilerStack.setRequestedContractNames(requestedContractNames(outputSelection)); + auto scannerFromSourceName = [&](string const& _sourceName) -> solidity::Scanner const& { return m_compilerStack.scanner(_sourceName); }; try diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 9a8bb358..821e81d2 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -1133,6 +1133,7 @@ ASTPointer<VariableDeclarationStatement> Parser::parseVariableDeclarationStateme options.allowVar = true; options.allowLocationSpecifier = true; variables.push_back(parseVariableDeclaration(options, _lookAheadArrayType)); + nodeFactory.setEndPositionFromNode(variables.back()); } if (m_scanner->currentToken() == Token::Assign) { diff --git a/lllc/main.cpp b/lllc/main.cpp index 06a0fc81..912ce16a 100644 --- a/lllc/main.cpp +++ b/lllc/main.cpp @@ -138,7 +138,7 @@ int main(int argc, char** argv) } else if (mode == Binary || mode == Hex) { - auto bs = compileLLL(src, optimise ? true : false, &errors); + auto bs = compileLLL(src, optimise ? true : false, &errors, contentsString); if (mode == Hex) cout << toHex(bs) << endl; else if (mode == Binary) @@ -147,7 +147,7 @@ int main(int argc, char** argv) else if (mode == ParseTree) cout << parseLLL(src) << endl; else if (mode == Assembly) - cout << compileLLLToAsm(src, optimise ? true : false, &errors) << endl; + cout << compileLLLToAsm(src, optimise ? true : false, &errors, contentsString) << endl; for (auto const& i: errors) cerr << i << endl; if ( errors.size() ) diff --git a/scripts/install_deps.sh b/scripts/install_deps.sh index 3a1abe10..01dee81d 100755 --- a/scripts/install_deps.sh +++ b/scripts/install_deps.sh @@ -256,15 +256,6 @@ case $(uname -s) in #------------------------------------------------------------------------------ # Ubuntu # -# TODO - I wonder whether all of the Ubuntu-variants need some special -# treatment? -# -# TODO - We should also test this code on Ubuntu Server, Ubuntu Snappy Core -# and Ubuntu Phone. -# -# TODO - Our Ubuntu build is only working for amd64 and i386 processors. -# It would be good to add armel, armhf and arm64. -# See https://github.com/ethereum/webthree-umbrella/issues/228. #------------------------------------------------------------------------------ Ubuntu) @@ -320,6 +311,14 @@ case $(uname -s) in libboost-all-dev \ "$install_z3" if [ "$CI" = true ]; then + # install Z3 from PPA if the distribution does not provide it + if ! dpkg -l libz3-dev > /dev/null 2>&1 + then + sudo apt-add-repository -y ppa:hvr/z3 + sudo apt-get -y update + sudo apt-get -y install libz3-dev + fi + # Install 'eth', for use in the Solidity Tests-over-IPC. # We will not use this 'eth', but its dependencies sudo add-apt-repository -y ppa:ethereum/ethereum diff --git a/scripts/tests.sh b/scripts/tests.sh index 5d7eb0e1..bf3e3455 100755 --- a/scripts/tests.sh +++ b/scripts/tests.sh @@ -42,8 +42,8 @@ elif [ -z $CI ]; then ETH_PATH="eth" else mkdir -p /tmp/test - wget -O /tmp/test/eth https://github.com/ethereum/cpp-ethereum/releases/download/solidityTester/eth - test "$(shasum /tmp/test/eth)" = "c132e8989229e4840831a4fb1a1d058b732a11d5 /tmp/test/eth" + wget -O /tmp/test/eth https://github.com/ethereum/cpp-ethereum/releases/download/solidityTester/eth_byzantium + test "$(shasum /tmp/test/eth)" = "6e16ae5e0a0079d85fd63fb43547be3c52410e7e /tmp/test/eth" sync chmod +x /tmp/test/eth sync # Otherwise we might get a "text file busy" error diff --git a/test/RPCSession.cpp b/test/RPCSession.cpp index c4fbfefb..768c8c4b 100644 --- a/test/RPCSession.cpp +++ b/test/RPCSession.cpp @@ -217,11 +217,11 @@ void RPCSession::test_setChainParams(vector<string> const& _accounts) { "sealEngine": "NoProof", "params": { - "accountStartNonce": "0x", + "accountStartNonce": "0x00", "maximumExtraDataSize": "0x1000000", "blockReward": "0x", - "allowFutureBlocks": "1", - "homsteadForkBlock": "0x00", + "allowFutureBlocks": true, + "homesteadForkBlock": "0x00", "EIP150ForkBlock": "0x00", "EIP158ForkBlock": "0x00" }, diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index 094b59c6..c8747a06 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -45,6 +45,8 @@ Options::Options() showMessages = true; else if (string(suite.argv[i]) == "--no-ipc") disableIPC = true; + else if (string(suite.argv[i]) == "--no-smt") + disableSMT = true; if (!disableIPC && ipcPath.empty()) if (auto path = getenv("ETH_TEST_IPC")) diff --git a/test/TestHelper.h b/test/TestHelper.h index d50568ad..d25c5cd8 100644 --- a/test/TestHelper.h +++ b/test/TestHelper.h @@ -35,6 +35,7 @@ struct Options: boost::noncopyable bool showMessages = false; bool optimize = false; bool disableIPC = false; + bool disableSMT = false; static Options const& get(); diff --git a/test/boostTest.cpp b/test/boostTest.cpp index d8c5b678..7b452e06 100644 --- a/test/boostTest.cpp +++ b/test/boostTest.cpp @@ -39,6 +39,17 @@ using namespace boost::unit_test; +namespace +{ +void removeTestSuite(std::string const& _name) +{ + master_test_suite_t& master = framework::master_test_suite(); + auto id = master.get(_name); + assert(id != INV_TEST_UNIT_ID); + master.remove(id); +} +} + test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ) { master_test_suite_t& master = framework::master_test_suite(); @@ -57,12 +68,10 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ) "SolidityEndToEndTest", "SolidityOptimizer" }) - { - auto id = master.get(suite); - assert(id != INV_TEST_UNIT_ID); - master.remove(id); - } + removeTestSuite(suite); } + if (dev::test::Options::get().disableSMT) + removeTestSuite("SMTChecker"); return 0; } diff --git a/test/liblll/Compiler.cpp b/test/liblll/Compiler.cpp new file mode 100644 index 00000000..2d66bce1 --- /dev/null +++ b/test/liblll/Compiler.cpp @@ -0,0 +1,128 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @author Alex Beregszaszi + * @date 2017 + * Unit tests for the LLL compiler. + */ + +#include <string> +#include <memory> +#include <boost/test/unit_test.hpp> +#include <liblll/Compiler.h> + +using namespace std; + +namespace dev +{ +namespace lll +{ +namespace test +{ + +namespace +{ + +bool successCompile(std::string const& _sourceCode) +{ + std::vector<std::string> errors; + bytes bytecode = eth::compileLLL(_sourceCode, false, &errors); + if (!errors.empty()) + return false; + if (bytecode.empty()) + return false; + return true; +} + +} + +BOOST_AUTO_TEST_SUITE(LLLCompiler) + +BOOST_AUTO_TEST_CASE(smoke_test) +{ + char const* sourceCode = "1"; + BOOST_CHECK(successCompile(sourceCode)); +} + +BOOST_AUTO_TEST_CASE(switch_valid) +{ + char const* sourceCode = R"( + (switch (origin)) + )"; + BOOST_CHECK(successCompile(sourceCode)); + sourceCode = R"( + (switch + 1 (panic) + 2 (panic)) + )"; + BOOST_CHECK(successCompile(sourceCode)); + sourceCode = R"( + (switch + 1 (panic) + 2 (panic) + (panic)) + )"; + BOOST_CHECK(successCompile(sourceCode)); + sourceCode = R"( + (switch + 1 (origin) + 2 (origin) + (origin)) + )"; + BOOST_CHECK(successCompile(sourceCode)); +} + +BOOST_AUTO_TEST_CASE(switch_invalid_arg_count) +{ + char const* sourceCode = R"( + (switch) + )"; + BOOST_CHECK(!successCompile(sourceCode)); +} + +BOOST_AUTO_TEST_CASE(switch_inconsistent_return_count) +{ + // cannot return stack items if the default case is not present + char const* sourceCode = R"( + (switch + 1 (origin) + 2 (origin) + )"; + BOOST_CHECK(!successCompile(sourceCode)); + // return count mismatch + sourceCode = R"( + (switch + 1 (origin) + 2 (origin) + (panic)) + )"; + BOOST_CHECK(!successCompile(sourceCode)); + // return count mismatch + sourceCode = R"( + (switch + 1 (panic) + 2 (panic) + (origin)) + )"; + BOOST_CHECK(!successCompile(sourceCode)); +} + +BOOST_AUTO_TEST_SUITE_END() + +} +} +} // end namespaces diff --git a/test/liblll/EndToEndTest.cpp b/test/liblll/EndToEndTest.cpp index 9292d963..1a5bb490 100644 --- a/test/liblll/EndToEndTest.cpp +++ b/test/liblll/EndToEndTest.cpp @@ -215,6 +215,92 @@ BOOST_AUTO_TEST_CASE(conditional_nested_then) BOOST_CHECK(callContractFunction("test()", 0xfc) == encodeArgs(u256(6))); } +BOOST_AUTO_TEST_CASE(conditional_switch) +{ + char const* sourceCode = R"( + (returnlll + (seq + (def 'input (calldataload 0x04)) + ;; Calculates width in bytes of utf-8 characters. + (return + (switch + (< input 0x80) 1 + (< input 0xE0) 2 + (< input 0xF0) 3 + (< input 0xF8) 4 + (< input 0xFC) 5 + 6)))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("test()", 0x00) == encodeArgs(u256(1))); + BOOST_CHECK(callContractFunction("test()", 0x80) == encodeArgs(u256(2))); + BOOST_CHECK(callContractFunction("test()", 0xe0) == encodeArgs(u256(3))); + BOOST_CHECK(callContractFunction("test()", 0xf0) == encodeArgs(u256(4))); + BOOST_CHECK(callContractFunction("test()", 0xf8) == encodeArgs(u256(5))); + BOOST_CHECK(callContractFunction("test()", 0xfc) == encodeArgs(u256(6))); +} + +BOOST_AUTO_TEST_CASE(conditional_switch_one_arg_with_deposit) +{ + char const* sourceCode = R"( + (returnlll + (return + (switch 42))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallback() == encodeArgs(u256(42))); +} + +BOOST_AUTO_TEST_CASE(conditional_switch_one_arg_no_deposit) +{ + char const* sourceCode = R"( + (returnlll + (seq + (switch [0]:42) + (return 0x00 0x20))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallback() == encodeArgs(u256(42))); +} + +BOOST_AUTO_TEST_CASE(conditional_switch_two_args) +{ + char const* sourceCode = R"( + (returnlll + (seq + (switch (= (calldataload 0x04) 1) [0]:42) + (return 0x00 0x20))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("test()", 0) == encodeArgs(u256(0))); + BOOST_CHECK(callContractFunction("test()", 1) == encodeArgs(u256(42))); +} + +BOOST_AUTO_TEST_CASE(conditional_switch_three_args_with_deposit) +{ + char const* sourceCode = R"( + (returnlll + (return + (switch (= (calldataload 0x04) 1) 41 42))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("test()", 0) == encodeArgs(u256(42))); + BOOST_CHECK(callContractFunction("test()", 1) == encodeArgs(u256(41))); +} + +BOOST_AUTO_TEST_CASE(conditional_switch_three_args_no_deposit) +{ + char const* sourceCode = R"( + (returnlll + (switch + (= (calldataload 0x04) 1) (return 41) + (return 42))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("test()", 0) == encodeArgs(u256(42))); + BOOST_CHECK(callContractFunction("test()", 1) == encodeArgs(u256(41))); +} + BOOST_AUTO_TEST_CASE(exp_operator_const) { char const* sourceCode = R"( diff --git a/test/libsolidity/AnalysisFramework.cpp b/test/libsolidity/AnalysisFramework.cpp index 5f5f6411..3bdc40a0 100644 --- a/test/libsolidity/AnalysisFramework.cpp +++ b/test/libsolidity/AnalysisFramework.cpp @@ -25,6 +25,8 @@ #include <libsolidity/ast/AST.h> +#include <libsolidity/parsing/Scanner.h> + #include <libdevcore/SHA3.h> #include <boost/test/unit_test.hpp> @@ -46,8 +48,7 @@ AnalysisFramework::parseAnalyseAndReturnError( 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."); + BOOST_ERROR("Parsing contract failed in analysis test suite:" + formatErrors()); } m_compiler.analyze(); @@ -56,15 +57,24 @@ AnalysisFramework::parseAnalyseAndReturnError( for (auto const& currentError: m_compiler.errors()) { solAssert(currentError->comment(), ""); - if (currentError->comment()->find("This is a pre-release compiler version") == 0) - continue; + if (currentError->type() == Error::Type::Warning) + { + bool ignoreWarning = false; + for (auto const& filter: m_warningsToFilter) + if (currentError->comment()->find(filter) == 0) + { + ignoreWarning = true; + break; + } + if (ignoreWarning) + continue; + } if (_reportWarnings || (currentError->type() != Error::Type::Warning)) { if (firstError && !_allowMultipleErrors) { - printErrors(); - BOOST_FAIL("Multiple errors found."); + BOOST_FAIL("Multiple errors found: " + formatErrors()); } if (!firstError) firstError = currentError; @@ -78,7 +88,10 @@ SourceUnit const* AnalysisFramework::parseAndAnalyse(string const& _source) { auto sourceAndError = parseAnalyseAndReturnError(_source); BOOST_REQUIRE(!!sourceAndError.first); - BOOST_REQUIRE(!sourceAndError.second); + string message; + if (sourceAndError.second) + message = "Unexpected error: " + formatError(*sourceAndError.second); + BOOST_REQUIRE_MESSAGE(!sourceAndError.second, message); return sourceAndError.first; } @@ -91,17 +104,23 @@ Error AnalysisFramework::expectError(std::string const& _source, bool _warning, { auto sourceAndError = parseAnalyseAndReturnError(_source, _warning, true, _allowMultiple); BOOST_REQUIRE(!!sourceAndError.second); - BOOST_REQUIRE(!!sourceAndError.first); + BOOST_REQUIRE_MESSAGE(!!sourceAndError.first, "Expected error, but no error happened."); return *sourceAndError.second; } -void AnalysisFramework::printErrors() +string AnalysisFramework::formatErrors() { + string message; for (auto const& error: m_compiler.errors()) - SourceReferenceFormatter::printExceptionInformation( - std::cerr, - *error, - (error->type() == Error::Type::Warning) ? "Warning" : "Error", + message += formatError(*error); + return message; +} + +string AnalysisFramework::formatError(Error const& _error) +{ + return SourceReferenceFormatter::formatExceptionInformation( + _error, + (_error.type() == Error::Type::Warning) ? "Warning" : "Error", [&](std::string const& _sourceName) -> solidity::Scanner const& { return m_compiler.scanner(_sourceName); } ); } diff --git a/test/libsolidity/AnalysisFramework.h b/test/libsolidity/AnalysisFramework.h index 172ae01b..a566ba1d 100644 --- a/test/libsolidity/AnalysisFramework.h +++ b/test/libsolidity/AnalysisFramework.h @@ -45,7 +45,7 @@ class AnalysisFramework { protected: - std::pair<SourceUnit const*, std::shared_ptr<Error const>> + virtual std::pair<SourceUnit const*, std::shared_ptr<Error const>> parseAnalyseAndReturnError( std::string const& _source, bool _reportWarnings = false, @@ -57,7 +57,8 @@ protected: bool success(std::string const& _source); Error expectError(std::string const& _source, bool _warning = false, bool _allowMultiple = false); - void printErrors(); + std::string formatErrors(); + std::string formatError(Error const& _error); static ContractDefinition const* retrieveContractByName(SourceUnit const& _source, std::string const& _name); static FunctionTypePointer retrieveFunctionBySignature( @@ -65,6 +66,7 @@ protected: std::string const& _signature ); + std::vector<std::string> m_warningsToFilter = {"This is a pre-release compiler version"}; dev::solidity::CompilerStack m_compiler; }; @@ -104,7 +106,10 @@ CHECK_ERROR_OR_WARNING(text, Warning, substring, true, true) do \ { \ auto sourceAndError = parseAnalyseAndReturnError((text), true); \ - BOOST_CHECK(sourceAndError.second == nullptr); \ + std::string message; \ + if (sourceAndError.second) \ + message = formatError(*sourceAndError.second); \ + BOOST_CHECK_MESSAGE(!sourceAndError.second, message); \ } \ while(0) diff --git a/test/libsolidity/GasMeter.cpp b/test/libsolidity/GasMeter.cpp index b759678f..c2886f5b 100644 --- a/test/libsolidity/GasMeter.cpp +++ b/test/libsolidity/GasMeter.cpp @@ -21,7 +21,6 @@ */ #include <test/libsolidity/SolidityExecutionFramework.h> -#include <libevmasm/EVMSchedule.h> #include <libevmasm/GasMeter.h> #include <libevmasm/KnownState.h> #include <libevmasm/PathGasMeter.h> @@ -63,15 +62,13 @@ public: void testCreationTimeGas(string const& _sourceCode) { - EVMSchedule schedule; - compileAndRun(_sourceCode); auto state = make_shared<KnownState>(); PathGasMeter meter(*m_compiler.assemblyItems()); GasMeter::GasConsumption gas = meter.estimateMax(0, state); u256 bytecodeSize(m_compiler.runtimeObject().bytecode.size()); // costs for deployment - gas += bytecodeSize * schedule.createDataGas; + gas += bytecodeSize * GasCosts::createDataGas; // costs for transaction gas += gasForTransaction(m_compiler.object().bytecode, true); @@ -103,10 +100,9 @@ public: static GasMeter::GasConsumption gasForTransaction(bytes const& _data, bool _isCreation) { - EVMSchedule schedule; - GasMeter::GasConsumption gas = _isCreation ? schedule.txCreateGas : schedule.txGas; + GasMeter::GasConsumption gas = _isCreation ? GasCosts::txCreateGas : GasCosts::txGas; for (auto i: _data) - gas += i != 0 ? schedule.txDataNonZeroGas : schedule.txDataZeroGas; + gas += i != 0 ? GasCosts::txDataNonZeroGas : GasCosts::txDataZeroGas; return gas; } diff --git a/test/libsolidity/SMTChecker.cpp b/test/libsolidity/SMTChecker.cpp new file mode 100644 index 00000000..d58f296f --- /dev/null +++ b/test/libsolidity/SMTChecker.cpp @@ -0,0 +1,86 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Unit tests for the SMT checker. + */ + +#include <test/libsolidity/AnalysisFramework.h> + +#include <boost/test/unit_test.hpp> + +#include <string> + +using namespace std; + +namespace dev +{ +namespace solidity +{ +namespace test +{ + +class SMTCheckerFramework: public AnalysisFramework +{ +public: + SMTCheckerFramework() + { + m_warningsToFilter.push_back("Experimental features are turned on."); + } + +protected: + virtual std::pair<SourceUnit const*, std::shared_ptr<Error const>> + parseAnalyseAndReturnError( + std::string const& _source, + bool _reportWarnings = false, + bool _insertVersionPragma = true, + bool _allowMultipleErrors = false + ) + { + return AnalysisFramework::parseAnalyseAndReturnError( + "pragma experimental SMTChecker;\n" + _source, + _reportWarnings, + _insertVersionPragma, + _allowMultipleErrors + ); + } +}; + +BOOST_FIXTURE_TEST_SUITE(SMTChecker, SMTCheckerFramework) + +BOOST_AUTO_TEST_CASE(smoke_test) +{ + string text = R"( + contract C { } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(simple_overflow) +{ + string text = R"( + contract C { + function f(uint a, uint b) public pure returns (uint) { return a + b; } + } + )"; + CHECK_WARNING(text, "Overflow (resulting value larger than"); +} + +BOOST_AUTO_TEST_SUITE_END() + +} +} +} diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index df9332c4..35916ec7 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -10151,6 +10151,31 @@ BOOST_AUTO_TEST_CASE(constant_string) ABI_CHECK(callContractFunction("h()"), encodeDyn(string("hello"))); } +BOOST_AUTO_TEST_CASE(address_overload_resolution) +{ + char const* sourceCode = R"( + contract C { + function balance() returns (uint) { + return 1; + } + function transfer(uint amount) returns (uint) { + return amount; + } + } + contract D { + function f() returns (uint) { + return (new C()).balance(); + } + function g() returns (uint) { + return (new C()).transfer(5); + } + } + )"; + compileAndRun(sourceCode, 0, "D"); + BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(1))); + BOOST_CHECK(callContractFunction("g()") == encodeArgs(u256(5))); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 39c47f9c..350d41f1 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -1395,6 +1395,61 @@ BOOST_AUTO_TEST_CASE(events_with_same_name) BOOST_CHECK(success(text)); } +BOOST_AUTO_TEST_CASE(events_with_same_name_unnamed_arguments) +{ + char const* text = R"( + contract test { + event A(uint); + event A(uint, uint); + } + )"; + CHECK_SUCCESS(text); +} + +BOOST_AUTO_TEST_CASE(events_with_same_name_different_types) +{ + char const* text = R"( + contract test { + event A(uint); + event A(bytes); + } + )"; + CHECK_SUCCESS(text); +} + +BOOST_AUTO_TEST_CASE(double_event_declaration) +{ + char const* text = R"( + contract test { + event A(uint i); + event A(uint i); + } + )"; + CHECK_ERROR(text, DeclarationError, "Event with same name and arguments defined twice."); +} + +BOOST_AUTO_TEST_CASE(double_event_declaration_ignores_anonymous) +{ + char const* text = R"( + contract test { + event A(uint i); + event A(uint i) anonymous; + } + )"; + CHECK_ERROR(text, DeclarationError, "Event with same name and arguments defined twice."); +} + +BOOST_AUTO_TEST_CASE(double_event_declaration_ignores_indexed) +{ + char const* text = R"( + contract test { + event A(uint i); + event A(uint indexed i); + } + )"; + CHECK_ERROR(text, DeclarationError, "Event with same name and arguments defined twice."); +} + BOOST_AUTO_TEST_CASE(event_call) { char const* text = R"( @@ -2306,17 +2361,28 @@ BOOST_AUTO_TEST_CASE(assigning_value_to_const_variable) CHECK_ERROR(text, TypeError, "Cannot assign to a constant variable."); } -BOOST_AUTO_TEST_CASE(assigning_state_to_const_variable) +BOOST_AUTO_TEST_CASE(assigning_state_to_const_variable_0_4_x) { char const* text = R"( contract C { address constant x = msg.sender; } )"; - // Change to TypeError for 0.5.0. CHECK_WARNING(text, "Initial value for constant variable has to be compile-time constant."); } +BOOST_AUTO_TEST_CASE(assigning_state_to_const_variable) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + + contract C { + address constant x = msg.sender; + } + )"; + CHECK_ERROR(text, TypeError, "Initial value for constant variable has to be compile-time constant."); +} + BOOST_AUTO_TEST_CASE(constant_string_literal_disallows_assignment) { char const* text = R"( @@ -2333,7 +2399,7 @@ BOOST_AUTO_TEST_CASE(constant_string_literal_disallows_assignment) CHECK_ERROR(text, TypeError, "Index access for string is not possible."); } -BOOST_AUTO_TEST_CASE(assign_constant_function_value_to_constant) +BOOST_AUTO_TEST_CASE(assign_constant_function_value_to_constant_0_4_x) { char const* text = R"( contract C { @@ -2341,10 +2407,22 @@ BOOST_AUTO_TEST_CASE(assign_constant_function_value_to_constant) uint constant y = x(); } )"; - // Change to TypeError for 0.5.0. CHECK_WARNING(text, "Initial value for constant variable has to be compile-time constant."); } +BOOST_AUTO_TEST_CASE(assign_constant_function_value_to_constant) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + + contract C { + function () constant returns (uint) x; + uint constant y = x(); + } + )"; + CHECK_ERROR(text, TypeError, "Initial value for constant variable has to be compile-time constant."); +} + BOOST_AUTO_TEST_CASE(assignment_to_const_var_involving_conversion) { char const* text = R"( @@ -4135,6 +4213,8 @@ BOOST_AUTO_TEST_CASE(rational_unary_operation) } )"; CHECK_SUCCESS_NO_WARNINGS(text); + + // Test deprecation warning under < 0.5.0 text = R"( contract test { function f() pure public { @@ -4154,6 +4234,29 @@ BOOST_AUTO_TEST_CASE(rational_unary_operation) } )"; CHECK_WARNING(text,"Use of unary + is deprecated"); + + // Test syntax error under 0.5.0 + text = R"( + pragma experimental "v0.5.0"; + contract test { + function f() pure public { + ufixed16x2 a = +3.25; + fixed16x2 b = -3.25; + a; b; + } + } + )"; + CHECK_ERROR(text, SyntaxError, "Use of unary + is deprecated"); + text = R"( + pragma experimental "v0.5.0"; + contract test { + function f(uint x) pure public { + uint y = +x; + y; + } + } + )"; + CHECK_ERROR(text, SyntaxError, "Use of unary + is deprecated"); } BOOST_AUTO_TEST_CASE(leading_zero_rationals_convert) @@ -4251,7 +4354,7 @@ BOOST_AUTO_TEST_CASE(invalid_array_declaration_with_rational) } } )"; - CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal"); + CHECK_ERROR(text, TypeError, "Array with fractional length specified."); } BOOST_AUTO_TEST_CASE(invalid_array_declaration_with_signed_fixed_type) @@ -4263,7 +4366,7 @@ BOOST_AUTO_TEST_CASE(invalid_array_declaration_with_signed_fixed_type) } } )"; - CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal"); + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal."); } BOOST_AUTO_TEST_CASE(invalid_array_declaration_with_unsigned_fixed_type) @@ -4275,7 +4378,7 @@ BOOST_AUTO_TEST_CASE(invalid_array_declaration_with_unsigned_fixed_type) } } )"; - CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal"); + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal."); } BOOST_AUTO_TEST_CASE(rational_to_bytes_implicit_conversion) @@ -6268,6 +6371,17 @@ BOOST_AUTO_TEST_CASE(function_override_is_not_shadowing) CHECK_SUCCESS_NO_WARNINGS(text); } +BOOST_AUTO_TEST_CASE(event_parameter_cannot_shadow_state_variable) +{ + char const* text = R"( + contract C { + address a; + event E(address a); + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + BOOST_AUTO_TEST_CASE(callable_crash) { char const* text = R"( @@ -6953,6 +7067,39 @@ BOOST_AUTO_TEST_CASE(warn_about_suicide) CHECK_WARNING(text, "\"suicide\" has been deprecated in favour of \"selfdestruct\""); } +BOOST_AUTO_TEST_CASE(address_overload_resolution) +{ + char const* text = R"( + contract C { + function balance() returns (uint) { + this.balance; // to avoid pureness warning + return 1; + } + function transfer(uint amount) { + address(this).transfer(amount); // to avoid pureness warning + } + } + contract D { + function f() { + var x = (new C()).balance(); + x; + (new C()).transfer(5); + } + } + )"; + CHECK_SUCCESS(text); +} + +BOOST_AUTO_TEST_CASE(array_length_validation) +{ + char const* text = R"( + contract C { + uint[8**90] ids; + } + )"; + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal."); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index 24f915c0..4504946b 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -226,6 +226,183 @@ BOOST_AUTO_TEST_CASE(basic_compilation) ); } +BOOST_AUTO_TEST_CASE(output_selection_explicit) +{ + char const* input = R"( + { + "language": "Solidity", + "settings": { + "outputSelection": { + "fileA": { + "A": [ + "abi" + ] + } + } + }, + "sources": { + "fileA": { + "content": "contract A { }" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsAtMostWarnings(result)); + Json::Value contract = getContractResult(result, "fileA", "A"); + BOOST_CHECK(contract.isObject()); + BOOST_CHECK(contract["abi"].isArray()); + BOOST_CHECK_EQUAL(dev::jsonCompactPrint(contract["abi"]), "[]"); +} + +BOOST_AUTO_TEST_CASE(output_selection_all_contracts) +{ + char const* input = R"( + { + "language": "Solidity", + "settings": { + "outputSelection": { + "fileA": { + "*": [ + "abi" + ] + } + } + }, + "sources": { + "fileA": { + "content": "contract A { }" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsAtMostWarnings(result)); + Json::Value contract = getContractResult(result, "fileA", "A"); + BOOST_CHECK(contract.isObject()); + BOOST_CHECK(contract["abi"].isArray()); + BOOST_CHECK_EQUAL(dev::jsonCompactPrint(contract["abi"]), "[]"); +} + +BOOST_AUTO_TEST_CASE(output_selection_all_files_single_contract) +{ + char const* input = R"( + { + "language": "Solidity", + "settings": { + "outputSelection": { + "*": { + "A": [ + "abi" + ] + } + } + }, + "sources": { + "fileA": { + "content": "contract A { }" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsAtMostWarnings(result)); + Json::Value contract = getContractResult(result, "fileA", "A"); + BOOST_CHECK(contract.isObject()); + BOOST_CHECK(contract["abi"].isArray()); + BOOST_CHECK_EQUAL(dev::jsonCompactPrint(contract["abi"]), "[]"); +} + +BOOST_AUTO_TEST_CASE(output_selection_all_files_all_contracts) +{ + char const* input = R"( + { + "language": "Solidity", + "settings": { + "outputSelection": { + "*": { + "*": [ + "abi" + ] + } + } + }, + "sources": { + "fileA": { + "content": "contract A { }" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsAtMostWarnings(result)); + Json::Value contract = getContractResult(result, "fileA", "A"); + BOOST_CHECK(contract.isObject()); + BOOST_CHECK(contract["abi"].isArray()); + BOOST_CHECK_EQUAL(dev::jsonCompactPrint(contract["abi"]), "[]"); +} + +BOOST_AUTO_TEST_CASE(output_selection_dependent_contract) +{ + char const* input = R"( + { + "language": "Solidity", + "settings": { + "outputSelection": { + "*": { + "A": [ + "abi" + ] + } + } + }, + "sources": { + "fileA": { + "content": "contract B { } contract A { function f() { new B(); } }" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsAtMostWarnings(result)); + Json::Value contract = getContractResult(result, "fileA", "A"); + BOOST_CHECK(contract.isObject()); + BOOST_CHECK(contract["abi"].isArray()); + BOOST_CHECK_EQUAL(dev::jsonCompactPrint(contract["abi"]), "[{\"constant\":false,\"inputs\":[],\"name\":\"f\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]"); +} + +BOOST_AUTO_TEST_CASE(output_selection_dependent_contract_with_import) +{ + char const* input = R"( + { + "language": "Solidity", + "settings": { + "outputSelection": { + "*": { + "A": [ + "abi" + ] + } + } + }, + "sources": { + "fileA": { + "content": "import \"fileB\"; contract A { function f() { new B(); } }" + }, + "fileB": { + "content": "contract B { }" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsAtMostWarnings(result)); + Json::Value contract = getContractResult(result, "fileA", "A"); + BOOST_CHECK(contract.isObject()); + BOOST_CHECK(contract["abi"].isArray()); + BOOST_CHECK_EQUAL(dev::jsonCompactPrint(contract["abi"]), "[{\"constant\":false,\"inputs\":[],\"name\":\"f\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]"); +} + BOOST_AUTO_TEST_SUITE_END() } |