aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml7
-rw-r--r--Changelog.md12
-rw-r--r--appveyor.yml2
-rw-r--r--docs/common-patterns.rst4
-rw-r--r--docs/contracts.rst2
-rw-r--r--docs/contributing.rst6
-rw-r--r--docs/frequently-asked-questions.rst4
-rw-r--r--libdevcore/Exceptions.cpp10
-rw-r--r--libevmasm/AssemblyItem.cpp4
-rw-r--r--libevmasm/EVMSchedule.h62
-rw-r--r--libevmasm/Instruction.h18
-rw-r--r--libevmasm/SemanticInformation.cpp4
-rw-r--r--liblll/CodeFragment.cpp97
-rw-r--r--liblll/CodeFragment.h7
-rw-r--r--liblll/Compiler.cpp9
-rw-r--r--liblll/Compiler.h6
-rw-r--r--liblll/CompilerState.cpp2
-rw-r--r--libsolidity/analysis/NameAndTypeResolver.cpp6
-rw-r--r--libsolidity/analysis/ReferencesResolver.cpp5
-rw-r--r--libsolidity/analysis/SyntaxChecker.cpp9
-rw-r--r--libsolidity/analysis/TypeChecker.cpp47
-rw-r--r--libsolidity/analysis/TypeChecker.h4
-rw-r--r--libsolidity/ast/Types.cpp41
-rw-r--r--libsolidity/ast/Types.h2
-rw-r--r--libsolidity/codegen/ABIFunctions.cpp34
-rw-r--r--libsolidity/codegen/ArrayUtils.cpp3
-rw-r--r--libsolidity/codegen/CompilerContext.cpp21
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp3
-rw-r--r--libsolidity/formal/Z3Interface.cpp9
-rw-r--r--libsolidity/inlineasm/AsmParser.cpp6
-rw-r--r--libsolidity/interface/CompilerStack.cpp11
-rw-r--r--libsolidity/interface/CompilerStack.h11
-rw-r--r--libsolidity/interface/GasEstimator.cpp2
-rw-r--r--libsolidity/interface/StandardCompiler.cpp19
-rw-r--r--libsolidity/parsing/Parser.cpp1
-rw-r--r--lllc/main.cpp4
-rwxr-xr-xscripts/install_deps.sh17
-rwxr-xr-xscripts/tests.sh4
-rw-r--r--test/RPCSession.cpp6
-rw-r--r--test/TestHelper.cpp2
-rw-r--r--test/TestHelper.h1
-rw-r--r--test/boostTest.cpp19
-rw-r--r--test/liblll/Compiler.cpp128
-rw-r--r--test/liblll/EndToEndTest.cpp86
-rw-r--r--test/libsolidity/AnalysisFramework.cpp45
-rw-r--r--test/libsolidity/AnalysisFramework.h11
-rw-r--r--test/libsolidity/GasMeter.cpp10
-rw-r--r--test/libsolidity/SMTChecker.cpp86
-rw-r--r--test/libsolidity/SolidityEndToEndTest.cpp25
-rw-r--r--test/libsolidity/SolidityNameAndTypeResolution.cpp161
-rw-r--r--test/libsolidity/StandardCompiler.cpp177
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()
}