Skip to content

Commit

Permalink
Refactor requireOrAssert to use revertWithError
Browse files Browse the repository at this point in the history
  • Loading branch information
nikola-matic committed Apr 26, 2024
1 parent 24e3c30 commit ad41449
Show file tree
Hide file tree
Showing 6 changed files with 324 additions and 26 deletions.
33 changes: 9 additions & 24 deletions libsolidity/codegen/YulUtilFunctions.cpp
Expand Up @@ -252,7 +252,7 @@ std::string YulUtilFunctions::revertWithError(
return templ.render();
}

std::string YulUtilFunctions::requireOrAssertFunction(bool _assert, Type const* _messageType)
std::string YulUtilFunctions::requireOrAssertFunction(bool _assert, Type const* _messageType, ASTPointer<Expression const> _stringArgumentExpression)
{
std::string functionName =
std::string(_assert ? "assert_helper" : "require_helper") +
Expand All @@ -271,34 +271,19 @@ std::string YulUtilFunctions::requireOrAssertFunction(bool _assert, Type const*
("functionName", functionName)
.render();

int const hashHeaderSize = 4;
u256 const errorHash = util::selectorFromSignatureU256("Error(string)");

std::string const encodeFunc = ABIFunctions(m_evmVersion, m_revertStrings, m_functionCollector)
.tupleEncoder(
{_messageType},
{TypeProvider::stringMemory()}
);
solAssert(_stringArgumentExpression, "Require with string must have a string argument");
solAssert(_stringArgumentExpression->annotation().type);
std::vector<std::string> functionParameterNames = IRVariable(*_stringArgumentExpression).stackSlots();

return Whiskers(R"(
function <functionName>(condition <messageVars>) {
if iszero(condition) {
let memPtr := <allocateUnbounded>()
mstore(memPtr, <errorHash>)
let end := <abiEncodeFunc>(add(memPtr, <hashHeaderSize>) <messageVars>)
revert(memPtr, sub(end, memPtr))
}
function <functionName>(condition <functionParameterNames>) {
if iszero(condition)
<revertWithError>
}
)")
("functionName", functionName)
("allocateUnbounded", allocateUnboundedFunction())
("errorHash", formatNumber(errorHash))
("abiEncodeFunc", encodeFunc)
("hashHeaderSize", std::to_string(hashHeaderSize))
("messageVars",
(_messageType->sizeOnStack() > 0 ? ", " : "") +
suffixedVariableNameList("message_", 1, 1 + _messageType->sizeOnStack())
)
("revertWithError", revertWithError("Error(string)", {_messageType}, {_stringArgumentExpression}))
("functionParameterNames", joinHumanReadablePrefixed(functionParameterNames))
.render();
});
}
Expand Down
6 changes: 5 additions & 1 deletion libsolidity/codegen/YulUtilFunctions.h
Expand Up @@ -104,7 +104,11 @@ class YulUtilFunctions

// @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);
std::string requireOrAssertFunction(
bool _assert,
Type const* _messageType = nullptr,
ASTPointer<Expression const> _stringArgumentExpression = nullptr
);

// @returns function that has equivalent logic of a require function, but with a custom
// error constructor parameter.
Expand Down
4 changes: 3 additions & 1 deletion libsolidity/codegen/ir/IRGeneratorForStatements.cpp
Expand Up @@ -1153,6 +1153,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
arguments.size() > 1 && m_context.revertStrings() != RevertStrings::Strip ?
arguments[1]->annotation().type :
nullptr;
ASTPointer<Expression const> stringArgumentExpression = messageArgumentType ? arguments[1] : nullptr;

