[isoltest] Adds support for fallback and ether functions.

This commit is contained in:
Erik Kundt 2019-09-09 20:09:15 +02:00
parent 02af613fa5
commit e92b921123
7 changed files with 62 additions and 30 deletions

View File

@ -92,6 +92,12 @@ public:
return callFallbackWithValue(0); 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) bytes const& callContractFunctionWithValueNoEncoding(std::string _sig, u256 const& _value, bytes const& _arguments)
{ {
FixedHash<4> hash(dev::keccak256(_sig)); FixedHash<4> hash(dev::keccak256(_sig));

View File

@ -98,11 +98,13 @@ TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePref
} }
else else
{ {
bytes output = callContractFunctionWithValueNoEncoding( bytes output = test.call().useCallWithoutSignature ?
test.call().signature, callLowLevel(test.call().arguments.rawBytes(), test.call().value) :
test.call().value, callContractFunctionWithValueNoEncoding(
test.call().arguments.rawBytes() test.call().signature,
); test.call().value,
test.call().arguments.rawBytes()
);
if ((m_transactionSuccessful == test.call().expectations.failure) || (output != test.call().expectations.rawBytes())) if ((m_transactionSuccessful == test.call().expectations.failure) || (output != test.call().expectations.rawBytes()))
success = false; success = false;

View File

@ -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) BOOST_AUTO_TEST_CASE(inherited_fallback_function)
{ {
char const* sourceCode = R"( char const* sourceCode = R"(

View File

@ -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)

View File

@ -262,6 +262,9 @@ struct FunctionCall
DisplayMode displayMode = DisplayMode::SingleLine; DisplayMode displayMode = DisplayMode::SingleLine;
/// Marks this function call as the constructor. /// Marks this function call as the constructor.
bool isConstructor = false; 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 /// Marks this function call as "short-handed", meaning
/// no `->` declared. /// no `->` declared.
bool omitsArrow = true; bool omitsArrow = true;

View File

@ -75,7 +75,7 @@ vector<dev::solidity::test::FunctionCall> TestFileParser::parseFunctionCalls(siz
try try
{ {
call.signature = parseFunctionSignature(); tie(call.signature, call.useCallWithoutSignature) = parseFunctionSignature();
if (accept(Token::Comma, true)) if (accept(Token::Comma, true))
call.value = parseFunctionCallValue(); call.value = parseFunctionCallValue();
if (accept(Token::Colon, true)) if (accept(Token::Colon, true))
@ -148,10 +148,17 @@ bool TestFileParser::expect(soltest::Token _token, bool const _advance)
return true; return true;
} }
string TestFileParser::parseFunctionSignature() pair<string, bool> TestFileParser::parseFunctionSignature()
{ {
string signature = m_scanner.currentLiteral(); string signature;
expect(Token::Identifier); bool hasName = false;
if (accept(Token::Identifier, false))
{
hasName = true;
signature = m_scanner.currentLiteral();
expect(Token::Identifier);
}
signature += formatToken(Token::LParen); signature += formatToken(Token::LParen);
expect(Token::LParen); expect(Token::LParen);
@ -169,11 +176,15 @@ string TestFileParser::parseFunctionSignature()
if (accept(Token::Arrow, true)) if (accept(Token::Arrow, true))
throw Error(Error::Type::ParserError, "Invalid signature detected: " + signature); 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); expect(Token::RParen);
signature += formatToken(Token::RParen); signature += formatToken(Token::RParen);
return signature;
return {signature, !hasName};
} }
u256 TestFileParser::parseFunctionCallValue() u256 TestFileParser::parseFunctionCallValue()

View File

@ -46,6 +46,8 @@ namespace test
* // -> 2, 3 * // -> 2, 3
* // h(uint256), 1 ether: 42 * // h(uint256), 1 ether: 42
* // -> FAILURE # If REVERT or other EVM failure was detected # * // -> FAILURE # If REVERT or other EVM failure was detected #
* // () # Call fallback function #
* // (), 1 ether # Call ether function #
* ... * ...
*/ */
class TestFileParser class TestFileParser
@ -128,8 +130,10 @@ private:
bool accept(soltest::Token _token, bool const _expect = false); bool accept(soltest::Token _token, bool const _expect = false);
bool expect(soltest::Token _token, bool const _advance = true); bool expect(soltest::Token _token, bool const _advance = true);
/// Parses a function call signature in the form of f(uint256, ...). /// Parses a function call signature in the form of `f(uint256, ...)` and
std::string parseFunctionSignature(); /// 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<std::string, bool> parseFunctionSignature();
/// Parses the optional ether value that can be passed alongside the /// Parses the optional ether value that can be passed alongside the
/// function call arguments. Throws an InvalidEtherValueEncoding exception /// function call arguments. Throws an InvalidEtherValueEncoding exception