From fda352094f0e9f586004dda48daef9788068f523 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 5 Nov 2020 18:03:32 +0100 Subject: [PATCH] Allow fallback function to return data. --- libsolidity/analysis/TypeChecker.cpp | 20 +++++++++----- libsolidity/codegen/ContractCompiler.cpp | 27 ++++++++++++++----- libsolidity/codegen/ir/IRGenerator.cpp | 12 +++++++-- .../semanticTests/fallback/falback_return.sol | 18 +++++++++++++ .../fallback/fallback_argument.sol | 17 ++++++++++++ .../fallback/fallback_argument_to_storage.sol | 16 +++++++++++ .../fallback/fallback_return_data.sol | 14 ++++++++++ .../syntaxTests/fallback/arguments.sol | 2 +- .../fallback/no_input_no_output.sol | 9 +++++++ .../fallback/return_value_number.sol | 2 +- .../fallback/return_value_type.sol | 2 +- .../fallback/return_value_unsupported.sol | 2 +- .../syntaxTests/fallback/returns.sol | 4 +++ .../075_fallback_function_with_arguments.sol | 2 +- ...llback_function_with_return_parameters.sol | 2 +- 15 files changed, 128 insertions(+), 21 deletions(-) create mode 100644 test/libsolidity/semanticTests/fallback/falback_return.sol create mode 100644 test/libsolidity/semanticTests/fallback/fallback_argument.sol create mode 100644 test/libsolidity/semanticTests/fallback/fallback_argument_to_storage.sol create mode 100644 test/libsolidity/semanticTests/fallback/fallback_return_data.sol create mode 100644 test/libsolidity/syntaxTests/fallback/no_input_no_output.sol create mode 100644 test/libsolidity/syntaxTests/fallback/returns.sol diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 0ddd69229..7bc4677e2 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -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) diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index d339e50b9..66c67f59a 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -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(parametersSize)); + else if (!_function.isConstructor()) // adding 1 for return address. m_context.adjustStackOffset(static_cast(parametersSize) + 1); for (ASTPointer const& variable: _function.parameters()) @@ -628,7 +642,8 @@ bool ContractCompiler::visit(FunctionDefinition const& _function) unsigned const c_returnValuesSize = CompilerUtils::sizeOnStack(_function.returnParameters()); vector stackLayout; - stackLayout.push_back(static_cast(c_returnValuesSize)); // target of return address + if (!_function.isConstructor() && !_function.isFallback()) + stackLayout.push_back(static_cast(c_returnValuesSize)); // target of return address stackLayout += vector(c_argumentsSize, -1); // discard all arguments for (size_t i = 0; i < c_returnValuesSize; ++i) stackLayout.push_back(static_cast(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(stackLayout.size() - 1)) + while (!stackLayout.empty() && stackLayout.back() != static_cast(stackLayout.size() - 1)) if (stackLayout.back() < 0) { m_context << Instruction::POP; diff --git a/libsolidity/codegen/ir/IRGenerator.cpp b/libsolidity/codegen/ir/IRGenerator.cpp index 46ae3381c..4a8804182 100644 --- a/libsolidity/codegen/ir/IRGenerator.cpp +++ b/libsolidity/codegen/ir/IRGenerator.cpp @@ -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); } diff --git a/test/libsolidity/semanticTests/fallback/falback_return.sol b/test/libsolidity/semanticTests/fallback/falback_return.sol new file mode 100644 index 000000000..2868d85d0 --- /dev/null +++ b/test/libsolidity/semanticTests/fallback/falback_return.sol @@ -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 diff --git a/test/libsolidity/semanticTests/fallback/fallback_argument.sol b/test/libsolidity/semanticTests/fallback/fallback_argument.sol new file mode 100644 index 000000000..1e3433d93 --- /dev/null +++ b/test/libsolidity/semanticTests/fallback/fallback_argument.sol @@ -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 diff --git a/test/libsolidity/semanticTests/fallback/fallback_argument_to_storage.sol b/test/libsolidity/semanticTests/fallback/fallback_argument_to_storage.sol new file mode 100644 index 000000000..1a995a8cd --- /dev/null +++ b/test/libsolidity/semanticTests/fallback/fallback_argument_to_storage.sol @@ -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" diff --git a/test/libsolidity/semanticTests/fallback/fallback_return_data.sol b/test/libsolidity/semanticTests/fallback/fallback_return_data.sol new file mode 100644 index 000000000..130212613 --- /dev/null +++ b/test/libsolidity/semanticTests/fallback/fallback_return_data.sol @@ -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 diff --git a/test/libsolidity/syntaxTests/fallback/arguments.sol b/test/libsolidity/syntaxTests/fallback/arguments.sol index 806874feb..c796161d2 100644 --- a/test/libsolidity/syntaxTests/fallback/arguments.sol +++ b/test/libsolidity/syntaxTests/fallback/arguments.sol @@ -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)". diff --git a/test/libsolidity/syntaxTests/fallback/no_input_no_output.sol b/test/libsolidity/syntaxTests/fallback/no_input_no_output.sol new file mode 100644 index 000000000..868671d2a --- /dev/null +++ b/test/libsolidity/syntaxTests/fallback/no_input_no_output.sol @@ -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)". diff --git a/test/libsolidity/syntaxTests/fallback/return_value_number.sol b/test/libsolidity/syntaxTests/fallback/return_value_number.sol index c41b837a8..e5d358c73 100644 --- a/test/libsolidity/syntaxTests/fallback/return_value_number.sol +++ b/test/libsolidity/syntaxTests/fallback/return_value_number.sol @@ -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)". diff --git a/test/libsolidity/syntaxTests/fallback/return_value_type.sol b/test/libsolidity/syntaxTests/fallback/return_value_type.sol index a6557139d..01363ebe4 100644 --- a/test/libsolidity/syntaxTests/fallback/return_value_type.sol +++ b/test/libsolidity/syntaxTests/fallback/return_value_type.sol @@ -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)". diff --git a/test/libsolidity/syntaxTests/fallback/return_value_unsupported.sol b/test/libsolidity/syntaxTests/fallback/return_value_unsupported.sol index 3b90b5ad2..88f181f3f 100644 --- a/test/libsolidity/syntaxTests/fallback/return_value_unsupported.sol +++ b/test/libsolidity/syntaxTests/fallback/return_value_unsupported.sol @@ -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)". diff --git a/test/libsolidity/syntaxTests/fallback/returns.sol b/test/libsolidity/syntaxTests/fallback/returns.sol new file mode 100644 index 000000000..a4d45c522 --- /dev/null +++ b/test/libsolidity/syntaxTests/fallback/returns.sol @@ -0,0 +1,4 @@ +contract C { + fallback(bytes calldata _input) external returns (bytes memory _output) {} +} +// ---- diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/075_fallback_function_with_arguments.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/075_fallback_function_with_arguments.sol index 8beac9f1a..6ed769c88 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/075_fallback_function_with_arguments.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/075_fallback_function_with_arguments.sol @@ -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)". diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/077_fallback_function_with_return_parameters.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/077_fallback_function_with_return_parameters.sol index 3ec82b390..268a7e69b 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/077_fallback_function_with_return_parameters.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/077_fallback_function_with_return_parameters.sol @@ -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)".