mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Yul generation of require
and assert
This commit is contained in:
parent
442742e497
commit
7f14352bbf
@ -104,6 +104,60 @@ string YulUtilFunctions::copyToMemoryFunction(bool _fromCalldata)
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::requireOrAssertFunction(bool _assert, Type const* _messageType)
|
||||
{
|
||||
string functionName =
|
||||
string(_assert ? "assert_helper" : "require_helper") +
|
||||
(_messageType ? ("_" + _messageType->identifier()) : "");
|
||||
|
||||
solAssert(!_assert || !_messageType, "Asserts can't have messages!");
|
||||
|
||||
return m_functionCollector->createFunction(functionName, [&]() {
|
||||
if (!_messageType)
|
||||
return Whiskers(R"(
|
||||
function <functionName>(condition) {
|
||||
if iszero(condition) { <invalidOrRevert> }
|
||||
}
|
||||
)")
|
||||
("invalidOrRevert", _assert ? "invalid()" : "revert(0, 0)")
|
||||
("functionName", functionName)
|
||||
.render();
|
||||
|
||||
|
||||
solUnimplemented("require() with two parameters is not yet implemented.");
|
||||
// TODO The code below is completely untested as we don't support StringLiterals yet
|
||||
int const hashHeaderSize = 4;
|
||||
int const byteSize = 8;
|
||||
u256 const errorHash =
|
||||
u256(FixedHash<hashHeaderSize>::Arith(
|
||||
FixedHash<hashHeaderSize>(dev::keccak256("Error(string)"))
|
||||
)) << (256 - hashHeaderSize * byteSize);
|
||||
|
||||
string const encodeFunc = ABIFunctions(m_evmVersion, m_functionCollector)
|
||||
.tupleEncoder(
|
||||
{_messageType},
|
||||
{TypeProvider::stringMemory()}
|
||||
);
|
||||
|
||||
return Whiskers(R"(
|
||||
function <functionName>(condition, message) {
|
||||
if iszero(condition) {
|
||||
let fmp := mload(<freeMemPointer>)
|
||||
mstore(fmp, <errorHash>)
|
||||
let end := <abiEncodeFunc>(add(fmp, <hashHeaderSize>), message)
|
||||
revert(fmp, sub(end, fmp))
|
||||
}
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("freeMemPointer", to_string(CompilerUtils::freeMemoryPointer))
|
||||
("errorHash", errorHash.str())
|
||||
("abiEncodeFunc", encodeFunc)
|
||||
("hashHeaderSize", to_string(hashHeaderSize))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::leftAlignFunction(Type const& _type)
|
||||
{
|
||||
string functionName = string("leftAlign_") + _type.identifier();
|
||||
|
@ -62,6 +62,10 @@ public:
|
||||
/// Pads with zeros and might write more than exactly length.
|
||||
std::string copyToMemoryFunction(bool _fromCalldata);
|
||||
|
||||
// @returns the name of a function that has the equivalent logic of an
|
||||
// `assert` or `require` call.
|
||||
std::string requireOrAssertFunction(bool _assert, Type const* _messageType = nullptr);
|
||||
|
||||
/// @returns the name of a function that takes a (cleaned) value of the given value type and
|
||||
/// left-aligns it, usually for use in non-padded encoding.
|
||||
std::string leftAlignFunction(Type const& _type);
|
||||
|
@ -89,6 +89,8 @@ public:
|
||||
/// @returns a new copy of the utility function generator (but using the same function set).
|
||||
YulUtilFunctions utils();
|
||||
|
||||
langutil::EVMVersion evmVersion() const { return m_evmVersion; };
|
||||
|
||||
private:
|
||||
langutil::EVMVersion m_evmVersion;
|
||||
OptimiserSettings m_optimiserSettings;
|
||||
|
@ -20,9 +20,11 @@
|
||||
|
||||
#include <libsolidity/codegen/ir/IRGeneratorForStatements.h>
|
||||
|
||||
#include <libsolidity/codegen/ABIFunctions.h>
|
||||
#include <libsolidity/codegen/ir/IRGenerationContext.h>
|
||||
#include <libsolidity/codegen/ir/IRLValue.h>
|
||||
#include <libsolidity/codegen/YulUtilFunctions.h>
|
||||
#include <libsolidity/codegen/CompilerUtils.h>
|
||||
#include <libsolidity/ast/TypeProvider.h>
|
||||
|
||||
#include <libyul/AsmPrinter.h>
|
||||
@ -30,6 +32,8 @@
|
||||
#include <libyul/optimiser/ASTCopier.h>
|
||||
|
||||
#include <libdevcore/StringUtils.h>
|
||||
#include <libdevcore/Whiskers.h>
|
||||
#include <libdevcore/Keccak256.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
@ -302,6 +306,24 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
")\n";
|
||||
break;
|
||||
}
|
||||
case FunctionType::Kind::Assert:
|
||||
case FunctionType::Kind::Require:
|
||||
{
|
||||
solAssert(arguments.size() > 0, "Expected at least one parameter for require/assert");
|
||||
solAssert(arguments.size() <= 2, "Expected no more than two parameters for require/assert");
|
||||
|
||||
string requireOrAssertFunction = m_utils.requireOrAssertFunction(
|
||||
functionType->kind() == FunctionType::Kind::Assert,
|
||||
arguments.size() > 1 ? arguments[1]->annotation().type : nullptr
|
||||
);
|
||||
|
||||
m_code << move(requireOrAssertFunction) << "(" << m_context.variable(*arguments[0]);
|
||||
if (arguments.size() > 1)
|
||||
m_code << ", " << m_context.variable(*arguments[1]);
|
||||
m_code << ")\n";
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
solUnimplemented("");
|
||||
}
|
||||
@ -340,7 +362,7 @@ bool IRGeneratorForStatements::visit(Identifier const& _identifier)
|
||||
|
||||
setLValue(_identifier, move(lvalue));
|
||||
}
|
||||
else
|
||||
else if (!dynamic_cast<MagicVariableDeclaration const*>(declaration))
|
||||
solUnimplemented("");
|
||||
return false;
|
||||
}
|
||||
|
22
test/libsolidity/semanticTests/viaYul/assert.sol
Normal file
22
test/libsolidity/semanticTests/viaYul/assert.sol
Normal file
@ -0,0 +1,22 @@
|
||||
contract C {
|
||||
function f(bool a) public pure returns (bool x) {
|
||||
bool b = a;
|
||||
x = b;
|
||||
assert(b);
|
||||
}
|
||||
function fail() public pure returns (bool x) {
|
||||
x = true;
|
||||
assert(false);
|
||||
}
|
||||
function succeed() public pure returns (bool x) {
|
||||
x = true;
|
||||
assert(true);
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: true
|
||||
// ----
|
||||
// f(bool): true -> true
|
||||
// f(bool): false -> FAILURE
|
||||
// fail() -> FAILURE
|
||||
// succeed() -> true
|
19
test/libsolidity/semanticTests/viaYul/assert_and_require.sol
Normal file
19
test/libsolidity/semanticTests/viaYul/assert_and_require.sol
Normal file
@ -0,0 +1,19 @@
|
||||
contract C {
|
||||
function f(bool a) public pure returns (bool x) {
|
||||
bool b = a;
|
||||
x = b;
|
||||
assert(b);
|
||||
}
|
||||
function f2(bool a) public pure returns (bool x) {
|
||||
bool b = a;
|
||||
x = b;
|
||||
require(b);
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: true
|
||||
// ----
|
||||
// f(bool): true -> true
|
||||
// f(bool): false -> FAILURE
|
||||
// f2(bool): true -> true
|
||||
// f2(bool): false -> FAILURE
|
28
test/libsolidity/semanticTests/viaYul/require.sol
Normal file
28
test/libsolidity/semanticTests/viaYul/require.sol
Normal file
@ -0,0 +1,28 @@
|
||||
contract C {
|
||||
function f(bool a) public pure returns (bool x) {
|
||||
bool b = a;
|
||||
x = b;
|
||||
require(b);
|
||||
}
|
||||
function fail() public pure returns (bool x) {
|
||||
x = true;
|
||||
require(false);
|
||||
}
|
||||
function succeed() public pure returns (bool x) {
|
||||
x = true;
|
||||
require(true);
|
||||
}
|
||||
/* Not properly supported by test system yet
|
||||
function f2(bool a) public pure returns (bool x) {
|
||||
x = a;
|
||||
string memory message;
|
||||
require(a, message);
|
||||
}*/
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: true
|
||||
// ----
|
||||
// f(bool): true -> true
|
||||
// f(bool): false -> FAILURE
|
||||
// fail() -> FAILURE
|
||||
// succeed() -> true
|
Loading…
Reference in New Issue
Block a user