diff --git a/test/ExecutionFramework.h b/test/ExecutionFramework.h index b20f10f9c..5019c0fbc 100644 --- a/test/ExecutionFramework.h +++ b/test/ExecutionFramework.h @@ -92,6 +92,12 @@ public: return callFallbackWithValue(0); } + bytes const& callLowLevel(bytes const& _data, u256 const& _value) + { + sendMessage(_data, false, _value); + return m_output; + } + bytes const& callContractFunctionWithValueNoEncoding(std::string _sig, u256 const& _value, bytes const& _arguments) { FixedHash<4> hash(dev::keccak256(_sig)); diff --git a/test/libsolidity/SemanticTest.cpp b/test/libsolidity/SemanticTest.cpp index dd95c4dee..cdb4e3625 100644 --- a/test/libsolidity/SemanticTest.cpp +++ b/test/libsolidity/SemanticTest.cpp @@ -98,11 +98,13 @@ TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePref } else { - bytes output = callContractFunctionWithValueNoEncoding( - test.call().signature, - test.call().value, - test.call().arguments.rawBytes() - ); + bytes output = test.call().useCallWithoutSignature ? + callLowLevel(test.call().arguments.rawBytes(), test.call().value) : + callContractFunctionWithValueNoEncoding( + test.call().signature, + test.call().value, + test.call().arguments.rawBytes() + ); if ((m_transactionSuccessful == test.call().expectations.failure) || (output != test.call().expectations.rawBytes())) success = false; diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 903740182..af940e0ef 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -2514,23 +2514,6 @@ BOOST_AUTO_TEST_CASE(super_alone) ) } -BOOST_AUTO_TEST_CASE(fallback_function) -{ - char const* sourceCode = R"( - contract A { - uint data; - function() external { data = 1; } - function getData() public returns (uint r) { return data; } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("getData()"), encodeArgs(0)); - ABI_CHECK(callContractFunction(""), encodeArgs()); - ABI_CHECK(callContractFunction("getData()"), encodeArgs(1)); - ) -} - BOOST_AUTO_TEST_CASE(inherited_fallback_function) { char const* sourceCode = R"( diff --git a/test/libsolidity/semanticTests/smoke/fallback.sol b/test/libsolidity/semanticTests/smoke/fallback.sol new file mode 100644 index 000000000..71d17cf0c --- /dev/null +++ b/test/libsolidity/semanticTests/smoke/fallback.sol @@ -0,0 +1,23 @@ +contract A { + uint public data; + uint public balance; + bytes public externalData; + function() external payable { + data += 1; + balance = msg.value; + externalData = msg.data; + } +} +// ---- +// data() -> 0 +// () +// data() -> 1 +// (): hex"42ef" +// data() -> 2 +// externalData() -> 0x20, 2, left(0x42ef) +// balance() -> 0 +// (), 1 ether +// balance() -> 1 +// (), 2 ether: hex"fefe" +// balance() -> 2 +// externalData() -> 0x20, 2, left(0xfefe) \ No newline at end of file diff --git a/test/libsolidity/util/SoltestTypes.h b/test/libsolidity/util/SoltestTypes.h index 0e7a3072c..275b1e657 100644 --- a/test/libsolidity/util/SoltestTypes.h +++ b/test/libsolidity/util/SoltestTypes.h @@ -262,6 +262,9 @@ struct FunctionCall DisplayMode displayMode = DisplayMode::SingleLine; /// Marks this function call as the constructor. bool isConstructor = false; + /// If this function call's signature has no name and no arguments, + /// a low-level call with unstructured calldata will be issued. + bool useCallWithoutSignature = false; /// Marks this function call as "short-handed", meaning /// no `->` declared. bool omitsArrow = true; diff --git a/test/libsolidity/util/TestFileParser.cpp b/test/libsolidity/util/TestFileParser.cpp index ebb865b4f..f33853dfe 100644 --- a/test/libsolidity/util/TestFileParser.cpp +++ b/test/libsolidity/util/TestFileParser.cpp @@ -75,7 +75,7 @@ vector TestFileParser::parseFunctionCalls(siz try { - call.signature = parseFunctionSignature(); + tie(call.signature, call.useCallWithoutSignature) = parseFunctionSignature(); if (accept(Token::Comma, true)) call.value = parseFunctionCallValue(); if (accept(Token::Colon, true)) @@ -148,10 +148,17 @@ bool TestFileParser::expect(soltest::Token _token, bool const _advance) return true; } -string TestFileParser::parseFunctionSignature() +pair TestFileParser::parseFunctionSignature() { - string signature = m_scanner.currentLiteral(); - expect(Token::Identifier); + string signature; + bool hasName = false; + + if (accept(Token::Identifier, false)) + { + hasName = true; + signature = m_scanner.currentLiteral(); + expect(Token::Identifier); + } signature += formatToken(Token::LParen); expect(Token::LParen); @@ -169,11 +176,15 @@ string TestFileParser::parseFunctionSignature() if (accept(Token::Arrow, true)) throw Error(Error::Type::ParserError, "Invalid signature detected: " + signature); - signature += parameters; + if (!hasName && !parameters.empty()) + throw Error(Error::Type::ParserError, "Signatures without a name cannot have parameters: " + signature); + else + signature += parameters; expect(Token::RParen); signature += formatToken(Token::RParen); - return signature; + + return {signature, !hasName}; } u256 TestFileParser::parseFunctionCallValue() diff --git a/test/libsolidity/util/TestFileParser.h b/test/libsolidity/util/TestFileParser.h index dc3775357..a460f4d40 100644 --- a/test/libsolidity/util/TestFileParser.h +++ b/test/libsolidity/util/TestFileParser.h @@ -46,6 +46,8 @@ namespace test * // -> 2, 3 * // h(uint256), 1 ether: 42 * // -> FAILURE # If REVERT or other EVM failure was detected # + * // () # Call fallback function # + * // (), 1 ether # Call ether function # * ... */ class TestFileParser @@ -128,8 +130,10 @@ private: bool accept(soltest::Token _token, bool const _expect = false); bool expect(soltest::Token _token, bool const _advance = true); - /// Parses a function call signature in the form of f(uint256, ...). - std::string parseFunctionSignature(); + /// Parses a function call signature in the form of `f(uint256, ...)` and + /// returns the signature and a flag that indicates if the function name was + /// empty. If so, the signature is not allowed to define any parameters. + std::pair parseFunctionSignature(); /// Parses the optional ether value that can be passed alongside the /// function call arguments. Throws an InvalidEtherValueEncoding exception