mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #6093 from ethereum/mapping-string-keys
Allow dynamic types in public mappings
This commit is contained in:
commit
ebec54aacb
@ -1,6 +1,7 @@
|
|||||||
### 0.5.5 (unreleased)
|
### 0.5.5 (unreleased)
|
||||||
|
|
||||||
Language Features:
|
Language Features:
|
||||||
|
* Add support for accessors for mappings with string or byte key types
|
||||||
* Meta programming: Provide access to the name of contracts via ``type(C).name``.
|
* Meta programming: Provide access to the name of contracts via ``type(C).name``.
|
||||||
|
|
||||||
|
|
||||||
|
@ -511,14 +511,6 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
|
|||||||
)
|
)
|
||||||
m_errorReporter.typeError(_variable.location(), "Array is too large to be encoded.");
|
m_errorReporter.typeError(_variable.location(), "Array is too large to be encoded.");
|
||||||
break;
|
break;
|
||||||
case Type::Category::Mapping:
|
|
||||||
if (auto mappingType = dynamic_cast<MappingType const*>(varType.get()))
|
|
||||||
if (
|
|
||||||
mappingType->keyType()->isDynamicallySized() &&
|
|
||||||
_variable.visibility() == Declaration::Visibility::Public
|
|
||||||
)
|
|
||||||
m_errorReporter.typeError(_variable.location(), "Dynamically-sized keys for public mappings are not supported.");
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -106,18 +106,49 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
|
|||||||
if (auto mappingType = dynamic_cast<MappingType const*>(returnType.get()))
|
if (auto mappingType = dynamic_cast<MappingType const*>(returnType.get()))
|
||||||
{
|
{
|
||||||
solAssert(CompilerUtils::freeMemoryPointer >= 0x40, "");
|
solAssert(CompilerUtils::freeMemoryPointer >= 0x40, "");
|
||||||
solUnimplementedAssert(
|
|
||||||
!paramTypes[i]->isDynamicallySized(),
|
|
||||||
"Accessors for mapping with dynamically-sized keys not yet implemented."
|
|
||||||
);
|
|
||||||
// pop offset
|
// pop offset
|
||||||
m_context << Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
|
if (paramTypes[i]->isDynamicallySized())
|
||||||
|
{
|
||||||
|
solAssert(
|
||||||
|
dynamic_cast<ArrayType const&>(*paramTypes[i]).isByteArray(),
|
||||||
|
"Expected string or byte array for mapping key type"
|
||||||
|
);
|
||||||
|
|
||||||
|
// stack: <keys..> <slot position>
|
||||||
|
|
||||||
|
// copy key[i] to top.
|
||||||
|
utils().copyToStackTop(paramTypes.size() - i + 1, 1);
|
||||||
|
|
||||||
|
m_context.appendInlineAssembly(R"({
|
||||||
|
let key_len := mload(key_ptr)
|
||||||
|
// Temp. use the memory after the array data for the slot
|
||||||
|
// position
|
||||||
|
let post_data_ptr := add(key_ptr, add(key_len, 0x20))
|
||||||
|
let orig_data := mload(post_data_ptr)
|
||||||
|
mstore(post_data_ptr, slot_pos)
|
||||||
|
let hash := keccak256(add(key_ptr, 0x20), add(key_len, 0x20))
|
||||||
|
mstore(post_data_ptr, orig_data)
|
||||||
|
slot_pos := hash
|
||||||
|
})", {"slot_pos", "key_ptr"});
|
||||||
|
|
||||||
|
m_context << Instruction::POP;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
solAssert(paramTypes[i]->isValueType(), "Expected value type for mapping key");
|
||||||
|
|
||||||
// move storage offset to memory.
|
// move storage offset to memory.
|
||||||
utils().storeInMemory(32);
|
utils().storeInMemory(32);
|
||||||
|
|
||||||
// move key to memory.
|
// move key to memory.
|
||||||
utils().copyToStackTop(paramTypes.size() - i, 1);
|
utils().copyToStackTop(paramTypes.size() - i, 1);
|
||||||
utils().storeInMemory(0);
|
utils().storeInMemory(0);
|
||||||
m_context << u256(64) << u256(0) << Instruction::KECCAK256;
|
m_context << u256(64) << u256(0);
|
||||||
|
m_context << Instruction::KECCAK256;
|
||||||
|
}
|
||||||
|
|
||||||
// push offset
|
// push offset
|
||||||
m_context << u256(0);
|
m_context << u256(0);
|
||||||
returnType = mappingType->valueType();
|
returnType = mappingType->valueType();
|
||||||
|
@ -51,6 +51,11 @@ namespace test
|
|||||||
|
|
||||||
BOOST_FIXTURE_TEST_SUITE(SolidityEndToEndTest, SolidityExecutionFramework)
|
BOOST_FIXTURE_TEST_SUITE(SolidityEndToEndTest, SolidityExecutionFramework)
|
||||||
|
|
||||||
|
int constexpr roundTo32(int _num)
|
||||||
|
{
|
||||||
|
return (_num + 31) / 32 * 32;
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(transaction_status)
|
BOOST_AUTO_TEST_CASE(transaction_status)
|
||||||
{
|
{
|
||||||
char const* sourceCode = R"(
|
char const* sourceCode = R"(
|
||||||
@ -8435,6 +8440,144 @@ BOOST_AUTO_TEST_CASE(string_as_mapping_key)
|
|||||||
), encodeArgs(u256(7 + i)));
|
), encodeArgs(u256(7 + i)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(string_as_public_mapping_key)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract Test {
|
||||||
|
mapping(string => uint) public data;
|
||||||
|
function set(string memory _s, uint _v) public { data[_s] = _v; }
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
compileAndRun(sourceCode, 0, "Test");
|
||||||
|
vector<string> strings{
|
||||||
|
"Hello, World!",
|
||||||
|
"Hello, World!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1111",
|
||||||
|
"",
|
||||||
|
"1"
|
||||||
|
};
|
||||||
|
for (unsigned i = 0; i < strings.size(); i++)
|
||||||
|
ABI_CHECK(callContractFunction(
|
||||||
|
"set(string,uint256)",
|
||||||
|
u256(0x40),
|
||||||
|
u256(7 + i),
|
||||||
|
u256(strings[i].size()),
|
||||||
|
strings[i]
|
||||||
|
), encodeArgs());
|
||||||
|
for (unsigned i = 0; i < strings.size(); i++)
|
||||||
|
ABI_CHECK(callContractFunction(
|
||||||
|
"data(string)",
|
||||||
|
u256(0x20),
|
||||||
|
u256(strings[i].size()),
|
||||||
|
strings[i]
|
||||||
|
), encodeArgs(u256(7 + i)));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(nested_string_as_public_mapping_key)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract Test {
|
||||||
|
mapping(string => mapping(string => uint)) public data;
|
||||||
|
function set(string memory _s, string memory _s2, uint _v) public {
|
||||||
|
data[_s][_s2] = _v; }
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
compileAndRun(sourceCode, 0, "Test");
|
||||||
|
vector<string> strings{
|
||||||
|
"Hello, World!",
|
||||||
|
"Hello, World!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1111",
|
||||||
|
"",
|
||||||
|
"1",
|
||||||
|
"last one"
|
||||||
|
};
|
||||||
|
for (unsigned i = 0; i + 1 < strings.size(); i++)
|
||||||
|
ABI_CHECK(callContractFunction(
|
||||||
|
"set(string,string,uint256)",
|
||||||
|
u256(0x60),
|
||||||
|
u256(roundTo32(0x80 + strings[i].size())),
|
||||||
|
u256(7 + i),
|
||||||
|
u256(strings[i].size()),
|
||||||
|
strings[i],
|
||||||
|
u256(strings[i+1].size()),
|
||||||
|
strings[i+1]
|
||||||
|
), encodeArgs());
|
||||||
|
for (unsigned i = 0; i + 1 < strings.size(); i++)
|
||||||
|
ABI_CHECK(callContractFunction(
|
||||||
|
"data(string,string)",
|
||||||
|
u256(0x40),
|
||||||
|
u256(roundTo32(0x60 + strings[i].size())),
|
||||||
|
u256(strings[i].size()),
|
||||||
|
strings[i],
|
||||||
|
u256(strings[i+1].size()),
|
||||||
|
strings[i+1]
|
||||||
|
), encodeArgs(u256(7 + i)));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(nested_mixed_string_as_public_mapping_key)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract Test {
|
||||||
|
mapping(string =>
|
||||||
|
mapping(int =>
|
||||||
|
mapping(address =>
|
||||||
|
mapping(bytes => int)))) public data;
|
||||||
|
|
||||||
|
function set(
|
||||||
|
string memory _s1,
|
||||||
|
int _s2,
|
||||||
|
address _s3,
|
||||||
|
bytes memory _s4,
|
||||||
|
int _value
|
||||||
|
) public
|
||||||
|
{
|
||||||
|
data[_s1][_s2][_s3][_s4] = _value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
compileAndRun(sourceCode, 0, "Test");
|
||||||
|
|
||||||
|
struct Index
|
||||||
|
{
|
||||||
|
string s1;
|
||||||
|
int s2;
|
||||||
|
int s3;
|
||||||
|
string s4;
|
||||||
|
};
|
||||||
|
|
||||||
|
vector<Index> data{
|
||||||
|
{ "aabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcbc", 4, 23, "efg" },
|
||||||
|
{ "tiaron", 456, 63245, "908apzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapz" },
|
||||||
|
{ "", 2345, 12934, "665i65i65i65i65i65i65i65i65i65i65i65i65i65i65i65i65i65i5iart" },
|
||||||
|
{ "¡¿…", 9781, 8148, "" },
|
||||||
|
{ "ρν♀♀ω₂₃♀", 929608, 303030, "" }
|
||||||
|
};
|
||||||
|
|
||||||
|
for (size_t i = 0; i + 1 < data.size(); i++)
|
||||||
|
ABI_CHECK(callContractFunction(
|
||||||
|
"set(string,int256,address,bytes,int256)",
|
||||||
|
u256(0xA0),
|
||||||
|
u256(data[i].s2),
|
||||||
|
u256(data[i].s3),
|
||||||
|
u256(roundTo32(0xC0 + data[i].s1.size())),
|
||||||
|
u256(i - 3),
|
||||||
|
u256(data[i].s1.size()),
|
||||||
|
data[i].s1,
|
||||||
|
u256(data[i].s4.size()),
|
||||||
|
data[i].s4
|
||||||
|
), encodeArgs());
|
||||||
|
for (size_t i = 0; i + 1 < data.size(); i++)
|
||||||
|
ABI_CHECK(callContractFunction(
|
||||||
|
"data(string,int256,address,bytes)",
|
||||||
|
u256(0x80),
|
||||||
|
u256(data[i].s2),
|
||||||
|
u256(data[i].s3),
|
||||||
|
u256(roundTo32(0xA0 + data[i].s1.size())),
|
||||||
|
u256(data[i].s1.size()),
|
||||||
|
data[i].s1,
|
||||||
|
u256(data[i].s4.size()),
|
||||||
|
data[i].s4
|
||||||
|
), encodeArgs(u256(i - 3)));
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(accessor_for_state_variable)
|
BOOST_AUTO_TEST_CASE(accessor_for_state_variable)
|
||||||
{
|
{
|
||||||
char const* sourceCode = R"(
|
char const* sourceCode = R"(
|
||||||
|
@ -2,4 +2,3 @@ contract c {
|
|||||||
mapping(string => uint) public data;
|
mapping(string => uint) public data;
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// TypeError: (14-49): Dynamically-sized keys for public mappings are not supported.
|
|
||||||
|
Loading…
Reference in New Issue
Block a user