mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Extract utility functions from ABI encoder.
This commit is contained in:
parent
40171c216d
commit
d515d77bfe
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
52
libsolidity/codegen/MultiUseYulFunctionCollector.cpp
Normal file
52
libsolidity/codegen/MultiUseYulFunctionCollector.cpp
Normal 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;
|
||||||
|
}
|
56
libsolidity/codegen/MultiUseYulFunctionCollector.h
Normal file
56
libsolidity/codegen/MultiUseYulFunctionCollector.h
Normal 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
438
libsolidity/codegen/YulUtilFunctions.cpp
Normal file
438
libsolidity/codegen/YulUtilFunctions.cpp
Normal 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();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
100
libsolidity/codegen/YulUtilFunctions.h
Normal file
100
libsolidity/codegen/YulUtilFunctions.h
Normal 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user