mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #10182 from ethereum/storeExternalFunctions
Store external functions
This commit is contained in:
commit
2f3b0bf9ef
@ -802,7 +802,7 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray(
|
|||||||
items[i]["inRange"] = "1";
|
items[i]["inRange"] = "1";
|
||||||
else
|
else
|
||||||
items[i]["inRange"] = "0";
|
items[i]["inRange"] = "0";
|
||||||
items[i]["extractFromSlot"] = m_utils.extractFromStorageValue(*_from.baseType(), i * storageBytes, false);
|
items[i]["extractFromSlot"] = m_utils.extractFromStorageValue(*_from.baseType(), i * storageBytes);
|
||||||
}
|
}
|
||||||
templ("items", items);
|
templ("items", items);
|
||||||
return templ.render();
|
return templ.render();
|
||||||
@ -888,7 +888,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"] = m_utils.extractFromStorageValue(*memberTypeFrom, intraSlotOffset, false) + "(slotValue)";
|
members.back()["retrieveValue"] = m_utils.extractFromStorageValue(*memberTypeFrom, intraSlotOffset) + "(slotValue)";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -2059,58 +2059,47 @@ string YulUtilFunctions::readFromStorage(Type const& _type, size_t _offset, bool
|
|||||||
string YulUtilFunctions::readFromStorageDynamic(Type const& _type, bool _splitFunctionTypes)
|
string YulUtilFunctions::readFromStorageDynamic(Type const& _type, bool _splitFunctionTypes)
|
||||||
{
|
{
|
||||||
solAssert(_type.isValueType(), "");
|
solAssert(_type.isValueType(), "");
|
||||||
return readFromStorageValueTypeDynamic(_type, _splitFunctionTypes);
|
return readFromStorageValueType(_type, {}, _splitFunctionTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
string YulUtilFunctions::readFromStorageValueType(Type const& _type, size_t _offset, bool _splitFunctionTypes)
|
string YulUtilFunctions::readFromStorageValueType(Type const& _type, optional<size_t> _offset, bool _splitFunctionTypes)
|
||||||
{
|
{
|
||||||
solAssert(_type.isValueType(), "");
|
solAssert(_type.isValueType(), "");
|
||||||
|
|
||||||
if (_type.category() == Type::Category::Function)
|
|
||||||
solUnimplementedAssert(!_splitFunctionTypes, "");
|
|
||||||
string functionName =
|
string functionName =
|
||||||
"read_from_storage_" +
|
"read_from_storage_" +
|
||||||
string(_splitFunctionTypes ? "split_" : "") +
|
string(_splitFunctionTypes ? "split_" : "") + (
|
||||||
"offset_" +
|
_offset.has_value() ?
|
||||||
to_string(_offset) +
|
"offset_" + to_string(*_offset) :
|
||||||
|
"dynamic"
|
||||||
|
) +
|
||||||
"_" +
|
"_" +
|
||||||
_type.identifier();
|
_type.identifier();
|
||||||
|
|
||||||
return m_functionCollector.createFunction(functionName, [&] {
|
return m_functionCollector.createFunction(functionName, [&] {
|
||||||
solAssert(_type.sizeOnStack() == 1, "");
|
Whiskers templ(R"(
|
||||||
return Whiskers(R"(
|
function <functionName>(slot<?dynamic>, offset</dynamic>) -> <?split>addr, selector<!split>value</split> {
|
||||||
function <functionName>(slot) -> value {
|
<?split>let</split> value := <extract>(sload(slot)<?dynamic>, offset</dynamic>)
|
||||||
value := <extract>(sload(slot))
|
<?split>
|
||||||
|
addr, selector := <splitFunction>(value)
|
||||||
|
</split>
|
||||||
}
|
}
|
||||||
)")
|
)");
|
||||||
("functionName", functionName)
|
templ("functionName", functionName);
|
||||||
("extract", extractFromStorageValue(_type, _offset, false))
|
templ("dynamic", !_offset.has_value());
|
||||||
.render();
|
if (_offset.has_value())
|
||||||
|
templ("extract", extractFromStorageValue(_type, *_offset));
|
||||||
|
else
|
||||||
|
templ("extract", extractFromStorageValueDynamic(_type));
|
||||||
|
auto const* funType = dynamic_cast<FunctionType const*>(&_type);
|
||||||
|
bool split = _splitFunctionTypes && funType && funType->kind() == FunctionType::Kind::External;
|
||||||
|
templ("split", split);
|
||||||
|
if (split)
|
||||||
|
templ("splitFunction", splitExternalFunctionIdFunction());
|
||||||
|
return templ.render();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
string YulUtilFunctions::readFromStorageValueTypeDynamic(Type const& _type, bool _splitFunctionTypes)
|
|
||||||
{
|
|
||||||
solAssert(_type.isValueType(), "");
|
|
||||||
if (_type.category() == Type::Category::Function)
|
|
||||||
solUnimplementedAssert(!_splitFunctionTypes, "");
|
|
||||||
|
|
||||||
string functionName =
|
|
||||||
"read_from_storage_value_type_dynamic" +
|
|
||||||
string(_splitFunctionTypes ? "split_" : "") +
|
|
||||||
"_" +
|
|
||||||
_type.identifier();
|
|
||||||
return m_functionCollector.createFunction(functionName, [&] {
|
|
||||||
solAssert(_type.sizeOnStack() == 1, "");
|
|
||||||
return Whiskers(R"(
|
|
||||||
function <functionName>(slot, offset) -> value {
|
|
||||||
value := <extract>(sload(slot), offset)
|
|
||||||
}
|
|
||||||
)")
|
|
||||||
("functionName", functionName)
|
|
||||||
("extract", extractFromStorageValueDynamic(_type, _splitFunctionTypes))
|
|
||||||
.render();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
string YulUtilFunctions::readFromStorageReferenceType(Type const& _type)
|
string YulUtilFunctions::readFromStorageReferenceType(Type const& _type)
|
||||||
{
|
{
|
||||||
solUnimplementedAssert(_type.category() == Type::Category::Struct, "");
|
solUnimplementedAssert(_type.category() == Type::Category::Struct, "");
|
||||||
@ -2343,9 +2332,7 @@ string YulUtilFunctions::updateStorageValueFunction(
|
|||||||
|
|
||||||
string YulUtilFunctions::writeToMemoryFunction(Type const& _type)
|
string YulUtilFunctions::writeToMemoryFunction(Type const& _type)
|
||||||
{
|
{
|
||||||
string const functionName =
|
string const functionName = "write_to_memory_" + _type.identifier();
|
||||||
string("write_to_memory_") +
|
|
||||||
_type.identifier();
|
|
||||||
|
|
||||||
return m_functionCollector.createFunction(functionName, [&] {
|
return m_functionCollector.createFunction(functionName, [&] {
|
||||||
solAssert(!dynamic_cast<StringLiteralType const*>(&_type), "");
|
solAssert(!dynamic_cast<StringLiteralType const*>(&_type), "");
|
||||||
@ -2399,14 +2386,10 @@ string YulUtilFunctions::writeToMemoryFunction(Type const& _type)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
string YulUtilFunctions::extractFromStorageValueDynamic(Type const& _type, bool _splitFunctionTypes)
|
string YulUtilFunctions::extractFromStorageValueDynamic(Type const& _type)
|
||||||
{
|
{
|
||||||
if (_type.category() == Type::Category::Function)
|
|
||||||
solUnimplementedAssert(!_splitFunctionTypes, "");
|
|
||||||
|
|
||||||
string functionName =
|
string functionName =
|
||||||
"extract_from_storage_value_dynamic" +
|
"extract_from_storage_value_dynamic" +
|
||||||
string(_splitFunctionTypes ? "split_" : "") +
|
|
||||||
_type.identifier();
|
_type.identifier();
|
||||||
return m_functionCollector.createFunction(functionName, [&] {
|
return m_functionCollector.createFunction(functionName, [&] {
|
||||||
return Whiskers(R"(
|
return Whiskers(R"(
|
||||||
@ -2416,21 +2399,14 @@ string YulUtilFunctions::extractFromStorageValueDynamic(Type const& _type, bool
|
|||||||
)")
|
)")
|
||||||
("functionName", functionName)
|
("functionName", functionName)
|
||||||
("shr", shiftRightFunctionDynamic())
|
("shr", shiftRightFunctionDynamic())
|
||||||
("cleanupStorage", cleanupFromStorageFunction(_type, _splitFunctionTypes))
|
("cleanupStorage", cleanupFromStorageFunction(_type))
|
||||||
.render();
|
.render();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
string YulUtilFunctions::extractFromStorageValue(Type const& _type, size_t _offset, bool _splitFunctionTypes)
|
string YulUtilFunctions::extractFromStorageValue(Type const& _type, size_t _offset)
|
||||||
{
|
{
|
||||||
solUnimplementedAssert(!_splitFunctionTypes, "");
|
string functionName = "extract_from_storage_value_offset_" + to_string(_offset) + _type.identifier();
|
||||||
|
|
||||||
string functionName =
|
|
||||||
"extract_from_storage_value_" +
|
|
||||||
string(_splitFunctionTypes ? "split_" : "") +
|
|
||||||
"offset_" +
|
|
||||||
to_string(_offset) +
|
|
||||||
_type.identifier();
|
|
||||||
return m_functionCollector.createFunction(functionName, [&] {
|
return m_functionCollector.createFunction(functionName, [&] {
|
||||||
return Whiskers(R"(
|
return Whiskers(R"(
|
||||||
function <functionName>(slot_value) -> value {
|
function <functionName>(slot_value) -> value {
|
||||||
@ -2439,18 +2415,16 @@ string YulUtilFunctions::extractFromStorageValue(Type const& _type, size_t _offs
|
|||||||
)")
|
)")
|
||||||
("functionName", functionName)
|
("functionName", functionName)
|
||||||
("shr", shiftRightFunction(_offset * 8))
|
("shr", shiftRightFunction(_offset * 8))
|
||||||
("cleanupStorage", cleanupFromStorageFunction(_type, _splitFunctionTypes))
|
("cleanupStorage", cleanupFromStorageFunction(_type))
|
||||||
.render();
|
.render();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
string YulUtilFunctions::cleanupFromStorageFunction(Type const& _type, bool _splitFunctionTypes)
|
string YulUtilFunctions::cleanupFromStorageFunction(Type const& _type)
|
||||||
{
|
{
|
||||||
solAssert(_type.isValueType(), "");
|
solAssert(_type.isValueType(), "");
|
||||||
if (_type.category() == Type::Category::Function)
|
|
||||||
solUnimplementedAssert(!_splitFunctionTypes, "");
|
|
||||||
|
|
||||||
string functionName = string("cleanup_from_storage_") + (_splitFunctionTypes ? "split_" : "") + _type.identifier();
|
string functionName = string("cleanup_from_storage_") + _type.identifier();
|
||||||
return m_functionCollector.createFunction(functionName, [&] {
|
return m_functionCollector.createFunction(functionName, [&] {
|
||||||
Whiskers templ(R"(
|
Whiskers templ(R"(
|
||||||
function <functionName>(value) -> cleaned {
|
function <functionName>(value) -> cleaned {
|
||||||
@ -2487,22 +2461,37 @@ string YulUtilFunctions::cleanupFromStorageFunction(Type const& _type, bool _spl
|
|||||||
|
|
||||||
string YulUtilFunctions::prepareStoreFunction(Type const& _type)
|
string YulUtilFunctions::prepareStoreFunction(Type const& _type)
|
||||||
{
|
{
|
||||||
if (_type.category() == Type::Category::Function)
|
|
||||||
solUnimplementedAssert(dynamic_cast<FunctionType const&>(_type).kind() == FunctionType::Kind::Internal, "");
|
|
||||||
|
|
||||||
string functionName = "prepare_store_" + _type.identifier();
|
string functionName = "prepare_store_" + _type.identifier();
|
||||||
return m_functionCollector.createFunction(functionName, [&]() {
|
return m_functionCollector.createFunction(functionName, [&]() {
|
||||||
Whiskers templ(R"(
|
solAssert(_type.isValueType(), "");
|
||||||
function <functionName>(value) -> ret {
|
auto const* funType = dynamic_cast<FunctionType const*>(&_type);
|
||||||
ret := <actualPrepare>
|
if (funType && funType->kind() == FunctionType::Kind::External)
|
||||||
}
|
{
|
||||||
)");
|
Whiskers templ(R"(
|
||||||
templ("functionName", functionName);
|
function <functionName>(addr, selector) -> ret {
|
||||||
if (_type.category() == Type::Category::FixedBytes)
|
ret := <prepareBytes>(<combine>(addr, selector))
|
||||||
templ("actualPrepare", shiftRightFunction(256 - 8 * _type.storageBytes()) + "(value)");
|
}
|
||||||
|
)");
|
||||||
|
templ("functionName", functionName);
|
||||||
|
templ("prepareBytes", prepareStoreFunction(*TypeProvider::fixedBytes(24)));
|
||||||
|
templ("combine", combineExternalFunctionIdFunction());
|
||||||
|
return templ.render();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
templ("actualPrepare", "value");
|
{
|
||||||
return templ.render();
|
solAssert(_type.sizeOnStack() == 1, "");
|
||||||
|
Whiskers templ(R"(
|
||||||
|
function <functionName>(value) -> ret {
|
||||||
|
ret := <actualPrepare>
|
||||||
|
}
|
||||||
|
)");
|
||||||
|
templ("functionName", functionName);
|
||||||
|
if (_type.category() == Type::Category::FixedBytes)
|
||||||
|
templ("actualPrepare", shiftRightFunction(256 - 8 * _type.storageBytes()) + "(value)");
|
||||||
|
else
|
||||||
|
templ("actualPrepare", "value");
|
||||||
|
return templ.render();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,10 +266,10 @@ public:
|
|||||||
/// @returns a function that extracts a value type from storage slot that has been
|
/// @returns a function that extracts a value type from storage slot that has been
|
||||||
/// retrieved already.
|
/// retrieved already.
|
||||||
/// Performs bit mask/sign extend cleanup and appropriate left / right shift, but not validation.
|
/// Performs bit mask/sign extend cleanup and appropriate left / right shift, but not validation.
|
||||||
/// @param _splitFunctionTypes if false, returns the address and function signature in a
|
///
|
||||||
/// single variable.
|
/// For external function types, input and output is in "compressed"/"unsplit" form.
|
||||||
std::string extractFromStorageValue(Type const& _type, size_t _offset, bool _splitFunctionTypes);
|
std::string extractFromStorageValue(Type const& _type, size_t _offset);
|
||||||
std::string extractFromStorageValueDynamic(Type const& _type, bool _splitFunctionTypes);
|
std::string extractFromStorageValueDynamic(Type const& _type);
|
||||||
|
|
||||||
/// Returns the name of a function will write the given value to
|
/// Returns the name of a function will write the given value to
|
||||||
/// the specified slot and offset. If offset is not given, it is expected as
|
/// the specified slot and offset. If offset is not given, it is expected as
|
||||||
@ -292,9 +292,8 @@ public:
|
|||||||
/// higher order bytes or left-aligns (in case of bytesNN).
|
/// higher order bytes or left-aligns (in case of bytesNN).
|
||||||
/// The storage cleanup expects the value to be right-aligned with potentially
|
/// The storage cleanup expects the value to be right-aligned with potentially
|
||||||
/// dirty higher order bytes.
|
/// dirty higher order bytes.
|
||||||
/// @param _splitFunctionTypes if false, returns the address and function signature in a
|
/// For external functions, input and output is in "compressed"/"unsplit" form.
|
||||||
/// single variable.
|
std::string cleanupFromStorageFunction(Type const& _type);
|
||||||
std::string cleanupFromStorageFunction(Type const& _type, bool _splitFunctionTypes);
|
|
||||||
|
|
||||||
/// @returns the name of a function that prepares a value of the given type
|
/// @returns the name of a function that prepares a value of the given type
|
||||||
/// for being stored in storage. This usually includes cleanup and right-alignment
|
/// for being stored in storage. This usually includes cleanup and right-alignment
|
||||||
@ -440,8 +439,8 @@ private:
|
|||||||
/// Performs bit mask/sign extend cleanup and appropriate left / right shift, but not validation.
|
/// Performs bit mask/sign extend cleanup and appropriate left / right shift, but not validation.
|
||||||
/// @param _splitFunctionTypes if false, returns the address and function signature in a
|
/// @param _splitFunctionTypes if false, returns the address and function signature in a
|
||||||
/// single variable.
|
/// single variable.
|
||||||
std::string readFromStorageValueType(Type const& _type, size_t _offset, bool _splitFunctionTypes);
|
/// @param _offset if provided, read from static offset, otherwise offset is a parameter of the Yul function.
|
||||||
std::string readFromStorageValueTypeDynamic(Type const& _type, bool _splitFunctionTypes);
|
std::string readFromStorageValueType(Type const& _type, std::optional<size_t> _offset, bool _splitFunctionTypes);
|
||||||
|
|
||||||
/// @returns a function that reads a reference type from storage to memory (performing a deep copy).
|
/// @returns a function that reads a reference type from storage to memory (performing a deep copy).
|
||||||
std::string readFromStorageReferenceType(Type const& _type);
|
std::string readFromStorageReferenceType(Type const& _type);
|
||||||
|
@ -2733,7 +2733,7 @@ IRVariable IRGeneratorForStatements::readFromLValue(IRLValue const& _lvalue)
|
|||||||
define(result) << _storage.slot << "\n";
|
define(result) << _storage.slot << "\n";
|
||||||
else if (std::holds_alternative<string>(_storage.offset))
|
else if (std::holds_alternative<string>(_storage.offset))
|
||||||
define(result) <<
|
define(result) <<
|
||||||
m_utils.readFromStorageDynamic(_lvalue.type, false) <<
|
m_utils.readFromStorageDynamic(_lvalue.type, true) <<
|
||||||
"(" <<
|
"(" <<
|
||||||
_storage.slot <<
|
_storage.slot <<
|
||||||
", " <<
|
", " <<
|
||||||
@ -2741,7 +2741,7 @@ IRVariable IRGeneratorForStatements::readFromLValue(IRLValue const& _lvalue)
|
|||||||
")\n";
|
")\n";
|
||||||
else
|
else
|
||||||
define(result) <<
|
define(result) <<
|
||||||
m_utils.readFromStorage(_lvalue.type, std::get<unsigned>(_storage.offset), false) <<
|
m_utils.readFromStorage(_lvalue.type, std::get<unsigned>(_storage.offset), true) <<
|
||||||
"(" <<
|
"(" <<
|
||||||
_storage.slot <<
|
_storage.slot <<
|
||||||
")\n";
|
")\n";
|
||||||
|
@ -24,5 +24,7 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// t() -> 9
|
// t() -> 9
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
struct S {
|
||||||
|
uint16 a;
|
||||||
|
function() external returns (uint) x;
|
||||||
|
uint16 b;
|
||||||
|
}
|
||||||
|
contract Flow {
|
||||||
|
S[2] t;
|
||||||
|
|
||||||
|
function X() public pure returns (uint) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Y() public pure returns (uint) {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
t[0].a = 0xff07;
|
||||||
|
t[0].b = 0xff07;
|
||||||
|
t[1].x = this.Y;
|
||||||
|
t[1].a = 0xff07;
|
||||||
|
t[1].b = 0xff07;
|
||||||
|
t[0].x = this.X;
|
||||||
|
}
|
||||||
|
|
||||||
|
function f() public returns (uint, uint) {
|
||||||
|
return (t[0].x(), t[1].x());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f() -> 1, 2
|
@ -39,6 +39,8 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// set() ->
|
// set() ->
|
||||||
// t1() -> 7
|
// t1() -> 7
|
||||||
|
Loading…
Reference in New Issue
Block a user