From 056180fb24ced86125a35786633a3e897f51a500 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 3 Aug 2015 18:09:39 +0200 Subject: [PATCH] strings as mapping keys. --- libsolidity/AST.cpp | 3 +- libsolidity/ExpressionCompiler.cpp | 35 +++++++++++++++++------ libsolidity/Types.cpp | 2 ++ test/libsolidity/SolidityEndToEndTest.cpp | 33 +++++++++++++++++++++ 4 files changed, 63 insertions(+), 10 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index ee6e52250..05e2d52e8 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -784,8 +784,7 @@ void Expression::expectType(Type const& _expectedType) " is not implicitly convertible to expected type " + _expectedType.toString() + "." - ) - ); + )); } void Expression::requireLValue() diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 786d386d9..f27cf5fe5 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -85,6 +85,10 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& if (auto mappingType = dynamic_cast(returnType.get())) { solAssert(CompilerUtils::freeMemoryPointer >= 0x40, ""); + solAssert( + !paramTypes[i]->isDynamicallySized(), + "Accessors for mapping with dynamically-sized keys not yet implemented." + ); // pop offset m_context << eth::Instruction::POP; // move storage offset to memory. @@ -803,15 +807,30 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) if (baseType.getCategory() == Type::Category::Mapping) { // stack: storage_base_ref - Type const& keyType = *dynamic_cast(baseType).getKeyType(); - m_context << u256(0); // memory position + auto const& mapping = dynamic_cast(baseType); + Type const& keyType = *mapping.getKeyType(); solAssert(_indexAccess.getIndexExpression(), "Index expression expected."); - solAssert(keyType.getCalldataEncodedSize() <= 0x20, "Dynamic keys not yet implemented."); - appendExpressionCopyToMemory(keyType, *_indexAccess.getIndexExpression()); - m_context << eth::Instruction::SWAP1; - solAssert(CompilerUtils::freeMemoryPointer >= 0x40, ""); - utils().storeInMemoryDynamic(IntegerType(256)); - m_context << u256(0) << eth::Instruction::SHA3; + if (keyType.isDynamicallySized()) + { + _indexAccess.getIndexExpression()->accept(*this); + utils().fetchFreeMemoryPointer(); + // stack: base index mem + // note: the following operations must not allocate memory! + utils().encodeToMemory(TypePointers{mapping.getKeyType()}, TypePointers(), false, true); + m_context << eth::Instruction::SWAP1; + utils().storeInMemoryDynamic(IntegerType(256)); + utils().toSizeAfterFreeMemoryPointer(); + } + else + { + m_context << u256(0); // memory position + appendExpressionCopyToMemory(keyType, *_indexAccess.getIndexExpression()); + m_context << eth::Instruction::SWAP1; + solAssert(CompilerUtils::freeMemoryPointer >= 0x40, ""); + utils().storeInMemoryDynamic(IntegerType(256)); + m_context << u256(0); + } + m_context << eth::Instruction::SHA3; m_context << u256(0); setLValueToStorageItem(_indexAccess); } diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index d85c0511f..89ff2134f 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -179,6 +179,8 @@ TypePointer Type::fromMapping(ElementaryTypeName& _keyType, TypeName& _valueType BOOST_THROW_EXCEPTION(_valueType.createTypeError("Invalid type name.")); // Convert value type to storage reference. valueType = ReferenceType::copyForLocationIfReference(DataLocation::Storage, valueType); + // Convert key type to memory. + keyType = ReferenceType::copyForLocationIfReference(DataLocation::Memory, keyType); return make_shared(keyType, valueType); } diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index d44545145..fc1d2eab8 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -5099,6 +5099,39 @@ BOOST_AUTO_TEST_CASE(memory_structs_with_mappings) BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(0))); } +BOOST_AUTO_TEST_CASE(string_as_mapping_key) +{ + char const* sourceCode = R"( + contract Test { + mapping(string => uint) data; + function set(string _s, uint _v) { data[_s] = _v; } + function get(string _s) returns (uint) { return data[_s]; } + } + )"; + compileAndRun(sourceCode, 0, "Test"); + vector strings{ + "Hello, World!", + "Hello, World!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1111", + "", + "1" + }; + for (unsigned i = 0; i < strings.size(); i++) + BOOST_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++) + BOOST_CHECK(callContractFunction( + "get(string)", + u256(0x20), + u256(strings[i].size()), + strings[i] + ) == encodeArgs(u256(7 + i))); +} + BOOST_AUTO_TEST_SUITE_END() }