aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Changelog.md3
-rw-r--r--docs/abi-spec.rst2
-rw-r--r--docs/assembly.rst4
-rw-r--r--docs/contracts.rst31
-rw-r--r--docs/control-structures.rst16
-rw-r--r--docs/miscellaneous.rst2
-rw-r--r--docs/security-considerations.rst8
-rw-r--r--docs/types.rst8
-rw-r--r--libdevcore/CommonData.h3
-rw-r--r--libdevcore/Exceptions.cpp49
-rw-r--r--libdevcore/Exceptions.h20
-rw-r--r--libevmasm/AssemblyItem.cpp11
-rw-r--r--libevmasm/GasMeter.cpp10
-rw-r--r--libjulia/backends/evm/EVMCodeTransform.cpp15
-rw-r--r--libsolidity/analysis/TypeChecker.cpp29
-rw-r--r--libsolidity/ast/AST.h17
-rw-r--r--libsolidity/ast/Types.cpp19
-rw-r--r--libsolidity/ast/Types.h9
-rw-r--r--libsolidity/inlineasm/AsmAnalysis.cpp18
-rw-r--r--libsolidity/inlineasm/AsmParser.cpp13
-rw-r--r--libsolidity/inlineasm/AsmPrinter.cpp7
-rw-r--r--libsolidity/inlineasm/AsmScopeFiller.cpp2
-rw-r--r--libsolidity/interface/Exceptions.cpp13
-rwxr-xr-xscripts/isolate_tests.py55
-rwxr-xr-xscripts/tests.sh21
-rwxr-xr-xtest/cmdlineTests.sh73
-rw-r--r--test/libjulia/Parser.cpp5
-rw-r--r--test/libsolidity/ErrorCheck.cpp4
-rw-r--r--test/libsolidity/InlineAssembly.cpp5
-rw-r--r--test/libsolidity/SolidityEndToEndTest.cpp18
-rw-r--r--test/libsolidity/SolidityNameAndTypeResolution.cpp113
31 files changed, 499 insertions, 104 deletions
diff --git a/Changelog.md b/Changelog.md
index 075523d7..62b52942 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -2,6 +2,9 @@
Features:
* Inline Assembly: Show useful error message if trying to access calldata variables.
+ * Inline Assembly: Support variable declaration without initial value (defaults to 0).
+ * Type Checker: Disallow value transfers to contracts without a payable fallback function.
+ * Type Checker: Raise proper error for arrays too large for ABI encoding.
Bugfixes:
* Type Checker: Fix invalid "specify storage keyword" warning for reference members of structs.
diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst
index d915973d..82c52159 100644
--- a/docs/abi-spec.rst
+++ b/docs/abi-spec.rst
@@ -182,6 +182,8 @@ Given the contract:
::
+ pragma solidity ^0.4.0;
+
contract Foo {
function bar(bytes3[2] xy) {}
function baz(uint32 x, bool y) returns (bool r) { r = x > 32 || y; }
diff --git a/docs/assembly.rst b/docs/assembly.rst
index 00601371..37222faf 100644
--- a/docs/assembly.rst
+++ b/docs/assembly.rst
@@ -110,7 +110,7 @@ these curly braces, the following can be used (see the later sections for more d
- opcodes (in "instruction style"), e.g. ``mload sload dup1 sstore``, for a list see below
- opcodes in functional style, e.g. ``add(1, mlod(0))``
- labels, e.g. ``name:``
- - variable declarations, e.g. ``let x := 7`` or ``let x := add(y, 3)``
+ - variable declarations, e.g. ``let x := 7``, ``let x := add(y, 3)`` or ``let x`` (initial value of empty (0) is assigned)
- identifiers (labels or assembly-local variables and externals if used as inline assembly), e.g. ``jump(name)``, ``3 x add``
- assignments (in "instruction style"), e.g. ``3 =: x``
- assignments in functional style, e.g. ``x := add(y, 3)``
@@ -679,6 +679,8 @@ Example:
We will follow an example compilation from Solidity to desugared assembly.
We consider the runtime bytecode of the following Solidity program::
+ pragma solidity ^0.4.0;
+
contract C {
function f(uint x) returns (uint y) {
y = 1;
diff --git a/docs/contracts.rst b/docs/contracts.rst
index e9ea1b3b..3c9769ff 100644
--- a/docs/contracts.rst
+++ b/docs/contracts.rst
@@ -213,6 +213,8 @@ In the following example, ``D``, can call ``c.getData()`` to retrieve the value
::
+ // This will not compile
+
pragma solidity ^0.4.0;
contract C {
@@ -545,9 +547,11 @@ Please ensure you test your fallback function thoroughly to ensure the execution
test.call(0xabcdef01); // hash does not exist
// results in test.x becoming == 1.
- // The following call will fail, reject the
- // Ether and return false:
- test.send(2 ether);
+ // The following will not compile, but even
+ // if someone sends ether to that contract,
+ // the transaction will fail and reject the
+ // Ether.
+ //test.send(2 ether);
}
}
@@ -773,13 +777,17 @@ seen in the following example::
pragma solidity ^0.4.0;
+ contract owned {
+ function owned() { owner = msg.sender; }
+ address owner;
+ }
+
contract mortal is owned {
function kill() {
if (msg.sender == owner) selfdestruct(owner);
}
}
-
contract Base1 is mortal {
function kill() { /* do cleanup 1 */ mortal.kill(); }
}
@@ -800,6 +808,11 @@ derived override, but this function will bypass
pragma solidity ^0.4.0;
+ contract owned {
+ function owned() { owner = msg.sender; }
+ address owner;
+ }
+
contract mortal is owned {
function kill() {
if (msg.sender == owner) selfdestruct(owner);
@@ -879,6 +892,8 @@ error "Linearization of inheritance graph impossible".
::
+ // This will not compile
+
pragma solidity ^0.4.0;
contract X {}
@@ -914,10 +929,16 @@ Contract functions can lack an implementation as in the following example (note
function utterance() returns (bytes32);
}
-Such contracts cannot be compiled (even if they contain implemented functions alongside non-implemented functions), but they can be used as base contracts::
+Such contracts cannot be compiled (even if they contain
+implemented functions alongside non-implemented functions),
+but they can be used as base contracts::
pragma solidity ^0.4.0;
+ contract Feline {
+ function utterance() returns (bytes32);
+ }
+
contract Cat is Feline {
function utterance() returns (bytes32) { return "miaow"; }
}
diff --git a/docs/control-structures.rst b/docs/control-structures.rst
index 03787c20..f4c0b776 100644
--- a/docs/control-structures.rst
+++ b/docs/control-structures.rst
@@ -20,6 +20,8 @@ For example, suppose we want our contract to
accept one kind of external calls with two integers, we would write
something like::
+ pragma solidity ^0.4.0;
+
contract Simple {
function taker(uint _a, uint _b) {
// do something with _a and _b.
@@ -34,6 +36,8 @@ The output parameters can be declared with the same syntax after the
the sum and the product of the two given integers, then we would
write::
+ pragma solidity ^0.4.0;
+
contract Simple {
function arithmetics(uint _a, uint _b) returns (uint o_sum, uint o_product) {
o_sum = _a + _b;
@@ -91,6 +95,8 @@ Internal Function Calls
Functions of the current contract can be called directly ("internally"), also recursively, as seen in
this nonsensical example::
+ pragma solidity ^0.4.0;
+
contract C {
function g(uint a) returns (uint ret) { return f(); }
function f() returns (uint ret) { return g(7) + f(); }
@@ -116,6 +122,8 @@ all function arguments have to be copied to memory.
When calling functions of other contracts, the amount of Wei sent with the call and
the gas can be specified with special options ``.value()`` and ``.gas()``, respectively::
+ pragma solidity ^0.4.0;
+
contract InfoFeed {
function info() payable returns (uint ret) { return 42; }
}
@@ -173,7 +181,9 @@ parameters from the function declaration, but can be in arbitrary order.
pragma solidity ^0.4.0;
contract C {
- function f(uint key, uint value) { ... }
+ function f(uint key, uint value) {
+ // ...
+ }
function g() {
// named arguments
@@ -261,6 +271,8 @@ Destructuring Assignments and Returning Multiple Values
Solidity internally allows tuple types, i.e. a list of objects of potentially different types whose size is a constant at compile-time. Those tuples can be used to return multiple values at the same time and also assign them to multiple variables (or LValues in general) at the same time::
+ pragma solidity ^0.4.0;
+
contract C {
uint[] data;
@@ -313,6 +325,8 @@ This happens because Solidity inherits its scoping rules from JavaScript.
This is in contrast to many languages where variables are only scoped where they are declared until the end of the semantic block.
As a result, the following code is illegal and cause the compiler to throw an error, ``Identifier already declared``::
+ // This will not compile
+
pragma solidity ^0.4.0;
contract ScopingErrors {
diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst
index 182de33a..1fcdb2fc 100644
--- a/docs/miscellaneous.rst
+++ b/docs/miscellaneous.rst
@@ -48,6 +48,8 @@ non-elementary type, the positions are found by adding an offset of ``keccak256(
So for the following contract snippet::
+ pragma solidity ^0.4.0;
+
contract C {
struct s { uint a; uint b; }
uint x;
diff --git a/docs/security-considerations.rst b/docs/security-considerations.rst
index 33c613d8..658c12b2 100644
--- a/docs/security-considerations.rst
+++ b/docs/security-considerations.rst
@@ -179,11 +179,13 @@ Never use tx.origin for authorization. Let's say you have a wallet contract like
}
}
-Now someone tricks you into sending ether to the address of this attack wallet:
+Now someone tricks you into sending ether to the address of this attack wallet::
-::
+ pragma solidity ^0.4.11;
- pragma solidity ^0.4.0;
+ interface TxUserWallet {
+ function transferTo(address dest, uint amount);
+ }
contract TxAttackWallet {
address owner;
diff --git a/docs/types.rst b/docs/types.rst
index 67549499..b9ecd083 100644
--- a/docs/types.rst
+++ b/docs/types.rst
@@ -376,7 +376,7 @@ Example that shows how to use internal function types::
function (uint, uint) returns (uint) f
)
internal
- returns (uint)
+ returns (uint r)
{
r = self[0];
for (uint i = 1; i < self.length; i++) {
@@ -599,6 +599,8 @@ possible:
::
+ // This will not compile.
+
pragma solidity ^0.4.0;
contract C {
@@ -606,6 +608,7 @@ possible:
// The next line creates a type error because uint[3] memory
// cannot be converted to uint[] memory.
uint[] x = [uint(1), 3, 4];
+ }
}
It is planned to remove this restriction in the future but currently creates
@@ -812,8 +815,9 @@ for each ``_KeyType``, recursively.
}
contract MappingUser {
+ address contractAddress = 0x42;
function f() returns (uint) {
- return MappingExample(<address>).balances(this);
+ return MappingExample(contractAddress).balances(this);
}
}
diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h
index 98ad548d..4297f606 100644
--- a/libdevcore/CommonData.h
+++ b/libdevcore/CommonData.h
@@ -23,13 +23,14 @@
#pragma once
+#include <libdevcore/Common.h>
+
#include <vector>
#include <algorithm>
#include <unordered_set>
#include <type_traits>
#include <cstring>
#include <string>
-#include "Common.h"
namespace dev
{
diff --git a/libdevcore/Exceptions.cpp b/libdevcore/Exceptions.cpp
new file mode 100644
index 00000000..f422d926
--- /dev/null
+++ b/libdevcore/Exceptions.cpp
@@ -0,0 +1,49 @@
+/*
+ This file is part of solidity.
+
+ solidity is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ solidity is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with solidity. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <libdevcore/Exceptions.h>
+
+#include <boost/lexical_cast.hpp>
+
+using namespace std;
+using namespace dev;
+
+char const* Exception::what() const noexcept
+{
+ if (string const* cmt = comment())
+ return cmt->c_str();
+ else
+ return nullptr;
+}
+
+string Exception::lineInfo() const
+{
+ char const* const* file = boost::get_error_info<boost::throw_file>(*this);
+ int const* line = boost::get_error_info<boost::throw_line>(*this);
+ string ret;
+ if (file)
+ ret += *file;
+ ret += ':';
+ if (line)
+ ret += boost::lexical_cast<string>(*line);
+ return ret;
+}
+
+string const* Exception::comment() const noexcept
+{
+ return boost::get_error_info<errinfo_comment>(*this);
+}
diff --git a/libdevcore/Exceptions.h b/libdevcore/Exceptions.h
index 4817e9e3..a3e638bf 100644
--- a/libdevcore/Exceptions.h
+++ b/libdevcore/Exceptions.h
@@ -14,23 +14,16 @@
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
-/** @file Exceptions.h
- * @author Gav Wood <i@gavwood.com>
- * @date 2014
- */
#pragma once
-#include <exception>
-#include <string>
#include <boost/exception/exception.hpp>
#include <boost/exception/info.hpp>
#include <boost/exception/info_tuple.hpp>
#include <boost/exception/diagnostic_information.hpp>
-#include <boost/throw_exception.hpp>
-#include <boost/tuple/tuple.hpp>
-#include "CommonData.h"
-#include "FixedHash.h"
+
+#include <exception>
+#include <string>
namespace dev
{
@@ -38,14 +31,15 @@ namespace dev
/// Base class for all exceptions.
struct Exception: virtual std::exception, virtual boost::exception
{
- Exception(std::string _message = std::string()): m_message(std::move(_message)) {}
- const char* what() const noexcept override { return m_message.empty() ? std::exception::what() : m_message.c_str(); }
+ const char* what() const noexcept override;
/// @returns "FileName:LineNumber" referring to the point where the exception was thrown.
std::string lineInfo() const;
+ /// @returns the errinfo_comment of this exception.
+ std::string const* comment() const noexcept;
+
private:
- std::string m_message;
};
#define DEV_SIMPLE_EXCEPTION(X) struct X: virtual Exception { const char* what() const noexcept override { return #X; } }
diff --git a/libevmasm/AssemblyItem.cpp b/libevmasm/AssemblyItem.cpp
index e69b5932..76104866 100644
--- a/libevmasm/AssemblyItem.cpp
+++ b/libevmasm/AssemblyItem.cpp
@@ -14,13 +14,14 @@
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
-/** @file Assembly.cpp
- * @author Gav Wood <i@gavwood.com>
- * @date 2014
- */
-#include "AssemblyItem.h"
+#include <libevmasm/AssemblyItem.h>
+
#include <libevmasm/SemanticInformation.h>
+
+#include <libdevcore/CommonData.h>
+#include <libdevcore/FixedHash.h>
+
#include <fstream>
using namespace std;
diff --git a/libevmasm/GasMeter.cpp b/libevmasm/GasMeter.cpp
index c96c6ca5..6a7c80e0 100644
--- a/libevmasm/GasMeter.cpp
+++ b/libevmasm/GasMeter.cpp
@@ -14,13 +14,13 @@
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
-/** @file GasMeter.cpp
- * @author Christian <c@ethdev.com>
- * @date 2015
- */
-#include "GasMeter.h"
+#include <libevmasm/GasMeter.h>
+
#include <libevmasm/KnownState.h>
+
+#include <libdevcore/FixedHash.h>
+
using namespace std;
using namespace dev;
using namespace dev::eth;
diff --git a/libjulia/backends/evm/EVMCodeTransform.cpp b/libjulia/backends/evm/EVMCodeTransform.cpp
index efbe5647..704aa3c1 100644
--- a/libjulia/backends/evm/EVMCodeTransform.cpp
+++ b/libjulia/backends/evm/EVMCodeTransform.cpp
@@ -37,10 +37,19 @@ void CodeTransform::operator()(VariableDeclaration const& _varDecl)
{
solAssert(m_scope, "");
- int expectedItems = _varDecl.variables.size();
+ int const numVariables = _varDecl.variables.size();
int height = m_assembly.stackHeight();
- boost::apply_visitor(*this, *_varDecl.value);
- expectDeposit(expectedItems, height);
+ if (_varDecl.value)
+ {
+ boost::apply_visitor(*this, *_varDecl.value);
+ expectDeposit(numVariables, height);
+ }
+ else
+ {
+ int variablesLeft = numVariables;
+ while (variablesLeft--)
+ m_assembly.appendConstant(u256(0));
+ }
for (auto const& variable: _varDecl.variables)
{
auto& var = boost::get<Scope::Variable>(m_scope->identifiers.at(variable.name));
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index 90043b43..29db7441 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -583,6 +583,16 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
!FunctionType(_variable).interfaceFunctionType()
)
m_errorReporter.typeError(_variable.location(), "Internal type is not allowed for public state variables.");
+
+ if (varType->category() == Type::Category::Array)
+ if (auto arrayType = dynamic_cast<ArrayType const*>(varType.get()))
+ if (
+ ((arrayType->location() == DataLocation::Memory) ||
+ (arrayType->location() == DataLocation::CallData)) &&
+ !arrayType->validForCalldata()
+ )
+ m_errorReporter.typeError(_variable.location(), "Array is too large to be encoded as calldata.");
+
return false;
}
@@ -1631,6 +1641,25 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
annotation.isLValue = annotation.referencedDeclaration->isLValue();
}
+ if (exprType->category() == Type::Category::Contract)
+ {
+ if (auto callType = dynamic_cast<FunctionType const*>(type(_memberAccess).get()))
+ {
+ auto kind = callType->kind();
+ auto contractType = dynamic_cast<ContractType const*>(exprType.get());
+ solAssert(!!contractType, "Should be contract type.");
+
+ if (
+ (kind == FunctionType::Kind::Send || kind == FunctionType::Kind::Transfer) &&
+ !contractType->isPayable()
+ )
+ m_errorReporter.typeError(
+ _memberAccess.location(),
+ "Value transfer to a contract without a payable fallback function."
+ );
+ }
+ }
+
// TODO some members might be pure, but for example `address(0x123).balance` is not pure
// although every subexpression is, so leaving this limited for now.
if (auto tt = dynamic_cast<TypeType const*>(exprType.get()))
diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h
index f90a9b2f..cde14ea0 100644
--- a/libsolidity/ast/AST.h
+++ b/libsolidity/ast/AST.h
@@ -23,19 +23,24 @@
#pragma once
-#include <string>
-#include <vector>
-#include <memory>
-#include <boost/noncopyable.hpp>
-#include <libevmasm/SourceLocation.h>
-#include <libevmasm/Instruction.h>
#include <libsolidity/ast/ASTForward.h>
#include <libsolidity/parsing/Token.h>
#include <libsolidity/ast/Types.h>
#include <libsolidity/interface/Exceptions.h>
#include <libsolidity/ast/ASTAnnotations.h>
+
+#include <libevmasm/SourceLocation.h>
+#include <libevmasm/Instruction.h>
+
+#include <libdevcore/FixedHash.h>
#include <json/json.h>
+#include <boost/noncopyable.hpp>
+
+#include <string>
+#include <vector>
+#include <memory>
+
namespace dev
{
namespace solidity
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
index 6ecf509d..76bfb1a8 100644
--- a/libsolidity/ast/Types.cpp
+++ b/libsolidity/ast/Types.cpp
@@ -1230,6 +1230,12 @@ bool ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const
_convertTo.category() == Category::Contract;
}
+bool ContractType::isPayable() const
+{
+ auto fallbackFunction = m_contract.fallbackFunction();
+ return fallbackFunction && fallbackFunction->isPayable();
+}
+
TypePointer ContractType::unaryOperatorResult(Token::Value _operator) const
{
return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer();
@@ -1389,12 +1395,23 @@ bool ArrayType::operator==(Type const& _other) const
return isDynamicallySized() || length() == other.length();
}
-unsigned ArrayType::calldataEncodedSize(bool _padded) const
+bool ArrayType::validForCalldata() const
+{
+ return unlimitedCalldataEncodedSize(true) <= numeric_limits<unsigned>::max();
+}
+
+bigint ArrayType::unlimitedCalldataEncodedSize(bool _padded) const
{
if (isDynamicallySized())
return 32;
bigint size = bigint(length()) * (isByteArray() ? 1 : baseType()->calldataEncodedSize(_padded));
size = ((size + 31) / 32) * 32;
+ return size;
+}
+
+unsigned ArrayType::calldataEncodedSize(bool _padded) const
+{
+ bigint size = unlimitedCalldataEncodedSize(_padded);
solAssert(size <= numeric_limits<unsigned>::max(), "Array size does not fit unsigned.");
return unsigned(size);
}
diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h
index 66eb039f..c24cc11a 100644
--- a/libsolidity/ast/Types.h
+++ b/libsolidity/ast/Types.h
@@ -616,6 +616,9 @@ public:
virtual TypePointer interfaceType(bool _inLibrary) const override;
virtual bool canBeUsedExternally(bool _inLibrary) const override;
+ /// @returns true if this is valid to be stored in calldata
+ bool validForCalldata() const;
+
/// @returns true if this is a byte array or a string
bool isByteArray() const { return m_arrayKind != ArrayKind::Ordinary; }
/// @returns true if this is a string
@@ -630,6 +633,8 @@ private:
/// String is interpreted as a subtype of Bytes.
enum class ArrayKind { Ordinary, Bytes, String };
+ bigint unlimitedCalldataEncodedSize(bool _padded) const;
+
///< Byte arrays ("bytes") and strings have different semantics from ordinary arrays.
ArrayKind m_arrayKind = ArrayKind::Ordinary;
TypePointer m_baseType;
@@ -675,6 +680,10 @@ public:
}
bool isSuper() const { return m_super; }
+
+ // @returns true if and only if the contract has a payable fallback function
+ bool isPayable() const;
+
ContractDefinition const& contractDefinition() const { return m_contract; }
/// Returns the function type of the constructor modified to return an object of the contract's type.
diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp
index 7e00ffae..76b0bbd5 100644
--- a/libsolidity/inlineasm/AsmAnalysis.cpp
+++ b/libsolidity/inlineasm/AsmAnalysis.cpp
@@ -174,14 +174,20 @@ bool AsmAnalyzer::operator()(assembly::Assignment const& _assignment)
bool AsmAnalyzer::operator()(assembly::VariableDeclaration const& _varDecl)
{
- int const expectedItems = _varDecl.variables.size();
- int const stackHeight = m_stackHeight;
- bool success = boost::apply_visitor(*this, *_varDecl.value);
- if ((m_stackHeight - stackHeight) != expectedItems)
+ bool success = true;
+ int const numVariables = _varDecl.variables.size();
+ if (_varDecl.value)
{
- m_errorReporter.declarationError(_varDecl.location, "Variable count mismatch.");
- return false;
+ int const stackHeight = m_stackHeight;
+ success = boost::apply_visitor(*this, *_varDecl.value);
+ if ((m_stackHeight - stackHeight) != numVariables)
+ {
+ m_errorReporter.declarationError(_varDecl.location, "Variable count mismatch.");
+ return false;
+ }
}
+ else
+ m_stackHeight += numVariables;
for (auto const& variable: _varDecl.variables)
{
diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp
index d282a30d..133f70b1 100644
--- a/libsolidity/inlineasm/AsmParser.cpp
+++ b/libsolidity/inlineasm/AsmParser.cpp
@@ -347,10 +347,15 @@ assembly::VariableDeclaration Parser::parseVariableDeclaration()
else
break;
}
- expectToken(Token::Colon);
- expectToken(Token::Assign);
- varDecl.value.reset(new Statement(parseExpression()));
- varDecl.location.end = locationOf(*varDecl.value).end;
+ if (currentToken() == Token::Colon)
+ {
+ expectToken(Token::Colon);
+ expectToken(Token::Assign);
+ varDecl.value.reset(new Statement(parseExpression()));
+ varDecl.location.end = locationOf(*varDecl.value).end;
+ }
+ else
+ varDecl.location.end = varDecl.variables.back().location.end;
return varDecl;
}
diff --git a/libsolidity/inlineasm/AsmPrinter.cpp b/libsolidity/inlineasm/AsmPrinter.cpp
index 062ff453..4f96a3e9 100644
--- a/libsolidity/inlineasm/AsmPrinter.cpp
+++ b/libsolidity/inlineasm/AsmPrinter.cpp
@@ -128,8 +128,11 @@ string AsmPrinter::operator()(assembly::VariableDeclaration const& _variableDecl
),
", "
);
- out += " := ";
- out += boost::apply_visitor(*this, *_variableDeclaration.value);
+ if (_variableDeclaration.value)
+ {
+ out += " := ";
+ out += boost::apply_visitor(*this, *_variableDeclaration.value);
+ }
return out;
}
diff --git a/libsolidity/inlineasm/AsmScopeFiller.cpp b/libsolidity/inlineasm/AsmScopeFiller.cpp
index 5b3174b8..b70ae9ac 100644
--- a/libsolidity/inlineasm/AsmScopeFiller.cpp
+++ b/libsolidity/inlineasm/AsmScopeFiller.cpp
@@ -27,6 +27,8 @@
#include <libsolidity/interface/ErrorReporter.h>
#include <libsolidity/interface/Exceptions.h>
+#include <libdevcore/CommonData.h>
+
#include <boost/range/adaptor/reversed.hpp>
#include <memory>
diff --git a/libsolidity/interface/Exceptions.cpp b/libsolidity/interface/Exceptions.cpp
index 9f2a2d06..a837dce6 100644
--- a/libsolidity/interface/Exceptions.cpp
+++ b/libsolidity/interface/Exceptions.cpp
@@ -67,16 +67,3 @@ Error::Error(Error::Type _type, const std::string& _description, const SourceLoc
*this << errinfo_sourceLocation(_location);
*this << errinfo_comment(_description);
}
-
-string Exception::lineInfo() const
-{
- char const* const* file = boost::get_error_info<boost::throw_file>(*this);
- int const* line = boost::get_error_info<boost::throw_line>(*this);
- string ret;
- if (file)
- ret += *file;
- ret += ':';
- if (line)
- ret += boost::lexical_cast<string>(*line);
- return ret;
-}
diff --git a/scripts/isolate_tests.py b/scripts/isolate_tests.py
index a1d1c75c..cfaef210 100755
--- a/scripts/isolate_tests.py
+++ b/scripts/isolate_tests.py
@@ -1,6 +1,6 @@
#!/usr/bin/python
#
-# This script reads C++ source files and writes all
+# This script reads C++ or RST source files and writes all
# multi-line strings into individual files.
# This can be used to extract the Solidity test cases
# into files for e.g. fuzz testing as
@@ -12,7 +12,7 @@ import os
import hashlib
from os.path import join
-def extract_cases(path):
+def extract_test_cases(path):
lines = open(path, 'rb').read().splitlines()
inside = False
@@ -34,6 +34,44 @@ def extract_cases(path):
return tests
+# Contract sources are indented by 4 spaces.
+# Look for `pragma solidity` and abort a line not indented properly.
+# If the comment `// This will not compile` is above the pragma,
+# the code is skipped.
+def extract_docs_cases(path):
+ # Note: this code works, because splitlines() removes empty new lines
+ # and thus even if the empty new lines are missing indentation
+ lines = open(path, 'rb').read().splitlines()
+
+ ignore = False
+ inside = False
+ tests = []
+
+ for l in lines:
+ if inside:
+ # Abort if indentation is missing
+ m = re.search(r'^[^ ]+', l)
+ if m:
+ inside = False
+ else:
+ tests[-1] += l + '\n'
+ else:
+ m = re.search(r'^ // This will not compile', l)
+ if m:
+ ignore = True
+
+ if ignore:
+ # Abort if indentation is missing
+ m = re.search(r'^[^ ]+', l)
+ if m:
+ ignore = False
+ else:
+ m = re.search(r'^ pragma solidity .*[0-9]+\.[0-9]+\.[0-9]+;$', l)
+ if m:
+ inside = True
+ tests += [l]
+
+ return tests
def write_cases(tests):
for test in tests:
@@ -41,8 +79,17 @@ def write_cases(tests):
if __name__ == '__main__':
path = sys.argv[1]
+ docs = False
+ if len(sys.argv) > 2 and sys.argv[2] == 'docs':
+ docs = True
- for root, dir, files in os.walk(path):
+ for root, subdirs, files in os.walk(path):
+ if '_build' in subdirs:
+ subdirs.remove('_build')
for f in files:
- cases = extract_cases(join(root, f))
+ path = join(root, f)
+ if docs:
+ cases = extract_docs_cases(path)
+ else:
+ cases = extract_test_cases(path)
write_cases(cases)
diff --git a/scripts/tests.sh b/scripts/tests.sh
index 64b4121f..5d7eb0e1 100755
--- a/scripts/tests.sh
+++ b/scripts/tests.sh
@@ -30,27 +30,6 @@ set -e
REPO_ROOT="$(dirname "$0")"/..
-echo "Checking that StandardToken.sol, owned.sol and mortal.sol produce bytecode..."
-output=$("$REPO_ROOT"/build/solc/solc --bin "$REPO_ROOT"/std/*.sol 2>/dev/null | grep "ffff" | wc -l)
-test "${output//[[:blank:]]/}" = "3"
-
-echo "Compiling various other contracts and libraries..."
-(
-cd "$REPO_ROOT"/test/compilationTests/
-for dir in *
-do
- if [ "$dir" != "README.md" ]
- then
- echo " - $dir"
- cd "$dir"
- ../../../build/solc/solc --optimize \
- --combined-json abi,asm,ast,bin,bin-runtime,clone-bin,compact-format,devdoc,hashes,interface,metadata,opcodes,srcmap,srcmap-runtime,userdoc \
- *.sol */*.sol > /dev/null 2>&1
- cd ..
- fi
-done
-)
-
echo "Running commandline tests..."
"$REPO_ROOT/test/cmdlineTests.sh"
diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh
index 4074ce55..eb5c714d 100755
--- a/test/cmdlineTests.sh
+++ b/test/cmdlineTests.sh
@@ -28,27 +28,87 @@
set -e
-REPO_ROOT="$(dirname "$0")"/..
+REPO_ROOT=$(cd $(dirname "$0")/.. && pwd)
+echo $REPO_ROOT
SOLC="$REPO_ROOT/build/solc/solc"
echo "Checking that the bug list is up to date..."
"$REPO_ROOT"/scripts/update_bugs_by_version.py
-echo "Compiling all files in std and examples..."
+echo "Checking that StandardToken.sol, owned.sol and mortal.sol produce bytecode..."
+output=$("$REPO_ROOT"/build/solc/solc --bin "$REPO_ROOT"/std/*.sol 2>/dev/null | grep "ffff" | wc -l)
+test "${output//[[:blank:]]/}" = "3"
-for f in "$REPO_ROOT"/std/*.sol
-do
- echo "Compiling $f..."
+function compileFull()
+{
+ files="$*"
+ set +e
+ "$SOLC" --optimize \
+ --combined-json abi,asm,ast,bin,bin-runtime,clone-bin,compact-format,devdoc,hashes,interface,metadata,opcodes,srcmap,srcmap-runtime,userdoc \
+ $files >/dev/null 2>&1
+ failed=$?
+ set -e
+ if [ $failed -ne 0 ]
+ then
+ echo "Compilation failed on:"
+ cat $files
+ false
+ fi
+}
+
+function compileWithoutWarning()
+{
+ files="$*"
set +e
- output=$("$SOLC" "$f" 2>&1)
+ output=$("$SOLC" $files 2>&1)
failed=$?
# Remove the pre-release warning from the compiler output
output=$(echo "$output" | grep -v 'pre-release')
echo "$output"
set -e
test -z "$output" -a "$failed" -eq 0
+}
+
+echo "Compiling various other contracts and libraries..."
+(
+cd "$REPO_ROOT"/test/compilationTests/
+for dir in *
+do
+ if [ "$dir" != "README.md" ]
+ then
+ echo " - $dir"
+ cd "$dir"
+ compileFull *.sol */*.sol
+ cd ..
+ fi
+done
+)
+
+echo "Compiling all files in std and examples..."
+
+for f in "$REPO_ROOT"/std/*.sol
+do
+ echo "$f"
+ compileWithoutWarning "$f"
done
+echo "Compiling all examples from the documentation..."
+TMPDIR=$(mktemp -d)
+(
+ set -e
+ cd "$REPO_ROOT"
+ REPO_ROOT=$(pwd) # make it absolute
+ cd "$TMPDIR"
+ "$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/docs/ docs
+ for f in *.sol
+ do
+ echo "$f"
+ compileFull "$TMPDIR/$f"
+ done
+)
+rm -rf "$TMPDIR"
+echo "Done."
+
echo "Testing library checksum..."
echo '' | "$SOLC" --link --libraries a:0x90f20564390eAe531E810af625A22f51385Cd222
! echo '' | "$SOLC" --link --libraries a:0x80f20564390eAe531E810af625A22f51385Cd222 2>/dev/null
@@ -77,6 +137,7 @@ TMPDIR=$(mktemp -d)
REPO_ROOT=$(pwd) # make it absolute
cd "$TMPDIR"
"$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/test/
+ "$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/docs/ docs
for f in *.sol
do
set +e
diff --git a/test/libjulia/Parser.cpp b/test/libjulia/Parser.cpp
index fa7c45ed..dd6f3d94 100644
--- a/test/libjulia/Parser.cpp
+++ b/test/libjulia/Parser.cpp
@@ -131,6 +131,11 @@ BOOST_AUTO_TEST_CASE(vardecl_bool)
BOOST_CHECK(successParse("{ let x:bool := false:bool }"));
}
+BOOST_AUTO_TEST_CASE(vardecl_empty)
+{
+ BOOST_CHECK(successParse("{ let x:u256 }"));
+}
+
BOOST_AUTO_TEST_CASE(assignment)
{
BOOST_CHECK(successParse("{ let x:u256 := 2:u256 let y:u256 := x }"));
diff --git a/test/libsolidity/ErrorCheck.cpp b/test/libsolidity/ErrorCheck.cpp
index 9b0f9fb7..b1e94061 100644
--- a/test/libsolidity/ErrorCheck.cpp
+++ b/test/libsolidity/ErrorCheck.cpp
@@ -28,11 +28,11 @@ using namespace std;
bool dev::solidity::searchErrorMessage(Error const& _err, std::string const& _substr)
{
- if (string const* errorMessage = boost::get_error_info<dev::errinfo_comment>(_err))
+ if (string const* errorMessage = _err.comment())
{
if (errorMessage->find(_substr) == std::string::npos)
{
- cout << "Expected message \"" << _substr << "\" but found" << *errorMessage << endl;
+ cout << "Expected message \"" << _substr << "\" but found \"" << *errorMessage << "\".\n";
return false;
}
return true;
diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp
index 5197f649..4bf4eb48 100644
--- a/test/libsolidity/InlineAssembly.cpp
+++ b/test/libsolidity/InlineAssembly.cpp
@@ -195,6 +195,11 @@ BOOST_AUTO_TEST_CASE(vardecl_bool)
CHECK_PARSE_ERROR("{ let x := false }", ParserError, "True and false are not valid literals.");
}
+BOOST_AUTO_TEST_CASE(vardecl_empty)
+{
+ BOOST_CHECK(successParse("{ let x }"));
+}
+
BOOST_AUTO_TEST_CASE(assignment)
{
BOOST_CHECK(successParse("{ let x := 2 7 8 add =: x }"));
diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp
index 7b130082..94d3e168 100644
--- a/test/libsolidity/SolidityEndToEndTest.cpp
+++ b/test/libsolidity/SolidityEndToEndTest.cpp
@@ -9723,6 +9723,24 @@ BOOST_AUTO_TEST_CASE(multi_modifiers)
BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(12)));
}
+BOOST_AUTO_TEST_CASE(inlineasm_empty_let)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f() returns (uint a, uint b) {
+ assembly {
+ let x
+ let y, z
+ a := x
+ b := z
+ }
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(0), u256(0)));
+}
+
BOOST_AUTO_TEST_SUITE_END()
}
diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp
index 2ee5baac..36b48cfd 100644
--- a/test/libsolidity/SolidityNameAndTypeResolution.cpp
+++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp
@@ -6146,6 +6146,94 @@ BOOST_AUTO_TEST_CASE(callable_crash)
CHECK_ERROR(text, TypeError, "Type is not callable");
}
+BOOST_AUTO_TEST_CASE(error_transfer_non_payable_fallback)
+{
+ char const* text = R"(
+ contract A {
+ function() {}
+ }
+
+ contract B {
+ A a;
+
+ function() {
+ a.transfer(100);
+ }
+ }
+ )";
+ CHECK_ERROR(text, TypeError, "Value transfer to a contract without a payable fallback function.");
+}
+
+BOOST_AUTO_TEST_CASE(error_transfer_no_fallback)
+{
+ char const* text = R"(
+ contract A {}
+
+ contract B {
+ A a;
+
+ function() {
+ a.transfer(100);
+ }
+ }
+ )";
+ CHECK_ERROR(text, TypeError, "Value transfer to a contract without a payable fallback function.");
+}
+
+BOOST_AUTO_TEST_CASE(error_send_non_payable_fallback)
+{
+ char const* text = R"(
+ contract A {
+ function() {}
+ }
+
+ contract B {
+ A a;
+
+ function() {
+ require(a.send(100));
+ }
+ }
+ )";
+ CHECK_ERROR(text, TypeError, "Value transfer to a contract without a payable fallback function.");
+}
+
+BOOST_AUTO_TEST_CASE(does_not_error_transfer_payable_fallback)
+{
+ char const* text = R"(
+ contract A {
+ function() payable {}
+ }
+
+ contract B {
+ A a;
+
+ function() {
+ a.transfer(100);
+ }
+ }
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(text);
+}
+
+BOOST_AUTO_TEST_CASE(does_not_error_transfer_regular_function)
+{
+ char const* text = R"(
+ contract A {
+ function transfer(uint) {}
+ }
+
+ contract B {
+ A a;
+
+ function() {
+ a.transfer(100);
+ }
+ }
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(text);
+}
+
BOOST_AUTO_TEST_CASE(returndatacopy_as_variable)
{
char const* text = R"(
@@ -6209,6 +6297,31 @@ BOOST_AUTO_TEST_CASE(implicit_conversion_disallowed)
CHECK_ERROR(text, TypeError, "Return argument type uint32 is not implicitly convertible to expected type (type of first return variable) bytes4.");
}
+BOOST_AUTO_TEST_CASE(too_large_arrays_for_calldata)
+{
+ char const* text = R"(
+ contract C {
+ function f(uint[85678901234] a) external {
+ }
+ }
+ )";
+ CHECK_ERROR(text, TypeError, "Array is too large to be encoded as calldata.");
+ text = R"(
+ contract C {
+ function f(uint[85678901234] a) internal {
+ }
+ }
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(text);
+ text = R"(
+ contract C {
+ function f(uint[85678901234] a) {
+ }
+ }
+ )";
+ CHECK_ERROR(text, TypeError, "Array is too large to be encoded as calldata.");
+}
+
BOOST_AUTO_TEST_SUITE_END()
}