Extract utility functions from ABI encoder.

This commit is contained in:
chriseth 2019-03-05 17:20:28 +01:00
parent 40171c216d
commit d515d77bfe
7 changed files with 705 additions and 497 deletions

View File

@ -63,6 +63,10 @@ set(sources
codegen/ExpressionCompiler.h codegen/ExpressionCompiler.h
codegen/LValue.cpp codegen/LValue.cpp
codegen/LValue.h codegen/LValue.h
codegen/MultiUseYulFunctionCollector.h
codegen/MultiUseYulFunctionCollector.cpp
codegen/YulUtilFunctions.h
codegen/YulUtilFunctions.cpp
formal/SMTChecker.cpp formal/SMTChecker.cpp
formal/SMTChecker.h formal/SMTChecker.h
formal/SMTLib2Interface.cpp formal/SMTLib2Interface.cpp

View File

@ -22,7 +22,6 @@
#include <libsolidity/codegen/ABIFunctions.h> #include <libsolidity/codegen/ABIFunctions.h>
#include <libsolidity/ast/AST.h>
#include <libsolidity/codegen/CompilerUtils.h> #include <libsolidity/codegen/CompilerUtils.h>
#include <libdevcore/Whiskers.h> #include <libdevcore/Whiskers.h>
@ -250,11 +249,9 @@ string ABIFunctions::tupleDecoder(TypePointers const& _types, bool _fromMemory)
pair<string, set<string>> ABIFunctions::requestedFunctions() pair<string, set<string>> ABIFunctions::requestedFunctions()
{ {
string result; std::set<string> empty;
for (auto const& f: m_requestedFunctions) swap(empty, m_externallyUsedFunctions);
result += f.second; return make_pair(m_functionCollector->requestedFunctions(), std::move(empty));
m_requestedFunctions.clear();
return make_pair(result, std::move(m_externallyUsedFunctions));
} }
string ABIFunctions::EncodingOptions::toFunctionNameSuffix() const string ABIFunctions::EncodingOptions::toFunctionNameSuffix() const
@ -271,6 +268,7 @@ string ABIFunctions::EncodingOptions::toFunctionNameSuffix() const
return suffix; return suffix;
} }
string ABIFunctions::cleanupFunction(Type const& _type, bool _revertOnFailure) string ABIFunctions::cleanupFunction(Type const& _type, bool _revertOnFailure)
{ {
string functionName = string("cleanup_") + (_revertOnFailure ? "revert_" : "assert_") + _type.identifier(); string functionName = string("cleanup_") + (_revertOnFailure ? "revert_" : "assert_") + _type.identifier();
@ -401,7 +399,7 @@ string ABIFunctions::conversionFunction(Type const& _from, Type const& _to)
FixedBytesType const& toBytesType = dynamic_cast<FixedBytesType const&>(_to); FixedBytesType const& toBytesType = dynamic_cast<FixedBytesType const&>(_to);
body = body =
Whiskers("converted := <shiftLeft>(<clean>(value))") Whiskers("converted := <shiftLeft>(<clean>(value))")
("shiftLeft", shiftLeftFunction(256 - toBytesType.numBytes() * 8)) ("shiftLeft", m_utils.shiftLeftFunction(256 - toBytesType.numBytes() * 8))
("clean", cleanupFunction(_from)) ("clean", cleanupFunction(_from))
.render(); .render();
} }
@ -477,7 +475,7 @@ string ABIFunctions::conversionFunction(Type const& _from, Type const& _to)
if (toCategory == Type::Category::Integer) if (toCategory == Type::Category::Integer)
body = body =
Whiskers("converted := <convert>(<shift>(value))") Whiskers("converted := <convert>(<shift>(value))")
("shift", shiftRightFunction(256 - from.numBytes() * 8)) ("shift", m_utils.shiftRightFunction(256 - from.numBytes() * 8))
("convert", conversionFunction(IntegerType(from.numBytes() * 8), _to)) ("convert", conversionFunction(IntegerType(from.numBytes() * 8), _to))
.render(); .render();
else if (toCategory == Type::Category::Address) else if (toCategory == Type::Category::Address)
@ -541,40 +539,6 @@ string ABIFunctions::cleanupCombinedExternalFunctionIdFunction()
}); });
} }
string ABIFunctions::combineExternalFunctionIdFunction()
{
string functionName = "combine_external_function_id";
return createFunction(functionName, [&]() {
return Whiskers(R"(
function <functionName>(addr, selector) -> combined {
combined := <shl64>(or(<shl32>(addr), and(selector, 0xffffffff)))
}
)")
("functionName", functionName)
("shl32", shiftLeftFunction(32))
("shl64", shiftLeftFunction(64))
.render();
});
}
string ABIFunctions::splitExternalFunctionIdFunction()
{
string functionName = "split_external_function_id";
return createFunction(functionName, [&]() {
return Whiskers(R"(
function <functionName>(combined) -> addr, selector {
combined := <shr64>(combined)
selector := and(combined, 0xffffffff)
addr := <shr32>(combined)
}
)")
("functionName", functionName)
("shr32", shiftRightFunction(32))
("shr64", shiftRightFunction(64))
.render();
});
}
string ABIFunctions::abiEncodingFunction( string ABIFunctions::abiEncodingFunction(
Type const& _from, Type const& _from,
Type const& _to, Type const& _to,
@ -655,7 +619,7 @@ string ABIFunctions::abiEncodingFunction(
else else
cleanupConvert = conversionFunction(_from, to) + "(value)"; cleanupConvert = conversionFunction(_from, to) + "(value)";
if (!_options.padded) if (!_options.padded)
cleanupConvert = leftAlignFunction(to) + "(" + cleanupConvert + ")"; cleanupConvert = m_utils.leftAlignFunction(to) + "(" + cleanupConvert + ")";
templ("cleanupConvert", cleanupConvert); templ("cleanupConvert", cleanupConvert);
} }
return templ.render(); return templ.render();
@ -745,8 +709,8 @@ string ABIFunctions::abiEncodingFunctionCalldataArray(
templ("functionName", functionName); templ("functionName", functionName);
templ("readableTypeNameFrom", _from.toString(true)); templ("readableTypeNameFrom", _from.toString(true));
templ("readableTypeNameTo", _to.toString(true)); templ("readableTypeNameTo", _to.toString(true));
templ("copyFun", copyToMemoryFunction(true)); templ("copyFun", m_utils.copyToMemoryFunction(true));
templ("lengthPadded", _options.padded ? roundUpFunction() + "(length)" : "length"); templ("lengthPadded", _options.padded ? m_utils.roundUpFunction() + "(length)" : "length");
return templ.render(); return templ.render();
}); });
} }
@ -816,16 +780,16 @@ string ABIFunctions::abiEncodingFunctionSimpleArray(
templ("readableTypeNameTo", _to.toString(true)); templ("readableTypeNameTo", _to.toString(true));
templ("return", dynamic ? " -> end " : ""); templ("return", dynamic ? " -> end " : "");
templ("assignEnd", dynamic ? "end := pos" : ""); templ("assignEnd", dynamic ? "end := pos" : "");
templ("lengthFun", arrayLengthFunction(_from)); templ("lengthFun", m_utils.arrayLengthFunction(_from));
templ("storeLength", arrayStoreLengthForEncodingFunction(_to, _options)); templ("storeLength", arrayStoreLengthForEncodingFunction(_to, _options));
templ("dataAreaFun", arrayDataAreaFunction(_from)); templ("dataAreaFun", m_utils.arrayDataAreaFunction(_from));
EncodingOptions subOptions(_options); EncodingOptions subOptions(_options);
subOptions.encodeFunctionFromStack = false; subOptions.encodeFunctionFromStack = false;
subOptions.padded = true; subOptions.padded = true;
templ("encodeToMemoryFun", abiEncodeAndReturnUpdatedPosFunction(*_from.baseType(), *_to.baseType(), subOptions)); templ("encodeToMemoryFun", abiEncodeAndReturnUpdatedPosFunction(*_from.baseType(), *_to.baseType(), subOptions));
templ("arrayElementAccess", inMemory ? "mload(srcPtr)" : _from.baseType()->isValueType() ? "sload(srcPtr)" : "srcPtr" ); templ("arrayElementAccess", inMemory ? "mload(srcPtr)" : _from.baseType()->isValueType() ? "sload(srcPtr)" : "srcPtr" );
templ("nextArrayElement", nextArrayElementFunction(_from)); templ("nextArrayElement", m_utils.nextArrayElementFunction(_from));
return templ.render(); return templ.render();
}); });
} }
@ -859,10 +823,10 @@ string ABIFunctions::abiEncodingFunctionMemoryByteArray(
} }
)"); )");
templ("functionName", functionName); templ("functionName", functionName);
templ("lengthFun", arrayLengthFunction(_from)); templ("lengthFun", m_utils.arrayLengthFunction(_from));
templ("storeLength", arrayStoreLengthForEncodingFunction(_to, _options)); templ("storeLength", arrayStoreLengthForEncodingFunction(_to, _options));
templ("copyFun", copyToMemoryFunction(false)); templ("copyFun", m_utils.copyToMemoryFunction(false));
templ("lengthPadded", _options.padded ? roundUpFunction() + "(length)" : "length"); templ("lengthPadded", _options.padded ? m_utils.roundUpFunction() + "(length)" : "length");
return templ.render(); return templ.render();
}); });
} }
@ -920,7 +884,7 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray(
templ("storeLength", arrayStoreLengthForEncodingFunction(_to, _options)); templ("storeLength", arrayStoreLengthForEncodingFunction(_to, _options));
templ("lengthPaddedShort", _options.padded ? "0x20" : "length"); templ("lengthPaddedShort", _options.padded ? "0x20" : "length");
templ("lengthPaddedLong", _options.padded ? "i" : "length"); templ("lengthPaddedLong", _options.padded ? "i" : "length");
templ("arrayDataSlot", arrayDataAreaFunction(_from)); templ("arrayDataSlot", m_utils.arrayDataAreaFunction(_from));
return templ.render(); return templ.render();
} }
else else
@ -961,9 +925,9 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray(
templ("readableTypeNameTo", _to.toString(true)); templ("readableTypeNameTo", _to.toString(true));
templ("return", dynamic ? " -> end " : ""); templ("return", dynamic ? " -> end " : "");
templ("assignEnd", dynamic ? "end := pos" : ""); templ("assignEnd", dynamic ? "end := pos" : "");
templ("lengthFun", arrayLengthFunction(_from)); templ("lengthFun", m_utils.arrayLengthFunction(_from));
templ("storeLength", arrayStoreLengthForEncodingFunction(_to, _options)); templ("storeLength", arrayStoreLengthForEncodingFunction(_to, _options));
templ("dataArea", arrayDataAreaFunction(_from)); templ("dataArea", m_utils.arrayDataAreaFunction(_from));
templ("itemsPerSlot", to_string(itemsPerSlot)); templ("itemsPerSlot", to_string(itemsPerSlot));
// We use padded size because array elements are always padded. // We use padded size because array elements are always padded.
string elementEncodedSize = toCompactHexWithPrefix(_to.baseType()->calldataEncodedSize()); string elementEncodedSize = toCompactHexWithPrefix(_to.baseType()->calldataEncodedSize());
@ -980,7 +944,7 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray(
templ("encodeToMemoryFun", encodeToMemoryFun); templ("encodeToMemoryFun", encodeToMemoryFun);
std::vector<std::map<std::string, std::string>> items(itemsPerSlot); std::vector<std::map<std::string, std::string>> items(itemsPerSlot);
for (size_t i = 0; i < itemsPerSlot; ++i) for (size_t i = 0; i < itemsPerSlot; ++i)
items[i]["shiftRightFun"] = shiftRightFunction(i * storageBytes * 8); items[i]["shiftRightFun"] = m_utils.shiftRightFunction(i * storageBytes * 8);
templ("items", items); templ("items", items);
return templ.render(); return templ.render();
} }
@ -1066,7 +1030,7 @@ string ABIFunctions::abiEncodingFunctionStruct(
members.back()["preprocess"] = "slotValue := sload(add(value, " + toCompactHexWithPrefix(storageSlotOffset) + "))"; members.back()["preprocess"] = "slotValue := sload(add(value, " + toCompactHexWithPrefix(storageSlotOffset) + "))";
previousSlotOffset = storageSlotOffset; previousSlotOffset = storageSlotOffset;
} }
members.back()["retrieveValue"] = shiftRightFunction(intraSlotOffset * 8) + "(slotValue)"; members.back()["retrieveValue"] = m_utils.shiftRightFunction(intraSlotOffset * 8) + "(slotValue)";
} }
else else
{ {
@ -1209,7 +1173,7 @@ string ABIFunctions::abiEncodingFunctionFunctionType(
} }
)") )")
("functionName", functionName) ("functionName", functionName)
("combineExtFun", combineExternalFunctionIdFunction()) ("combineExtFun", m_utils.combineExternalFunctionIdFunction())
.render(); .render();
}); });
else else
@ -1331,8 +1295,8 @@ string ABIFunctions::abiDecodingFunctionArray(ArrayType const& _type, bool _from
templ("functionName", functionName); templ("functionName", functionName);
templ("readableTypeName", _type.toString(true)); templ("readableTypeName", _type.toString(true));
templ("retrieveLength", !_type.isDynamicallySized() ? toCompactHexWithPrefix(_type.length()) : load + "(offset)"); templ("retrieveLength", !_type.isDynamicallySized() ? toCompactHexWithPrefix(_type.length()) : load + "(offset)");
templ("allocate", allocationFunction()); templ("allocate", m_utils.allocationFunction());
templ("allocationSize", arrayAllocationSizeFunction(_type)); templ("allocationSize", m_utils.arrayAllocationSizeFunction(_type));
if (_type.isDynamicallySized()) if (_type.isDynamicallySized())
templ("storeLength", "mstore(array, length) offset := add(offset, 0x20) dst := add(dst, 0x20)"); templ("storeLength", "mstore(array, length) offset := add(offset, 0x20) dst := add(dst, 0x20)");
else else
@ -1426,9 +1390,9 @@ string ABIFunctions::abiDecodingFunctionByteArray(ArrayType const& _type, bool _
); );
templ("functionName", functionName); templ("functionName", functionName);
templ("load", _fromMemory ? "mload" : "calldataload"); templ("load", _fromMemory ? "mload" : "calldataload");
templ("allocate", allocationFunction()); templ("allocate", m_utils.allocationFunction());
templ("allocationSize", arrayAllocationSizeFunction(_type)); templ("allocationSize", m_utils.arrayAllocationSizeFunction(_type));
templ("copyToMemFun", copyToMemoryFunction(!_fromMemory)); templ("copyToMemFun", m_utils.copyToMemoryFunction(!_fromMemory));
return templ.render(); return templ.render();
}); });
} }
@ -1480,7 +1444,7 @@ string ABIFunctions::abiDecodingFunctionStruct(StructType const& _type, bool _fr
)"); )");
templ("functionName", functionName); templ("functionName", functionName);
templ("readableTypeName", _type.toString(true)); templ("readableTypeName", _type.toString(true));
templ("allocate", allocationFunction()); templ("allocate", m_utils.allocationFunction());
solAssert(_type.memorySize() < u256("0xffffffffffffffff"), ""); solAssert(_type.memorySize() < u256("0xffffffffffffffff"), "");
templ("memorySize", toCompactHexWithPrefix(_type.memorySize())); templ("memorySize", toCompactHexWithPrefix(_type.memorySize()));
size_t headPos = 0; size_t headPos = 0;
@ -1540,7 +1504,7 @@ string ABIFunctions::abiDecodingFunctionFunctionType(FunctionType const& _type,
)") )")
("functionName", functionName) ("functionName", functionName)
("load", _fromMemory ? "mload" : "calldataload") ("load", _fromMemory ? "mload" : "calldataload")
("splitExtFun", splitExternalFunctionIdFunction()) ("splitExtFun", m_utils.splitExternalFunctionIdFunction())
.render(); .render();
} }
else else
@ -1558,357 +1522,6 @@ string ABIFunctions::abiDecodingFunctionFunctionType(FunctionType const& _type,
}); });
} }
string ABIFunctions::copyToMemoryFunction(bool _fromCalldata)
{
string functionName = "copy_" + string(_fromCalldata ? "calldata" : "memory") + "_to_memory";
return createFunction(functionName, [&]() {
if (_fromCalldata)
{
return Whiskers(R"(
function <functionName>(src, dst, length) {
calldatacopy(dst, src, length)
// clear end
mstore(add(dst, length), 0)
}
)")
("functionName", functionName)
.render();
}
else
{
return Whiskers(R"(
function <functionName>(src, dst, length) {
let i := 0
for { } lt(i, length) { i := add(i, 32) }
{
mstore(add(dst, i), mload(add(src, i)))
}
if gt(i, length)
{
// clear end
mstore(add(dst, length), 0)
}
}
)")
("functionName", functionName)
.render();
}
});
}
string ABIFunctions::leftAlignFunction(Type const& _type)
{
string functionName = string("leftAlign_") + _type.identifier();
return createFunction(functionName, [&]() {
Whiskers templ(R"(
function <functionName>(value) -> aligned {
<body>
}
)");
templ("functionName", functionName);
switch (_type.category())
{
case Type::Category::Address:
templ("body", "aligned := " + leftAlignFunction(IntegerType(160)) + "(value)");
break;
case Type::Category::Integer:
{
IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
if (type.numBits() == 256)
templ("body", "aligned := value");
else
templ("body", "aligned := " + shiftLeftFunction(256 - type.numBits()) + "(value)");
break;
}
case Type::Category::RationalNumber:
solAssert(false, "Left align requested for rational number.");
break;
case Type::Category::Bool:
templ("body", "aligned := " + leftAlignFunction(IntegerType(8)) + "(value)");
break;
case Type::Category::FixedPoint:
solUnimplemented("Fixed point types not implemented.");
break;
case Type::Category::Array:
case Type::Category::Struct:
solAssert(false, "Left align requested for non-value type.");
break;
case Type::Category::FixedBytes:
templ("body", "aligned := value");
break;
case Type::Category::Contract:
templ("body", "aligned := " + leftAlignFunction(AddressType::address()) + "(value)");
break;
case Type::Category::Enum:
{
unsigned storageBytes = dynamic_cast<EnumType const&>(_type).storageBytes();
templ("body", "aligned := " + leftAlignFunction(IntegerType(8 * storageBytes)) + "(value)");
break;
}
case Type::Category::InaccessibleDynamic:
solAssert(false, "Left align requested for inaccessible dynamic type.");
break;
default:
solAssert(false, "Left align of type " + _type.identifier() + " requested.");
}
return templ.render();
});
}
string ABIFunctions::shiftLeftFunction(size_t _numBits)
{
solAssert(_numBits < 256, "");
string functionName = "shift_left_" + to_string(_numBits);
if (m_evmVersion.hasBitwiseShifting())
{
return createFunction(functionName, [&]() {
return
Whiskers(R"(
function <functionName>(value) -> newValue {
newValue := shl(<numBits>, value)
}
)")
("functionName", functionName)
("numBits", to_string(_numBits))
.render();
});
}
else
{
return createFunction(functionName, [&]() {
return
Whiskers(R"(
function <functionName>(value) -> newValue {
newValue := mul(value, <multiplier>)
}
)")
("functionName", functionName)
("multiplier", toCompactHexWithPrefix(u256(1) << _numBits))
.render();
});
}
}
string ABIFunctions::shiftRightFunction(size_t _numBits)
{
solAssert(_numBits < 256, "");
// Note that if this is extended with signed shifts,
// the opcodes SAR and SDIV behave differently with regards to rounding!
string functionName = "shift_right_" + to_string(_numBits) + "_unsigned";
if (m_evmVersion.hasBitwiseShifting())
{
return createFunction(functionName, [&]() {
return
Whiskers(R"(
function <functionName>(value) -> newValue {
newValue := shr(<numBits>, value)
}
)")
("functionName", functionName)
("numBits", to_string(_numBits))
.render();
});
}
else
{
return createFunction(functionName, [&]() {
return
Whiskers(R"(
function <functionName>(value) -> newValue {
newValue := div(value, <multiplier>)
}
)")
("functionName", functionName)
("multiplier", toCompactHexWithPrefix(u256(1) << _numBits))
.render();
});
}
}
string ABIFunctions::roundUpFunction()
{
string functionName = "round_up_to_mul_of_32";
return createFunction(functionName, [&]() {
return
Whiskers(R"(
function <functionName>(value) -> result {
result := and(add(value, 31), not(31))
}
)")
("functionName", functionName)
.render();
});
}
string ABIFunctions::arrayLengthFunction(ArrayType const& _type)
{
string functionName = "array_length_" + _type.identifier();
return createFunction(functionName, [&]() {
Whiskers w(R"(
function <functionName>(value) -> length {
<body>
}
)");
w("functionName", functionName);
string body;
if (!_type.isDynamicallySized())
body = "length := " + toCompactHexWithPrefix(_type.length());
else
{
switch (_type.location())
{
case DataLocation::CallData:
solAssert(false, "called regular array length function on calldata array");
break;
case DataLocation::Memory:
body = "length := mload(value)";
break;
case DataLocation::Storage:
if (_type.isByteArray())
{
// Retrieve length both for in-place strings and off-place strings:
// Computes (x & (0x100 * (ISZERO (x & 1)) - 1)) / 2
// i.e. for short strings (x & 1 == 0) it does (x & 0xff) / 2 and for long strings it
// computes (x & (-1)) / 2, which is equivalent to just x / 2.
body = R"(
length := sload(value)
let mask := sub(mul(0x100, iszero(and(length, 1))), 1)
length := div(and(length, mask), 2)
)";
}
else
body = "length := sload(value)";
break;
}
}
solAssert(!body.empty(), "");
w("body", body);
return w.render();
});
}
string ABIFunctions::arrayAllocationSizeFunction(ArrayType const& _type)
{
solAssert(_type.dataStoredIn(DataLocation::Memory), "");
string functionName = "array_allocation_size_" + _type.identifier();
return createFunction(functionName, [&]() {
Whiskers w(R"(
function <functionName>(length) -> size {
// Make sure we can allocate memory without overflow
if gt(length, 0xffffffffffffffff) { revert(0, 0) }
size := <allocationSize>
<addLengthSlot>
}
)");
w("functionName", functionName);
if (_type.isByteArray())
// Round up
w("allocationSize", "and(add(length, 0x1f), not(0x1f))");
else
w("allocationSize", "mul(length, 0x20)");
if (_type.isDynamicallySized())
w("addLengthSlot", "size := add(size, 0x20)");
else
w("addLengthSlot", "");
return w.render();
});
}
string ABIFunctions::arrayDataAreaFunction(ArrayType const& _type)
{
string functionName = "array_dataslot_" + _type.identifier();
return createFunction(functionName, [&]() {
if (_type.dataStoredIn(DataLocation::Memory))
{
if (_type.isDynamicallySized())
return Whiskers(R"(
function <functionName>(memPtr) -> dataPtr {
dataPtr := add(memPtr, 0x20)
}
)")
("functionName", functionName)
.render();
else
return Whiskers(R"(
function <functionName>(memPtr) -> dataPtr {
dataPtr := memPtr
}
)")
("functionName", functionName)
.render();
}
else if (_type.dataStoredIn(DataLocation::Storage))
{
if (_type.isDynamicallySized())
{
Whiskers w(R"(
function <functionName>(slot) -> dataSlot {
mstore(0, slot)
dataSlot := keccak256(0, 0x20)
}
)");
w("functionName", functionName);
return w.render();
}
else
{
Whiskers w(R"(
function <functionName>(slot) -> dataSlot {
dataSlot := slot
}
)");
w("functionName", functionName);
return w.render();
}
}
else
{
// Not used for calldata
solAssert(false, "");
}
});
}
string ABIFunctions::nextArrayElementFunction(ArrayType const& _type)
{
solAssert(!_type.isByteArray(), "");
solAssert(
_type.location() == DataLocation::Memory ||
_type.location() == DataLocation::Storage,
""
);
solAssert(
_type.location() == DataLocation::Memory ||
_type.baseType()->storageBytes() > 16,
""
);
string functionName = "array_nextElement_" + _type.identifier();
return createFunction(functionName, [&]() {
if (_type.location() == DataLocation::Memory)
return Whiskers(R"(
function <functionName>(memPtr) -> nextPtr {
nextPtr := add(memPtr, 0x20)
}
)")
("functionName", functionName)
.render();
else if (_type.location() == DataLocation::Storage)
return Whiskers(R"(
function <functionName>(slot) -> nextSlot {
nextSlot := add(slot, 1)
}
)")
("functionName", functionName)
.render();
else
solAssert(false, "");
});
}
string ABIFunctions::arrayStoreLengthForEncodingFunction(ArrayType const& _type, EncodingOptions const& _options) string ABIFunctions::arrayStoreLengthForEncodingFunction(ArrayType const& _type, EncodingOptions const& _options)
{ {
string functionName = "array_storeLengthForEncoding_" + _type.identifier() + _options.toFunctionNameSuffix(); string functionName = "array_storeLengthForEncoding_" + _type.identifier() + _options.toFunctionNameSuffix();
@ -1933,34 +1546,9 @@ string ABIFunctions::arrayStoreLengthForEncodingFunction(ArrayType const& _type,
}); });
} }
string ABIFunctions::allocationFunction()
{
string functionName = "allocateMemory";
return createFunction(functionName, [&]() {
return Whiskers(R"(
function <functionName>(size) -> memPtr {
memPtr := mload(<freeMemoryPointer>)
let newFreePtr := add(memPtr, size)
// protect against overflow
if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { revert(0, 0) }
mstore(<freeMemoryPointer>, newFreePtr)
}
)")
("freeMemoryPointer", to_string(CompilerUtils::freeMemoryPointer))
("functionName", functionName)
.render();
});
}
string ABIFunctions::createFunction(string const& _name, function<string ()> const& _creator) string ABIFunctions::createFunction(string const& _name, function<string ()> const& _creator)
{ {
if (!m_requestedFunctions.count(_name)) return m_functionCollector->createFunction(_name, _creator);
{
auto fun = _creator();
solAssert(!fun.empty(), "");
m_requestedFunctions[_name] = fun;
}
return _name;
} }
string ABIFunctions::createExternallyUsedFunction(string const& _name, function<string ()> const& _creator) string ABIFunctions::createExternallyUsedFunction(string const& _name, function<string ()> const& _creator)

