Allow fallback function to return data.

This commit is contained in:
chriseth 2020-11-05 18:03:32 +01:00
parent b62de4f16d
commit fda352094f
15 changed files with 128 additions and 21 deletions

View File

@ -1831,15 +1831,21 @@ void TypeChecker::typeCheckFallbackFunction(FunctionDefinition const& _function)
);
if (_function.visibility() != Visibility::External)
m_errorReporter.typeError(1159_error, _function.location(), "Fallback function must be defined as \"external\".");
if (!_function.returnParameters().empty())
if (!_function.returnParameters().empty() || !_function.parameters().empty())
{
if (_function.returnParameters().size() > 1 || *type(*_function.returnParameters().front()) != *TypeProvider::bytesMemory())
m_errorReporter.typeError(5570_error, _function.returnParameterList()->location(), "Fallback function can only have a single \"bytes memory\" return value.");
else
m_errorReporter.typeError(6151_error, _function.returnParameterList()->location(), "Return values for fallback functions are not yet implemented.");
if (
_function.returnParameters().size() != 1 ||
*type(*_function.returnParameters().front()) != *TypeProvider::bytesMemory() ||
_function.parameters().size() != 1 ||
*type(*_function.parameters().front()) != *TypeProvider::bytesCalldata()
)
m_errorReporter.typeError(
5570_error,
_function.returnParameterList()->location(),
"Fallback function either has to have the signature \"fallback()\" or \"fallback(bytes calldata) returns (bytes memory)\"."
);
}
if (!_function.parameters().empty())
m_errorReporter.typeError(3978_error, _function.parameterList().location(), "Fallback function cannot take parameters.");
}
void TypeChecker::typeCheckReceiveFunction(FunctionDefinition const& _function)

View File