auto const* magicType = dynamic_cast<MagicType const*>(messageArgumentType);
if (magicType && magicType->kind() == MagicType::Kind::Error)
Expand All @@ -1168,7 +1169,8 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
{
std::string requireOrAssertFunction = m_utils.requireOrAssertFunction(
functionType->kind() == FunctionType::Kind::Assert,
messageArgumentType
messageArgumentType,
stringArgumentExpression
);
appendCode() << std::move(requireOrAssertFunction) << "(" << IRVariable(*arguments[0]).name();
if (messageArgumentType && messageArgumentType->sizeOnStack() > 0)
Expand Down
1 change: 1 addition & 0 deletions test/cmdlineTests/require_with_string_ir/args
@@ -0,0 +1 @@
--ir --debug-info none
16 changes: 16 additions & 0 deletions test/cmdlineTests/require_with_string_ir/input.sol
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.0;

contract C
{
function f(bool condition) external pure
{
string memory message = "Condition must be satisfied";
require(condition, message);
}

function g(bool condition) external pure
{
require(condition, "Condition must be satisfied");
}
}
290 changes: 290 additions & 0 deletions test/cmdlineTests/require_with_string_ir/output
@@ -0,0 +1,290 @@
IR:

/// @use-src 0:"require_with_string_ir/input.sol"
object "C_28" {
code {

mstore(64, memoryguard(128))
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }

constructor_C_28()

let _1 := allocate_unbounded()
codecopy(_1, dataoffset("C_28_deployed"), datasize("C_28_deployed"))

return(_1, datasize("C_28_deployed"))

function allocate_unbounded() -> memPtr {
memPtr := mload(64)
}

function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() {
revert(0, 0)
}

function constructor_C_28() {

}

}
/// @use-src 0:"require_with_string_ir/input.sol"
object "C_28_deployed" {
code {

mstore(64, memoryguard(128))

if iszero(lt(calldatasize(), 4))
{
let selector := shift_right_224_unsigned(calldataload(0))
switch selector

case 0x98c3a6c1
{
// f(bool)

external_fun_f_16()
}

case 0xd48092f7
{
// g(bool)

external_fun_g_27()
}

default {}
}

revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74()

function shift_right_224_unsigned(value) -> newValue {
newValue :=

shr(224, value)

}

function allocate_unbounded() -> memPtr {
memPtr := mload(64)
}

function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() {
revert(0, 0)
}

function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() {
revert(0, 0)
}

function revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db() {
revert(0, 0)
}

function cleanup_t_bool(value) -> cleaned {
cleaned := iszero(iszero(value))
}

function validator_revert_t_bool(value) {
if iszero(eq(value, cleanup_t_bool(value))) { revert(0, 0) }
}

function abi_decode_t_bool(offset, end) -> value {
value := calldataload(offset)
validator_revert_t_bool(value)
}

function abi_decode_tuple_t_bool(headStart, dataEnd) -> value0 {
if slt(sub(dataEnd, headStart), 32) { revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() }

{

let offset := 0

value0 := abi_decode_t_bool(add(headStart, offset), dataEnd)
}

}

function abi_encode_tuple__to__fromStack(headStart ) -> tail {
tail := add(headStart, 0)

}

function external_fun_f_16() {

if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
let param_0 := abi_decode_tuple_t_bool(4, calldatasize())
fun_f_16(param_0)
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple__to__fromStack(memPos )
return(memPos, sub(memEnd, memPos))

}

function external_fun_g_27() {

if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
let param_0 := abi_decode_tuple_t_bool(4, calldatasize())
fun_g_27(param_0)
let memPos := allocate_unbounded()
let memEnd := abi_encode_tuple__to__fromStack(memPos )
return(memPos, sub(memEnd, memPos))

}

function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() {
revert(0, 0)
}

function round_up_to_mul_of_32(value) -> result {
result := and(add(value, 31), not(31))
}

function panic_error_0x41() {
mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856)
mstore(4, 0x41)
revert(0, 0x24)
}

function finalize_allocation(memPtr, size) {
let newFreePtr := add(memPtr, round_up_to_mul_of_32(size))
// protect against overflow
if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() }
mstore(64, newFreePtr)
}

function allocate_memory(size) -> memPtr {
memPtr := allocate_unbounded()
finalize_allocation(memPtr, size)
}

function array_allocation_size_t_string_memory_ptr(length) -> size {
// Make sure we can allocate memory without overflow
if gt(length, 0xffffffffffffffff) { panic_error_0x41() }

size := round_up_to_mul_of_32(length)

// add length slot
size := add(size, 0x20)

}

function allocate_memory_array_t_string_memory_ptr(length) -> memPtr {
let allocSize := array_allocation_size_t_string_memory_ptr(length)
memPtr := allocate_memory(allocSize)

mstore(memPtr, length)

}

function store_literal_in_memory_b25d43cff48de8365ad132c5f45b74f4593f9e69e5b749d86685f77282c88e46(memPtr) {

mstore(add(memPtr, 0), "Condition must be satisfied")

}

function copy_literal_to_memory_b25d43cff48de8365ad132c5f45b74f4593f9e69e5b749d86685f77282c88e46() -> memPtr {
memPtr := allocate_memory_array_t_string_memory_ptr(27)
store_literal_in_memory_b25d43cff48de8365ad132c5f45b74f4593f9e69e5b749d86685f77282c88e46(add(memPtr, 32))
}

function convert_t_stringliteral_b25d43cff48de8365ad132c5f45b74f4593f9e69e5b749d86685f77282c88e46_to_t_string_memory_ptr() -> converted {
converted := copy_literal_to_memory_b25d43cff48de8365ad132c5f45b74f4593f9e69e5b749d86685f77282c88e46()
}

function array_length_t_string_memory_ptr(value) -> length {

length := mload(value)

}

function array_storeLengthForEncoding_t_string_memory_ptr_fromStack(pos, length) -> updated_pos {
mstore(pos, length)
updated_pos := add(pos, 0x20)
}

function copy_memory_to_memory_with_cleanup(src, dst, length) {

mcopy(dst, src, length)
mstore(add(dst, length), 0)

}

function abi_encode_t_string_memory_ptr_to_t_string_memory_ptr_fromStack(value, pos) -> end {
let length := array_length_t_string_memory_ptr(value)
pos := array_storeLengthForEncoding_t_string_memory_ptr_fromStack(pos, length)
copy_memory_to_memory_with_cleanup(add(value, 0x20), pos, length)
end := add(pos, round_up_to_mul_of_32(length))
}

function abi_encode_tuple_t_string_memory_ptr__to_t_string_memory_ptr__fromStack(headStart , value0) -> tail {
tail := add(headStart, 32)

mstore(add(headStart, 0), sub(tail, headStart))
tail := abi_encode_t_string_memory_ptr_to_t_string_memory_ptr_fromStack(value0, tail)

}

function require_helper_t_string_memory_ptr(condition , expr_12_mpos) {
if iszero(condition)
{
let memPtr := allocate_unbounded()
mstore(memPtr, 0x08c379a000000000000000000000000000000000000000000000000000000000)
let end := abi_encode_tuple_t_string_memory_ptr__to_t_string_memory_ptr__fromStack(add(memPtr, 4) , expr_12_mpos)
revert(memPtr, sub(end, memPtr))
}
}

function fun_f_16(var_condition_3) {

let var_message_7_mpos := convert_t_stringliteral_b25d43cff48de8365ad132c5f45b74f4593f9e69e5b749d86685f77282c88e46_to_t_string_memory_ptr()

let _1 := var_condition_3
let expr_11 := _1

let _2_mpos := var_message_7_mpos
let expr_12_mpos := _2_mpos

require_helper_t_string_memory_ptr(expr_11, expr_12_mpos)

}

function abi_encode_t_stringliteral_b25d43cff48de8365ad132c5f45b74f4593f9e69e5b749d86685f77282c88e46_to_t_string_memory_ptr_fromStack(pos) -> end {
pos := array_storeLengthForEncoding_t_string_memory_ptr_fromStack(pos, 27)
store_literal_in_memory_b25d43cff48de8365ad132c5f45b74f4593f9e69e5b749d86685f77282c88e46(pos)
end := add(pos, 32)
}

function abi_encode_tuple_t_stringliteral_b25d43cff48de8365ad132c5f45b74f4593f9e69e5b749d86685f77282c88e46__to_t_string_memory_ptr__fromStack(headStart ) -> tail {
tail := add(headStart, 32)

mstore(add(headStart, 0), sub(tail, headStart))
tail := abi_encode_t_stringliteral_b25d43cff48de8365ad132c5f45b74f4593f9e69e5b749d86685f77282c88e46_to_t_string_memory_ptr_fromStack( tail)

}

function require_helper_t_stringliteral_b25d43cff48de8365ad132c5f45b74f4593f9e69e5b749d86685f77282c88e46(condition ) {
if iszero(condition)
{
let memPtr := allocate_unbounded()
mstore(memPtr, 0x08c379a000000000000000000000000000000000000000000000000000000000)
let end := abi_encode_tuple_t_stringliteral_b25d43cff48de8365ad132c5f45b74f4593f9e69e5b749d86685f77282c88e46__to_t_string_memory_ptr__fromStack(add(memPtr, 4) )
revert(memPtr, sub(end, memPtr))
}
}

function fun_g_27(var_condition_18) {

let _3 := var_condition_18
let expr_22 := _3

require_helper_t_stringliteral_b25d43cff48de8365ad132c5f45b74f4593f9e69e5b749d86685f77282c88e46(expr_22)

}

}

data ".metadata" hex"<BYTECODE REMOVED>"
}

}

0 comments on commit ad41449

Please sign in to comment.