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)
|
string YulUtilFunctions::readFromStorage(Type const& _type, size_t _offset, bool _splitFunctionTypes)
|
||||||
{
|
{
|
||||||
|
@ -35,6 +35,7 @@ namespace solidity
|
|||||||
|
|
||||||
class Type;
|
class Type;
|
||||||
class ArrayType;
|
class ArrayType;
|
||||||
|
class MappingType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that can generate various useful Yul functions.
|
* 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.
|
/// Only works for memory arrays, calldata arrays and storage arrays that store one item per slot.
|
||||||
std::string nextArrayElementFunction(ArrayType const& _type);
|
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.
|
/// @returns a function that reads a value type from storage.
|
||||||
/// 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
|
||||||
|
@ -614,6 +614,51 @@ bool IRGeneratorForStatements::visit(InlineAssembly const& _inlineAsm)
|
|||||||
return false;
|
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)
|
void IRGeneratorForStatements::endVisit(Identifier const& _identifier)
|
||||||
{
|
{
|
||||||
Declaration const* declaration = _identifier.annotation().referencedDeclaration;
|
Declaration const* declaration = _identifier.annotation().referencedDeclaration;
|
||||||
|
@ -57,6 +57,7 @@ public:
|
|||||||
void endVisit(FunctionCall const& _funCall) override;
|
void endVisit(FunctionCall const& _funCall) override;
|
||||||
void endVisit(MemberAccess const& _memberAccess) override;
|
void endVisit(MemberAccess const& _memberAccess) override;
|
||||||
bool visit(InlineAssembly const& _inlineAsm) override;
|
bool visit(InlineAssembly const& _inlineAsm) override;
|
||||||
|
void endVisit(IndexAccess const& _indexAccess) override;
|
||||||
void endVisit(Identifier const& _identifier) override;
|
void endVisit(Identifier const& _identifier) override;
|
||||||
bool visit(Literal const& _literal) override;
|
bool visit(Literal const& _literal) override;
|
||||||
|
|
||||||
|
@ -60,6 +60,19 @@ IRStorageItem::IRStorageItem(
|
|||||||
m_offset = offset;
|
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
|
string IRStorageItem::retrieveValue() const
|
||||||
{
|
{
|
||||||
if (!m_type->isValueType())
|
if (!m_type->isValueType())
|
||||||
|
@ -82,6 +82,13 @@ public:
|
|||||||
IRGenerationContext& _context,
|
IRGenerationContext& _context,
|
||||||
VariableDeclaration const& _varDecl
|
VariableDeclaration const& _varDecl
|
||||||
);
|
);
|
||||||
|
IRStorageItem(
|
||||||
|
std::ostream& _code,
|
||||||
|
IRGenerationContext& _context,
|
||||||
|
std::string _slot,
|
||||||
|
unsigned _offset,
|
||||||
|
Type const& _type
|
||||||
|
);
|
||||||
std::string retrieveValue() const override;
|
std::string retrieveValue() const override;
|
||||||
void storeValue(std::string const& _value, Type const& _type) 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