mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #12016 from ethereum/external-fp-10358
Add ``.address`` and ``.selector`` in inside assembly for external function pointers
This commit is contained in:
commit
ecfcca1a27
@ -1,6 +1,7 @@
|
|||||||
### 0.8.10 (unreleased)
|
### 0.8.10 (unreleased)
|
||||||
|
|
||||||
Language Features:
|
Language Features:
|
||||||
|
* Inline Assembly: Support ``.address`` and ``.selector`` on external function pointers to access their address and function selector.
|
||||||
|
|
||||||
|
|
||||||
Compiler Features:
|
Compiler Features:
|
||||||
|
@ -136,6 +136,27 @@ evaluate to the address of the variable in calldata, not the value itself.
|
|||||||
The variable can also be assigned a new offset, but note that no validation to ensure that
|
The variable can also be assigned a new offset, but note that no validation to ensure that
|
||||||
the variable will not point beyond ``calldatasize()`` is performed.
|
the variable will not point beyond ``calldatasize()`` is performed.
|
||||||
|
|
||||||
|
For external function pointers the address and the function selector can be
|
||||||
|
accessed using ``x.address`` and ``x.selector``.
|
||||||
|
The selector consists of four right-aligned bytes.
|
||||||
|
Both values are can be assigned to. For example:
|
||||||
|
|
||||||
|
.. code-block:: solidity
|
||||||
|
:force:
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
|
pragma solidity >=0.8.10 <0.9.0;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
// Assigns a new selector and address to the return variable @fun
|
||||||
|
function combineToFunctionPointer(address newAddress, uint newSelector) public pure returns (function() external fun) {
|
||||||
|
assembly {
|
||||||
|
fun.selector := newSelector
|
||||||
|
fun.address := newAddress
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
For dynamic calldata arrays, you can access
|
For dynamic calldata arrays, you can access
|
||||||
their calldata offset (in bytes) and length (number of elements) using ``x.offset`` and ``x.length``.
|
their calldata offset (in bytes) and length (number of elements) using ``x.offset`` and ``x.length``.
|
||||||
Both expressions can also be assigned to, but as for the static case, no validation will be performed
|
Both expressions can also be assigned to, but as for the static case, no validation will be performed
|
||||||
|
@ -219,13 +219,15 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier)
|
|||||||
{
|
{
|
||||||
solAssert(nativeLocationOf(_identifier) == originLocationOf(_identifier), "");
|
solAssert(nativeLocationOf(_identifier) == originLocationOf(_identifier), "");
|
||||||
|
|
||||||
static set<string> suffixes{"slot", "offset", "length"};
|
static set<string> suffixes{"slot", "offset", "length", "address", "selector"};
|
||||||
string suffix;
|
string suffix;
|
||||||
for (string const& s: suffixes)
|
for (string const& s: suffixes)
|
||||||
if (boost::algorithm::ends_with(_identifier.name.str(), "." + s))
|
if (boost::algorithm::ends_with(_identifier.name.str(), "." + s))
|
||||||
suffix = s;
|
suffix = s;
|
||||||
|
|
||||||
// Could also use `pathFromCurrentScope`, split by '.'
|
// Could also use `pathFromCurrentScope`, split by '.'.
|
||||||
|
// If we do that, suffix should only be set for when it has a special
|
||||||
|
// meaning, not for normal identifierPaths.
|
||||||
auto declarations = m_resolver.nameFromCurrentScope(_identifier.name.str());
|
auto declarations = m_resolver.nameFromCurrentScope(_identifier.name.str());
|
||||||
if (!suffix.empty())
|
if (!suffix.empty())
|
||||||
{
|
{
|
||||||
|
@ -828,7 +828,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
|
|||||||
if (!identifierInfo.suffix.empty())
|
if (!identifierInfo.suffix.empty())
|
||||||
{
|
{
|
||||||
string const& suffix = identifierInfo.suffix;
|
string const& suffix = identifierInfo.suffix;
|
||||||
solAssert((set<string>{"offset", "slot", "length"}).count(suffix), "");
|
solAssert((set<string>{"offset", "slot", "length", "selector", "address"}).count(suffix), "");
|
||||||
if (!var->isConstant() && (var->isStateVariable() || var->type()->dataStoredIn(DataLocation::Storage)))
|
if (!var->isConstant() && (var->isStateVariable() || var->type()->dataStoredIn(DataLocation::Storage)))
|
||||||
{
|
{
|
||||||
if (suffix != "slot" && suffix != "offset")
|
if (suffix != "slot" && suffix != "offset")
|
||||||
@ -861,6 +861,19 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (auto const* fpType = dynamic_cast<FunctionTypePointer>(var->type()))
|
||||||
|
{
|
||||||
|
if (suffix != "selector" && suffix != "address")
|
||||||
|
{
|
||||||
|
m_errorReporter.typeError(9272_error, nativeLocationOf(_identifier), "Variables of type function pointer only support \".selector\" and \".address\".");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (fpType->kind() != FunctionType::Kind::External)
|
||||||
|
{
|
||||||
|
m_errorReporter.typeError(8533_error, nativeLocationOf(_identifier), "Only Variables of type external function pointer support \".selector\" and \".address\".");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_errorReporter.typeError(3622_error, nativeLocationOf(_identifier), "The suffix \"." + suffix + "\" is not supported by this variable or type.");
|
m_errorReporter.typeError(3622_error, nativeLocationOf(_identifier), "The suffix \"." + suffix + "\" is not supported by this variable or type.");
|
||||||
|
@ -211,7 +211,7 @@ struct InlineAssemblyAnnotation: StatementAnnotation
|
|||||||
struct ExternalIdentifierInfo
|
struct ExternalIdentifierInfo
|
||||||
{
|
{
|
||||||
Declaration const* declaration = nullptr;
|
Declaration const* declaration = nullptr;
|
||||||
/// Suffix used, one of "slot", "offset", "length" or empty.
|
/// Suffix used, one of "slot", "offset", "length", "address", "selector" or empty.
|
||||||
std::string suffix;
|
std::string suffix;
|
||||||
size_t valueSize = size_t(-1);
|
size_t valueSize = size_t(-1);
|
||||||
};
|
};
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
#include <libsolutil/JSON.h>
|
#include <libsolutil/JSON.h>
|
||||||
#include <libsolutil/UTF8.h>
|
#include <libsolutil/UTF8.h>
|
||||||
|
#include <libsolutil/CommonData.h>
|
||||||
|
|
||||||
#include <boost/algorithm/string/join.hpp>
|
#include <boost/algorithm/string/join.hpp>
|
||||||
|
|
||||||
@ -178,9 +179,12 @@ Json::Value ASTJsonConverter::inlineAssemblyIdentifierToJson(pair<yul::Identifie
|
|||||||
tuple["declaration"] = idOrNull(_info.second.declaration);
|
tuple["declaration"] = idOrNull(_info.second.declaration);
|
||||||
tuple["isSlot"] = Json::Value(_info.second.suffix == "slot");
|
tuple["isSlot"] = Json::Value(_info.second.suffix == "slot");
|
||||||
tuple["isOffset"] = Json::Value(_info.second.suffix == "offset");
|
tuple["isOffset"] = Json::Value(_info.second.suffix == "offset");
|
||||||
|
|
||||||
if (!_info.second.suffix.empty())
|
if (!_info.second.suffix.empty())
|
||||||
tuple["suffix"] = Json::Value(_info.second.suffix);
|
tuple["suffix"] = Json::Value(_info.second.suffix);
|
||||||
|
|
||||||
tuple["valueSize"] = Json::Value(Json::LargestUInt(_info.second.valueSize));
|
tuple["valueSize"] = Json::Value(Json::LargestUInt(_info.second.valueSize));
|
||||||
|
|
||||||
return tuple;
|
return tuple;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -823,6 +823,16 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
|
|||||||
if (suffix == "length")
|
if (suffix == "length")
|
||||||
stackDiff--;
|
stackDiff--;
|
||||||
}
|
}
|
||||||
|
else if (
|
||||||
|
auto const* functionType = dynamic_cast<FunctionType const*>(variable->type());
|
||||||
|
functionType && functionType->kind() == FunctionType::Kind::External
|
||||||
|
)
|
||||||
|
{
|
||||||
|
solAssert(suffix == "selector" || suffix == "address", "");
|
||||||
|
solAssert(variable->type()->sizeOnStack() == 2, "");
|
||||||
|
if (suffix == "selector")
|
||||||
|
stackDiff--;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
solAssert(false, "");
|
solAssert(false, "");
|
||||||
}
|
}
|
||||||
@ -889,6 +899,16 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
|
|||||||
solAssert(suffix.empty(), "");
|
solAssert(suffix.empty(), "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (
|
||||||
|
auto const* functionType = dynamic_cast<FunctionType const*>(variable->type());
|
||||||
|
functionType && functionType->kind() == FunctionType::Kind::External
|
||||||
|
)
|
||||||
|
{
|
||||||
|
solAssert(suffix == "selector" || suffix == "address", "");
|
||||||
|
solAssert(variable->type()->sizeOnStack() == 2, "");
|
||||||
|
if (suffix == "selector")
|
||||||
|
stackDiff--;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
solAssert(suffix.empty(), "");
|
solAssert(suffix.empty(), "");
|
||||||
|
|
||||||
|
@ -188,6 +188,18 @@ private:
|
|||||||
solAssert(suffix == "offset" || suffix == "length", "");
|
solAssert(suffix == "offset" || suffix == "length", "");
|
||||||
value = IRVariable{*varDecl}.part(suffix).name();
|
value = IRVariable{*varDecl}.part(suffix).name();
|
||||||
}
|
}
|
||||||
|
else if (
|
||||||
|
auto const* functionType = dynamic_cast<FunctionType const*>(varDecl->type());
|
||||||
|
functionType && functionType->kind() == FunctionType::Kind::External
|
||||||
|
)
|
||||||
|
{
|
||||||
|
solAssert(suffix == "selector" || suffix == "address", "");
|
||||||
|
solAssert(varDecl->type()->sizeOnStack() == 2, "");
|
||||||
|
if (suffix == "selector")
|
||||||
|
value = IRVariable{*varDecl}.part("functionSelector").name();
|
||||||
|
else
|
||||||
|
value = IRVariable{*varDecl}.part("address").name();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
solAssert(false, "");
|
solAssert(false, "");
|
||||||
|
|
||||||
|
@ -124,7 +124,9 @@ done < <(
|
|||||||
# Skipping license error, unrelated to the grammar
|
# Skipping license error, unrelated to the grammar
|
||||||
grep -v -E 'license/license_double5.sol' |
|
grep -v -E 'license/license_double5.sol' |
|
||||||
grep -v -E 'license/license_hidden_unicode.sol' |
|
grep -v -E 'license/license_hidden_unicode.sol' |
|
||||||
grep -v -E 'license/license_unicode.sol'
|
grep -v -E 'license/license_unicode.sol' |
|
||||||
|
# Skipping tests with 'something.address' as 'address' as the grammar fails on those
|
||||||
|
grep -v -E 'inlineAssembly/external_function_pointer_address.*.sol'
|
||||||
)
|
)
|
||||||
|
|
||||||
YUL_FILES=()
|
YUL_FILES=()
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
contract C {
|
||||||
|
function testFunction() external {}
|
||||||
|
|
||||||
|
function testYul() public returns (address adr) {
|
||||||
|
function() external fp = this.testFunction;
|
||||||
|
|
||||||
|
assembly {
|
||||||
|
adr := fp.address
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function testSol() public returns (address) {
|
||||||
|
return this.testFunction.address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// testYul() -> 0x0fdd67305928fcac8d213d1e47bfa6165cd0b87b
|
||||||
|
// testSol() -> 0x0fdd67305928fcac8d213d1e47bfa6165cd0b87b
|
@ -0,0 +1,18 @@
|
|||||||
|
contract C {
|
||||||
|
function testFunction() external {}
|
||||||
|
|
||||||
|
function testYul(address newAddress) view public returns (address adr) {
|
||||||
|
function() external fp = this.testFunction;
|
||||||
|
|
||||||
|
assembly {
|
||||||
|
fp.address := newAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
return fp.address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// testYul(address): 0x1234567890 -> 0x1234567890
|
||||||
|
// testYul(address): 0xC0FFEE3EA7 -> 0xC0FFEE3EA7
|
@ -0,0 +1,23 @@
|
|||||||
|
contract C {
|
||||||
|
function testFunction() external {}
|
||||||
|
|
||||||
|
function testYul() public returns (uint32) {
|
||||||
|
function() external fp = this.testFunction;
|
||||||
|
uint selectorValue = 0;
|
||||||
|
|
||||||
|
assembly {
|
||||||
|
selectorValue := fp.selector
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value is right-aligned, we shift it so it can be compared
|
||||||
|
return uint32(bytes4(bytes32(selectorValue << (256 - 32))));
|
||||||
|
}
|
||||||
|
function testSol() public returns (uint32) {
|
||||||
|
return uint32(this.testFunction.selector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// testYul() -> 0xe16b4a9b
|
||||||
|
// testSol() -> 0xe16b4a9b
|
@ -0,0 +1,18 @@
|
|||||||
|
contract C {
|
||||||
|
function testFunction() external {}
|
||||||
|
|
||||||
|
function testYul(uint32 newSelector) view public returns (uint32) {
|
||||||
|
function() external fp = this.testFunction;
|
||||||
|
|
||||||
|
assembly {
|
||||||
|
fp.selector := newSelector
|
||||||
|
}
|
||||||
|
|
||||||
|
return uint32(fp.selector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// testYul(uint32): 0x12345678 -> 0x12345678
|
||||||
|
// testYul(uint32): 0xABCDEF00 -> 0xABCDEF00
|
@ -0,0 +1,15 @@
|
|||||||
|
contract C {
|
||||||
|
function testFunction() external {}
|
||||||
|
|
||||||
|
function testYul() public {
|
||||||
|
function() external fp = this.testFunction;
|
||||||
|
|
||||||
|
uint myOffset;
|
||||||
|
|
||||||
|
assembly {
|
||||||
|
myOffset := fp.offset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError 9272: (173-182): Variables of type function pointer only support ".selector" and ".address".
|
@ -0,0 +1,18 @@
|
|||||||
|
contract C {
|
||||||
|
function testFunction() internal {}
|
||||||
|
|
||||||
|
function testYul() public returns (address adr) {
|
||||||
|
function() internal fp = testFunction;
|
||||||
|
uint selectorValue = 0;
|
||||||
|
|
||||||
|
assembly {
|
||||||
|
adr := fp.address
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function testSol() public returns (address) {
|
||||||
|
return testFunction.address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError 8533: (193-203): Only Variables of type external function pointer support ".selector" and ".address".
|
||||||
|
// TypeError 9582: (267-287): Member "address" not found or not visible after argument-dependent lookup in function ().
|
@ -0,0 +1,20 @@
|
|||||||
|
contract C {
|
||||||
|
function testFunction() internal {}
|
||||||
|
|
||||||
|
function testYul() public returns (uint32) {
|
||||||
|
function() internal fp = testFunction;
|
||||||
|
uint selectorValue = 0;
|
||||||
|
|
||||||
|
assembly {
|
||||||
|
selectorValue := fp.selector
|
||||||
|
}
|
||||||
|
|
||||||
|
return uint32(bytes4(bytes32(selectorValue)));
|
||||||
|
}
|
||||||
|
function testSol() public returns (uint32) {
|
||||||
|
return uint32(testFunction.selector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError 8533: (198-209): Only Variables of type external function pointer support ".selector" and ".address".
|
||||||
|
// TypeError 9582: (329-350): Member "selector" not found or not visible after argument-dependent lookup in function ().
|
Loading…
Reference in New Issue
Block a user