mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #6655 from ethereum/storageMappings
[SolYul] Storage mappings
This commit is contained in:
commit
4d460915f3
@ -539,6 +539,48 @@ string YulUtilFunctions::nextArrayElementFunction(ArrayType const& _type)
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::mappingIndexAccessFunction(MappingType const& _mappingType, Type const& _keyType)
|
||||
{
|
||||
solAssert(_keyType.sizeOnStack() <= 1, "");
|
||||
|
||||
string functionName = "mapping_index_access_" + _mappingType.identifier() + "_of_" + _keyType.identifier();
|
||||
return m_functionCollector->createFunction(functionName, [&]() {
|
||||
if (_mappingType.keyType()->isDynamicallySized())
|
||||
return Whiskers(R"(
|
||||
function <functionName>(slot <comma> <key>) -> dataSlot {
|
||||
dataSlot := <hash>(slot <comma> <key>)
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("key", _keyType.sizeOnStack() > 0 ? "key" : "")
|
||||
("comma", _keyType.sizeOnStack() > 0 ? "," : "")
|
||||
("hash", packedHashFunction(
|
||||
{&_keyType, TypeProvider::uint256()},
|
||||
{_mappingType.keyType(), TypeProvider::uint256()}
|
||||
))
|
||||
.render();
|
||||
else
|
||||
{
|
||||
solAssert(CompilerUtils::freeMemoryPointer >= 0x40, "");
|
||||
solAssert(!_mappingType.keyType()->isDynamicallyEncoded(), "");
|
||||
solAssert(_mappingType.keyType()->calldataEncodedSize(false) <= 0x20, "");
|
||||
Whiskers templ(R"(
|
||||
function <functionName>(slot <key>) -> dataSlot {
|
||||
mstore(0, <convertedKey>)
|
||||
mstore(0x20, slot)
|
||||
dataSlot := keccak256(0, 0x40)
|
||||
}
|
||||
)");
|
||||
templ("functionName", functionName);
|
||||
templ("key", _keyType.sizeOnStack() == 1 ? ", key" : "");
|
||||
if (_keyType.sizeOnStack() == 0)
|
||||
templ("convertedKey", conversionFunction(_keyType, *_mappingType.keyType()) + "()");
|
||||
else
|
||||
templ("convertedKey", conversionFunction(_keyType, *_mappingType.keyType()) + "(key)");
|
||||
return templ.render();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::readFromStorage(Type const& _type, size_t _offset, bool _splitFunctionTypes)
|
||||
{
|
||||
|
@ -35,6 +35,7 @@ namespace solidity
|
||||
|
||||
class Type;
|
||||
class ArrayType;
|
||||
class MappingType;
|
||||
|
||||
/**
|
||||
* Component that can generate various useful Yul functions.
|
||||
@ -98,6 +99,11 @@ public:
|
||||
/// Only works for memory arrays, calldata arrays and storage arrays that store one item per slot.
|
||||
std::string nextArrayElementFunction(ArrayType const& _type);
|
||||
|
||||
/// @returns the name of a function that performs index access for mappings.
|
||||
/// @param _mappingType the type of the mapping
|
||||
/// @param _keyType the type of the value provided
|
||||
std::string mappingIndexAccessFunction(MappingType const& _mappingType, Type const& _keyType);
|
||||
|
||||
/// @returns a function that reads a value type from storage.
|
||||
/// 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
|
||||
|
@ -614,6 +614,51 @@ bool IRGeneratorForStatements::visit(InlineAssembly const& _inlineAsm)
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess)
|
||||
{
|
||||
Type const& baseType = *_indexAccess.baseExpression().annotation().type;
|
||||
|
||||
if (baseType.category() == Type::Category::Mapping)
|
||||
{
|
||||
solAssert(_indexAccess.indexExpression(), "Index expression expected.");
|
||||
|
||||
MappingType const& mappingType = dynamic_cast<MappingType const&>(baseType);
|
||||
Type const& keyType = *_indexAccess.indexExpression()->annotation().type;
|
||||
solAssert(keyType.sizeOnStack() <= 1, "");
|
||||
|
||||
string slot = m_context.newYulVariable();
|
||||
Whiskers templ("let <slot> := <indexAccess>(<base> <key>)\n");
|
||||
templ("slot", slot);
|
||||
templ("indexAccess", m_utils.mappingIndexAccessFunction(mappingType, keyType));
|
||||
templ("base", m_context.variable(_indexAccess.baseExpression()));
|
||||
if (keyType.sizeOnStack() == 0)
|
||||
templ("key", "");
|
||||
else
|
||||
templ("key", ", " + m_context.variable(*_indexAccess.indexExpression()));
|
||||
m_code << templ.render();
|
||||
setLValue(_indexAccess, make_unique<IRStorageItem>(
|
||||
m_code,
|
||||
m_context,
|
||||
slot,
|
||||
0,
|
||||
*_indexAccess.annotation().type
|
||||
));
|
||||
}
|
||||
else if (baseType.category() == Type::Category::Array)
|
||||
solUnimplementedAssert(false, "");
|
||||
else if (baseType.category() == Type::Category::FixedBytes)
|
||||
solUnimplementedAssert(false, "");
|
||||
else if (baseType.category() == Type::Category::TypeType)
|
||||
{
|
||||
solAssert(baseType.sizeOnStack() == 0, "");
|
||||
solAssert(_indexAccess.annotation().type->sizeOnStack() == 0, "");
|
||||
// no-op - this seems to be a lone array type (`structType[];`)
|
||||
}
|
||||
else
|
||||
solAssert(false, "Index access only allowed for mappings or arrays.");
|
||||
}
|
||||
|
||||
void IRGeneratorForStatements::endVisit(Identifier const& _identifier)
|
||||
{
|
||||
Declaration const* declaration = _identifier.annotation().referencedDeclaration;
|
||||
|
@ -57,6 +57,7 @@ public:
|
||||
void endVisit(FunctionCall const& _funCall) override;
|
||||
void endVisit(MemberAccess const& _memberAccess) override;
|
||||
bool visit(InlineAssembly const& _inlineAsm) override;
|
||||
void endVisit(IndexAccess const& _indexAccess) override;
|
||||
void endVisit(Identifier const& _identifier) override;
|
||||
bool visit(Literal const& _literal) override;
|
||||
|
||||
|
@ -60,6 +60,19 @@ IRStorageItem::IRStorageItem(
|
||||
m_offset = offset;
|
||||
}
|
||||
|
||||
IRStorageItem::IRStorageItem(
|
||||
ostream& _code,
|
||||
IRGenerationContext& _context,
|
||||
string _slot,
|
||||
unsigned _offset,
|
||||
Type const& _type
|
||||
):
|
||||
IRLValue(_code, _context, &_type),
|
||||
m_slot(move(_slot)),
|
||||
m_offset(_offset)
|
||||
{
|
||||
}
|
||||
|
||||
string IRStorageItem::retrieveValue() const
|
||||
{
|
||||
if (!m_type->isValueType())
|
||||
|
@ -82,6 +82,13 @@ public:
|
||||
IRGenerationContext& _context,
|
||||
VariableDeclaration const& _varDecl
|
||||
);
|
||||
IRStorageItem(
|
||||
std::ostream& _code,
|
||||
IRGenerationContext& _context,
|
||||
std::string _slot,
|
||||
unsigned _offset,
|
||||
Type const& _type
|
||||
);
|
||||
std::string retrieveValue() const override;
|
||||
void storeValue(std::string const& _value, Type const& _type) const override;
|
||||
|
||||
|
39
test/libsolidity/semanticTests/viaYul/storage/mappings.sol
Normal file
39
test/libsolidity/semanticTests/viaYul/storage/mappings.sol
Normal file
@ -0,0 +1,39 @@
|
||||
contract C {
|
||||
mapping(uint => uint) simple;
|
||||
mapping(uint16 => uint) cleanup;
|
||||
mapping(string => uint) str;
|
||||
mapping(uint => mapping(uint => uint)) twodim;
|
||||
function test_simple(uint _off) public returns (uint _a, uint _b, uint _c) {
|
||||
simple[_off + 2] = 3;
|
||||
simple[_off + 3] = 4;
|
||||
simple[uint(-1)] = 5;
|
||||
_c = simple[uint(-1)];
|
||||
_b = simple[3 + _off];
|
||||
_a = simple[2 + _off];
|
||||
}
|
||||
function test_cleanup() public returns (bool) {
|
||||
uint16 x;
|
||||
assembly { x := 0xffff0001 }
|
||||
cleanup[x] = 3;
|
||||
return cleanup[1] == 3;
|
||||
}
|
||||
function test_str() public returns (bool) {
|
||||
str["abc"] = 3;
|
||||
string memory s = "abc";
|
||||
return str[s] == 3;
|
||||
}
|
||||
function test_twodim() public returns (uint a, uint b) {
|
||||
twodim[2][3] = 3;
|
||||
a = twodim[3][2];
|
||||
b = twodim[2][3];
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: true
|
||||
// ----
|
||||
// test_simple(uint256): 0 -> 3, 4, 5
|
||||
// test_simple(uint256): 1 -> 3, 4, 5
|
||||
// test_simple(uint256): 2 -> 3, 4, 5
|
||||
// test_cleanup() -> true
|
||||
// test_str() -> true
|
||||
// test_twodim() -> 0, 3
|
Loading…
Reference in New Issue
Block a user