mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #2762 from ethereum/statemutability-view
Introduce view state-mutability (keep constant as alias)
This commit is contained in:
commit
0ffc5db82b
@ -3,6 +3,7 @@
|
|||||||
Features:
|
Features:
|
||||||
* ABI JSON: Include new field ``statemutability`` with values ``view``, ``nonpayable`` and ``payable``.
|
* ABI JSON: Include new field ``statemutability`` with values ``view``, ``nonpayable`` and ``payable``.
|
||||||
* Parser: Display previous visibility specifier in error if multiple are found.
|
* Parser: Display previous visibility specifier in error if multiple are found.
|
||||||
|
* Parser: Introduce ``view`` keyword on functions (``constant`` remains an alias for ``view``).
|
||||||
* Syntax Checker: Support ``pragma experimental <feature>;`` to turn on experimental features.
|
* Syntax Checker: Support ``pragma experimental <feature>;`` to turn on experimental features.
|
||||||
* Static Analyzer: Warn about large storage structures.
|
* Static Analyzer: Warn about large storage structures.
|
||||||
* Metadata: Store experimental flag in metadata CBOR.
|
* Metadata: Store experimental flag in metadata CBOR.
|
||||||
|
@ -293,7 +293,7 @@ The JSON format for a contract's interface is given by an array of function and/
|
|||||||
* `name`: the name of the parameter;
|
* `name`: the name of the parameter;
|
||||||
* `type`: the canonical type of the parameter.
|
* `type`: the canonical type of the parameter.
|
||||||
- `outputs`: an array of objects similar to `inputs`, can be omitted if function doesn't return anything;
|
- `outputs`: an array of objects similar to `inputs`, can be omitted if function doesn't return anything;
|
||||||
- `constant`: `true` if function is :ref:`specified to not modify blockchain state <constant-functions>`);
|
- `constant`: `true` if function is :ref:`specified to not modify blockchain state <view-functions>`);
|
||||||
- `payable`: `true` if function accepts ether, defaults to `false`;
|
- `payable`: `true` if function accepts ether, defaults to `false`;
|
||||||
- `statemutability`: a string with one of the following values: `view` (same as `constant` above), `nonpayable` and `payable` (same as `payable` above).
|
- `statemutability`: a string with one of the following values: `view` (same as `constant` above), `nonpayable` and `payable` (same as `payable` above).
|
||||||
|
|
||||||
|
@ -461,29 +461,32 @@ value types and strings.
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.. _constant-functions:
|
.. _view-functions:
|
||||||
|
|
||||||
******************
|
**************
|
||||||
Constant Functions
|
View Functions
|
||||||
******************
|
**************
|
||||||
|
|
||||||
Functions can be declared constant in which case they promise not to modify the state.
|
Functions can be declared ``view`` in which case they promise not to modify the state.
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
pragma solidity ^0.4.0;
|
pragma solidity ^0.4.0;
|
||||||
|
|
||||||
contract C {
|
contract C {
|
||||||
function f(uint a, uint b) constant returns (uint) {
|
function f(uint a, uint b) view returns (uint) {
|
||||||
return a * (b + 42);
|
return a * (b + 42);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
Getter methods are marked constant.
|
``constant`` is an alias to ``view``.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
Getter methods are marked ``view``.
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
The compiler does not enforce yet that a constant method is not modifying state.
|
The compiler does not enforce yet that a ``view`` method is not modifying state.
|
||||||
|
|
||||||
.. index:: ! fallback function, function;fallback
|
.. index:: ! fallback function, function;fallback
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ ArrayTypeName = TypeName '[' Expression? ']'
|
|||||||
FunctionTypeName = 'function' TypeNameList ( 'internal' | 'external' | StateMutability )*
|
FunctionTypeName = 'function' TypeNameList ( 'internal' | 'external' | StateMutability )*
|
||||||
( 'returns' TypeNameList )?
|
( 'returns' TypeNameList )?
|
||||||
StorageLocation = 'memory' | 'storage'
|
StorageLocation = 'memory' | 'storage'
|
||||||
StateMutability = 'constant' | 'payable'
|
StateMutability = 'constant' | 'view' | 'payable'
|
||||||
|
|
||||||
Block = '{' Statement* '}'
|
Block = '{' Statement* '}'
|
||||||
Statement = IfStatement | WhileStatement | ForStatement | Block | InlineAssemblyStatement |
|
Statement = IfStatement | WhileStatement | ForStatement | Block | InlineAssemblyStatement |
|
||||||
|
@ -505,11 +505,12 @@ Function Visibility Specifiers
|
|||||||
Modifiers
|
Modifiers
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
- ``view`` for functions: Disallow modification of state - this is not enforced yet.
|
||||||
|
- ``payable`` for functions: Allows them to receive Ether together with a call.
|
||||||
- ``constant`` for state variables: Disallows assignment (except initialisation), does not occupy storage slot.
|
- ``constant`` for state variables: Disallows assignment (except initialisation), does not occupy storage slot.
|
||||||
- ``constant`` for functions: Disallows modification of state - this is not enforced yet.
|
- ``constant`` for functions: Same as ``view``.
|
||||||
- ``anonymous`` for events: Does not store event signature as topic.
|
- ``anonymous`` for events: Does not store event signature as topic.
|
||||||
- ``indexed`` for event parameters: Stores the parameter as topic.
|
- ``indexed`` for event parameters: Stores the parameter as topic.
|
||||||
- ``payable`` for functions: Allows them to receive Ether together with a call.
|
|
||||||
|
|
||||||
Reserved Keywords
|
Reserved Keywords
|
||||||
=================
|
=================
|
||||||
|
@ -337,7 +337,7 @@ be passed via and returned from external function calls.
|
|||||||
|
|
||||||
Function types are notated as follows::
|
Function types are notated as follows::
|
||||||
|
|
||||||
function (<parameter types>) {internal|external} [constant|payable] [returns (<return types>)]
|
function (<parameter types>) {internal|external} [constant|view|payable] [returns (<return types>)]
|
||||||
|
|
||||||
In contrast to the parameter types, the return types cannot be empty - if the
|
In contrast to the parameter types, the return types cannot be empty - if the
|
||||||
function type should not return anything, the whole ``returns (<return types>)``
|
function type should not return anything, the whole ``returns (<return types>)``
|
||||||
|
@ -323,6 +323,7 @@ bool ASTJsonConverter::visit(FunctionDefinition const& _node)
|
|||||||
{
|
{
|
||||||
std::vector<pair<string, Json::Value>> attributes = {
|
std::vector<pair<string, Json::Value>> attributes = {
|
||||||
make_pair("name", _node.name()),
|
make_pair("name", _node.name()),
|
||||||
|
// FIXME: remove with next breaking release
|
||||||
make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.stateMutability() == StateMutability::View),
|
make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.stateMutability() == StateMutability::View),
|
||||||
make_pair("payable", _node.isPayable()),
|
make_pair("payable", _node.isPayable()),
|
||||||
make_pair("statemutability", stateMutabilityToString(_node.stateMutability())),
|
make_pair("statemutability", stateMutabilityToString(_node.stateMutability())),
|
||||||
@ -415,6 +416,7 @@ bool ASTJsonConverter::visit(FunctionTypeName const& _node)
|
|||||||
make_pair("payable", _node.isPayable()),
|
make_pair("payable", _node.isPayable()),
|
||||||
make_pair("visibility", Declaration::visibilityToString(_node.visibility())),
|
make_pair("visibility", Declaration::visibilityToString(_node.visibility())),
|
||||||
make_pair("statemutability", stateMutabilityToString(_node.stateMutability())),
|
make_pair("statemutability", stateMutabilityToString(_node.stateMutability())),
|
||||||
|
// FIXME: remove with next breaking release
|
||||||
make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.stateMutability() == StateMutability::View),
|
make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.stateMutability() == StateMutability::View),
|
||||||
make_pair("parameterTypes", toJson(*_node.parameterTypeList())),
|
make_pair("parameterTypes", toJson(*_node.parameterTypeList())),
|
||||||
make_pair("returnParameterTypes", toJson(*_node.returnParameterTypeList())),
|
make_pair("returnParameterTypes", toJson(*_node.returnParameterTypeList())),
|
||||||
|
@ -993,7 +993,6 @@ public:
|
|||||||
return *m_declaration;
|
return *m_declaration;
|
||||||
}
|
}
|
||||||
bool hasDeclaration() const { return !!m_declaration; }
|
bool hasDeclaration() const { return !!m_declaration; }
|
||||||
bool isConstant() const { return m_stateMutability == StateMutability::View; }
|
|
||||||
/// @returns true if the the result of this function only depends on its arguments
|
/// @returns true if the the result of this function only depends on its arguments
|
||||||
/// and it does not modify the state.
|
/// and it does not modify the state.
|
||||||
/// Currently, this will only return true for internal functions like keccak and ecrecover.
|
/// Currently, this will only return true for internal functions like keccak and ecrecover.
|
||||||
|
@ -35,7 +35,8 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef)
|
|||||||
Json::Value method;
|
Json::Value method;
|
||||||
method["type"] = "function";
|
method["type"] = "function";
|
||||||
method["name"] = it.second->declaration().name();
|
method["name"] = it.second->declaration().name();
|
||||||
method["constant"] = it.second->isConstant();
|
// TODO: deprecate constant in a future release
|
||||||
|
method["constant"] = it.second->stateMutability() == StateMutability::View;
|
||||||
method["payable"] = it.second->isPayable();
|
method["payable"] = it.second->isPayable();
|
||||||
method["statemutability"] = stateMutabilityToString(it.second->stateMutability());
|
method["statemutability"] = stateMutabilityToString(it.second->stateMutability());
|
||||||
method["inputs"] = formatTypeList(
|
method["inputs"] = formatTypeList(
|
||||||
|
@ -337,7 +337,8 @@ StateMutability Parser::parseStateMutability(Token::Value _token)
|
|||||||
StateMutability stateMutability(StateMutability::NonPayable);
|
StateMutability stateMutability(StateMutability::NonPayable);
|
||||||
if (_token == Token::Payable)
|
if (_token == Token::Payable)
|
||||||
stateMutability = StateMutability::Payable;
|
stateMutability = StateMutability::Payable;
|
||||||
else if (_token == Token::Constant)
|
// FIXME: constant should be removed at the next breaking release
|
||||||
|
else if (_token == Token::View || _token == Token::Constant)
|
||||||
stateMutability = StateMutability::View;
|
stateMutability = StateMutability::View;
|
||||||
else
|
else
|
||||||
solAssert(false, "Invalid state mutability specifier.");
|
solAssert(false, "Invalid state mutability specifier.");
|
||||||
|
@ -176,6 +176,7 @@ namespace solidity
|
|||||||
K(Throw, "throw", 0) \
|
K(Throw, "throw", 0) \
|
||||||
K(Using, "using", 0) \
|
K(Using, "using", 0) \
|
||||||
K(Var, "var", 0) \
|
K(Var, "var", 0) \
|
||||||
|
K(View, "view", 0) \
|
||||||
K(While, "while", 0) \
|
K(While, "while", 0) \
|
||||||
\
|
\
|
||||||
/* Ether subdenominations */ \
|
/* Ether subdenominations */ \
|
||||||
@ -236,7 +237,6 @@ namespace solidity
|
|||||||
K(Try, "try", 0) \
|
K(Try, "try", 0) \
|
||||||
K(Type, "type", 0) \
|
K(Type, "type", 0) \
|
||||||
K(TypeOf, "typeof", 0) \
|
K(TypeOf, "typeof", 0) \
|
||||||
K(View, "view", 0) \
|
|
||||||
/* Illegal token - not able to scan. */ \
|
/* Illegal token - not able to scan. */ \
|
||||||
T(Illegal, "ILLEGAL", 0) \
|
T(Illegal, "ILLEGAL", 0) \
|
||||||
\
|
\
|
||||||
@ -290,7 +290,7 @@ public:
|
|||||||
static bool isVisibilitySpecifier(Value op) { return isVariableVisibilitySpecifier(op) || op == External; }
|
static bool isVisibilitySpecifier(Value op) { return isVariableVisibilitySpecifier(op) || op == External; }
|
||||||
static bool isVariableVisibilitySpecifier(Value op) { return op == Public || op == Private || op == Internal; }
|
static bool isVariableVisibilitySpecifier(Value op) { return op == Public || op == Private || op == Internal; }
|
||||||
static bool isLocationSpecifier(Value op) { return op == Memory || op == Storage; }
|
static bool isLocationSpecifier(Value op) { return op == Memory || op == Storage; }
|
||||||
static bool isStateMutabilitySpecifier(Value op) { return op == Constant || op == Payable; }
|
static bool isStateMutabilitySpecifier(Value op) { return op == Constant || op == View || op == Payable; }
|
||||||
static bool isEtherSubdenomination(Value op) { return op == SubWei || op == SubSzabo || op == SubFinney || op == SubEther; }
|
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 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); }
|
static bool isReservedKeyword(Value op) { return (Abstract <= op && op <= TypeOf); }
|
||||||
|
@ -250,7 +250,63 @@ BOOST_AUTO_TEST_CASE(multiple_methods_order)
|
|||||||
checkInterface(sourceCode, interface);
|
checkInterface(sourceCode, interface);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(const_function)
|
BOOST_AUTO_TEST_CASE(view_function)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract test {
|
||||||
|
function foo(uint a, uint b) returns(uint d) { return a + b; }
|
||||||
|
function boo(uint32 a) view returns(uint b) { return a * 4; }
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
char const* interface = R"([
|
||||||
|
{
|
||||||
|
"name": "foo",
|
||||||
|
"constant": false,
|
||||||
|
"payable" : false,
|
||||||
|
"statemutability": "nonpayable",
|
||||||
|
"type": "function",
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "a",
|
||||||
|
"type": "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "b",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "d",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "boo",
|
||||||
|
"constant": true,
|
||||||
|
"payable" : false,
|
||||||
|
"statemutability": "view",
|
||||||
|
"type": "function",
|
||||||
|
"inputs": [{
|
||||||
|
"name": "a",
|
||||||
|
"type": "uint32"
|
||||||
|
}],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "b",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
])";
|
||||||
|
|
||||||
|
checkInterface(sourceCode, interface);
|
||||||
|
}
|
||||||
|
|
||||||
|
// constant is an alias to view above
|
||||||
|
BOOST_AUTO_TEST_CASE(constant_function)
|
||||||
{
|
{
|
||||||
char const* sourceCode = R"(
|
char const* sourceCode = R"(
|
||||||
contract test {
|
contract test {
|
||||||
|
@ -1172,7 +1172,7 @@ BOOST_AUTO_TEST_CASE(state_variable_accessors)
|
|||||||
BOOST_REQUIRE(function && function->hasDeclaration());
|
BOOST_REQUIRE(function && function->hasDeclaration());
|
||||||
auto returnParams = function->returnParameterTypes();
|
auto returnParams = function->returnParameterTypes();
|
||||||
BOOST_CHECK_EQUAL(returnParams.at(0)->canonicalName(false), "uint256");
|
BOOST_CHECK_EQUAL(returnParams.at(0)->canonicalName(false), "uint256");
|
||||||
BOOST_CHECK(function->isConstant());
|
BOOST_CHECK(function->stateMutability() == StateMutability::View);
|
||||||
|
|
||||||
function = retrieveFunctionBySignature(*contract, "map(uint256)");
|
function = retrieveFunctionBySignature(*contract, "map(uint256)");
|
||||||
BOOST_REQUIRE(function && function->hasDeclaration());
|
BOOST_REQUIRE(function && function->hasDeclaration());
|
||||||
@ -1180,7 +1180,7 @@ BOOST_AUTO_TEST_CASE(state_variable_accessors)
|
|||||||
BOOST_CHECK_EQUAL(params.at(0)->canonicalName(false), "uint256");
|
BOOST_CHECK_EQUAL(params.at(0)->canonicalName(false), "uint256");
|
||||||
returnParams = function->returnParameterTypes();
|
returnParams = function->returnParameterTypes();
|
||||||
BOOST_CHECK_EQUAL(returnParams.at(0)->canonicalName(false), "bytes4");
|
BOOST_CHECK_EQUAL(returnParams.at(0)->canonicalName(false), "bytes4");
|
||||||
BOOST_CHECK(function->isConstant());
|
BOOST_CHECK(function->stateMutability() == StateMutability::View);
|
||||||
|
|
||||||
function = retrieveFunctionBySignature(*contract, "multiple_map(uint256,uint256)");
|
function = retrieveFunctionBySignature(*contract, "multiple_map(uint256,uint256)");
|
||||||
BOOST_REQUIRE(function && function->hasDeclaration());
|
BOOST_REQUIRE(function && function->hasDeclaration());
|
||||||
@ -1189,7 +1189,7 @@ BOOST_AUTO_TEST_CASE(state_variable_accessors)
|
|||||||
BOOST_CHECK_EQUAL(params.at(1)->canonicalName(false), "uint256");
|
BOOST_CHECK_EQUAL(params.at(1)->canonicalName(false), "uint256");
|
||||||
returnParams = function->returnParameterTypes();
|
returnParams = function->returnParameterTypes();
|
||||||
BOOST_CHECK_EQUAL(returnParams.at(0)->canonicalName(false), "bytes4");
|
BOOST_CHECK_EQUAL(returnParams.at(0)->canonicalName(false), "bytes4");
|
||||||
BOOST_CHECK(function->isConstant());
|
BOOST_CHECK(function->stateMutability() == StateMutability::View);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(function_clash_with_state_variable_accessor)
|
BOOST_AUTO_TEST_CASE(function_clash_with_state_variable_accessor)
|
||||||
|
@ -918,6 +918,11 @@ BOOST_AUTO_TEST_CASE(multiple_statemutability_specifiers)
|
|||||||
function f() constant constant {}
|
function f() constant constant {}
|
||||||
})";
|
})";
|
||||||
CHECK_PARSE_ERROR(text, "State mutability already specified as \"view\".");
|
CHECK_PARSE_ERROR(text, "State mutability already specified as \"view\".");
|
||||||
|
text = R"(
|
||||||
|
contract c {
|
||||||
|
function f() constant view {}
|
||||||
|
})";
|
||||||
|
CHECK_PARSE_ERROR(text, "State mutability already specified as \"view\".");
|
||||||
text = R"(
|
text = R"(
|
||||||
contract c {
|
contract c {
|
||||||
function f() payable constant {}
|
function f() payable constant {}
|
||||||
|
Loading…
Reference in New Issue
Block a user