mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #9056 from ethereum/solYulGetters
[Sol->Yul] Implement getters.
This commit is contained in:
commit
f38e3a3574
@ -1276,6 +1276,7 @@ string YulUtilFunctions::mappingIndexAccessFunction(MappingType const& _mappingT
|
|||||||
|
|
||||||
string YulUtilFunctions::readFromStorage(Type const& _type, size_t _offset, bool _splitFunctionTypes)
|
string YulUtilFunctions::readFromStorage(Type const& _type, size_t _offset, bool _splitFunctionTypes)
|
||||||
{
|
{
|
||||||
|
if (_type.category() == Type::Category::Function)
|
||||||
solUnimplementedAssert(!_splitFunctionTypes, "");
|
solUnimplementedAssert(!_splitFunctionTypes, "");
|
||||||
string functionName =
|
string functionName =
|
||||||
"read_from_storage_" +
|
"read_from_storage_" +
|
||||||
@ -1299,6 +1300,7 @@ 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)
|
||||||
{
|
{
|
||||||
|
if (_type.category() == Type::Category::Function)
|
||||||
solUnimplementedAssert(!_splitFunctionTypes, "");
|
solUnimplementedAssert(!_splitFunctionTypes, "");
|
||||||
string functionName =
|
string functionName =
|
||||||
"read_from_storage_dynamic" +
|
"read_from_storage_dynamic" +
|
||||||
@ -1429,6 +1431,7 @@ string YulUtilFunctions::writeToMemoryFunction(Type const& _type)
|
|||||||
|
|
||||||
string YulUtilFunctions::extractFromStorageValueDynamic(Type const& _type, bool _splitFunctionTypes)
|
string YulUtilFunctions::extractFromStorageValueDynamic(Type const& _type, bool _splitFunctionTypes)
|
||||||
{
|
{
|
||||||
|
if (_type.category() == Type::Category::Function)
|
||||||
solUnimplementedAssert(!_splitFunctionTypes, "");
|
solUnimplementedAssert(!_splitFunctionTypes, "");
|
||||||
|
|
||||||
string functionName =
|
string functionName =
|
||||||
@ -1474,6 +1477,7 @@ string YulUtilFunctions::extractFromStorageValue(Type const& _type, size_t _offs
|
|||||||
string YulUtilFunctions::cleanupFromStorageFunction(Type const& _type, bool _splitFunctionTypes)
|
string YulUtilFunctions::cleanupFromStorageFunction(Type const& _type, bool _splitFunctionTypes)
|
||||||
{
|
{
|
||||||
solAssert(_type.isValueType(), "");
|
solAssert(_type.isValueType(), "");
|
||||||
|
if (_type.category() == Type::Category::Function)
|
||||||
solUnimplementedAssert(!_splitFunctionTypes, "");
|
solUnimplementedAssert(!_splitFunctionTypes, "");
|
||||||
|
|
||||||
string functionName = string("cleanup_from_storage_") + (_splitFunctionTypes ? "split_" : "") + _type.identifier();
|
string functionName = string("cleanup_from_storage_") + (_splitFunctionTypes ? "split_" : "") + _type.identifier();
|
||||||
|
@ -268,65 +268,16 @@ string IRGenerator::generateFunction(FunctionDefinition const& _function)
|
|||||||
string IRGenerator::generateGetter(VariableDeclaration const& _varDecl)
|
string IRGenerator::generateGetter(VariableDeclaration const& _varDecl)
|
||||||
{
|
{
|
||||||
string functionName = IRNames::function(_varDecl);
|
string functionName = IRNames::function(_varDecl);
|
||||||
|
return m_context.functionCollector().createFunction(functionName, [&]() {
|
||||||
Type const* type = _varDecl.annotation().type;
|
Type const* type = _varDecl.annotation().type;
|
||||||
|
|
||||||
solAssert(_varDecl.isStateVariable(), "");
|
solAssert(_varDecl.isStateVariable(), "");
|
||||||
|
|
||||||
if (auto const* mappingType = dynamic_cast<MappingType const*>(type))
|
FunctionType accessorType(_varDecl);
|
||||||
return m_context.functionCollector().createFunction(functionName, [&]() {
|
TypePointers paramTypes = accessorType.parameterTypes();
|
||||||
solAssert(!_varDecl.isConstant() && !_varDecl.immutable(), "");
|
|
||||||
pair<u256, unsigned> slot_offset = m_context.storageLocationOfVariable(_varDecl);
|
|
||||||
solAssert(slot_offset.second == 0, "");
|
|
||||||
FunctionType funType(_varDecl);
|
|
||||||
solUnimplementedAssert(funType.returnParameterTypes().size() == 1, "");
|
|
||||||
TypePointer returnType = funType.returnParameterTypes().front();
|
|
||||||
unsigned num_keys = 0;
|
|
||||||
stringstream indexAccesses;
|
|
||||||
string slot = m_context.newYulVariable();
|
|
||||||
do
|
|
||||||
{
|
|
||||||
solUnimplementedAssert(
|
|
||||||
mappingType->keyType()->sizeOnStack() == 1,
|
|
||||||
"Multi-slot mapping key unimplemented - might not be a problem"
|
|
||||||
);
|
|
||||||
indexAccesses <<
|
|
||||||
slot <<
|
|
||||||
" := " <<
|
|
||||||
m_utils.mappingIndexAccessFunction(*mappingType, *mappingType->keyType()) <<
|
|
||||||
"(" <<
|
|
||||||
slot;
|
|
||||||
if (mappingType->keyType()->sizeOnStack() > 0)
|
|
||||||
indexAccesses <<
|
|
||||||
", " <<
|
|
||||||
suffixedVariableNameList("key", num_keys, num_keys + mappingType->keyType()->sizeOnStack());
|
|
||||||
indexAccesses << ")\n";
|
|
||||||
num_keys += mappingType->keyType()->sizeOnStack();
|
|
||||||
}
|
|
||||||
while ((mappingType = dynamic_cast<MappingType const*>(mappingType->valueType())));
|
|
||||||
|
|
||||||
return Whiskers(R"(
|
|
||||||
function <functionName>(<keys>) -> rval {
|
|
||||||
let <slot> := <base>
|
|
||||||
<indexAccesses>
|
|
||||||
rval := <readStorage>(<slot>)
|
|
||||||
}
|
|
||||||
)")
|
|
||||||
("functionName", functionName)
|
|
||||||
("keys", suffixedVariableNameList("key", 0, num_keys))
|
|
||||||
("readStorage", m_utils.readFromStorage(*returnType, 0, false))
|
|
||||||
("indexAccesses", indexAccesses.str())
|
|
||||||
("slot", slot)
|
|
||||||
("base", slot_offset.first.str())
|
|
||||||
.render();
|
|
||||||
});
|
|
||||||
else
|
|
||||||
{
|
|
||||||
solUnimplementedAssert(type->isValueType(), "");
|
|
||||||
|
|
||||||
return m_context.functionCollector().createFunction(functionName, [&]() {
|
|
||||||
if (_varDecl.immutable())
|
if (_varDecl.immutable())
|
||||||
{
|
{
|
||||||
|
solAssert(paramTypes.empty(), "");
|
||||||
solUnimplementedAssert(type->sizeOnStack() == 1, "");
|
solUnimplementedAssert(type->sizeOnStack() == 1, "");
|
||||||
return Whiskers(R"(
|
return Whiskers(R"(
|
||||||
function <functionName>() -> rval {
|
function <functionName>() -> rval {
|
||||||
@ -338,6 +289,8 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl)
|
|||||||
.render();
|
.render();
|
||||||
}
|
}
|
||||||
else if (_varDecl.isConstant())
|
else if (_varDecl.isConstant())
|
||||||
|
{
|
||||||
|
solAssert(paramTypes.empty(), "");
|
||||||
return Whiskers(R"(
|
return Whiskers(R"(
|
||||||
function <functionName>() -> <ret> {
|
function <functionName>() -> <ret> {
|
||||||
<ret> := <constantValueFunction>()
|
<ret> := <constantValueFunction>()
|
||||||
@ -347,22 +300,113 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl)
|
|||||||
("constantValueFunction", IRGeneratorForStatements(m_context, m_utils).constantValueFunction(_varDecl))
|
("constantValueFunction", IRGeneratorForStatements(m_context, m_utils).constantValueFunction(_varDecl))
|
||||||
("ret", suffixedVariableNameList("ret_", 0, _varDecl.type()->sizeOnStack()))
|
("ret", suffixedVariableNameList("ret_", 0, _varDecl.type()->sizeOnStack()))
|
||||||
.render();
|
.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
string code;
|
||||||
|
|
||||||
|
auto const& location = m_context.storageLocationOfVariable(_varDecl);
|
||||||
|
code += Whiskers(R"(
|
||||||
|
let slot := <slot>
|
||||||
|
let offset := <offset>
|
||||||
|
)")
|
||||||
|
("slot", location.first.str())
|
||||||
|
("offset", to_string(location.second))
|
||||||
|
.render();
|
||||||
|
|
||||||
|
if (!paramTypes.empty())
|
||||||
|
solAssert(
|
||||||
|
location.second == 0,
|
||||||
|
"If there are parameters, we are dealing with structs or mappings and thus should have offset zero."
|
||||||
|
);
|
||||||
|
|
||||||
|
// The code of an accessor is of the form `x[a][b][c]` (it is slightly more complicated
|
||||||
|
// if the final type is a struct).
|
||||||
|
// In each iteration of the loop below, we consume one parameter, perform an
|
||||||
|
// index access, reassign the yul variable `slot` and move @a currentType further "down".
|
||||||
|
// The initial value of @a currentType is only used if we skip the loop completely.
|
||||||
|
TypePointer currentType = _varDecl.annotation().type;
|
||||||
|
|
||||||
|
vector<string> parameters;
|
||||||
|
vector<string> returnVariables;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < paramTypes.size(); ++i)
|
||||||
|
{
|
||||||
|
MappingType const* mappingType = dynamic_cast<MappingType const*>(currentType);
|
||||||
|
ArrayType const* arrayType = dynamic_cast<ArrayType const*>(currentType);
|
||||||
|
solAssert(mappingType || arrayType, "");
|
||||||
|
|
||||||
|
vector<string> keys = IRVariable("key_" + to_string(i),
|
||||||
|
mappingType ? *mappingType->keyType() : *TypeProvider::uint256()
|
||||||
|
).stackSlots();
|
||||||
|
parameters += keys;
|
||||||
|
code += Whiskers(R"(
|
||||||
|
slot<?array>, offset</array> := <indexAccess>(slot<?+keys>, <keys></+keys>)
|
||||||
|
)")
|
||||||
|
(
|
||||||
|
"indexAccess",
|
||||||
|
mappingType ?
|
||||||
|
m_utils.mappingIndexAccessFunction(*mappingType, *mappingType->keyType()) :
|
||||||
|
m_utils.storageArrayIndexAccessFunction(*arrayType)
|
||||||
|
)
|
||||||
|
("array", arrayType != nullptr)
|
||||||
|
("keys", joinHumanReadable(keys))
|
||||||
|
.render();
|
||||||
|
|
||||||
|
currentType = mappingType ? mappingType->valueType() : arrayType->baseType();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto returnTypes = accessorType.returnParameterTypes();
|
||||||
|
solAssert(returnTypes.size() >= 1, "");
|
||||||
|
if (StructType const* structType = dynamic_cast<StructType const*>(currentType))
|
||||||
|
{
|
||||||
|
solAssert(location.second == 0, "");
|
||||||
|
auto const& names = accessorType.returnParameterNames();
|
||||||
|
for (size_t i = 0; i < names.size(); ++i)
|
||||||
|
{
|
||||||
|
if (returnTypes[i]->category() == Type::Category::Mapping)
|
||||||
|
continue;
|
||||||
|
if (auto arrayType = dynamic_cast<ArrayType const*>(returnTypes[i]))
|
||||||
|
if (!arrayType->isByteArray())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// TODO conversion from storage byte arrays is not yet implemented.
|
||||||
|
pair<u256, unsigned> const& offsets = structType->storageOffsetsOfMember(names[i]);
|
||||||
|
vector<string> retVars = IRVariable("ret_" + to_string(returnVariables.size()), *returnTypes[i]).stackSlots();
|
||||||
|
returnVariables += retVars;
|
||||||
|
code += Whiskers(R"(
|
||||||
|
<ret> := <readStorage>(add(slot, <slotOffset>))
|
||||||
|
)")
|
||||||
|
("ret", joinHumanReadable(retVars))
|
||||||
|
("readStorage", m_utils.readFromStorage(*returnTypes[i], offsets.second, true))
|
||||||
|
("slotOffset", offsets.first.str())
|
||||||
|
.render();
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
pair<u256, unsigned> slot_offset = m_context.storageLocationOfVariable(_varDecl);
|
solAssert(returnTypes.size() == 1, "");
|
||||||
|
vector<string> retVars = IRVariable("ret", *returnTypes.front()).stackSlots();
|
||||||
|
returnVariables += retVars;
|
||||||
|
// TODO conversion from storage byte arrays is not yet implemented.
|
||||||
|
code += Whiskers(R"(
|
||||||
|
<ret> := <readStorage>(slot, offset)
|
||||||
|
)")
|
||||||
|
("ret", joinHumanReadable(retVars))
|
||||||
|
("readStorage", m_utils.readFromStorageDynamic(*returnTypes.front(), true))
|
||||||
|
.render();
|
||||||
|
}
|
||||||
|
|
||||||
return Whiskers(R"(
|
return Whiskers(R"(
|
||||||
function <functionName>() -> rval {
|
function <functionName>(<params>) -> <retVariables> {
|
||||||
rval := <readStorage>(<slot>)
|
<code>
|
||||||
}
|
}
|
||||||
)")
|
)")
|
||||||
("functionName", functionName)
|
("functionName", functionName)
|
||||||
("readStorage", m_utils.readFromStorage(*type, slot_offset.second, false))
|
("params", joinHumanReadable(parameters))
|
||||||
("slot", slot_offset.first.str())
|
("retVariables", joinHumanReadable(returnVariables))
|
||||||
|
("code", std::move(code))
|
||||||
.render();
|
.render();
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
string IRGenerator::generateInitialAssignment(VariableDeclaration const& _varDecl)
|
string IRGenerator::generateInitialAssignment(VariableDeclaration const& _varDecl)
|
||||||
|
14
test/libsolidity/semanticTests/getters/arrays.sol
Normal file
14
test/libsolidity/semanticTests/getters/arrays.sol
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
contract C {
|
||||||
|
uint8[][2] public a;
|
||||||
|
constructor() public {
|
||||||
|
a[1].push(3);
|
||||||
|
a[1].push(4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// a(uint256,uint256): 0, 0 -> FAILURE
|
||||||
|
// a(uint256,uint256): 1, 0 -> 3
|
||||||
|
// a(uint256,uint256): 1, 1 -> 4
|
||||||
|
// a(uint256,uint256): 2, 0 -> FAILURE
|
11
test/libsolidity/semanticTests/getters/mapping.sol
Normal file
11
test/libsolidity/semanticTests/getters/mapping.sol
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
contract C {
|
||||||
|
mapping(uint => mapping(uint => uint)) public x;
|
||||||
|
constructor() public {
|
||||||
|
x[1][2] = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// x(uint256,uint256): 1, 2 -> 3
|
||||||
|
// x(uint256,uint256): 0, 0 -> 0
|
19
test/libsolidity/semanticTests/getters/small_types.sol
Normal file
19
test/libsolidity/semanticTests/getters/small_types.sol
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
contract C {
|
||||||
|
uint8 public a;
|
||||||
|
uint16 public b;
|
||||||
|
uint128 public c;
|
||||||
|
uint public d;
|
||||||
|
constructor() public {
|
||||||
|
a = 3;
|
||||||
|
b = 4;
|
||||||
|
c = 5;
|
||||||
|
d = 6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// a() -> 3
|
||||||
|
// b() -> 4
|
||||||
|
// c() -> 5
|
||||||
|
// d() -> 6
|
@ -8,6 +8,8 @@ contract test {
|
|||||||
dynamicData[2][2] = 8;
|
dynamicData[2][2] = 8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// data(uint256,uint256): 2, 2 -> 8
|
// data(uint256,uint256): 2, 2 -> 8
|
||||||
// data(uint256,uint256): 2, 8 -> FAILURE # NB: the original code contained a bug here #
|
// data(uint256,uint256): 2, 8 -> FAILURE # NB: the original code contained a bug here #
|
||||||
|
Loading…
Reference in New Issue
Block a user