mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
abi.encodeCall for declarations.
This commit is contained in:
parent
b28cd00aa0
commit
4c838d9cf5
@ -1,5 +1,9 @@
|
|||||||
### 0.8.12 (unreleased)
|
### 0.8.12 (unreleased)
|
||||||
|
|
||||||
|
Language Features:
|
||||||
|
* General: Support ``ContractName.functionName`` for ``abi.encodeCall``, in addition to external function pointers.
|
||||||
|
|
||||||
|
|
||||||
Compiler Features:
|
Compiler Features:
|
||||||
|
|
||||||
|
|
||||||
|
@ -2122,16 +2122,35 @@ void TypeChecker::typeCheckABIEncodeCallFunction(FunctionCall const& _functionCa
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (functionPointerType->kind() != FunctionType::Kind::External)
|
if (
|
||||||
|
functionPointerType->kind() != FunctionType::Kind::External &&
|
||||||
|
functionPointerType->kind() != FunctionType::Kind::Declaration
|
||||||
|
)
|
||||||
{
|
{
|
||||||
string msg = "Function must be \"public\" or \"external\".";
|
string msg = "Expected regular external function type, or external view on public function.";
|
||||||
|
if (functionPointerType->kind() == FunctionType::Kind::Internal)
|
||||||
|
msg += " Provided internal function.";
|
||||||
|
else if (functionPointerType->kind() == FunctionType::Kind::DelegateCall)
|
||||||
|
msg += " Cannot use library functions for abi.encodeCall.";
|
||||||
|
else if (functionPointerType->kind() == FunctionType::Kind::Creation)
|
||||||
|
msg += " Provided creation function.";
|
||||||
|
else
|
||||||
|
msg += " Cannot use special function.";
|
||||||
SecondarySourceLocation ssl{};
|
SecondarySourceLocation ssl{};
|
||||||
|
|
||||||
if (functionPointerType->hasDeclaration())
|
if (functionPointerType->hasDeclaration())
|
||||||
{
|
{
|
||||||
ssl.append("Function is declared here:", functionPointerType->declaration().location());
|
ssl.append("Function is declared here:", functionPointerType->declaration().location());
|
||||||
if (functionPointerType->declaration().scope() == m_currentContract)
|
if (
|
||||||
|
functionPointerType->declaration().visibility() == Visibility::Public &&
|
||||||
|
functionPointerType->declaration().scope() == m_currentContract
|
||||||
|
)
|
||||||
msg += " Did you forget to prefix \"this.\"?";
|
msg += " Did you forget to prefix \"this.\"?";
|
||||||
|
else if (contains(
|
||||||
|
m_currentContract->annotation().linearizedBaseContracts,
|
||||||
|
functionPointerType->declaration().scope()
|
||||||
|
) && functionPointerType->declaration().scope() != m_currentContract)
|
||||||
|
msg += " Functions from base contracts have to be external.";
|
||||||
}
|
}
|
||||||
|
|
||||||
m_errorReporter.typeError(3509_error, arguments[0]->location(), ssl, msg);
|
m_errorReporter.typeError(3509_error, arguments[0]->location(), ssl, msg);
|
||||||
|
@ -1255,7 +1255,6 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
|
|
||||||
auto const functionPtr = dynamic_cast<FunctionTypePointer>(arguments[0]->annotation().type);
|
auto const functionPtr = dynamic_cast<FunctionTypePointer>(arguments[0]->annotation().type);
|
||||||
solAssert(functionPtr);
|
solAssert(functionPtr);
|
||||||
solAssert(functionPtr->sizeOnStack() == 2);
|
|
||||||
|
|
||||||
// Account for tuples with one component which become that component
|
// Account for tuples with one component which become that component
|
||||||
if (auto const tupleType = dynamic_cast<TupleType const*>(arguments[1]->annotation().type))
|
if (auto const tupleType = dynamic_cast<TupleType const*>(arguments[1]->annotation().type))
|
||||||
@ -1330,9 +1329,20 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
}
|
}
|
||||||
else if (function.kind() == FunctionType::Kind::ABIEncodeCall)
|
else if (function.kind() == FunctionType::Kind::ABIEncodeCall)
|
||||||
{
|
{
|
||||||
// stack: <memory pointer> <functionPointer>
|
auto const& funType = dynamic_cast<FunctionType const&>(*selectorType);
|
||||||
// Extract selector from the stack
|
if (funType.kind() == FunctionType::Kind::Declaration)
|
||||||
m_context << Instruction::SWAP1 << Instruction::POP;
|
{
|
||||||
|
solAssert(funType.hasDeclaration());
|
||||||
|
solAssert(selectorType->sizeOnStack() == 0);
|
||||||
|
m_context << funType.externalIdentifier();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
solAssert(selectorType->sizeOnStack() == 2);
|
||||||
|
// stack: <memory pointer> <functionPointer>
|
||||||
|
// Extract selector from the stack
|
||||||
|
m_context << Instruction::SWAP1 << Instruction::POP;
|
||||||
|
}
|
||||||
// Conversion will be done below
|
// Conversion will be done below
|
||||||
dataOnStack = TypeProvider::uint(32);
|
dataOnStack = TypeProvider::uint(32);
|
||||||
}
|
}
|
||||||
|
@ -1150,10 +1150,21 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (functionType->kind() == FunctionType::Kind::ABIEncodeCall)
|
if (functionType->kind() == FunctionType::Kind::ABIEncodeCall)
|
||||||
selector = convert(
|
{
|
||||||
IRVariable(*arguments[0]).part("functionSelector"),
|
auto const& selectorType = dynamic_cast<FunctionType const&>(type(*arguments.front()));
|
||||||
*TypeProvider::fixedBytes(4)
|
if (selectorType.kind() == FunctionType::Kind::Declaration)
|
||||||
).name();
|
{
|
||||||
|
solAssert(selectorType.hasDeclaration());
|
||||||
|
selector = formatNumber(selectorType.externalIdentifier() << (256 - 32));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
selector = convert(
|
||||||
|
IRVariable(*arguments[0]).part("functionSelector"),
|
||||||
|
*TypeProvider::fixedBytes(4)
|
||||||
|
).name();
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (functionType->kind() == FunctionType::Kind::ABIEncodeWithSignature)
|
else if (functionType->kind() == FunctionType::Kind::ABIEncodeWithSignature)
|
||||||
{
|
{
|
||||||
// hash the signature
|
// hash the signature
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
pragma abicoder v2;
|
||||||
|
|
||||||
|
contract X {
|
||||||
|
// no "returns" on purpose
|
||||||
|
function a(uint) public pure {}
|
||||||
|
function b(uint) external pure {}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract Base {
|
||||||
|
function a(uint x) external pure returns (uint) { return x + 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
contract C is Base {
|
||||||
|
function test() public view returns (uint r) {
|
||||||
|
bool success;
|
||||||
|
bytes memory result;
|
||||||
|
(success, result) = address(this).staticcall(abi.encodeCall(X.a, 1));
|
||||||
|
require(success && result.length == 32);
|
||||||
|
r += abi.decode(result, (uint));
|
||||||
|
require(r == 2);
|
||||||
|
|
||||||
|
(success, result) = address(this).staticcall(abi.encodeCall(X.b, 10));
|
||||||
|
require(success && result.length == 32);
|
||||||
|
r += abi.decode(result, (uint));
|
||||||
|
require(r == 13);
|
||||||
|
|
||||||
|
(success, result) = address(this).staticcall(abi.encodeCall(Base.a, 100));
|
||||||
|
require(success && result.length == 32);
|
||||||
|
r += abi.decode(result, (uint));
|
||||||
|
require(r == 114);
|
||||||
|
|
||||||
|
(success, result) = address(this).staticcall(abi.encodeCall(this.a, 1000));
|
||||||
|
require(success && result.length == 32);
|
||||||
|
r += abi.decode(result, (uint));
|
||||||
|
require(r == 1115);
|
||||||
|
|
||||||
|
(success, result) = address(this).staticcall(abi.encodeCall(C.b, 10000));
|
||||||
|
require(success && result.length == 32);
|
||||||
|
r += abi.decode(result, (uint));
|
||||||
|
require(r == 11116);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
function b(uint x) external view returns (uint) {
|
||||||
|
return this.a(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// EVMVersion: >=byzantium
|
||||||
|
// ----
|
||||||
|
// test() -> 11116
|
@ -2,81 +2,47 @@ interface I {
|
|||||||
function fExternal(uint256 p, string memory t) external;
|
function fExternal(uint256 p, string memory t) external;
|
||||||
}
|
}
|
||||||
|
|
||||||
library L {
|
contract Other {
|
||||||
function fExternal(uint256 p, string memory t) external {}
|
function fExternal(uint) external pure {}
|
||||||
|
function fPublic(uint) public pure {}
|
||||||
|
function fInternal(uint) internal pure {}
|
||||||
}
|
}
|
||||||
|
|
||||||
contract C {
|
library L {
|
||||||
using L for uint256;
|
function fExternal(uint256 p, string memory t) external {}
|
||||||
|
function fInternal(uint256 p, string memory t) internal {}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract Base {
|
||||||
|
function baseFunctionExternal(uint) external pure {}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract C is Base {
|
||||||
function f(int a) public {}
|
function f(int a) public {}
|
||||||
function f2(int a, string memory b) public {}
|
function f2(int a, string memory b) public {}
|
||||||
function f3(int a, int b) public {}
|
|
||||||
function f4() public {}
|
function f4() public {}
|
||||||
function fInternal(uint256 p, string memory t) internal {}
|
|
||||||
|
|
||||||
function failFunctionArgsWrongType() public returns(bytes memory) {
|
function successFunctionArgsIntLiteralTuple() public view returns(bytes memory) {
|
||||||
return abi.encodeCall(this.f, ("test"));
|
|
||||||
}
|
|
||||||
function failFunctionArgsTooMany() public returns(bytes memory) {
|
|
||||||
return abi.encodeCall(this.f, (1, 2));
|
|
||||||
}
|
|
||||||
function failFunctionArgsTooFew0() public returns(bytes memory) {
|
|
||||||
return abi.encodeCall(this.f, ());
|
|
||||||
}
|
|
||||||
function failFunctionArgsTooFew1() public returns(bytes memory) {
|
|
||||||
return abi.encodeCall(this.f);
|
|
||||||
}
|
|
||||||
function failFunctionPtrMissing() public returns(bytes memory) {
|
|
||||||
return abi.encodeCall(1, this.f);
|
|
||||||
}
|
|
||||||
function failFunctionPtrWrongType() public returns(bytes memory) {
|
|
||||||
return abi.encodeCall(abi.encodeCall, (1, 2, 3, "test"));
|
|
||||||
}
|
|
||||||
function failFunctionInternal() public returns(bytes memory) {
|
|
||||||
return abi.encodeCall(fInternal, (1, "123"));
|
|
||||||
}
|
|
||||||
function failFunctionInternalFromVariable() public returns(bytes memory) {
|
|
||||||
function(uint256, string memory) internal localFunctionPointer = fInternal;
|
|
||||||
return abi.encodeCall(localFunctionPointer, (1, "123"));
|
|
||||||
}
|
|
||||||
function failFunctionArgsArrayLiteral() public returns(bytes memory) {
|
|
||||||
return abi.encodeCall(this.f3, [1, 2]);
|
|
||||||
}
|
|
||||||
function failLibraryPointerCall() public returns (bytes memory) {
|
|
||||||
return abi.encodeCall(L.fExternal, (1, "123"));
|
|
||||||
}
|
|
||||||
function failBoundLibraryPointerCall() public returns (bytes memory) {
|
|
||||||
uint256 x = 1;
|
|
||||||
return abi.encodeCall(x.fExternal, (1, "123"));
|
|
||||||
}
|
|
||||||
function failInterfacePointerCall() public returns (bytes memory) {
|
|
||||||
return abi.encodeCall(I.fExternal, (1, "123"));
|
|
||||||
}
|
|
||||||
function successFunctionArgsIntLiteralTuple() public returns(bytes memory) {
|
|
||||||
return abi.encodeCall(this.f, (1));
|
return abi.encodeCall(this.f, (1));
|
||||||
}
|
}
|
||||||
function successFunctionArgsIntLiteral() public returns(bytes memory) {
|
function successFunctionArgsIntLiteral() public view returns(bytes memory) {
|
||||||
return abi.encodeCall(this.f, 1);
|
return abi.encodeCall(this.f, 1);
|
||||||
}
|
}
|
||||||
function successFunctionArgsLiteralTuple() public returns(bytes memory) {
|
function successFunctionArgsLiteralTuple() public view returns(bytes memory) {
|
||||||
return abi.encodeCall(this.f2, (1, "test"));
|
return abi.encodeCall(this.f2, (1, "test"));
|
||||||
}
|
}
|
||||||
function successFunctionArgsEmptyTuple() public returns(bytes memory) {
|
function successFunctionArgsEmptyTuple() public view returns(bytes memory) {
|
||||||
return abi.encodeCall(this.f4, ());
|
return abi.encodeCall(this.f4, ());
|
||||||
}
|
}
|
||||||
|
function viaDeclaration() public pure returns (bytes memory) {
|
||||||
|
return bytes.concat(
|
||||||
|
abi.encodeCall(Other.fExternal, (1)),
|
||||||
|
abi.encodeCall(Other.fPublic, (1)),
|
||||||
|
abi.encodeCall(I.fExternal, (1, "123"))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
function viaBaseDeclaration() public pure returns (bytes memory) {
|
||||||
|
return abi.encodeCall(Base.baseFunctionExternal, (1));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// TypeError 5407: (486-494): Cannot implicitly convert component at position 0 from "literal_string "test"" to "int256".
|
|
||||||
// TypeError 7788: (576-606): Expected 1 instead of 2 components for the tuple parameter.
|
|
||||||
// TypeError 7788: (687-713): Expected 1 instead of 0 components for the tuple parameter.
|
|
||||||
// TypeError 6219: (794-816): Expected two arguments: a function pointer followed by a tuple.
|
|
||||||
// TypeError 5511: (911-912): Expected first argument to be a function pointer, not "int_const 1".
|
|
||||||
// TypeError 3509: (1018-1032): Function must be "public" or "external".
|
|
||||||
// TypeError 3509: (1145-1154): Function must be "public" or "external". Did you forget to prefix "this."?
|
|
||||||
// TypeError 3509: (1350-1370): Function must be "public" or "external".
|
|
||||||
// TypeError 7515: (1469-1500): Expected a tuple with 2 components instead of a single non-tuple parameter.
|
|
||||||
// TypeError 5407: (1493-1499): Cannot implicitly convert component at position 0 from "uint8[2]" to "int256".
|
|
||||||
// TypeError 3509: (1596-1607): Function must be "public" or "external".
|
|
||||||
// TypeError 3509: (1738-1749): Function must be "public" or "external".
|
|
||||||
// TypeError 3509: (1860-1871): Function must be "public" or "external".
|
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
contract C {
|
||||||
|
function f(int a) public {}
|
||||||
|
function f3(int a, int b) public {}
|
||||||
|
|
||||||
|
function failFunctionArgsWrongType() public returns(bytes memory) {
|
||||||
|
return abi.encodeCall(this.f, ("test"));
|
||||||
|
}
|
||||||
|
function failFunctionArgsTooMany() public returns(bytes memory) {
|
||||||
|
return abi.encodeCall(this.f, (1, 2));
|
||||||
|
}
|
||||||
|
function failFunctionArgsTooFew0() public returns(bytes memory) {
|
||||||
|
return abi.encodeCall(this.f, ());
|
||||||
|
}
|
||||||
|
function failFunctionArgsTooFew1() public returns(bytes memory) {
|
||||||
|
return abi.encodeCall(this.f);
|
||||||
|
}
|
||||||
|
function failFunctionArgsArrayLiteral() public returns(bytes memory) {
|
||||||
|
return abi.encodeCall(this.f3, [1, 2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError 5407: (181-189): Cannot implicitly convert component at position 0 from "literal_string "test"" to "int256".
|
||||||
|
// TypeError 7788: (271-301): Expected 1 instead of 2 components for the tuple parameter.
|
||||||
|
// TypeError 7788: (382-408): Expected 1 instead of 0 components for the tuple parameter.
|
||||||
|
// TypeError 6219: (489-511): Expected two arguments: a function pointer followed by a tuple.
|
||||||
|
// TypeError 7515: (597-628): Expected a tuple with 2 components instead of a single non-tuple parameter.
|
||||||
|
// TypeError 5407: (621-627): Cannot implicitly convert component at position 0 from "uint8[2]" to "int256".
|
@ -0,0 +1,78 @@
|
|||||||
|
interface I {
|
||||||
|
function fExternal(uint256 p, string memory t) external;
|
||||||
|
}
|
||||||
|
|
||||||
|
contract Other {
|
||||||
|
function fExternal(uint) external pure {}
|
||||||
|
function fPublic(uint) public pure {}
|
||||||
|
function fInternal(uint) internal pure {}
|
||||||
|
}
|
||||||
|
|
||||||
|
library L {
|
||||||
|
function fExternal(uint256 p, string memory t) external {}
|
||||||
|
function fInternal(uint256 p, string memory t) internal {}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract Base {
|
||||||
|
function baseFunctionInternal(uint) internal pure {}
|
||||||
|
function baseFunctionPublic(uint) public pure {}
|
||||||
|
}
|
||||||
|
|
||||||
|
function fileLevel(uint) pure {}
|
||||||
|
|
||||||
|
contract C is Base {
|
||||||
|
using L for uint256;
|
||||||
|
|
||||||
|
function fPublic(int a) public {}
|
||||||
|
function fInternal(uint256 p, string memory t) internal {}
|
||||||
|
|
||||||
|
function failFunctionPtrMissing() public returns(bytes memory) {
|
||||||
|
return abi.encodeCall(1, this.fPublic);
|
||||||
|
}
|
||||||
|
function failFunctionPtrWrongType() public returns(bytes memory) {
|
||||||
|
return abi.encodeCall(abi.encodeCall, (1, 2, 3, "test"));
|
||||||
|
}
|
||||||
|
function failFunctionInternal() public returns(bytes memory) {
|
||||||
|
return abi.encodeCall(fInternal, (1, "123"));
|
||||||
|
}
|
||||||
|
function failFunctionInternalFromVariable() public returns(bytes memory) {
|
||||||
|
function(uint256, string memory) internal localFunctionPointer = fInternal;
|
||||||
|
return abi.encodeCall(localFunctionPointer, (1, "123"));
|
||||||
|
}
|
||||||
|
function failLibraryPointerCall() public {
|
||||||
|
abi.encodeCall(L.fInternal, (1, "123"));
|
||||||
|
abi.encodeCall(L.fExternal, (1, "123"));
|
||||||
|
}
|
||||||
|
function failBoundLibraryPointerCall() public returns (bytes memory) {
|
||||||
|
uint256 x = 1;
|
||||||
|
return abi.encodeCall(x.fExternal, (1, "123"));
|
||||||
|
}
|
||||||
|
function viaBaseDeclaration() public pure returns (bytes memory) {
|
||||||
|
return abi.encodeCall(C.fPublic, (2));
|
||||||
|
}
|
||||||
|
function viaBaseDeclaration2() public pure returns (bytes memory) {
|
||||||
|
return bytes.concat(
|
||||||
|
abi.encodeCall(Base.baseFunctionPublic, (1)),
|
||||||
|
abi.encodeCall(Base.baseFunctionInternal, (1))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
function fileLevelFunction() public pure returns (bytes memory) {
|
||||||
|
return abi.encodeCall(fileLevel, (2));
|
||||||
|
}
|
||||||
|
function createFunction() public pure returns (bytes memory) {
|
||||||
|
return abi.encodeCall(new Other, (2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError 5511: (742-743): Expected first argument to be a function pointer, not "int_const 1".
|
||||||
|
// TypeError 3509: (855-869): Expected regular external function type, or external view on public function. Cannot use special function.
|
||||||
|
// TypeError 3509: (982-991): Expected regular external function type, or external view on public function. Provided internal function.
|
||||||
|
// TypeError 3509: (1187-1207): Expected regular external function type, or external view on public function. Provided internal function.
|
||||||
|
// TypeError 3509: (1286-1297): Expected regular external function type, or external view on public function. Provided internal function.
|
||||||
|
// TypeError 3509: (1329-1340): Expected regular external function type, or external view on public function. Cannot use library functions for abi.encodeCall.
|
||||||
|
// TypeError 3509: (1471-1482): Expected regular external function type, or external view on public function. Cannot use library functions for abi.encodeCall.
|
||||||
|
// TypeError 3509: (1592-1601): Expected regular external function type, or external view on public function. Provided internal function. Did you forget to prefix "this."?
|
||||||
|
// TypeError 3509: (1722-1745): Expected regular external function type, or external view on public function. Provided internal function. Functions from base contracts have to be external.
|
||||||
|
// TypeError 3509: (1771-1796): Expected regular external function type, or external view on public function. Provided internal function. Functions from base contracts have to be external.
|
||||||
|
// TypeError 3509: (1902-1911): Expected regular external function type, or external view on public function. Provided internal function.
|
||||||
|
// TypeError 3509: (2010-2019): Expected regular external function type, or external view on public function. Provided creation function.
|
Loading…
Reference in New Issue
Block a user