aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Changelog.md1
-rw-r--r--docs/abi-spec.rst2
-rw-r--r--docs/types.rst11
-rw-r--r--libsolidity/ast/Types.cpp5
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp9
-rw-r--r--test/libsolidity/SolidityEndToEndTest.cpp24
-rw-r--r--test/libsolidity/SolidityNameAndTypeResolution.cpp81
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"(