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)
Features:
* Code Generator: Added ``.selector`` member on external function types to retrieve their signature.
* Optimizer: Add new optimization step to remove unused ``JUMPDEST``s.
* Type Checker: Display helpful warning for unused function arguments/return parameters.
* 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.
.. _abi_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,
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::
pragma solidity ^0.4.5;

View File

@ -2436,6 +2436,11 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
case Kind::BareDelegateCall:
{
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 (isPayable())

View File

@ -1068,6 +1068,13 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
solAssert(false, "Invalid member access to integer");
break;
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),
"Invalid member access to function.");
break;

View File

@ -10051,6 +10051,30 @@ BOOST_AUTO_TEST_CASE(delegatecall_return_value)
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()
}

View File

@ -6285,6 +6285,87 @@ BOOST_AUTO_TEST_CASE(modifiers_access_storage_pointer)
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)
{
char const* text = R"(