aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Changelog.md41
-rw-r--r--docs/common-patterns.rst22
-rw-r--r--docs/contracts.rst32
-rw-r--r--docs/control-structures.rst17
-rw-r--r--docs/introduction-to-smart-contracts.rst4
-rw-r--r--docs/layout-of-source-files.rst2
-rw-r--r--docs/security-considerations.rst8
-rw-r--r--docs/solidity-by-example.rst12
-rw-r--r--docs/structure-of-a-contract.rst12
-rw-r--r--docs/style-guide.rst2
-rw-r--r--docs/types.rst14
-rw-r--r--libsolidity/analysis/TypeChecker.cpp40
-rw-r--r--libsolidity/ast/AST.h4
-rw-r--r--libsolidity/ast/ASTJsonConverter.cpp17
-rw-r--r--libsolidity/ast/Types.cpp95
-rw-r--r--libsolidity/ast/Types.h26
-rw-r--r--libsolidity/codegen/CompilerUtils.cpp8
-rw-r--r--libsolidity/codegen/ContractCompiler.cpp14
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp13
-rw-r--r--libsolidity/grammar.txt9
-rw-r--r--libsolidity/interface/InterfaceHandler.cpp3
-rw-r--r--libsolidity/parsing/Parser.cpp9
-rw-r--r--libsolidity/parsing/ParserBase.cpp12
-rw-r--r--libsolidity/parsing/Token.h5
-rwxr-xr-xscripts/install_deps.sh2
-rw-r--r--solc/CommandLineInterface.cpp40
-rw-r--r--solc/jsonCompiler.cpp16
-rw-r--r--std/owned.sol2
-rw-r--r--test/contracts/AuctionRegistrar.cpp9
-rw-r--r--test/contracts/FixedFeeRegistrar.cpp4
-rw-r--r--test/contracts/Wallet.cpp8
-rw-r--r--test/libsolidity/ASTJSON.cpp26
-rw-r--r--test/libsolidity/Assembly.cpp2
-rw-r--r--test/libsolidity/SolidityABIJSON.cpp67
-rw-r--r--test/libsolidity/SolidityEndToEndTest.cpp196
-rw-r--r--test/libsolidity/SolidityNameAndTypeResolution.cpp166
-rw-r--r--test/libsolidity/SolidityParser.cpp26
37 files changed, 793 insertions, 192 deletions
diff --git a/Changelog.md b/Changelog.md
index 25890890..0c6fef19 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -7,38 +7,47 @@ enforce some safety features. The most important change is
Breaking Changes:
* Source files have to specify the compiler version they are
- compatible with using e.g. `pragma solidity ^0.4.0;` or
- `pragma solidity >=0.4.0 <0.4.8;`
- * Contracts that want to receive Ether have to implement a fallback
- function (contracts now throw if no fallback function is defined
- and no function matches the signature).
- * Failing contract creation through "new" throws now.
- * Throw on division / modulus by zero
+ compatible with using e.g. ``pragma solidity ^0.4.0;`` or
+ ``pragma solidity >=0.4.0 <0.4.8;``
+ * Functions that want to receive Ether have to specify the
+ new ``payable`` modifier (otherwise they throw).
+ * Contracts that want to receive Ether with a plain "send"
+ have to implement a fallback function with the ``payable``
+ modifier. Contracts now throw if no payable fallback
+ function is defined and no function matches the signature.
+ * Failing contract creation through "new" throws.
+ * Division / modulus by zero throws.
* Function call throws if target contract does not have code
* Modifiers are required to contain ``_`` (use ``if (false) _`` as a workaround if needed).
- * Modifiers: return does not skip part in modifier after ``_``
- * ``ecrecover`` now returns zero if the input is malformed (it previously returned garbage)
+ * Modifiers: return does not skip part in modifier after ``_``.
+ * Placeholder statement `_` in modifier now requires explicit `;`.
+ * ``ecrecover`` now returns zero if the input is malformed (it previously returned garbage).
+ * The ``constant`` keyword cannot be used for constructors or the fallback function.
* Removed ``--interface`` (Solidity interface) output option
* JSON AST: General cleanup, renamed many nodes to match their C++ names.
- * Json Output: srcmap-runtime renamed to srcmapRuntime
+ * JSON output: ``srcmap-runtime`` renamed to ``srcmapRuntime``.
* Moved (and reworked) standard library contracts from inside the compiler to github.com/ethereum/solidity/std
(``import "std";`` or ``import owned;`` do not work anymore).
- * Confusing and undocumented keyword "after" was removed.
- * New reserved words: hex, payable, abstract, static, interface
+ * Confusing and undocumented keyword ``after`` was removed.
+ * New reserved words: ``abstract``, ``hex``, ``interface``, ``payable``, ``pure``, ``static``, ``view``.
Features:
* Hexadecimal string literals: ``hex"ab1248fe"``
* Internal: Inline assembly usable by the code generator.
* Commandline interface: Using ``-`` as filename allows reading from stdin.
- * Interface Json: Fallback function is now part of the ABI.
- * Interface: Version string now semver compatible.
+ * Interface JSON: Fallback function is now part of the ABI.
+ * Interface: Version string now *semver* compatible.
+ * Code generator: Do not provide "new account gas" if we know the called account exists.
Bugfixes:
- * JSON AST: nodes were added at wrong parent
- * Why3 translator: crash fix for exponentiation
+ * JSON AST: Nodes were added at wrong parent
+ * Why3 translator: Crash fix for exponentiation
+ * Commandline Interface: linking libraries with underscores in their name.
* Type Checker: Fallback function cannot return data anymore.
+ * Code Generator: Fix crash when ``sha3()`` was used on unsupported types.
+ * Code Generator: Manually set gas stipend for ``.send(0)``.
Lots of changes to the documentation mainly by voluntary external contributors.
diff --git a/docs/common-patterns.rst b/docs/common-patterns.rst
index 531a94ad..e56e7a6c 100644
--- a/docs/common-patterns.rst
+++ b/docs/common-patterns.rst
@@ -28,6 +28,8 @@ become the new richest.
::
+ pragma solidity ^0.4.0;
+
contract WithdrawalContract {
address public richest;
uint public mostSent;
@@ -68,6 +70,8 @@ This is as opposed to the more intuitive sending pattern.
::
+ pragma solidity ^0.4.0;
+
contract SendContract {
address public richest;
uint public mostSent;
@@ -131,6 +135,8 @@ restrictions highly readable.
::
+ pragma solidity ^0.4.0;
+
contract AccessRestriction {
// These will be assigned at the construction
// phase, where `msg.sender` is the account
@@ -148,10 +154,10 @@ restrictions highly readable.
{
if (msg.sender != _account)
throw;
- // Do not forget the "_"! It will
+ // Do not forget the "_;"! It will
// be replaced by the actual function
// body when the modifier is invoked.
- _
+ _;
}
/// Make `_newOwner` the new owner of this
@@ -164,7 +170,7 @@ restrictions highly readable.
modifier onlyAfter(uint _time) {
if (now < _time) throw;
- _
+ _;
}
/// Erase ownership information.
@@ -187,7 +193,7 @@ restrictions highly readable.
modifier costs(uint _amount) {
if (msg.value < _amount)
throw;
- _
+ _;
if (msg.value > _amount)
msg.sender.send(msg.value - _amount);
}
@@ -270,6 +276,8 @@ function finishes.
::
+ pragma solidity ^0.4.0;
+
contract StateMachine {
enum Stages {
AcceptingBlindedBids,
@@ -286,7 +294,7 @@ function finishes.
modifier atStage(Stages _stage) {
if (stage != _stage) throw;
- _
+ _;
}
function nextStage() internal {
@@ -304,7 +312,7 @@ function finishes.
now >= creationTime + 12 days)
nextStage();
// The other stages transition by transaction
- _
+ _;
}
// Order of the modifiers matters here!
@@ -328,7 +336,7 @@ function finishes.
// automatically.
modifier transitionNext()
{
- _
+ _;
nextStage();
}
diff --git a/docs/contracts.rst b/docs/contracts.rst
index a22a3544..4fe046ed 100644
--- a/docs/contracts.rst
+++ b/docs/contracts.rst
@@ -65,6 +65,8 @@ This means that cyclic creation dependencies are impossible.
::
+ pragma solidity ^0.4.0;
+
contract OwnedToken {
// TokenCreator is a contract type that is defined below.
// It is fine to reference it as long as it is not used
@@ -189,6 +191,8 @@ return parameter list for functions.
::
+ pragma solidity ^0.4.0;
+
contract C {
function f(uint a) private returns (uint b) { return a + 1; }
function setData(uint a) internal { data = a; }
@@ -201,6 +205,8 @@ In the following example, ``D``, can call ``c.getData()`` to retrieve the value
::
+ pragma solidity ^0.4.0;
+
contract C {
uint private data;
@@ -243,6 +249,8 @@ be done at declaration.
::
+ pragma solidity ^0.4.0;
+
contract C {
uint public data = 42;
}
@@ -262,6 +270,8 @@ it is evaluated as a state variable and if it is accessed externally
::
+ pragma solidity ^0.4.0;
+
contract C {
uint public data;
function x() {
@@ -274,6 +284,8 @@ The next example is a bit more complex:
::
+ pragma solidity ^0.4.0;
+
contract Complex {
struct Data {
uint a;
@@ -307,6 +319,8 @@ inheritable properties of contracts and may be overridden by derived contracts.
::
+ pragma solidity ^0.4.0;
+
contract owned {
function owned() { owner = msg.sender; }
address owner;
@@ -321,7 +335,7 @@ inheritable properties of contracts and may be overridden by derived contracts.
modifier onlyOwner {
if (msg.sender != owner)
throw;
- _
+ _;
}
}
@@ -341,7 +355,7 @@ inheritable properties of contracts and may be overridden by derived contracts.
// Modifiers can receive arguments:
modifier costs(uint price) {
if (msg.value >= price) {
- _
+ _;
}
}
}
@@ -367,7 +381,7 @@ inheritable properties of contracts and may be overridden by derived contracts.
modifier noReentrancy() {
if (locked) throw;
locked = true;
- _
+ _;
locked = false;
}
@@ -408,6 +422,8 @@ for array and struct types and not possible for mapping types).
::
+ pragma solidity ^0.4.0;
+
contract C {
uint constant x = 32**22 + 8;
string constant text = "abc";
@@ -455,6 +471,8 @@ Please ensure you test your fallback function thoroughly to ensure the execution
::
+ pragma solidity ^0.4.0;
+
contract Test {
function() { x = 1; }
uint x;
@@ -523,6 +541,8 @@ All non-indexed arguments will be stored in the data part of the log.
::
+ pragma solidity ^0.4.0;
+
contract ClientReceipt {
event Deposit(
address indexed _from,
@@ -791,6 +811,8 @@ error "Linearization of inheritance graph impossible".
::
+ pragma solidity ^0.4.0;
+
contract X {}
contract A is X {}
contract C is A, X {}
@@ -861,6 +883,8 @@ more advanced example to implement a set).
::
+ pragma solidity ^0.4.0;
+
library Set {
// We define a new struct datatype that will be used to
// hold its data in the calling contract.
@@ -931,6 +955,8 @@ custom types without the overhead of external function calls:
::
+ pragma solidity ^0.4.0;
+
library BigInt {
struct bigint {
uint[] limbs;
diff --git a/docs/control-structures.rst b/docs/control-structures.rst
index 0e430b6b..61a8611e 100644
--- a/docs/control-structures.rst
+++ b/docs/control-structures.rst
@@ -98,6 +98,8 @@ parameters from the function declaration, but can be in arbitrary order.
::
+ pragma solidity ^0.4.0;
+
contract C {
function f(uint key, uint value) { ... }
@@ -115,6 +117,8 @@ Those names will still be present on the stack, but they are inaccessible.
::
+ pragma solidity ^0.4.0;
+
contract C {
// omitted name for parameter
function func(uint k, uint) returns(uint) {
@@ -136,6 +140,8 @@ creation-dependencies are now possible.
::
+ pragma solidity ^0.4.0;
+
contract D {
uint x;
function D(uint a) {
@@ -343,6 +349,8 @@ idea is that assembly libraries will be used to enhance the language in such way
.. code::
+ pragma solidity ^0.4.0;
+
library GetCode {
function at(address _addr) returns (bytes o_code) {
assembly {
@@ -368,6 +376,8 @@ you really know what you are doing.
.. code::
+ pragma solidity ^0.4.0;
+
library VectorSum {
// This function is less efficient because the optimizer currently fails to
// remove the bounds checks in array access.
@@ -523,7 +533,8 @@ The opcodes ``pushi`` and ``jumpdest`` cannot be used directly.
+-------------------------+------+-----------------------------------------------------------------+
| call(g, a, v, in, | | call contract at address a with input mem[in..(in+insize)) |
| insize, out, outsize) | | providing g gas and v wei and output area |
-| | | mem[out..(out+outsize)) returning 1 on error (out of gas) |
+| | | mem[out..(out+outsize)) returning 0 on error (eg. out of gas) |
+| | | and 1 on success |
+-------------------------+------+-----------------------------------------------------------------+
| callcode(g, a, v, in, | | identical to `call` but only use the code from a and stay |
| insize, out, outsize) | | in the context of the current contract otherwise |
@@ -622,6 +633,8 @@ It is planned that the stack height changes can be specified in inline assembly.
.. code::
+ pragma solidity ^0.4.0;
+
contract C {
uint b;
function f(uint x) returns (uint r) {
@@ -696,6 +709,8 @@ be just ``0``, but it can also be a complex functional-style expression.
.. code::
+ pragma solidity ^0.4.0;
+
contract C {
function f(uint x) returns (uint b) {
assembly {
diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst
index 6fcd4854..de126a5a 100644
--- a/docs/introduction-to-smart-contracts.rst
+++ b/docs/introduction-to-smart-contracts.rst
@@ -16,6 +16,8 @@ Storage
::
+ pragma solidity ^0.4.0;
+
contract SimpleStorage {
uint storedData;
@@ -63,6 +65,8 @@ registering with username and password - all you need is an Ethereum keypair.
::
+ pragma solidity ^0.4.0;
+
contract Coin {
// The keyword "public" makes those variables
// readable from outside.
diff --git a/docs/layout-of-source-files.rst b/docs/layout-of-source-files.rst
index fdb7b5e8..17ac8c6f 100644
--- a/docs/layout-of-source-files.rst
+++ b/docs/layout-of-source-files.rst
@@ -192,6 +192,8 @@ for the two input parameters and two returned values.
::
+ pragma solidity ^0.4.0;
+
/** @title Shape calculator.*/
contract shapeCalculator{
/**@dev Calculates a rectangle's surface and perimeter.
diff --git a/docs/security-considerations.rst b/docs/security-considerations.rst
index 7e846674..8800487c 100644
--- a/docs/security-considerations.rst
+++ b/docs/security-considerations.rst
@@ -51,6 +51,8 @@ complete contract):
::
+ pragma solidity ^0.4.0;
+
// THIS CONTRACT CONTAINS A BUG - DO NOT USE
contract Fund {
/// Mapping of ether shares of the contract.
@@ -73,6 +75,8 @@ outlined further below:
::
+ pragma solidity ^0.4.0;
+
contract Fund {
/// Mapping of ether shares of the contract.
mapping(address => uint) shares;
@@ -149,6 +153,8 @@ Never use tx.origin for authorization. Let's say you have a wallet contract like
::
+ pragma solidity ^0.4.0;
+
contract TxUserWallet {
address owner;
@@ -166,6 +172,8 @@ Now someone tricks you into sending ether to the address of this attack wallet:
::
+ pragma solidity ^0.4.0;
+
contract TxAttackWallet {
address owner;
diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst
index 8e23dafd..1eefa4d4 100644
--- a/docs/solidity-by-example.rst
+++ b/docs/solidity-by-example.rst
@@ -36,6 +36,8 @@ of votes.
::
+ pragma solidity ^0.4.0;
+
/// @title Voting with delegation.
contract Ballot {
// This declares a new complex type which will
@@ -208,6 +210,8 @@ activate themselves.
::
+ pragma solidity ^0.4.0;
+
contract SimpleAuction {
// Parameters of the auction. Times are either
// absolute unix timestamps (seconds since 1970-01-01)
@@ -377,6 +381,8 @@ high or low invalid bids.
::
+ pragma solidity ^0.4.0;
+
contract BlindAuction {
struct Bid {
bytes32 blindedBid;
@@ -403,8 +409,8 @@ high or low invalid bids.
/// functions. `onlyBefore` is applied to `bid` below:
/// The new function body is the modifier's body where
/// `_` is replaced by the old function body.
- modifier onlyBefore(uint _time) { if (now >= _time) throw; _ }
- modifier onlyAfter(uint _time) { if (now <= _time) throw; _ }
+ modifier onlyBefore(uint _time) { if (now >= _time) throw; _; }
+ modifier onlyAfter(uint _time) { if (now <= _time) throw; _; }
function BlindAuction(
uint _biddingTime,
@@ -543,6 +549,8 @@ Safe Remote Purchase
::
+ pragma solidity ^0.4.0;
+
contract Purchase {
uint public value;
address public seller;
diff --git a/docs/structure-of-a-contract.rst b/docs/structure-of-a-contract.rst
index 79f78422..1036289a 100644
--- a/docs/structure-of-a-contract.rst
+++ b/docs/structure-of-a-contract.rst
@@ -20,6 +20,8 @@ State variables are values which are permanently stored in contract storage.
::
+ pragma solidity ^0.4.0;
+
contract SimpleStorage {
uint storedData; // State variable
// ...
@@ -38,6 +40,8 @@ Functions are the executable units of code within a contract.
::
+ pragma solidity ^0.4.0;
+
contract SimpleAuction {
function bid() { // Function
// ...
@@ -58,6 +62,8 @@ Function modifiers can be used to amend the semantics of functions in a declarat
::
+ pragma solidity ^0.4.0;
+
contract Purchase {
address public seller;
@@ -80,6 +86,8 @@ Events are convenience interfaces with the EVM logging facilities.
::
+ pragma solidity ^0.4.0;
+
contract SimpleAuction {
event HighestBidIncreased(address bidder, uint amount); // Event
@@ -102,6 +110,8 @@ Structs are custom defined types that can group several variables (see
::
+ pragma solidity ^0.4.0;
+
contract Ballot {
struct Voter { // Struct
uint weight;
@@ -121,6 +131,8 @@ Enums can be used to create custom types with a finite set of values (see
::
+ pragma solidity ^0.4.0;
+
contract Purchase {
enum State { Created, Locked, Inactive } // Enum
}
diff --git a/docs/style-guide.rst b/docs/style-guide.rst
index f4b419c0..d2d922df 100644
--- a/docs/style-guide.rst
+++ b/docs/style-guide.rst
@@ -355,7 +355,7 @@ No::
selfdestruct(owner);
}
-For long function declarations, it is recommended to drop each arguent onto
+For long function declarations, it is recommended to drop each argument onto
it's own line at the same indentation level as the function body. The closing
parenthesis and opening bracket should be placed on their own line as well at
the same indentation level as the function declaration.
diff --git a/docs/types.rst b/docs/types.rst
index 737fbe7d..988a0ed0 100644
--- a/docs/types.rst
+++ b/docs/types.rst
@@ -241,6 +241,8 @@ to and from all integer types but implicit conversion is not allowed.
::
+ pragma solidity ^0.4.0;
+
contract test {
enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill }
ActionChoices choice;
@@ -300,6 +302,8 @@ memory-stored reference type does not create a copy.
::
+ pragma solidity ^0.4.0;
+
contract C {
uint[] x; // the data location of x is storage
@@ -378,6 +382,8 @@ the ``.length`` member.
::
+ pragma solidity ^0.4.0;
+
contract C {
function f(uint len) {
uint[] memory a = new uint[](7);
@@ -397,6 +403,8 @@ assigned to a variable right away.
::
+ pragma solidity ^0.4.0;
+
contract C {
function f() {
g([uint(1), 2, 3]);
@@ -416,6 +424,8 @@ possible:
::
+ pragma solidity ^0.4.0;
+
contract C {
function f() {
// The next line creates a type error because uint[3] memory
@@ -452,6 +462,8 @@ Members
::
+ pragma solidity ^0.4.0;
+
contract ArrayContract {
uint[2**20] m_aLotOfIntegers;
// Note that the following is not a pair of arrays but an array of pairs.
@@ -521,6 +533,8 @@ shown in the following example:
::
+ pragma solidity ^0.4.0;
+
contract CrowdFunding {
// Defines a new type with two fields.
struct Funder {
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index bc03da01..d9c54f75 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -75,8 +75,14 @@ bool TypeChecker::visit(ContractDefinition const& _contract)
checkContractAbstractConstructors(_contract);
FunctionDefinition const* function = _contract.constructor();
- if (function && !function->returnParameters().empty())
- typeError(function->returnParameterList()->location(), "Non-empty \"returns\" directive for constructor.");
+ if (function) {
+ if (!function->returnParameters().empty())
+ typeError(function->returnParameterList()->location(), "Non-empty \"returns\" directive for constructor.");
+ if (function->isDeclaredConst())
+ typeError(function->location(), "Constructor cannot be defined as constant.");
+ if (function->visibility() != FunctionDefinition::Visibility::Public && function->visibility() != FunctionDefinition::Visibility::Internal)
+ typeError(function->location(), "Constructor must be public or internal.");
+ }
FunctionDefinition const* fallbackFunction = nullptr;
for (FunctionDefinition const* function: _contract.definedFunctions())
@@ -94,6 +100,8 @@ bool TypeChecker::visit(ContractDefinition const& _contract)
fallbackFunction = function;
if (_contract.isLibrary())
typeError(fallbackFunction->location(), "Libraries cannot have fallback functions.");
+ if (fallbackFunction->isDeclaredConst())
+ typeError(fallbackFunction->location(), "Fallback function cannot be declared constant.");
if (!fallbackFunction->parameters().empty())
typeError(fallbackFunction->parameterList().location(), "Fallback function cannot take parameters.");
if (!fallbackFunction->returnParameters().empty())
@@ -272,6 +280,7 @@ void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contr
if (
overriding->visibility() != function->visibility() ||
overriding->isDeclaredConst() != function->isDeclaredConst() ||
+ overriding->isPayable() != function->isPayable() ||
overridingType != functionType
)
typeError(overriding->location(), "Override changes extended function signature.");
@@ -348,7 +357,7 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance)
typeError(_inheritance.location(), "Libraries cannot be inherited from.");
auto const& arguments = _inheritance.arguments();
- TypePointers parameterTypes = ContractType(*base).constructorType()->parameterTypes();
+ TypePointers parameterTypes = ContractType(*base).newExpressionType()->parameterTypes();
if (!arguments.empty() && parameterTypes.size() != arguments.size())
{
typeError(
@@ -416,6 +425,15 @@ bool TypeChecker::visit(StructDefinition const& _struct)
bool TypeChecker::visit(FunctionDefinition const& _function)
{
bool isLibraryFunction = dynamic_cast<ContractDefinition const&>(*_function.scope()).isLibrary();
+ if (_function.isPayable())
+ {
+ if (isLibraryFunction)
+ typeError(_function.location(), "Library functions cannot be payable.");
+ if (!_function.isConstructor() && !_function.name().empty() && !_function.isPartOfExternalInterface())
+ typeError(_function.location(), "Internal functions cannot be payable.");
+ if (_function.isDeclaredConst())
+ typeError(_function.location(), "Functions cannot be constant and payable at the same time.");
+ }
for (ASTPointer<VariableDeclaration> const& var: _function.parameters() + _function.returnParameters())
{
if (!type(*var)->canLiveOutsideStorage())
@@ -1256,15 +1274,7 @@ void TypeChecker::endVisit(NewExpression const& _newExpression)
"Circular reference for contract creation (cannot create instance of derived or same contract)."
);
- auto contractType = make_shared<ContractType>(*contract);
- TypePointers parameterTypes = contractType->constructorType()->parameterTypes();
- _newExpression.annotation().type = make_shared<FunctionType>(
- parameterTypes,
- TypePointers{contractType},
- strings(),
- strings(),
- FunctionType::Location::Creation
- );
+ _newExpression.annotation().type = FunctionType::newExpressionType(*contract);
}
else if (type->category() == Type::Category::Array)
{
@@ -1328,14 +1338,16 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
fatalTypeError(
_memberAccess.location(),
"Member \"" + memberName + "\" not found or not visible "
- "after argument-dependent lookup in " + exprType->toString()
+ "after argument-dependent lookup in " + exprType->toString() +
+ (memberName == "value" ? " - did you forget the \"payable\" modifier?" : "")
);
}
else if (possibleMembers.size() > 1)
fatalTypeError(
_memberAccess.location(),
"Member \"" + memberName + "\" not unique "
- "after argument-dependent lookup in " + exprType->toString()
+ "after argument-dependent lookup in " + exprType->toString() +
+ (memberName == "value" ? " - did you forget the \"payable\" modifier?" : "")
);
auto& annotation = _memberAccess.annotation();
diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h
index 761d85fe..8fd1584d 100644
--- a/libsolidity/ast/AST.h
+++ b/libsolidity/ast/AST.h
@@ -540,6 +540,7 @@ public:
bool _isDeclaredConst,
std::vector<ASTPointer<ModifierInvocation>> const& _modifiers,
ASTPointer<ParameterList> const& _returnParameters,
+ bool _isPayable,
ASTPointer<Block> const& _body
):
CallableDeclaration(_location, _name, _visibility, _parameters, _returnParameters),
@@ -547,6 +548,7 @@ public:
ImplementationOptional(_body != nullptr),
m_isConstructor(_isConstructor),
m_isDeclaredConst(_isDeclaredConst),
+ m_isPayable(_isPayable),
m_functionModifiers(_modifiers),
m_body(_body)
{}
@@ -556,6 +558,7 @@ public:
bool isConstructor() const { return m_isConstructor; }
bool isDeclaredConst() const { return m_isDeclaredConst; }
+ bool isPayable() const { return m_isPayable; }
std::vector<ASTPointer<ModifierInvocation>> const& modifiers() const { return m_functionModifiers; }
std::vector<ASTPointer<VariableDeclaration>> const& returnParameters() const { return m_returnParameters->parameters(); }
Block const& body() const { return *m_body; }
@@ -578,6 +581,7 @@ public:
private:
bool m_isConstructor;
bool m_isDeclaredConst;
+ bool m_isPayable;
std::vector<ASTPointer<ModifierInvocation>> m_functionModifiers;
ASTPointer<Block> m_body;
};
diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp
index 49ee6d34..0ea5e093 100644
--- a/libsolidity/ast/ASTJsonConverter.cpp
+++ b/libsolidity/ast/ASTJsonConverter.cpp
@@ -22,6 +22,7 @@
#include <libsolidity/ast/ASTJsonConverter.h>
#include <boost/algorithm/string/join.hpp>
+#include <libdevcore/UTF8.h>
#include <libsolidity/ast/AST.h>
using namespace std;
@@ -397,9 +398,21 @@ bool ASTJsonConverter::visit(ElementaryTypeNameExpression const& _node)
bool ASTJsonConverter::visit(Literal const& _node)
{
char const* tokenString = Token::toString(_node.token());
+ size_t invalidPos = 0;
+ Json::Value value{_node.value()};
+ if (!dev::validate(_node.value(), invalidPos))
+ value = Json::nullValue;
+ Token::Value subdenomination = Token::Value(_node.subDenomination());
addJsonNode(_node, "Literal", {
- make_pair("string", tokenString ? tokenString : Json::Value()),
- make_pair("value", _node.value()),
+ make_pair("token", tokenString ? tokenString : Json::Value()),
+ make_pair("value", value),
+ make_pair("hexvalue", toHex(_node.value())),
+ make_pair(
+ "subdenomination",
+ subdenomination == Token::Illegal ?
+ Json::nullValue :
+ Json::Value{Token::toString(subdenomination)}
+ ),
make_pair("type", type(_node))
});
return true;
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
index d86a2caf..4b5f12ce 100644
--- a/libsolidity/ast/Types.cpp
+++ b/libsolidity/ast/Types.cpp
@@ -362,8 +362,8 @@ MemberList::MemberMap IntegerType::nativeMembers(ContractDefinition const*) cons
if (isAddress())
return {
{"balance", make_shared<IntegerType >(256)},
- {"call", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::Bare, true)},
- {"callcode", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::BareCallCode, true)},
+ {"call", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::Bare, true, false, true)},
+ {"callcode", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::BareCallCode, true, false, true)},
{"delegatecall", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::BareDelegateCall, true)},
{"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Location::Send)}
};
@@ -1329,16 +1329,10 @@ MemberList::MemberMap ContractType::nativeMembers(ContractDefinition const*) con
return members;
}
-shared_ptr<FunctionType const> const& ContractType::constructorType() const
+shared_ptr<FunctionType const> const& ContractType::newExpressionType() const
{
if (!m_constructorType)
- {
- FunctionDefinition const* constructor = m_contract.constructor();
- if (constructor)
- m_constructorType = make_shared<FunctionType>(*constructor);
- else
- m_constructorType = make_shared<FunctionType>(TypePointers(), TypePointers());
- }
+ m_constructorType = FunctionType::newExpressionType(m_contract);
return m_constructorType;
}
@@ -1653,6 +1647,7 @@ TypePointer TupleType::closestTemporaryType(TypePointer const& _targetType) cons
FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal):
m_location(_isInternal ? Location::Internal : Location::External),
m_isConstant(_function.isDeclaredConst()),
+ m_isPayable(_function.isPayable()),
m_declaration(&_function)
{
TypePointers params;
@@ -1737,7 +1732,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
swap(retParamNames, m_returnParameterNames);
}
-FunctionType::FunctionType(const EventDefinition& _event):
+FunctionType::FunctionType(EventDefinition const& _event):
m_location(Location::Event), m_isConstant(true), m_declaration(&_event)
{
TypePointers params;
@@ -1753,6 +1748,35 @@ FunctionType::FunctionType(const EventDefinition& _event):
swap(paramNames, m_parameterNames);
}
+FunctionTypePointer FunctionType::newExpressionType(ContractDefinition const& _contract)
+{
+ FunctionDefinition const* constructor = _contract.constructor();
+ TypePointers parameters;
+ strings parameterNames;
+ bool payable = false;
+
+ if (constructor)
+ {
+ for (ASTPointer<VariableDeclaration> const& var: constructor->parameters())
+ {
+ parameterNames.push_back(var->name());
+ parameters.push_back(var->annotation().type);
+ }
+ payable = constructor->isPayable();
+ }
+ return make_shared<FunctionType>(
+ parameters,
+ TypePointers{make_shared<ContractType>(_contract)},
+ parameterNames,
+ strings{""},
+ Location::Creation,
+ false,
+ nullptr,
+ false,
+ payable
+ );
+}
+
vector<string> FunctionType::parameterNames() const
{
if (!bound())
@@ -1871,7 +1895,12 @@ FunctionTypePointer FunctionType::interfaceFunctionType() const
if (variable && retParamTypes.empty())
return FunctionTypePointer();
- return make_shared<FunctionType>(paramTypes, retParamTypes, m_parameterNames, m_returnParameterNames, m_location, m_arbitraryParameters);
+ return make_shared<FunctionType>(
+ paramTypes, retParamTypes,
+ m_parameterNames, m_returnParameterNames,
+ m_location, m_arbitraryParameters,
+ m_declaration, m_isConstant, m_isPayable
+ );
}
MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) const
@@ -1889,20 +1918,25 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
{
MemberList::MemberMap members;
if (m_location != Location::BareDelegateCall && m_location != Location::DelegateCall)
- members.push_back(MemberList::Member(
- "value",
- make_shared<FunctionType>(
- parseElementaryTypeVector({"uint"}),
- TypePointers{copyAndSetGasOrValue(false, true)},
- strings(),
- strings(),
- Location::SetValue,
- false,
- nullptr,
- m_gasSet,
- m_valueSet
- )
- ));
+ {
+ if (m_isPayable)
+ members.push_back(MemberList::Member(
+ "value",
+ make_shared<FunctionType>(
+ parseElementaryTypeVector({"uint"}),
+ TypePointers{copyAndSetGasOrValue(false, true)},
+ strings(),
+ strings(),
+ Location::SetValue,
+ false,
+ nullptr,
+ false,
+ false,
+ m_gasSet,
+ m_valueSet
+ )
+ ));
+ }
if (m_location != Location::Creation)
members.push_back(MemberList::Member(
"gas",
@@ -1914,6 +1948,8 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
Location::SetGas,
false,
nullptr,
+ false,
+ false,
m_gasSet,
m_valueSet
)
@@ -2019,6 +2055,8 @@ TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) con
m_location,
m_arbitraryParameters,
m_declaration,
+ m_isConstant,
+ m_isPayable,
m_gasSet || _setGas,
m_valueSet || _setValue,
m_bound
@@ -2064,6 +2102,8 @@ FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary, bool _bound)
location,
m_arbitraryParameters,
m_declaration,
+ m_isConstant,
+ m_isPayable,
m_gasSet,
m_valueSet,
_bound
@@ -2090,7 +2130,8 @@ vector<string> const FunctionType::returnParameterTypeNames(bool _addDataLocatio
TypePointer FunctionType::selfType() const
{
- solAssert(bound(), "");
+ solAssert(bound(), "Function is not bound.");
+ solAssert(m_parameterTypes.size() > 0, "Function has no self type.");
return m_parameterTypes.at(0);
}
diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h
index 1282e5d8..9173f39a 100644
--- a/libsolidity/ast/Types.h
+++ b/libsolidity/ast/Types.h
@@ -640,9 +640,8 @@ public:
bool isSuper() const { return m_super; }
ContractDefinition const& contractDefinition() const { return m_contract; }
- /// Returns the function type of the constructor. Note that the location part of the function type
- /// is not used, as this type cannot be the type of a variable or expression.
- FunctionTypePointer const& constructorType() const;
+ /// Returns the function type of the constructor modified to return an object of the contract's type.
+ FunctionTypePointer const& newExpressionType() const;
/// @returns the identifier of the function with the given name or Invalid256 if such a name does
/// not exist.
@@ -820,21 +819,32 @@ public:
explicit FunctionType(VariableDeclaration const& _varDecl);
/// Creates the function type of an event.
explicit FunctionType(EventDefinition const& _event);
+ /// Function type constructor to be used for a plain type (not derived from a declaration).
FunctionType(
strings const& _parameterTypes,
strings const& _returnParameterTypes,
Location _location = Location::Internal,
- bool _arbitraryParameters = false
+ bool _arbitraryParameters = false,
+ bool _constant = false,
+ bool _payable = false
): FunctionType(
parseElementaryTypeVector(_parameterTypes),
parseElementaryTypeVector(_returnParameterTypes),
strings(),
strings(),
_location,
- _arbitraryParameters
+ _arbitraryParameters,
+ nullptr,
+ _constant,
+ _payable
)
{
}
+
+ /// @returns the type of the "new Contract" function, i.e. basically the constructor.
+ static FunctionTypePointer newExpressionType(ContractDefinition const& _contract);
+
+ /// Detailed constructor, use with care.
FunctionType(
TypePointers const& _parameterTypes,
TypePointers const& _returnParameterTypes,
@@ -843,6 +853,8 @@ public:
Location _location = Location::Internal,
bool _arbitraryParameters = false,
Declaration const* _declaration = nullptr,
+ bool _isConstant = false,
+ bool _isPayable = false,
bool _gasSet = false,
bool _valueSet = false,
bool _bound = false
@@ -856,6 +868,8 @@ public:
m_gasSet(_gasSet),
m_valueSet(_valueSet),
m_bound(_bound),
+ m_isConstant(_isConstant),
+ m_isPayable(_isPayable),
m_declaration(_declaration)
{}
@@ -905,6 +919,7 @@ public:
}
bool hasDeclaration() const { return !!m_declaration; }
bool isConstant() const { return m_isConstant; }
+ bool isPayable() const { return m_isPayable; }
/// @return A shared pointer of an ASTString.
/// Can contain a nullptr in which case indicates absence of documentation
ASTPointer<ASTString> documentation() const;
@@ -942,6 +957,7 @@ private:
bool const m_valueSet = false; ///< true iff the value to be sent is on the stack
bool const m_bound = false; ///< true iff the function is called as arg1.fun(arg2, ..., argn)
bool m_isConstant = false;
+ bool m_isPayable = false;
Declaration const* m_declaration = nullptr;
};
diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp
index d7d17b8e..ec496df8 100644
--- a/libsolidity/codegen/CompilerUtils.cpp
+++ b/libsolidity/codegen/CompilerUtils.cpp
@@ -160,7 +160,15 @@ void CompilerUtils::encodeToMemory(
TypePointers targetTypes = _targetTypes.empty() ? _givenTypes : _targetTypes;
solAssert(targetTypes.size() == _givenTypes.size(), "");
for (TypePointer& t: targetTypes)
+ {
+ solAssert(
+ t->mobileType() &&
+ t->mobileType()->interfaceType(_encodeAsLibraryTypes) &&
+ t->mobileType()->interfaceType(_encodeAsLibraryTypes)->encodingType(),
+ "Encoding type \"" + t->toString() + "\" not yet implemented."
+ );
t = t->mobileType()->interfaceType(_encodeAsLibraryTypes)->encodingType();
+ }
// Stack during operation:
// <v1> <v2> ... <vn> <mem_start> <dyn_head_1> ... <dyn_head_r> <end_of_mem>
diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp
index 9d77ccdc..33571bc0 100644
--- a/libsolidity/codegen/ContractCompiler.cpp
+++ b/libsolidity/codegen/ContractCompiler.cpp
@@ -242,6 +242,12 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
m_context << notFound;
if (fallback)
{
+ if (!fallback->isPayable())
+ {
+ // Throw if function is not payable but call contained ether.
+ m_context << Instruction::CALLVALUE;
+ m_context.appendConditionalJumpTo(m_context.errorTag());
+ }
eth::AssemblyItem returnTag = m_context.pushNewTag();
fallback->accept(*this);
m_context << returnTag;
@@ -255,7 +261,15 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
FunctionTypePointer const& functionType = it.second;
solAssert(functionType->hasDeclaration(), "");
CompilerContext::LocationSetter locationSetter(m_context, functionType->declaration());
+
m_context << callDataUnpackerEntryPoints.at(it.first);
+ if (!functionType->isPayable())
+ {
+ // Throw if function is not payable but call contained ether.
+ m_context << Instruction::CALLVALUE;
+ m_context.appendConditionalJumpTo(m_context.errorTag());
+ }
+
eth::AssemblyItem returnTag = m_context.pushNewTag();
m_context << CompilerUtils::dataStartOffset;
appendCalldataUnpacker(functionType->parameterTypes());
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index 4a81e27d..96ca4296 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -568,12 +568,17 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
break;
case Location::Send:
_functionCall.expression().accept(*this);
- m_context << u256(0); // do not send gas (there still is the stipend)
+ // Provide the gas stipend manually at first because we may send zero ether.
+ // Will be zeroed if we send more than zero ether.
+ m_context << u256(eth::GasCosts::callStipend);
arguments.front()->accept(*this);
utils().convertType(
*arguments.front()->annotation().type,
*function.parameterTypes().front(), true
);
+ // gas <- gas * !value
+ m_context << Instruction::SWAP1 << Instruction::DUP2;
+ m_context << Instruction::ISZERO << Instruction::MUL << Instruction::SWAP1;
appendExternalFunctionCall(
FunctionType(
TypePointers{},
@@ -583,6 +588,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
Location::Bare,
false,
nullptr,
+ false,
+ false,
true,
true
),
@@ -1524,11 +1531,13 @@ void ExpressionCompiler::appendExternalFunctionCall(
m_context << u256(0);
m_context << dupInstruction(m_context.baseToCurrentStackOffset(contractStackPos));
+ bool existenceChecked = false;
// Check the the target contract exists (has code) for non-low-level calls.
if (funKind == FunctionKind::External || funKind == FunctionKind::CallCode || funKind == FunctionKind::DelegateCall)
{
m_context << Instruction::DUP1 << Instruction::EXTCODESIZE << Instruction::ISZERO;
m_context.appendConditionalJumpTo(m_context.errorTag());
+ existenceChecked = true;
}
if (_functionType.gasSet())
@@ -1540,7 +1549,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
u256 gasNeededByCaller = eth::GasCosts::callGas + 10;
if (_functionType.valueSet())
gasNeededByCaller += eth::GasCosts::callValueTransferGas;
- if (!isCallCode && !isDelegateCall)
+ if (!isCallCode && !isDelegateCall && !existenceChecked)
gasNeededByCaller += eth::GasCosts::callNewAccountGas; // we never know
m_context <<
gasNeededByCaller <<
diff --git a/libsolidity/grammar.txt b/libsolidity/grammar.txt
index 0230729a..755cf281 100644
--- a/libsolidity/grammar.txt
+++ b/libsolidity/grammar.txt
@@ -41,10 +41,11 @@ ArrayTypeName = TypeName StorageLocation? '[' Expression? ']'
StorageLocation = 'memory' | 'storage'
Block = '{' Statement* '}'
-Statement = IfStatement | WhileStatement | ForStatement | Block | PlaceholderStatement |
- ( Continue | Break | Return | Throw | SimpleStatement | ExpressionStatement ) ';'
+Statement = IfStatement | WhileStatement | ForStatement | Block |
+ ( PlaceholderStatement | Continue | Break | Return |
+ Throw | SimpleStatement ) ';'
-ExpressionStatement = Expression | VariableDefinition
+ExpressionStatement = Expression
IfStatement = 'if' '(' Expression ')' Statement ( 'else' Statement )?
WhileStatement = 'while' '(' Expression ')' Statement
PlaceholderStatement = '_'
@@ -78,7 +79,7 @@ Expression =
PrimaryExpression = Identifier | BooleanLiteral | NumberLiteral | StringLiteral
-FunctionCall = Identifier '(' Expression? ( ',' Expression )* ')'
+FunctionCall = ( PrimaryExpression | NewExpression | TypeName ) ( ( '.' Identifier ) | ( '[' Expression ']' ) )* '(' Expression? ( ',' Expression )* ')'
NewExpression = 'new' Identifier
MemberAccess = Expression '.' Identifier
IndexAccess = Expression '[' Expression? ']'
diff --git a/libsolidity/interface/InterfaceHandler.cpp b/libsolidity/interface/InterfaceHandler.cpp
index d39f8285..de16a372 100644
--- a/libsolidity/interface/InterfaceHandler.cpp
+++ b/libsolidity/interface/InterfaceHandler.cpp
@@ -52,6 +52,7 @@ string InterfaceHandler::abiInterface(ContractDefinition const& _contractDef)
method["type"] = "function";
method["name"] = it.second->declaration().name();
method["constant"] = it.second->isConstant();
+ method["payable"] = it.second->isPayable();
method["inputs"] = populateParameters(
externalFunctionType->parameterNames(),
externalFunctionType->parameterTypeNames(_contractDef.isLibrary())
@@ -80,7 +81,7 @@ string InterfaceHandler::abiInterface(ContractDefinition const& _contractDef)
solAssert(!!externalFunctionType, "");
Json::Value method;
method["type"] = "fallback";
- method["constant"] = externalFunctionType->isConstant();
+ method["payable"] = externalFunctionType->isPayable();
abi.append(method);
}
for (auto const& it: _contractDef.interfaceEvents())
diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp
index b2f4a156..0e99d1e7 100644
--- a/libsolidity/parsing/Parser.cpp
+++ b/libsolidity/parsing/Parser.cpp
@@ -305,6 +305,7 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(ASTString const*
options.allowLocationSpecifier = true;
ASTPointer<ParameterList> parameters(parseParameterList(options));
bool isDeclaredConst = false;
+ bool isPayable = false;
Declaration::Visibility visibility(Declaration::Visibility::Default);
vector<ASTPointer<ModifierInvocation>> modifiers;
while (true)
@@ -315,6 +316,11 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(ASTString const*
isDeclaredConst = true;
m_scanner->next();
}
+ else if (m_scanner->currentToken() == Token::Payable)
+ {
+ isPayable = true;
+ m_scanner->next();
+ }
else if (token == Token::Identifier)
modifiers.push_back(parseModifierInvocation());
else if (Token::isVisibilitySpecifier(token))
@@ -354,6 +360,7 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(ASTString const*
isDeclaredConst,
modifiers,
returnParameters,
+ isPayable,
block
);
}
@@ -753,7 +760,7 @@ ASTPointer<Statement> Parser::parseStatement()
{
statement = ASTNodeFactory(*this).createNode<PlaceholderStatement>(docString);
m_scanner->next();
- return statement;
+ break;
}
// fall-through
default:
diff --git a/libsolidity/parsing/ParserBase.cpp b/libsolidity/parsing/ParserBase.cpp
index 71085a4d..2abf58cc 100644
--- a/libsolidity/parsing/ParserBase.cpp
+++ b/libsolidity/parsing/ParserBase.cpp
@@ -47,7 +47,17 @@ void ParserBase::expectToken(Token::Value _value)
Token::Value tok = m_scanner->currentToken();
if (tok != _value)
{
- if (Token::isElementaryTypeName(tok)) //for the sake of accuracy in reporting
+ if (Token::isReservedKeyword(tok))
+ {
+ fatalParserError(
+ string("Expected token ") +
+ string(Token::name(_value)) +
+ string(" got reserved keyword '") +
+ string(Token::name(tok)) +
+ string("'")
+ );
+ }
+ else if (Token::isElementaryTypeName(tok)) //for the sake of accuracy in reporting
{
ElementaryTypeNameToken elemTypeName = m_scanner->currentElementaryTypeNameToken();
fatalParserError(
diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h
index 15d4860f..5dd42992 100644
--- a/libsolidity/parsing/Token.h
+++ b/libsolidity/parsing/Token.h
@@ -166,6 +166,7 @@ namespace solidity
K(Memory, "memory", 0) \
K(Modifier, "modifier", 0) \
K(New, "new", 0) \
+ K(Payable, "payable", 0) \
K(Public, "public", 0) \
K(Pragma, "pragma", 0) \
K(Private, "private", 0) \
@@ -229,13 +230,14 @@ namespace solidity
K(Let, "let", 0) \
K(Match, "match", 0) \
K(Of, "of", 0) \
- K(Payable, "payable", 0) \
+ K(Pure, "pure", 0) \
K(Relocatable, "relocatable", 0) \
K(Static, "static", 0) \
K(Switch, "switch", 0) \
K(Try, "try", 0) \
K(Type, "type", 0) \
K(TypeOf, "typeof", 0) \
+ K(View, "view", 0) \
/* Illegal token - not able to scan. */ \
T(Illegal, "ILLEGAL", 0) \
\
@@ -291,6 +293,7 @@ public:
static bool isLocationSpecifier(Value op) { return op == Memory || op == Storage; }
static bool isEtherSubdenomination(Value op) { return op == SubWei || op == SubSzabo || op == SubFinney || op == SubEther; }
static bool isTimeSubdenomination(Value op) { return op == SubSecond || op == SubMinute || op == SubHour || op == SubDay || op == SubWeek || op == SubYear; }
+ static bool isReservedKeyword(Value op) { return (Abstract <= op && op <= TypeOf); }
// @returns a string corresponding to the JS token string
// (.e., "<" for the token LT) or NULL if the token doesn't
diff --git a/scripts/install_deps.sh b/scripts/install_deps.sh
index a67fea82..7c8523a8 100755
--- a/scripts/install_deps.sh
+++ b/scripts/install_deps.sh
@@ -44,6 +44,8 @@
# (c) 2016 solidity contributors.
#------------------------------------------------------------------------------
+set -e
+
# Check for 'uname' and abort if it is not available.
uname -v > /dev/null 2>&1 || { echo >&2 "ERROR - solidity requires 'uname' to identify the platform."; exit 1; }
diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp
index fbef56f0..f0a34632 100644
--- a/solc/CommandLineInterface.cpp
+++ b/solc/CommandLineInterface.cpp
@@ -778,37 +778,43 @@ void CommandLineInterface::actOnInput()
bool CommandLineInterface::link()
{
+ // Map from how the libraries will be named inside the bytecode to their addresses.
+ map<string, h160> librariesReplacements;
+ int const placeholderSize = 40; // 20 bytes or 40 hex characters
+ for (auto const& library: m_libraries)
+ {
+ string const& name = library.first;
+ // Library placeholders are 40 hex digits (20 bytes) that start and end with '__'.
+ // This leaves 36 characters for the library name, while too short library names are
+ // padded on the right with '_' and too long names are truncated.
+ string replacement = "__";
+ for (size_t i = 0; i < placeholderSize - 4; ++i)
+ replacement.push_back(i < name.size() ? name[i] : '_');
+ replacement += "__";
+ librariesReplacements[replacement] = library.second;
+ }
for (auto& src: m_sourceCodes)
{
auto end = src.second.end();
for (auto it = src.second.begin(); it != end;)
{
while (it != end && *it != '_') ++it;
- auto insertStart = it;
- while (it != end && *it == '_') ++it;
- auto nameStart = it;
- while (it != end && *it != '_') ++it;
- auto nameEnd = it;
- while (it != end && *it == '_') ++it;
- auto insertEnd = it;
-
- if (insertStart == end)
- break;
-
- if (insertEnd - insertStart != 40)
+ if (it == end) break;
+ if (end - it < placeholderSize)
{
- cerr << "Error in binary object file " << src.first << " at position " << (insertStart - src.second.begin()) << endl;
+ cerr << "Error in binary object file " << src.first << " at position " << (end - src.second.begin()) << endl;
return false;
}
- string name(nameStart, nameEnd);
- if (m_libraries.count(name))
+ string name(it, it + placeholderSize);
+ if (librariesReplacements.count(name))
{
- string hexStr(toHex(m_libraries.at(name).asBytes()));
- copy(hexStr.begin(), hexStr.end(), insertStart);
+ string hexStr(toHex(librariesReplacements.at(name).asBytes()));
+ copy(hexStr.begin(), hexStr.end(), it);
}
else
cerr << "Reference \"" << name << "\" in file \"" << src.first << "\" still unresolved." << endl;
+ it += placeholderSize;
}
}
return true;
diff --git a/solc/jsonCompiler.cpp b/solc/jsonCompiler.cpp
index 671b8c0f..ef69105e 100644
--- a/solc/jsonCompiler.cpp
+++ b/solc/jsonCompiler.cpp
@@ -223,7 +223,14 @@ string compile(StringMap const& _sources, bool _optimize, CStyleReadFileCallback
contractData["assembly"] = compiler.streamAssembly(unused, contractName, _sources, true);
output["contracts"][contractName] = contractData;
}
+ }
+ catch (...)
+ {
+ output["errors"].append("Unknown exception while generating contract data output.");
+ }
+ try
+ {
// Do not taint the internal error list
ErrorList formalErrors;
if (compiler.prepareFormalAnalysis(&formalErrors))
@@ -239,7 +246,14 @@ string compile(StringMap const& _sources, bool _optimize, CStyleReadFileCallback
));
output["formal"]["errors"] = errors;
}
+ }
+ catch (...)
+ {
+ output["errors"].append("Unknown exception while generating formal method output.");
+ }
+ try
+ {
// Indices into this array are used to abbreviate source names in source locations.
output["sourceList"] = Json::Value(Json::arrayValue);
for (auto const& source: compiler.sourceNames())
@@ -250,7 +264,7 @@ string compile(StringMap const& _sources, bool _optimize, CStyleReadFileCallback
}
catch (...)
{
- output["errors"].append("Unknown exception while generating compiler output.");
+ output["errors"].append("Unknown exception while generating source name output.");
}
}
diff --git a/std/owned.sol b/std/owned.sol
index 37f0ecb9..3d7674f5 100644
--- a/std/owned.sol
+++ b/std/owned.sol
@@ -3,7 +3,7 @@ contract owned {
modifier onlyowner() {
if (msg.sender == owner) {
- _
+ _;
}
}
diff --git a/test/contracts/AuctionRegistrar.cpp b/test/contracts/AuctionRegistrar.cpp
index 681caa26..05da3490 100644
--- a/test/contracts/AuctionRegistrar.cpp
+++ b/test/contracts/AuctionRegistrar.cpp
@@ -115,11 +115,6 @@ contract GlobalRegistrar is Registrar, AuctionSystem {
// TODO: Populate with hall-of-fame.
}
- function() {
- // prevent people from just sending funds to the registrar
- throw;
- }
-
function onAuctionEnd(string _name) internal {
var auction = m_auctions[_name];
var record = m_toRecord[_name];
@@ -136,7 +131,7 @@ contract GlobalRegistrar is Registrar, AuctionSystem {
}
}
- function reserve(string _name) external {
+ function reserve(string _name) external payable {
if (bytes(_name).length == 0)
throw;
bool needAuction = requiresAuction(_name);
@@ -158,7 +153,7 @@ contract GlobalRegistrar is Registrar, AuctionSystem {
return bytes(_name).length < c_freeBytes;
}
- modifier onlyrecordowner(string _name) { if (m_toRecord[_name].owner == msg.sender) _ }
+ modifier onlyrecordowner(string _name) { if (m_toRecord[_name].owner == msg.sender) _; }
function transfer(string _name, address _newOwner) onlyrecordowner(_name) {
m_toRecord[_name].owner = _newOwner;
diff --git a/test/contracts/FixedFeeRegistrar.cpp b/test/contracts/FixedFeeRegistrar.cpp
index fd0861f7..af8ee595 100644
--- a/test/contracts/FixedFeeRegistrar.cpp
+++ b/test/contracts/FixedFeeRegistrar.cpp
@@ -71,9 +71,9 @@ contract FixedFeeRegistrar is Registrar {
address owner;
}
- modifier onlyrecordowner(string _name) { if (m_record(_name).owner == msg.sender) _ }
+ modifier onlyrecordowner(string _name) { if (m_record(_name).owner == msg.sender) _; }
- function reserve(string _name) {
+ function reserve(string _name) payable {
Record rec = m_record(_name);
if (rec.owner == 0 && msg.value >= c_fee) {
rec.owner = msg.sender;
diff --git a/test/contracts/Wallet.cpp b/test/contracts/Wallet.cpp
index 55c2e1af..b4f29a87 100644
--- a/test/contracts/Wallet.cpp
+++ b/test/contracts/Wallet.cpp
@@ -86,14 +86,14 @@ contract multiowned {
// simple single-sig function modifier.
modifier onlyowner {
if (isOwner(msg.sender))
- _
+ _;
}
// multi-sig function modifier: the operation must have an intrinsic hash in order
// that later attempts can be realised as the same underlying operation and
// thus count as confirmations.
modifier onlymanyowners(bytes32 _operation) {
if (confirmAndCheck(_operation))
- _
+ _;
}
// METHODS
@@ -281,7 +281,7 @@ contract daylimit is multiowned {
// simple modifier for daily limit.
modifier limitedDaily(uint _value) {
if (underLimit(_value))
- _
+ _;
}
// METHODS
@@ -378,7 +378,7 @@ contract Wallet is multisig, multiowned, daylimit {
}
// gets called when no other function matches
- function() {
+ function() payable {
// just being sent some cash?
if (msg.value > 0)
Deposit(msg.sender, msg.value);
diff --git a/test/libsolidity/ASTJSON.cpp b/test/libsolidity/ASTJSON.cpp
index ec60b668..a0fc5dd7 100644
--- a/test/libsolidity/ASTJSON.cpp
+++ b/test/libsolidity/ASTJSON.cpp
@@ -128,7 +128,7 @@ BOOST_AUTO_TEST_CASE(enum_value)
BOOST_AUTO_TEST_CASE(modifier_definition)
{
CompilerStack c;
- c.addSource("a", "contract C { modifier M(uint i) { _ } function F() M(1) {} }");
+ c.addSource("a", "contract C { modifier M(uint i) { _; } function F() M(1) {} }");
c.parse();
map<string, unsigned> sourceIndices;
sourceIndices["a"] = 1;
@@ -136,20 +136,20 @@ BOOST_AUTO_TEST_CASE(modifier_definition)
Json::Value modifier = astJson["children"][0]["children"][0];
BOOST_CHECK_EQUAL(modifier["name"], "ModifierDefinition");
BOOST_CHECK_EQUAL(modifier["attributes"]["name"], "M");
- BOOST_CHECK_EQUAL(modifier["src"], "13:24:1");
+ BOOST_CHECK_EQUAL(modifier["src"], "13:25:1");
}
BOOST_AUTO_TEST_CASE(modifier_invocation)
{
CompilerStack c;
- c.addSource("a", "contract C { modifier M(uint i) { _ } function F() M(1) {} }");
+ c.addSource("a", "contract C { modifier M(uint i) { _; } function F() M(1) {} }");
c.parse();
map<string, unsigned> sourceIndices;
sourceIndices["a"] = 1;
Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json();
Json::Value modifier = astJson["children"][0]["children"][1]["children"][2];
BOOST_CHECK_EQUAL(modifier["name"], "ModifierInvocation");
- BOOST_CHECK_EQUAL(modifier["src"], "51:4:1");
+ BOOST_CHECK_EQUAL(modifier["src"], "52:4:1");
BOOST_CHECK_EQUAL(modifier["children"][0]["attributes"]["type"], "modifier (uint256)");
BOOST_CHECK_EQUAL(modifier["children"][0]["attributes"]["value"], "M");
BOOST_CHECK_EQUAL(modifier["children"][1]["attributes"]["value"], "1");
@@ -185,7 +185,7 @@ BOOST_AUTO_TEST_CASE(array_type_name)
BOOST_AUTO_TEST_CASE(placeholder_statement)
{
CompilerStack c;
- c.addSource("a", "contract C { modifier M { _ } }");
+ c.addSource("a", "contract C { modifier M { _; } }");
c.parse();
map<string, unsigned> sourceIndices;
sourceIndices["a"] = 1;
@@ -195,6 +195,22 @@ BOOST_AUTO_TEST_CASE(placeholder_statement)
BOOST_CHECK_EQUAL(placeholder["src"], "26:1:1");
}
+BOOST_AUTO_TEST_CASE(non_utf8)
+{
+ CompilerStack c;
+ c.addSource("a", "contract C { function f() { var x = hex\"ff\"; } }");
+ c.parse();
+ map<string, unsigned> sourceIndices;
+ sourceIndices["a"] = 1;
+ Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json();
+ Json::Value literal = astJson["children"][0]["children"][0]["children"][2]["children"][0]["children"][1];
+ BOOST_CHECK_EQUAL(literal["name"], "Literal");
+ BOOST_CHECK_EQUAL(literal["attributes"]["hexvalue"], "ff");
+ BOOST_CHECK_EQUAL(literal["attributes"]["token"], Json::nullValue);
+ BOOST_CHECK_EQUAL(literal["attributes"]["value"], Json::nullValue);
+ BOOST_CHECK(literal["attributes"]["type"].asString().find("invalid") != string::npos);
+}
+
BOOST_AUTO_TEST_SUITE_END()
}
diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp
index 81332f4f..8d7a3540 100644
--- a/test/libsolidity/Assembly.cpp
+++ b/test/libsolidity/Assembly.cpp
@@ -115,7 +115,7 @@ BOOST_AUTO_TEST_CASE(location_test)
AssemblyItems items = compileContract(sourceCode);
vector<SourceLocation> locations =
vector<SourceLocation>(18, SourceLocation(2, 75, n)) +
- vector<SourceLocation>(28, SourceLocation(20, 72, n)) +
+ vector<SourceLocation>(31, SourceLocation(20, 72, n)) +
vector<SourceLocation>{SourceLocation(42, 51, n), SourceLocation(65, 67, n)} +
vector<SourceLocation>(4, SourceLocation(58, 67, n)) +
vector<SourceLocation>(3, SourceLocation(20, 72, n));
diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp
index ec621104..073d7d97 100644
--- a/test/libsolidity/SolidityABIJSON.cpp
+++ b/test/libsolidity/SolidityABIJSON.cpp
@@ -68,6 +68,7 @@ BOOST_AUTO_TEST_CASE(basic_test)
{
"name": "f",
"constant": false,
+ "payable" : false,
"type": "function",
"inputs": [
{
@@ -107,6 +108,7 @@ BOOST_AUTO_TEST_CASE(multiple_methods)
{
"name": "f",
"constant": false,
+ "payable" : false,
"type": "function",
"inputs": [
{
@@ -124,6 +126,7 @@ BOOST_AUTO_TEST_CASE(multiple_methods)
{
"name": "g",
"constant": false,
+ "payable" : false,
"type": "function",
"inputs": [
{
@@ -153,6 +156,7 @@ BOOST_AUTO_TEST_CASE(multiple_params)
{
"name": "f",
"constant": false,
+ "payable" : false,
"type": "function",
"inputs": [
{
@@ -188,6 +192,7 @@ BOOST_AUTO_TEST_CASE(multiple_methods_order)
{
"name": "c",
"constant": false,
+ "payable" : false,
"type": "function",
"inputs": [
{
@@ -205,6 +210,7 @@ BOOST_AUTO_TEST_CASE(multiple_methods_order)
{
"name": "f",
"constant": false,
+ "payable" : false,
"type": "function",
"inputs": [
{
@@ -235,6 +241,7 @@ BOOST_AUTO_TEST_CASE(const_function)
{
"name": "foo",
"constant": false,
+ "payable" : false,
"type": "function",
"inputs": [
{
@@ -256,6 +263,7 @@ BOOST_AUTO_TEST_CASE(const_function)
{
"name": "boo",
"constant": true,
+ "payable" : false,
"type": "function",
"inputs": [{
"name": "a",
@@ -284,6 +292,7 @@ BOOST_AUTO_TEST_CASE(events)
{
"name": "f",
"constant": false,
+ "payable" : false,
"type": "function",
"inputs": [
{
@@ -361,6 +370,7 @@ BOOST_AUTO_TEST_CASE(inherited)
{
"name": "baseFunction",
"constant": false,
+ "payable" : false,
"type": "function",
"inputs":
[{
@@ -376,6 +386,7 @@ BOOST_AUTO_TEST_CASE(inherited)
{
"name": "derivedFunction",
"constant": false,
+ "payable" : false,
"type": "function",
"inputs":
[{
@@ -429,6 +440,7 @@ BOOST_AUTO_TEST_CASE(empty_name_input_parameter_with_named_one)
{
"name": "f",
"constant": false,
+ "payable" : false,
"type": "function",
"inputs": [
{
@@ -469,6 +481,7 @@ BOOST_AUTO_TEST_CASE(empty_name_return_parameter)
{
"name": "f",
"constant": false,
+ "payable" : false,
"type": "function",
"inputs": [
{
@@ -536,6 +549,7 @@ BOOST_AUTO_TEST_CASE(return_param_in_abi)
[
{
"constant" : false,
+ "payable" : false,
"inputs" : [],
"name" : "ret",
"outputs" : [
@@ -573,6 +587,7 @@ BOOST_AUTO_TEST_CASE(strings_and_arrays)
[
{
"constant" : false,
+ "payable" : false,
"name": "f",
"inputs": [
{ "name": "a", "type": "string" },
@@ -600,6 +615,7 @@ BOOST_AUTO_TEST_CASE(library_function)
[
{
"constant" : false,
+ "payable" : false,
"name": "f",
"inputs": [
{ "name": "b", "type": "test.StructType storage" },
@@ -628,7 +644,58 @@ BOOST_AUTO_TEST_CASE(include_fallback_function)
char const* interface = R"(
[
{
+ "payable": false,
+ "type" : "fallback"
+ }
+ ]
+ )";
+ checkInterface(sourceCode, interface);
+}
+
+BOOST_AUTO_TEST_CASE(payable_function)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function f() {}
+ function g() payable {}
+ }
+ )";
+
+ char const* interface = R"(
+ [
+ {
"constant" : false,
+ "payable": false,
+ "inputs": [],
+ "name": "f",
+ "outputs": [],
+ "type" : "function"
+ },
+ {
+ "constant" : false,
+ "payable": true,
+ "inputs": [],
+ "name": "g",
+ "outputs": [],
+ "type" : "function"
+ }
+ ]
+ )";
+ checkInterface(sourceCode, interface);
+}
+
+BOOST_AUTO_TEST_CASE(payable_fallback_unction)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function () payable {}
+ }
+ )";
+
+ char const* interface = R"(
+ [
+ {
+ "payable": true,
"type" : "fallback"
}
]
diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp
index 35b7078d..3c85d8a8 100644
--- a/test/libsolidity/SolidityEndToEndTest.cpp
+++ b/test/libsolidity/SolidityEndToEndTest.cpp
@@ -1320,7 +1320,7 @@ BOOST_AUTO_TEST_CASE(balance)
BOOST_AUTO_TEST_CASE(blockchain)
{
char const* sourceCode = "contract test {\n"
- " function someInfo() returns (uint256 value, address coinbase, uint256 blockNumber) {\n"
+ " function someInfo() payable returns (uint256 value, address coinbase, uint256 blockNumber) {\n"
" value = msg.value;\n"
" coinbase = block.coinbase;\n"
" blockNumber = block.number;\n"
@@ -1342,7 +1342,7 @@ BOOST_AUTO_TEST_CASE(msg_sig)
}
)";
compileAndRun(sourceCode);
- BOOST_CHECK(callContractFunctionWithValue("foo(uint256)", 13) == encodeArgs(asString(FixedHash<4>(dev::sha3("foo(uint256)")).asBytes())));
+ BOOST_CHECK(callContractFunction("foo(uint256)") == encodeArgs(asString(FixedHash<4>(dev::sha3("foo(uint256)")).asBytes())));
}
BOOST_AUTO_TEST_CASE(msg_sig_after_internal_call_is_same)
@@ -1358,7 +1358,7 @@ BOOST_AUTO_TEST_CASE(msg_sig_after_internal_call_is_same)
}
)";
compileAndRun(sourceCode);
- BOOST_CHECK(callContractFunctionWithValue("foo(uint256)", 13) == encodeArgs(asString(FixedHash<4>(dev::sha3("foo(uint256)")).asBytes())));
+ BOOST_CHECK(callContractFunction("foo(uint256)") == encodeArgs(asString(FixedHash<4>(dev::sha3("foo(uint256)")).asBytes())));
}
BOOST_AUTO_TEST_CASE(now)
@@ -1526,8 +1526,10 @@ BOOST_AUTO_TEST_CASE(convert_uint_to_fixed_bytes_greater_size)
}
})";
compileAndRun(sourceCode);
- BOOST_CHECK(callContractFunction("UintToBytes(uint16)", u256("0x6162")) ==
- encodeArgs(string("\0\0\0\0\0\0ab", 8)));
+ BOOST_CHECK(
+ callContractFunction("UintToBytes(uint16)", u256("0x6162")) ==
+ encodeArgs(string("\0\0\0\0\0\0ab", 8))
+ );
}
BOOST_AUTO_TEST_CASE(send_ether)
@@ -2053,11 +2055,11 @@ BOOST_AUTO_TEST_CASE(contracts_as_addresses)
{
char const* sourceCode = R"(
contract helper {
- function() { } // can receive ether
+ function() payable { } // can receive ether
}
contract test {
helper h;
- function test() { h = new helper(); h.send(5); }
+ function test() payable { h = new helper(); h.send(5); }
function getBalance() returns (uint256 myBalance, uint256 helperBalance) {
myBalance = this.balance;
helperBalance = h.balance;
@@ -2065,6 +2067,7 @@ BOOST_AUTO_TEST_CASE(contracts_as_addresses)
}
)";
compileAndRun(sourceCode, 20);
+ BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 20 - 5);
BOOST_REQUIRE(callContractFunction("getBalance()") == encodeArgs(u256(20 - 5), u256(5)));
}
@@ -2073,7 +2076,7 @@ BOOST_AUTO_TEST_CASE(gas_and_value_basic)
char const* sourceCode = R"(
contract helper {
bool flag;
- function getBalance() returns (uint256 myBalance) {
+ function getBalance() payable returns (uint256 myBalance) {
return this.balance;
}
function setFlag() { flag = true; }
@@ -2081,8 +2084,8 @@ BOOST_AUTO_TEST_CASE(gas_and_value_basic)
}
contract test {
helper h;
- function test() { h = new helper(); }
- function sendAmount(uint amount) returns (uint256 bal) {
+ function test() payable { h = new helper(); }
+ function sendAmount(uint amount) payable returns (uint256 bal) {
return h.getBalance.value(amount)();
}
function outOfGas() returns (bool ret) {
@@ -2098,8 +2101,8 @@ BOOST_AUTO_TEST_CASE(gas_and_value_basic)
compileAndRun(sourceCode, 20);
BOOST_REQUIRE(callContractFunction("sendAmount(uint256)", 5) == encodeArgs(5));
// call to helper should not succeed but amount should be transferred anyway
- BOOST_REQUIRE(callContractFunction("outOfGas()", 5) == bytes());
- BOOST_REQUIRE(callContractFunction("checkState()", 5) == encodeArgs(false, 20 - 5));
+ BOOST_REQUIRE(callContractFunction("outOfGas()") == bytes());
+ BOOST_REQUIRE(callContractFunction("checkState()") == encodeArgs(false, 20 - 5));
}
BOOST_AUTO_TEST_CASE(gas_for_builtin)
@@ -2121,14 +2124,14 @@ BOOST_AUTO_TEST_CASE(value_complex)
{
char const* sourceCode = R"(
contract helper {
- function getBalance() returns (uint256 myBalance) {
+ function getBalance() payable returns (uint256 myBalance) {
return this.balance;
}
}
contract test {
helper h;
- function test() { h = new helper(); }
- function sendAmount(uint amount) returns (uint256 bal) {
+ function test() payable { h = new helper(); }
+ function sendAmount(uint amount) payable returns (uint256 bal) {
var x1 = h.getBalance.value(amount);
uint someStackElement = 20;
var x2 = x1.gas(1000);
@@ -2144,13 +2147,13 @@ BOOST_AUTO_TEST_CASE(value_insane)
{
char const* sourceCode = R"(
contract helper {
- function getBalance() returns (uint256 myBalance) {
+ function getBalance() payable returns (uint256 myBalance) {
return this.balance;
}
}
contract test {
helper h;
- function test() { h = new helper(); }
+ function test() payable { h = new helper(); }
function sendAmount(uint amount) returns (uint256 bal) {
var x1 = h.getBalance.value;
var x2 = x1(amount).gas;
@@ -2169,7 +2172,7 @@ BOOST_AUTO_TEST_CASE(value_for_constructor)
contract Helper {
bytes3 name;
bool flag;
- function Helper(bytes3 x, bool f) {
+ function Helper(bytes3 x, bool f) payable {
name = x;
flag = f;
}
@@ -2178,7 +2181,7 @@ BOOST_AUTO_TEST_CASE(value_for_constructor)
}
contract Main {
Helper h;
- function Main() {
+ function Main() payable {
h = (new Helper).value(10)("abc", true);
}
function getFlag() returns (bool ret) { return h.getFlag(); }
@@ -2352,8 +2355,8 @@ BOOST_AUTO_TEST_CASE(function_modifier)
{
char const* sourceCode = R"(
contract C {
- function getOne() nonFree returns (uint r) { return 1; }
- modifier nonFree { if (msg.value > 0) _ }
+ function getOne() payable nonFree returns (uint r) { return 1; }
+ modifier nonFree { if (msg.value > 0) _; }
}
)";
compileAndRun(sourceCode);
@@ -2365,8 +2368,8 @@ BOOST_AUTO_TEST_CASE(function_modifier_local_variables)
{
char const* sourceCode = R"(
contract C {
- modifier mod1 { var a = 1; var b = 2; _ }
- modifier mod2(bool a) { if (a) return; else _ }
+ modifier mod1 { var a = 1; var b = 2; _; }
+ modifier mod2(bool a) { if (a) return; else _; }
function f(bool a) mod1 mod2(a) returns (uint r) { return 3; }
}
)";
@@ -2379,7 +2382,7 @@ BOOST_AUTO_TEST_CASE(function_modifier_loop)
{
char const* sourceCode = R"(
contract C {
- modifier repeat(uint count) { for (var i = 0; i < count; ++i) _ }
+ modifier repeat(uint count) { for (var i = 0; i < count; ++i) _; }
function f() repeat(10) returns (uint r) { r += 1; }
}
)";
@@ -2391,7 +2394,7 @@ BOOST_AUTO_TEST_CASE(function_modifier_multi_invocation)
{
char const* sourceCode = R"(
contract C {
- modifier repeat(bool twice) { if (twice) _ _ }
+ modifier repeat(bool twice) { if (twice) _; _; }
function f(bool twice) repeat(twice) returns (uint r) { r += 1; }
}
)";
@@ -2406,7 +2409,7 @@ BOOST_AUTO_TEST_CASE(function_modifier_multi_with_return)
// modifier code block.
char const* sourceCode = R"(
contract C {
- modifier repeat(bool twice) { if (twice) _ _ }
+ modifier repeat(bool twice) { if (twice) _; _; }
function f(bool twice) repeat(twice) returns (uint r) { r += 1; return r; }
}
)";
@@ -2420,10 +2423,10 @@ BOOST_AUTO_TEST_CASE(function_modifier_overriding)
char const* sourceCode = R"(
contract A {
function f() mod returns (bool r) { return true; }
- modifier mod { _ }
+ modifier mod { _; }
}
contract C is A {
- modifier mod { if (false) _ }
+ modifier mod { if (false) _; }
}
)";
compileAndRun(sourceCode);
@@ -2439,12 +2442,12 @@ BOOST_AUTO_TEST_CASE(function_modifier_calling_functions_in_creation_context)
function f1() mod2 { data |= 0x1; }
function f2() { data |= 0x20; }
function f3() { }
- modifier mod1 { f2(); _ }
- modifier mod2 { f3(); if (false) _ }
+ modifier mod1 { f2(); _; }
+ modifier mod2 { f3(); if (false) _; }
function getData() returns (uint r) { return data; }
}
contract C is A {
- modifier mod1 { f4(); _ }
+ modifier mod1 { f4(); _; }
function f3() { data |= 0x300; }
function f4() { data |= 0x4000; }
}
@@ -2459,11 +2462,11 @@ BOOST_AUTO_TEST_CASE(function_modifier_for_constructor)
contract A {
uint data;
function A() mod1 { data |= 2; }
- modifier mod1 { data |= 1; _ }
+ modifier mod1 { data |= 1; _; }
function getData() returns (uint r) { return data; }
}
contract C is A {
- modifier mod1 { data |= 4; _ }
+ modifier mod1 { data |= 4; _; }
}
)";
compileAndRun(sourceCode);
@@ -2559,7 +2562,7 @@ BOOST_AUTO_TEST_CASE(event)
char const* sourceCode = R"(
contract ClientReceipt {
event Deposit(address indexed _from, bytes32 indexed _id, uint _value);
- function deposit(bytes32 _id, bool _manually) {
+ function deposit(bytes32 _id, bool _manually) payable {
if (_manually) {
bytes32 s = 0x19dacbf83c5de6658e14cbf7bcae5c15eca2eedecf1c66fbca928e4d351bea0f;
log3(bytes32(msg.value), s, bytes32(msg.sender), _id);
@@ -2624,7 +2627,7 @@ BOOST_AUTO_TEST_CASE(event_anonymous_with_topics)
char const* sourceCode = R"(
contract ClientReceipt {
event Deposit(address indexed _from, bytes32 indexed _id, uint indexed _value, uint indexed _value2, bytes32 data) anonymous;
- function deposit(bytes32 _id, bool _manually) {
+ function deposit(bytes32 _id, bool _manually) payable {
Deposit(msg.sender, _id, msg.value, 2, "abc");
}
}
@@ -2648,7 +2651,7 @@ BOOST_AUTO_TEST_CASE(event_lots_of_data)
char const* sourceCode = R"(
contract ClientReceipt {
event Deposit(address _from, bytes32 _id, uint _value, bool _flag);
- function deposit(bytes32 _id) {
+ function deposit(bytes32 _id) payable {
Deposit(msg.sender, _id, msg.value, true);
}
}
@@ -2874,9 +2877,10 @@ BOOST_AUTO_TEST_CASE(generic_call)
char const* sourceCode = R"**(
contract receiver {
uint public received;
- function receive(uint256 x) { received = x; }
+ function receive(uint256 x) payable { received = x; }
}
contract sender {
+ function sender() payable {}
function doSend(address rec) returns (uint d)
{
bytes4 signature = bytes4(bytes32(sha3("receive(uint256)")));
@@ -2897,10 +2901,11 @@ BOOST_AUTO_TEST_CASE(generic_callcode)
char const* sourceCode = R"**(
contract receiver {
uint public received;
- function receive(uint256 x) { received = x; }
+ function receive(uint256 x) payable { received = x; }
}
contract sender {
uint public received;
+ function sender() payable { }
function doSend(address rec) returns (uint d)
{
bytes4 signature = bytes4(bytes32(sha3("receive(uint256)")));
@@ -2930,16 +2935,16 @@ BOOST_AUTO_TEST_CASE(generic_delegatecall)
uint public received;
address public sender;
uint public value;
- function receive(uint256 x) { received = x; sender = msg.sender; value = msg.value; }
+ function receive(uint256 x) payable { received = x; sender = msg.sender; value = msg.value; }
}
contract sender {
uint public received;
address public sender;
uint public value;
- function doSend(address rec)
+ function doSend(address rec) payable
{
bytes4 signature = bytes4(bytes32(sha3("receive(uint256)")));
- rec.delegatecall(signature, 23);
+ if (rec.delegatecall(signature, 23)) {}
}
}
)**";
@@ -4620,6 +4625,26 @@ BOOST_AUTO_TEST_CASE(failing_send)
BOOST_REQUIRE(callContractFunction("callHelper(address)", c_helperAddress) == encodeArgs(true, 20));
}
+BOOST_AUTO_TEST_CASE(send_zero_ether)
+{
+ // Sending zero ether to a contract should still invoke the fallback function
+ // (it previously did not because the gas stipend was not provided by the EVM)
+ char const* sourceCode = R"(
+ contract Receiver {
+ function () payable {
+ }
+ }
+ contract Main {
+ function s() returns (bool) {
+ var r = new Receiver();
+ return r.send(0);
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 20, "Main");
+ BOOST_REQUIRE(callContractFunction("s()") == encodeArgs(true));
+}
+
BOOST_AUTO_TEST_CASE(reusing_memory)
{
// Invoke some features that use memory and test that they do not interfere with each other.
@@ -5958,7 +5983,7 @@ BOOST_AUTO_TEST_CASE(reject_ether_sent_to_library)
function f(address x) returns (bool) {
return x.send(1);
}
- function () {}
+ function () payable {}
}
)";
compileAndRun(sourceCode, 0, "lib");
@@ -6876,7 +6901,7 @@ BOOST_AUTO_TEST_CASE(skip_dynamic_types_for_structs)
BOOST_AUTO_TEST_CASE(failed_create)
{
char const* sourceCode = R"(
- contract D { }
+ contract D { function D() payable {} }
contract C {
uint public x;
function f(uint amount) returns (address) {
@@ -6920,7 +6945,7 @@ BOOST_AUTO_TEST_CASE(return_does_not_skip_modifier)
contract C {
uint public x;
modifier setsx {
- _
+ _;
x = 9;
}
function f() setsx returns (uint) {
@@ -6941,7 +6966,7 @@ BOOST_AUTO_TEST_CASE(break_in_modifier)
uint public x;
modifier run() {
for (uint i = 0; i < 10; i++) {
- _
+ _;
break;
}
}
@@ -6963,7 +6988,7 @@ BOOST_AUTO_TEST_CASE(stacked_return_with_modifiers)
uint public x;
modifier run() {
for (uint i = 0; i < 10; i++) {
- _
+ _;
break;
}
}
@@ -6986,7 +7011,7 @@ BOOST_AUTO_TEST_CASE(mutex)
modifier protected {
if (locked) throw;
locked = true;
- _
+ _;
locked = false;
}
}
@@ -7026,7 +7051,7 @@ BOOST_AUTO_TEST_CASE(mutex)
else
return fund.withdrawUnprotected(10);
}
- function() {
+ function() payable {
callDepth++;
if (callDepth < 4)
attackInternal();
@@ -7087,6 +7112,81 @@ BOOST_AUTO_TEST_CASE(calling_nonexisting_contract_throws)
BOOST_CHECK(callContractFunction("h()") == encodeArgs(u256(7)));
}
+BOOST_AUTO_TEST_CASE(payable_constructor)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function C() payable { }
+ }
+ )";
+ compileAndRun(sourceCode, 27, "C");
+}
+
+BOOST_AUTO_TEST_CASE(payable_function)
+{
+ char const* sourceCode = R"(
+ contract C {
+ uint public a;
+ function f() payable returns (uint) {
+ return msg.value;
+ }
+ function() payable {
+ a = msg.value + 1;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunctionWithValue("f()", 27) == encodeArgs(u256(27)));
+ BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 27);
+ BOOST_CHECK(callContractFunctionWithValue("", 27) == encodeArgs());
+ BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 27 + 27);
+ BOOST_CHECK(callContractFunction("a()") == encodeArgs(u256(28)));
+ BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 27 + 27);
+}
+
+BOOST_AUTO_TEST_CASE(non_payable_throw)
+{
+ char const* sourceCode = R"(
+ contract C {
+ uint public a;
+ function f() returns (uint) {
+ return msg.value;
+ }
+ function() {
+ a = msg.value + 1;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunctionWithValue("f()", 27) == encodeArgs());
+ BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 0);
+ BOOST_CHECK(callContractFunction("") == encodeArgs());
+ BOOST_CHECK(callContractFunction("a()") == encodeArgs(u256(1)));
+ BOOST_CHECK(callContractFunctionWithValue("", 27) == encodeArgs());
+ BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 0);
+ BOOST_CHECK(callContractFunction("a()") == encodeArgs(u256(1)));
+ BOOST_CHECK(callContractFunctionWithValue("a()", 27) == encodeArgs());
+ BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 0);
+}
+
+BOOST_AUTO_TEST_CASE(no_nonpayable_circumvention_by_modifier)
+{
+ char const* sourceCode = R"(
+ contract C {
+ modifier tryCircumvent {
+ if (false) _; // avoid the function, we should still not accept ether
+ }
+ function f() tryCircumvent returns (uint) {
+ return msg.value;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunctionWithValue("f()", 27) == encodeArgs());
+ BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 0);
+}
+
+
BOOST_AUTO_TEST_SUITE_END()
}
diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp
index 2c126b65..58736025 100644
--- a/test/libsolidity/SolidityNameAndTypeResolution.cpp
+++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp
@@ -859,8 +859,8 @@ BOOST_AUTO_TEST_CASE(function_modifier_invocation)
char const* text = R"(
contract B {
function f() mod1(2, true) mod2("0123456") { }
- modifier mod1(uint a, bool b) { if (b) _ }
- modifier mod2(bytes7 a) { while (a == "1234567") _ }
+ modifier mod1(uint a, bool b) { if (b) _; }
+ modifier mod2(bytes7 a) { while (a == "1234567") _; }
}
)";
BOOST_CHECK(success(text));
@@ -871,7 +871,7 @@ BOOST_AUTO_TEST_CASE(invalid_function_modifier_type)
char const* text = R"(
contract B {
function f() mod1(true) { }
- modifier mod1(uint a) { if (a > 0) _ }
+ modifier mod1(uint a) { if (a > 0) _; }
}
)";
BOOST_CHECK(expectError(text) == Error::Type::TypeError);
@@ -882,8 +882,8 @@ BOOST_AUTO_TEST_CASE(function_modifier_invocation_parameters)
char const* text = R"(
contract B {
function f(uint8 a) mod1(a, true) mod2(r) returns (bytes7 r) { }
- modifier mod1(uint a, bool b) { if (b) _ }
- modifier mod2(bytes7 a) { while (a == "1234567") _ }
+ modifier mod1(uint a, bool b) { if (b) _; }
+ modifier mod2(bytes7 a) { while (a == "1234567") _; }
}
)";
BOOST_CHECK(success(text));
@@ -894,7 +894,7 @@ BOOST_AUTO_TEST_CASE(function_modifier_invocation_local_variables)
char const* text = R"(
contract B {
function f() mod(x) { uint x = 7; }
- modifier mod(uint a) { if (a > 0) _ }
+ modifier mod(uint a) { if (a > 0) _; }
}
)";
BOOST_CHECK(success(text));
@@ -903,8 +903,8 @@ BOOST_AUTO_TEST_CASE(function_modifier_invocation_local_variables)
BOOST_AUTO_TEST_CASE(legal_modifier_override)
{
char const* text = R"(
- contract A { modifier mod(uint a) { _ } }
- contract B is A { modifier mod(uint a) { _ } }
+ contract A { modifier mod(uint a) { _; } }
+ contract B is A { modifier mod(uint a) { _; } }
)";
BOOST_CHECK(success(text));
}
@@ -912,8 +912,8 @@ BOOST_AUTO_TEST_CASE(legal_modifier_override)
BOOST_AUTO_TEST_CASE(illegal_modifier_override)
{
char const* text = R"(
- contract A { modifier mod(uint a) { _ } }
- contract B is A { modifier mod(uint8 a) { _ } }
+ contract A { modifier mod(uint a) { _; } }
+ contract B is A { modifier mod(uint8 a) { _; } }
)";
BOOST_CHECK(expectError(text) == Error::Type::TypeError);
}
@@ -921,7 +921,7 @@ BOOST_AUTO_TEST_CASE(illegal_modifier_override)
BOOST_AUTO_TEST_CASE(modifier_overrides_function)
{
char const* text = R"(
- contract A { modifier mod(uint a) { _ } }
+ contract A { modifier mod(uint a) { _; } }
contract B is A { function mod(uint a) { } }
)";
BOOST_CHECK(expectError(text) == Error::Type::TypeError);
@@ -931,7 +931,7 @@ BOOST_AUTO_TEST_CASE(function_overrides_modifier)
{
char const* text = R"(
contract A { function mod(uint a) { } }
- contract B is A { modifier mod(uint a) { _ } }
+ contract B is A { modifier mod(uint a) { _; } }
)";
BOOST_CHECK(expectError(text) == Error::Type::TypeError);
}
@@ -941,7 +941,7 @@ BOOST_AUTO_TEST_CASE(modifier_returns_value)
char const* text = R"(
contract A {
function f(uint a) mod(2) returns (uint r) { }
- modifier mod(uint a) { _ return 7; }
+ modifier mod(uint a) { _; return 7; }
}
)";
BOOST_CHECK(expectError(text) == Error::Type::TypeError);
@@ -1124,6 +1124,17 @@ BOOST_AUTO_TEST_CASE(fallback_function_with_return_parameters)
BOOST_CHECK(expectError(text) == Error::Type::TypeError);
}
+BOOST_AUTO_TEST_CASE(fallback_function_with_constant_modifier)
+{
+ char const* text = R"(
+ contract C {
+ uint x;
+ function() constant { x = 2; }
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
BOOST_AUTO_TEST_CASE(fallback_function_twice)
{
char const* text = R"(
@@ -3852,7 +3863,113 @@ BOOST_AUTO_TEST_CASE(modifier_without_underscore)
modifier m() {}
}
)";
- BOOST_CHECK(expectError(text, true) == Error::Type::SyntaxError);
+ BOOST_CHECK(expectError(text) == Error::Type::SyntaxError);
+}
+
+BOOST_AUTO_TEST_CASE(payable_in_library)
+{
+ char const* text = R"(
+ library test {
+ function f() payable {}
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(payable_external)
+{
+ char const* text = R"(
+ contract test {
+ function f() payable external {}
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(payable_internal)
+{
+ char const* text = R"(
+ contract test {
+ function f() payable internal {}
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(payable_private)
+{
+ char const* text = R"(
+ contract test {
+ function f() payable private {}
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(illegal_override_payable)
+{
+ char const* text = R"(
+ contract B { function f() payable {} }
+ contract C is B { function f() {} }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(illegal_override_payable_nonpayable)
+{
+ char const* text = R"(
+ contract B { function f() {} }
+ contract C is B { function f() payable {} }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(payable_constant_conflict)
+{
+ char const* text = R"(
+ contract C { function f() payable constant {} }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(calling_payable)
+{
+ char const* text = R"(
+ contract receiver { function pay() payable {} }
+ contract test {
+ funciton f() { (new receiver()).pay.value(10)(); }
+ recevier r = new receiver();
+ function g() { r.pay.value(10)(); }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(calling_nonpayable)
+{
+ char const* text = R"(
+ contract receiver { function nopay() {} }
+ contract test {
+ function f() { (new receiver()).nopay.value(10)(); }
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(non_payable_constructor)
+{
+ char const* text = R"(
+ contract C {
+ function C() { }
+ }
+ contract D {
+ function f() returns (uint) {
+ (new C).value(2)();
+ return 2;
+ }
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
}
BOOST_AUTO_TEST_CASE(warn_nonpresent_pragma)
@@ -3872,6 +3989,27 @@ BOOST_AUTO_TEST_CASE(unsatisfied_version)
BOOST_CHECK(expectError(text, true) == Error::Type::SyntaxError);
}
+BOOST_AUTO_TEST_CASE(constant_constructor)
+{
+ char const* text = R"(
+ contract test {
+ function test() constant {}
+ }
+ )";
+ BOOST_CHECK(expectError(text, false) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(external_constructor)
+{
+ char const* text = R"(
+ contract test {
+ function test() external {}
+ }
+ )";
+ BOOST_CHECK(expectError(text, false) == Error::Type::TypeError);
+}
+
+
BOOST_AUTO_TEST_SUITE_END()
}
diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp
index 92f5a142..a81a9828 100644
--- a/test/libsolidity/SolidityParser.cpp
+++ b/test/libsolidity/SolidityParser.cpp
@@ -681,15 +681,23 @@ BOOST_AUTO_TEST_CASE(placeholder_in_function_context)
BOOST_AUTO_TEST_CASE(modifier)
{
char const* text = "contract c {\n"
- " modifier mod { if (msg.sender == 0) _ }\n"
+ " modifier mod { if (msg.sender == 0) _; }\n"
"}\n";
BOOST_CHECK(successParse(text));
}
+BOOST_AUTO_TEST_CASE(modifier_without_semicolon)
+{
+ char const* text = "contract c {\n"
+ " modifier mod { if (msg.sender == 0) _ }\n"
+ "}\n";
+ BOOST_CHECK(!successParse(text));
+}
+
BOOST_AUTO_TEST_CASE(modifier_arguments)
{
char const* text = "contract c {\n"
- " modifier mod(uint a) { if (msg.sender == a) _ }\n"
+ " modifier mod(uint a) { if (msg.sender == a) _; }\n"
"}\n";
BOOST_CHECK(successParse(text));
}
@@ -697,8 +705,8 @@ BOOST_AUTO_TEST_CASE(modifier_arguments)
BOOST_AUTO_TEST_CASE(modifier_invocation)
{
char const* text = "contract c {\n"
- " modifier mod1(uint a) { if (msg.sender == a) _ }\n"
- " modifier mod2 { if (msg.sender == 2) _ }\n"
+ " modifier mod1(uint a) { if (msg.sender == a) _; }\n"
+ " modifier mod2 { if (msg.sender == 2) _; }\n"
" function f() mod1(7) mod2 { }\n"
"}\n";
BOOST_CHECK(successParse(text));
@@ -1223,6 +1231,16 @@ BOOST_AUTO_TEST_CASE(invalid_fixed_conversion_leading_zeroes_check)
BOOST_CHECK(!successParse(text));
}
+BOOST_AUTO_TEST_CASE(payable_accessor)
+{
+ char const* text = R"(
+ contract test {
+ uint payable x;
+ }
+ )";
+ BOOST_CHECK(!successParse(text));
+}
+
BOOST_AUTO_TEST_SUITE_END()
}