Merge pull request #2473 from ethereum/functiontype-sig

Add .selector member on function types
This commit is contained in:
Alex Beregszaszi 2017-09-13 17:35:48 +01:00 committed by GitHub
commit 5c9dbd5083
7 changed files with 132 additions and 1 deletions

View File

@ -1,6 +1,7 @@
### 0.4.17 (unreleased) ### 0.4.17 (unreleased)
Features: Features:
* Code Generator: Added ``.selector`` member on external function types to retrieve their signature.
* Optimizer: Add new optimization step to remove unused ``JUMPDEST``s. * Optimizer: Add new optimization step to remove unused ``JUMPDEST``s.
* Type Checker: Display helpful warning for unused function arguments/return parameters. * Type Checker: Display helpful warning for unused function arguments/return parameters.
* Type Checker: Do not show the same error multiple times for events. * Type Checker: Do not show the same error multiple times for events.

View File

@ -17,6 +17,8 @@ We assume the interface functions of a contract are strongly typed, known at com
This specification does not address contracts whose interface is dynamic or otherwise known only at run-time. Should these cases become important they can be adequately handled as facilities built within the Ethereum ecosystem. This specification does not address contracts whose interface is dynamic or otherwise known only at run-time. Should these cases become important they can be adequately handled as facilities built within the Ethereum ecosystem.
.. _abi_function_selector:
Function Selector Function Selector
================= =================

View File

@ -400,6 +400,17 @@ Note that public functions of the current contract can be used both as an
internal and as an external function. To use ``f`` as an internal function, internal and as an external function. To use ``f`` as an internal function,
just use ``f``, if you want to use its external form, use ``this.f``. just use ``f``, if you want to use its external form, use ``this.f``.
Additionally, public (or external) functions also have a special member called ``selector``,
which returns the :ref:`ABI function selector <abi_function_selector>`::
pragma solidity ^0.4.0;
contract Selector {
function f() returns (bytes4) {
return this.f.selector;
}
}
Example that shows how to use internal function types:: Example that shows how to use internal function types::
pragma solidity ^0.4.5; pragma solidity ^0.4.5;

View File

@ -2436,6 +2436,11 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
case Kind::BareDelegateCall: case Kind::BareDelegateCall:
{ {
MemberList::MemberMap members; MemberList::MemberMap members;
if (m_kind == Kind::External)
members.push_back(MemberList::Member(
"selector",
make_shared<FixedBytesType>(4)
));
if (m_kind != Kind::BareDelegateCall && m_kind != Kind::DelegateCall) if (m_kind != Kind::BareDelegateCall && m_kind != Kind::DelegateCall)
{ {
if (isPayable()) if (isPayable())

View File

@ -1068,6 +1068,13 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
solAssert(false, "Invalid member access to integer"); solAssert(false, "Invalid member access to integer");
break; break;
case Type::Category::Function: case Type::Category::Function:
if (member == "selector")
{
m_context << Instruction::SWAP1 << Instruction::POP;
/// need to store store it as bytes4
utils().leftShiftNumberOnStack(224);
}
else
solAssert(!!_memberAccess.expression().annotation().type->memberType(member), solAssert(!!_memberAccess.expression().annotation().type->memberType(member),
"Invalid member access to function."); "Invalid member access to function.");
break; break;

View File

@ -10051,6 +10051,30 @@ BOOST_AUTO_TEST_CASE(delegatecall_return_value)
BOOST_CHECK(callContractFunction("get_delegated()") == encodeArgs(u256(1))); BOOST_CHECK(callContractFunction("get_delegated()") == encodeArgs(u256(1)));
} }
BOOST_AUTO_TEST_CASE(function_types_sig)
{
char const* sourceCode = R"(
contract C {
function f() returns (bytes4) {
return this.f.selector;
}
function g() returns (bytes4) {
function () external returns (bytes4) fun = this.f;
return fun.selector;
}
function h() returns (bytes4) {
function () external returns (bytes4) fun = this.f;
var funvar = fun;
return funvar.selector;
}
}
)";
compileAndRun(sourceCode, 0, "C");
BOOST_CHECK(callContractFunction("f()") == encodeArgs(asString(FixedHash<4>(dev::keccak256("f()")).asBytes())));
BOOST_CHECK(callContractFunction("g()") == encodeArgs(asString(FixedHash<4>(dev::keccak256("f()")).asBytes())));
BOOST_CHECK(callContractFunction("h()") == encodeArgs(asString(FixedHash<4>(dev::keccak256("f()")).asBytes())));
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

View File

@ -6285,6 +6285,87 @@ BOOST_AUTO_TEST_CASE(modifiers_access_storage_pointer)
CHECK_SUCCESS_NO_WARNINGS(text); CHECK_SUCCESS_NO_WARNINGS(text);
} }
BOOST_AUTO_TEST_CASE(function_types_sig)
{
char const* text = R"(
contract C {
function f() returns (bytes4) {
return f.selector;
}
}
)";
CHECK_ERROR(text, TypeError, "Member \"selector\" not found");
text = R"(
contract C {
function g() internal {
}
function f() returns (bytes4) {
return g.selector;
}
}
)";
CHECK_ERROR(text, TypeError, "Member \"selector\" not found");
text = R"(
contract C {
function f() returns (bytes4) {
function () g;
return g.selector;
}
}
)";
CHECK_ERROR(text, TypeError, "Member \"selector\" not found");
text = R"(
contract C {
function f() returns (bytes4) {
return this.f.selector;
}
}
)";
CHECK_SUCCESS_NO_WARNINGS(text);
text = R"(
contract C {
function f() external returns (bytes4) {
return this.f.selector;
}
}
)";
CHECK_SUCCESS_NO_WARNINGS(text);
text = R"(
contract C {
function h() external {
}
function f() external returns (bytes4) {
var g = this.h;
return g.selector;
}
}
)";
CHECK_SUCCESS_NO_WARNINGS(text);
text = R"(
contract C {
function h() external {
}
function f() external returns (bytes4) {
function () external g = this.h;
return g.selector;
}
}
)";
CHECK_SUCCESS_NO_WARNINGS(text);
text = R"(
contract C {
function h() external {
}
function f() external returns (bytes4) {
function () external g = this.h;
var i = g;
return i.selector;
}
}
)";
CHECK_SUCCESS_NO_WARNINGS(text);
}
BOOST_AUTO_TEST_CASE(using_this_in_constructor) BOOST_AUTO_TEST_CASE(using_this_in_constructor)
{ {
char const* text = R"( char const* text = R"(