View File

@ -22,7 +22,9 @@
#pragma once #pragma once
#include <libsolidity/ast/ASTForward.h> #include <libsolidity/codegen/MultiUseYulFunctionCollector.h>
#include <libsolidity/codegen/YulUtilFunctions.h>
#include <liblangutil/EVMVersion.h> #include <liblangutil/EVMVersion.h>
#include <functional> #include <functional>
@ -30,8 +32,10 @@
#include <set> #include <set>
#include <vector> #include <vector>
namespace dev { namespace dev
namespace solidity { {
namespace solidity
{
class Type; class Type;
class ArrayType; class ArrayType;
@ -40,17 +44,25 @@ class FunctionType;
using TypePointer = std::shared_ptr<Type const>; using TypePointer = std::shared_ptr<Type const>;
using TypePointers = std::vector<TypePointer>; using TypePointers = std::vector<TypePointer>;
/// /**
/// Class to generate encoding and decoding functions. Also maintains a collection * Class to generate encoding and decoding functions. Also maintains a collection
/// of "functions to be generated" in order to avoid generating the same function * of "functions to be generated" in order to avoid generating the same function
/// multiple times. * multiple times.
/// *
/// Make sure to include the result of ``requestedFunctions()`` to a block that * Make sure to include the result of ``requestedFunctions()`` to a block that
/// is visible from the code that was generated here, or use named labels. * is visible from the code that was generated here, or use named labels.
*/
class ABIFunctions class ABIFunctions
{ {
public: public:
explicit ABIFunctions(langutil::EVMVersion _evmVersion = langutil::EVMVersion{}) : m_evmVersion(_evmVersion) {} explicit ABIFunctions(
langutil::EVMVersion _evmVersion,
std::shared_ptr<MultiUseYulFunctionCollector> _functionCollector = std::make_shared<MultiUseYulFunctionCollector>()
):
m_evmVersion(_evmVersion),
m_functionCollector(std::move(_functionCollector)),
m_utils(_evmVersion, m_functionCollector)
{}
/// @returns name of an assembly function to ABI-encode values of @a _givenTypes /// @returns name of an assembly function to ABI-encode values of @a _givenTypes
/// into memory, converting the types to @a _targetTypes on the fly. /// into memory, converting the types to @a _targetTypes on the fly.
@ -129,14 +141,6 @@ private:
std::string cleanupCombinedExternalFunctionIdFunction(); std::string cleanupCombinedExternalFunctionIdFunction();
/// @returns a function that combines the address and selector to a single value
/// for use in the ABI.
std::string combineExternalFunctionIdFunction();
/// @returns a function that splits the address and selector from a single value
/// for use in the ABI.
std::string splitExternalFunctionIdFunction();
/// @returns the name of the ABI encoding function with the given type /// @returns the name of the ABI encoding function with the given type
/// and queues the generation of the function to the requested functions. /// and queues the generation of the function to the requested functions.
/// @param _fromStack if false, the input value was just loaded from storage /// @param _fromStack if false, the input value was just loaded from storage
@ -229,34 +233,6 @@ private:
/// Part of @a abiDecodingFunction for array types. /// Part of @a abiDecodingFunction for array types.
std::string abiDecodingFunctionFunctionType(FunctionType const& _type, bool _fromMemory, bool _forUseOnStack); std::string abiDecodingFunctionFunctionType(FunctionType const& _type, bool _fromMemory, bool _forUseOnStack);
/// @returns a function that copies raw bytes of dynamic length from calldata
/// or memory to memory.
/// Pads with zeros and might write more than exactly length.
std::string copyToMemoryFunction(bool _fromCalldata);
/// @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);
std::string shiftLeftFunction(size_t _numBits);
std::string shiftRightFunction(size_t _numBits);
/// @returns the name of a function that rounds its input to the next multiple
/// of 32 or the input if it is a multiple of 32.
std::string roundUpFunction();
std::string arrayLengthFunction(ArrayType const& _type);
/// @returns the name of a function that computes the number of bytes required
/// to store an array in memory given its length (internally encoded, not ABI encoded).
/// The function reverts for too large lengths.
std::string arrayAllocationSizeFunction(ArrayType const& _type);
/// @returns the name of a function that converts a storage slot number
/// or a memory pointer to the slot number / memory pointer for the data position of an array
/// which is stored in that slot / memory area.
std::string arrayDataAreaFunction(ArrayType const& _type);
/// @returns the name of a function that advances an array data pointer to the next element.
/// Only works for memory arrays and storage arrays that store one item per slot.
std::string nextArrayElementFunction(ArrayType const& _type);
/// @returns the name of a function used during encoding that stores the length /// @returns the name of a function used during encoding that stores the length
/// if the array is dynamically sized (and the options do not request in-place encoding). /// if the array is dynamically sized (and the options do not request in-place encoding).
/// It returns the new encoding position. /// It returns the new encoding position.
@ -264,12 +240,6 @@ private:
/// does nothing and just returns the position again. /// does nothing and just returns the position again.
std::string arrayStoreLengthForEncodingFunction(ArrayType const& _type, EncodingOptions const& _options); std::string arrayStoreLengthForEncodingFunction(ArrayType const& _type, EncodingOptions const& _options);
/// @returns the name of a function that allocates memory.
/// Modifies the "free memory pointer"
/// Arguments: size
/// Return value: pointer
std::string allocationFunction();
/// Helper function that uses @a _creator to create a function and add it to /// Helper function that uses @a _creator to create a function and add it to
/// @a m_requestedFunctions if it has not been created yet and returns @a _name in both /// @a m_requestedFunctions if it has not been created yet and returns @a _name in both
/// cases. /// cases.
@ -283,10 +253,10 @@ private:
/// @returns the size of the static part of the encoding of the given types. /// @returns the size of the static part of the encoding of the given types.
static size_t headSize(TypePointers const& _targetTypes); static size_t headSize(TypePointers const& _targetTypes);
/// Map from function name to code for a multi-use function.
std::map<std::string, std::string> m_requestedFunctions;
std::set<std::string> m_externallyUsedFunctions;
langutil::EVMVersion m_evmVersion; langutil::EVMVersion m_evmVersion;
std::shared_ptr<MultiUseYulFunctionCollector> m_functionCollector;
std::set<std::string> m_externallyUsedFunctions;
YulUtilFunctions m_utils;
}; };
} }

View File

@ -0,0 +1,52 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Container of (unparsed) Yul functions identified by name which are meant to be generated
* only once.
*/
#include <libsolidity/codegen/MultiUseYulFunctionCollector.h>
#include <liblangutil/Exceptions.h>
#include <boost/algorithm/string/join.hpp>
#include <boost/range/adaptor/reversed.hpp>
using namespace std;
using namespace dev;
using namespace dev::solidity;
string MultiUseYulFunctionCollector::requestedFunctions()
{
string result;
for (auto const& f: m_requestedFunctions)
result += f.second;
m_requestedFunctions.clear();
return result;
}
string MultiUseYulFunctionCollector::createFunction(string const& _name, function<string ()> const& _creator)
{
if (!m_requestedFunctions.count(_name))
{
string fun = _creator();
solAssert(!fun.empty(), "");
solAssert(fun.find("function " + _name) != string::npos, "Function not properly named.");
m_requestedFunctions[_name] = std::move(fun);
}
return _name;
}

View File

@ -0,0 +1,56 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Container of (unparsed) Yul functions identified by name which are meant to be generated
* only once.
*/
#pragma once
#include <functional>
#include <map>
#include <string>
namespace dev
{
namespace solidity
{
/**
* Container of (unparsed) Yul functions identified by name which are meant to be generated
* only once.
*/
class MultiUseYulFunctionCollector
{
public:
/// Helper function that uses @a _creator to create a function and add it to
/// @a m_requestedFunctions if it has not been created yet and returns @a _name in both
/// cases.
std::string createFunction(std::string const& _name, std::function<std::string()> const& _creator);
/// @returns concatenation of all generated functions.
/// Clears the internal list, i.e. calling it again will result in an
/// empty return value.
std::string requestedFunctions();
private:
/// Map from function name to code for a multi-use function.
std::map<std::string, std::string> m_requestedFunctions;
};
}
}

View File

@ -0,0 +1,438 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Component that can generate various useful Yul functions.
*/
#include <libsolidity/codegen/YulUtilFunctions.h>
#include <libsolidity/codegen/MultiUseYulFunctionCollector.h>
#include <libsolidity/ast/AST.h>
#include <libsolidity/codegen/CompilerUtils.h>
#include <libdevcore/Whiskers.h>
#include <boost/algorithm/string/join.hpp>
#include <boost/range/adaptor/reversed.hpp>
using namespace std;
using namespace dev;
using namespace dev::solidity;
string YulUtilFunctions::combineExternalFunctionIdFunction()
{
string functionName = "combine_external_function_id";
return m_functionCollector->createFunction(functionName, [&]() {
return Whiskers(R"(
function <functionName>(addr, selector) -> combined {
combined := <shl64>(or(<shl32>(addr), and(selector, 0xffffffff)))
}
)")
("functionName", functionName)
("shl32", shiftLeftFunction(32))
("shl64", shiftLeftFunction(64))
.render();
});
}
string YulUtilFunctions::splitExternalFunctionIdFunction()
{
string functionName = "split_external_function_id";
return m_functionCollector->createFunction(functionName, [&]() {
return Whiskers(R"(
function <functionName>(combined) -> addr, selector {
combined := <shr64>(combined)
selector := and(combined, 0xffffffff)
addr := <shr32>(combined)
}
)")
("functionName", functionName)
("shr32", shiftRightFunction(32))
("shr64", shiftRightFunction(64))
.render();
});
}
string YulUtilFunctions::copyToMemoryFunction(bool _fromCalldata)
{
string functionName = "copy_" + string(_fromCalldata ? "calldata" : "memory") + "_to_memory";
return m_functionCollector->createFunction(functionName, [&]() {
if (_fromCalldata)
{
return Whiskers(R"(
function <functionName>(src, dst, length) {
calldatacopy(dst, src, length)
// clear end
mstore(add(dst, length), 0)
}
)")
("functionName", functionName)
.render();
}
else
{
return Whiskers(R"(
function <functionName>(src, dst, length) {
let i := 0
for { } lt(i, length) { i := add(i, 32) }
{
mstore(add(dst, i), mload(add(src, i)))
}
if gt(i, length)
{
// clear end
mstore(add(dst, length), 0)
}
}
)")
("functionName", functionName)
.render();
}
});
}
string YulUtilFunctions::leftAlignFunction(Type const& _type)
{
string functionName = string("leftAlign_") + _type.identifier();
return m_functionCollector->createFunction(functionName, [&]() {
Whiskers templ(R"(
function <functionName>(value) -> aligned {
<body>
}
)");
templ("functionName", functionName);
switch (_type.category())
{
case Type::Category::Address:
templ("body", "aligned := " + leftAlignFunction(IntegerType(160)) + "(value)");
break;
case Type::Category::Integer:
{
IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
if (type.numBits() == 256)
templ("body", "aligned := value");
else
templ("body", "aligned := " + shiftLeftFunction(256 - type.numBits()) + "(value)");
break;
}
case Type::Category::RationalNumber:
solAssert(false, "Left align requested for rational number.");
break;
case Type::Category::Bool:
templ("body", "aligned := " + leftAlignFunction(IntegerType(8)) + "(value)");
break;
case Type::Category::FixedPoint:
solUnimplemented("Fixed point types not implemented.");
break;
case Type::Category::Array:
case Type::Category::Struct:
solAssert(false, "Left align requested for non-value type.");
break;
case Type::Category::FixedBytes:
templ("body", "aligned := value");
break;
case Type::Category::Contract:
templ("body", "aligned := " + leftAlignFunction(AddressType::address()) + "(value)");
break;
case Type::Category::Enum:
{
unsigned storageBytes = dynamic_cast<EnumType const&>(_type).storageBytes();
templ("body", "aligned := " + leftAlignFunction(IntegerType(8 * storageBytes)) + "(value)");
break;
}
case Type::Category::InaccessibleDynamic:
solAssert(false, "Left align requested for inaccessible dynamic type.");
break;
default:
solAssert(false, "Left align of type " + _type.identifier() + " requested.");
}
return templ.render();
});
}
string YulUtilFunctions::shiftLeftFunction(size_t _numBits)
{
solAssert(_numBits < 256, "");
string functionName = "shift_left_" + to_string(_numBits);
if (m_evmVersion.hasBitwiseShifting())
{
return m_functionCollector->createFunction(functionName, [&]() {
return
Whiskers(R"(
function <functionName>(value) -> newValue {
newValue := shl(<numBits>, value)
}
)")
("functionName", functionName)
("numBits", to_string(_numBits))
.render();
});
}
else
{
return m_functionCollector->createFunction(functionName, [&]() {
return
Whiskers(R"(
function <functionName>(value) -> newValue {
newValue := mul(value, <multiplier>)
}
)")
("functionName", functionName)
("multiplier", toCompactHexWithPrefix(u256(1) << _numBits))
.render();
});
}
}
string YulUtilFunctions::shiftRightFunction(size_t _numBits)
{
solAssert(_numBits < 256, "");
// Note that if this is extended with signed shifts,
// the opcodes SAR and SDIV behave differently with regards to rounding!
string functionName = "shift_right_" + to_string(_numBits) + "_unsigned";
if (m_evmVersion.hasBitwiseShifting())
{
return m_functionCollector->createFunction(functionName, [&]() {
return
Whiskers(R"(
function <functionName>(value) -> newValue {
newValue := shr(<numBits>, value)
}
)")
("functionName", functionName)
("numBits", to_string(_numBits))
.render();
});
}
else
{
return m_functionCollector->createFunction(functionName, [&]() {
return
Whiskers(R"(
function <functionName>(value) -> newValue {
newValue := div(value, <multiplier>)
}
)")
("functionName", functionName)
("multiplier", toCompactHexWithPrefix(u256(1) << _numBits))
.render();
});
}
}
string YulUtilFunctions::roundUpFunction()
{
string functionName = "round_up_to_mul_of_32";
return m_functionCollector->createFunction(functionName, [&]() {
return
Whiskers(R"(
function <functionName>(value) -> result {
result := and(add(value, 31), not(31))
}
)")
("functionName", functionName)
.render();
});
}
string YulUtilFunctions::arrayLengthFunction(ArrayType const& _type)
{
string functionName = "array_length_" + _type.identifier();
return m_functionCollector->createFunction(functionName, [&]() {
Whiskers w(R"(
function <functionName>(value) -> length {
<body>
}
)");
w("functionName", functionName);
string body;
if (!_type.isDynamicallySized())
body = "length := " + toCompactHexWithPrefix(_type.length());
else
{
switch (_type.location())
{
case DataLocation::CallData:
solAssert(false, "called regular array length function on calldata array");
break;
case DataLocation::Memory:
body = "length := mload(value)";
break;
case DataLocation::Storage:
if (_type.isByteArray())
{
// Retrieve length both for in-place strings and off-place strings:
// Computes (x & (0x100 * (ISZERO (x & 1)) - 1)) / 2
// i.e. for short strings (x & 1 == 0) it does (x & 0xff) / 2 and for long strings it
// computes (x & (-1)) / 2, which is equivalent to just x / 2.
body = R"(
length := sload(value)
let mask := sub(mul(0x100, iszero(and(length, 1))), 1)
length := div(and(length, mask), 2)
)";
}
else
body = "length := sload(value)";
break;
}
}
solAssert(!body.empty(), "");
w("body", body);
return w.render();
});
}
string YulUtilFunctions::arrayAllocationSizeFunction(ArrayType const& _type)
{
solAssert(_type.dataStoredIn(DataLocation::Memory), "");
string functionName = "array_allocation_size_" + _type.identifier();
return m_functionCollector->createFunction(functionName, [&]() {
Whiskers w(R"(
function <functionName>(length) -> size {
// Make sure we can allocate memory without overflow
if gt(length, 0xffffffffffffffff) { revert(0, 0) }
size := <allocationSize>
<addLengthSlot>
}
)");
w("functionName", functionName);
if (_type.isByteArray())
// Round up
w("allocationSize", "and(add(length, 0x1f), not(0x1f))");
else
w("allocationSize", "mul(length, 0x20)");
if (_type.isDynamicallySized())
w("addLengthSlot", "size := add(size, 0x20)");
else
w("addLengthSlot", "");
return w.render();
});
}
string YulUtilFunctions::arrayDataAreaFunction(ArrayType const& _type)
{
string functionName = "array_dataslot_" + _type.identifier();
return m_functionCollector->createFunction(functionName, [&]() {
if (_type.dataStoredIn(DataLocation::Memory))
{
if (_type.isDynamicallySized())
return Whiskers(R"(
function <functionName>(memPtr) -> dataPtr {
dataPtr := add(memPtr, 0x20)
}
)")
("functionName", functionName)
.render();
else
return Whiskers(R"(
function <functionName>(memPtr) -> dataPtr {
dataPtr := memPtr
}
)")
("functionName", functionName)
.render();
}
else if (_type.dataStoredIn(DataLocation::Storage))
{
if (_type.isDynamicallySized())
{
Whiskers w(R"(
function <functionName>(slot) -> dataSlot {
mstore(0, slot)
dataSlot := keccak256(0, 0x20)
}
)");
w("functionName", functionName);
return w.render();
}
else
{
Whiskers w(R"(
function <functionName>(slot) -> dataSlot {
dataSlot := slot
}
)");
w("functionName", functionName);
return w.render();
}
}
else
{
// Not used for calldata
solAssert(false, "");
}
});
}
string YulUtilFunctions::nextArrayElementFunction(ArrayType const& _type)
{
solAssert(!_type.isByteArray(), "");
solAssert(
_type.location() == DataLocation::Memory ||
_type.location() == DataLocation::Storage,
""
);
solAssert(
_type.location() == DataLocation::Memory ||
_type.baseType()->storageBytes() > 16,
""
);
string functionName = "array_nextElement_" + _type.identifier();
return m_functionCollector->createFunction(functionName, [&]() {
if (_type.location() == DataLocation::Memory)
return Whiskers(R"(
function <functionName>(memPtr) -> nextPtr {
nextPtr := add(memPtr, 0x20)
}
)")
("functionName", functionName)
.render();
else if (_type.location() == DataLocation::Storage)
return Whiskers(R"(
function <functionName>(slot) -> nextSlot {
nextSlot := add(slot, 1)
}
)")
("functionName", functionName)
.render();
else
solAssert(false, "");
});
}
string YulUtilFunctions::allocationFunction()
{
string functionName = "allocateMemory";
return m_functionCollector->createFunction(functionName, [&]() {
return Whiskers(R"(
function <functionName>(size) -> memPtr {
memPtr := mload(<freeMemoryPointer>)
let newFreePtr := add(memPtr, size)
// protect against overflow
if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { revert(0, 0) }
mstore(<freeMemoryPointer>, newFreePtr)
}
)")
("freeMemoryPointer", to_string(CompilerUtils::freeMemoryPointer))
("functionName", functionName)
.render();
});
}

View File

@ -0,0 +1,100 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Component that can generate various useful Yul functions.
*/
#pragma once
#include <liblangutil/EVMVersion.h>
#include <libsolidity/codegen/MultiUseYulFunctionCollector.h>
#include <memory>
#include <string>
namespace dev
{
namespace solidity
{
class Type;
class ArrayType;
/**
* Component that can generate various useful Yul functions.
*/
class YulUtilFunctions
{
public:
explicit YulUtilFunctions(
langutil::EVMVersion _evmVersion,
std::shared_ptr<MultiUseYulFunctionCollector> _functionCollector
):
m_evmVersion(_evmVersion),
m_functionCollector(std::move(_functionCollector))
{}
/// @returns a function that combines the address and selector to a single value
/// for use in the ABI.
std::string combineExternalFunctionIdFunction();
/// @returns a function that splits the address and selector from a single value
/// for use in the ABI.
std::string splitExternalFunctionIdFunction();
/// @returns a function that copies raw bytes of dynamic length from calldata
/// or memory to memory.
/// Pads with zeros and might write more than exactly length.
std::string copyToMemoryFunction(bool _fromCalldata);
/// @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);
std::string shiftLeftFunction(size_t _numBits);
std::string shiftRightFunction(size_t _numBits);
/// @returns the name of a function that rounds its input to the next multiple
/// of 32 or the input if it is a multiple of 32.
std::string roundUpFunction();
std::string arrayLengthFunction(ArrayType const& _type);
/// @returns the name of a function that computes the number of bytes required
/// to store an array in memory given its length (internally encoded, not ABI encoded).
/// The function reverts for too large lengths.
std::string arrayAllocationSizeFunction(ArrayType const& _type);
/// @returns the name of a function that converts a storage slot number
/// or a memory pointer to the slot number / memory pointer for the data position of an array
/// which is stored in that slot / memory area.
std::string arrayDataAreaFunction(ArrayType const& _type);
/// @returns the name of a function that advances an array data pointer to the next element.
/// Only works for memory arrays and storage arrays that store one item per slot.
std::string nextArrayElementFunction(ArrayType const& _type);
/// @returns the name of a function that allocates memory.
/// Modifies the "free memory pointer"
/// Arguments: size
/// Return value: pointer
std::string allocationFunction();
private:
langutil::EVMVersion m_evmVersion;
std::shared_ptr<MultiUseYulFunctionCollector> m_functionCollector;
};
}
}