(value) -> newValue {
newValue := (value, )
})")
("functionName", functionName)
("div", _signed ? "sdiv" : "div")
("multiplier", toCompactHexWithPrefix(u256(1) << _numBits))
.render();
});
}
string ABIFunctions::roundUpFunction()
{
string functionName = "round_up_to_mul_of_32";
return createFunction(functionName, [&]() {
return
Whiskers(R"(function (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 (value) -> length {
}
)");
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::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 (memPtr) -> dataPtr {
dataPtr := add(memPtr, 0x20)
}
)")
("functionName", functionName)
.render();
else
return Whiskers(R"(
function (memPtr) -> dataPtr {
dataPtr := memPtr
}
)")
("functionName", functionName)
.render();
}
else if (_type.dataStoredIn(DataLocation::Storage))
{
if (_type.isDynamicallySized())
{
Whiskers w(R"(
function (slot) -> dataSlot {
mstore(0, slot)
dataSlot := keccak256(0, 0x20)
}
)");
w("functionName", functionName);
return w.render();
}
else
{
Whiskers w(R"(
function (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 (memPtr) -> nextPtr {
nextPtr := add(memPtr, 0x20)
}
)")
("functionName", functionName)
.render();
else if (_type.location() == DataLocation::Storage)
return Whiskers(R"(
function (slot) -> nextSlot {
nextSlot := add(slot, 1)
}
)")
("functionName", functionName)
.render();
else
solAssert(false, "");
});
}
string ABIFunctions::createFunction(string const& _name, function const& _creator)
{
if (!m_requestedFunctions.count(_name))
{
auto fun = _creator();
solAssert(!fun.empty(), "");
m_requestedFunctions[_name] = fun;
}
return _name;
}
size_t ABIFunctions::headSize(TypePointers const& _targetTypes)
{
size_t headSize = 0;
for (auto const& t: _targetTypes)
{
if (t->isDynamicallyEncoded())
headSize += 0x20;
else
{
solAssert(t->calldataEncodedSize() > 0, "");
headSize += t->calldataEncodedSize();
}
}
return headSize;
}