@ -467,10 +467,21 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
appendCallValueCheck();
solAssert(fallback->isFallback(), "");
solAssert(FunctionType(*fallback).parameterTypes().empty(), "");
solAssert(FunctionType(*fallback).returnParameterTypes().empty(), "");
m_context.setStackOffset(0);
if (!FunctionType(*fallback).parameterTypes().empty())
m_context << u256(0) << Instruction::CALLDATASIZE;
fallback->accept(*this);
m_context << Instruction::STOP;
if (FunctionType(*fallback).returnParameterTypes().empty())
m_context << Instruction::STOP;
else
{
m_context << Instruction::DUP1 << Instruction::MLOAD << Instruction::SWAP1;
m_context << u256(0x20) << Instruction::ADD;
m_context << Instruction::RETURN;
}
}
else
m_context.appendRevert("Unknown signature and no fallback defined");
@ -479,6 +490,7 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
for (auto const& it: interfaceFunctions)
{
m_context.setStackOffset(1);
FunctionTypePointer const& functionType = it.second;
solAssert(functionType->hasDeclaration(), "");
CompilerContext::LocationSetter locationSetter(m_context, functionType->declaration());
@ -588,7 +600,9 @@ bool ContractCompiler::visit(FunctionDefinition const& _function)
// reserve additional slots: [retarg0] ... [retargm]
unsigned parametersSize = CompilerUtils::sizeOnStack(_function.parameters());
if (!_function.isConstructor())
if (_function.isFallback())
m_context.adjustStackOffset(static_cast<int>(parametersSize));
else if (!_function.isConstructor())
// adding 1 for return address.
m_context.adjustStackOffset(static_cast<int>(parametersSize) + 1);
for (ASTPointer<VariableDeclaration> const& variable: _function.parameters())
@ -628,7 +642,8 @@ bool ContractCompiler::visit(FunctionDefinition const& _function)
unsigned const c_returnValuesSize = CompilerUtils::sizeOnStack(_function.returnParameters());
vector<int> stackLayout;
stackLayout.push_back(static_cast<int>(c_returnValuesSize)); // target of return address
if (!_function.isConstructor() && !_function.isFallback())
stackLayout.push_back(static_cast<int>(c_returnValuesSize)); // target of return address
stackLayout += vector<int>(c_argumentsSize, -1); // discard all arguments
for (size_t i = 0; i < c_returnValuesSize; ++i)
stackLayout.push_back(static_cast<int>(i));
@ -639,7 +654,7 @@ bool ContractCompiler::visit(FunctionDefinition const& _function)
errinfo_sourceLocation(_function.location()) <<
errinfo_comment("Stack too deep, try removing local variables.")
);
while (stackLayout.back() != static_cast<int>(stackLayout.size() - 1))
while (!stackLayout.empty() && stackLayout.back() != static_cast<int>(stackLayout.size() - 1))
if (stackLayout.back() < 0)
{
m_context << Instruction::POP;

View File

@ -652,8 +652,16 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
{
string fallbackCode;
if (!fallback->isPayable())
fallbackCode += callValueCheck();
fallbackCode += m_context.enqueueFunctionForCodeGeneration(*fallback) + "() stop()";
fallbackCode += callValueCheck() + "\n";
if (fallback->parameters().empty())
fallbackCode += m_context.enqueueFunctionForCodeGeneration(*fallback) + "() stop()";
else
{
solAssert(fallback->parameters().size() == 1 && fallback->returnParameters().size() == 1, "");
fallbackCode += "let retval := " + m_context.enqueueFunctionForCodeGeneration(*fallback) + "(0, calldatasize())\n";
fallbackCode += "return(add(retval, 0x20), mload(retval))\n";
}
t("fallback", fallbackCode);
}

View File

@ -0,0 +1,18 @@
contract A {
uint public x;
fallback () external {
if (x == 2) return;
x++;
}
}
// ====
// compileViaYul: also
// ----
// ()
// x() -> 1
// ()
// x() -> 2
// ()
// x() -> 2
// ()
// x() -> 2

View File

@ -0,0 +1,17 @@
contract A {
uint public x;
fallback (bytes calldata _input) external returns (bytes memory) {
x = _input.length;
return "";
}
function f() public returns (bool, bytes memory) {
(bool success, bytes memory retval) = address(this).call("abc");
return (success, retval);
}
}
// ====
// compileViaYul: also
// EVMVersion: >=byzantium
// ----
// f() -> 0x01, 0x40, 0x00
// x() -> 3

View File

@ -0,0 +1,16 @@
contract A {
bytes public x;
fallback (bytes calldata _input) external returns (bytes memory) {
x = _input;
return "";
}
function f() public returns (bool, bytes memory) {
(bool success, bytes memory retval) = address(this).call("abc");
return (success, retval);
}
}
// ====
// EVMVersion: >=byzantium
// ----
// f() -> 0x01, 0x40, 0x00
// x() -> 0x20, 3, "abc"

View File

@ -0,0 +1,14 @@
contract A {
fallback (bytes calldata _input) external returns (bytes memory) {
return _input;
}
function f() public returns (bool, bytes memory) {
(bool success, bytes memory retval) = address(this).call("abc");
return (success, retval);
}
}
// ====
// compileViaYul: also
// EVMVersion: >=byzantium
// ----
// f() -> 0x01, 0x40, 0x03, 0x6162630000000000000000000000000000000000000000000000000000000000

View File

@ -2,4 +2,4 @@ contract C {
fallback(uint256) external {}
}
// ----
// TypeError 3978: (25-34): Fallback function cannot take parameters.
// TypeError 5570: (44-44): Fallback function either has to have the signature "fallback()" or "fallback(bytes calldata) returns (bytes memory)".

View File

@ -0,0 +1,9 @@
contract C {
fallback() external returns (bytes memory _output) {}
}
contract D {
fallback(bytes calldata _input) external {}
}
// ----
// TypeError 5570: (45-67): Fallback function either has to have the signature "fallback()" or "fallback(bytes calldata) returns (bytes memory)".
// TypeError 5570: (131-131): Fallback function either has to have the signature "fallback()" or "fallback(bytes calldata) returns (bytes memory)".

View File

@ -2,4 +2,4 @@ contract C {
fallback() external returns (bytes memory, bytes memory) {}
}
// ----
// TypeError 5570: (45-73): Fallback function can only have a single "bytes memory" return value.
// TypeError 5570: (45-73): Fallback function either has to have the signature "fallback()" or "fallback(bytes calldata) returns (bytes memory)".

View File

@ -2,4 +2,4 @@ contract C {
fallback() external returns (uint256) {}
}
// ----
// TypeError 5570: (45-54): Fallback function can only have a single "bytes memory" return value.
// TypeError 5570: (45-54): Fallback function either has to have the signature "fallback()" or "fallback(bytes calldata) returns (bytes memory)".

View File

@ -2,4 +2,4 @@ contract C {
fallback() external returns (bytes memory) {}
}
// ----
// TypeError 6151: (45-59): Return values for fallback functions are not yet implemented.
// TypeError 5570: (45-59): Fallback function either has to have the signature "fallback()" or "fallback(bytes calldata) returns (bytes memory)".

View File

@ -0,0 +1,4 @@
contract C {
fallback(bytes calldata _input) external returns (bytes memory _output) {}
}
// ----

View File

@ -3,4 +3,4 @@ contract C {
fallback(uint a) external { x = 2; }
}
// ----
// TypeError 3978: (37-45): Fallback function cannot take parameters.
// TypeError 5570: (55-55): Fallback function either has to have the signature "fallback()" or "fallback(bytes calldata) returns (bytes memory)".

View File

@ -2,4 +2,4 @@ contract C {
fallback() external returns (uint) { }
}
// ----
// TypeError 5570: (45-51): Fallback function can only have a single "bytes memory" return value.
// TypeError 5570: (45-51): Fallback function either has to have the signature "fallback()" or "fallback(bytes calldata) returns (bytes memory)".