diff options
-rw-r--r-- | Changelog.md | 1 | ||||
-rw-r--r-- | docs/abi-spec.rst | 2 | ||||
-rw-r--r-- | docs/types.rst | 11 | ||||
-rw-r--r-- | libsolidity/ast/Types.cpp | 5 | ||||
-rw-r--r-- | libsolidity/codegen/ExpressionCompiler.cpp | 9 | ||||
-rw-r--r-- | test/libsolidity/SolidityEndToEndTest.cpp | 24 | ||||
-rw-r--r-- | test/libsolidity/SolidityNameAndTypeResolution.cpp | 81 |
7 files changed, 132 insertions, 1 deletions
diff --git a/Changelog.md b/Changelog.md index 62e4eb09..d5f27e53 100644 --- a/Changelog.md +++ b/Changelog.md @@ -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. diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index fc1a3adb..3ce7f50c 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -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 ================= diff --git a/docs/types.rst b/docs/types.rst index 3335655a..5c291f35 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -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; diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 10424858..705d0f7f 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -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()) diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 5c9b743a..631a25ff 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1068,7 +1068,14 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) solAssert(false, "Invalid member access to integer"); break; case Type::Category::Function: - solAssert(!!_memberAccess.expression().annotation().type->memberType(member), + 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; case Type::Category::Magic: diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index fa4d675c..ea924fcb 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -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() } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 04d3d2d3..d3c9802b 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -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"( |