diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 875e00bc2..f0c3af226 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -867,19 +867,17 @@ unsigned ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedT void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& _varDecl) { - FunctionType thisType(_varDecl); - solAssert(thisType.getReturnParameterTypes().size() == 1, ""); - TypePointer const& resultType = thisType.getReturnParameterTypes().front(); - unsigned sizeOnStack; + FunctionType accessorType(_varDecl); unsigned length = 0; - TypePointers const& params = thisType.getParameterTypes(); + TypePointers const& params = accessorType.getParameterTypes(); // move arguments to memory for (TypePointer const& param: boost::adaptors::reverse(params)) length += appendTypeConversionAndMoveToMemory(*param, *param, Location(), length); - // retrieve the position of the mapping + // retrieve the position of the variable m_context << m_context.getStorageLocationOfVariable(_varDecl); + TypePointer returnType = _varDecl.getType(); for (TypePointer const& param: params) { @@ -888,13 +886,40 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& unsigned argLen = CompilerUtils::getPaddedSize(param->getCalldataEncodedSize()); length -= argLen; m_context << u256(argLen + 32) << u256(length) << eth::Instruction::SHA3; + + returnType = dynamic_cast(*returnType).getValueType(); } - m_currentLValue = LValue(m_context, LValue::STORAGE, *resultType); - m_currentLValue.retrieveValue(resultType, Location(), true); - sizeOnStack = resultType->getSizeOnStack(); - solAssert(sizeOnStack <= 15, "Stack too deep."); - m_context << eth::dupInstruction(sizeOnStack + 1) << eth::Instruction::JUMP; + unsigned retSizeOnStack = 0; + solAssert(accessorType.getReturnParameterTypes().size() >= 1, ""); + if (StructType const* structType = dynamic_cast(returnType.get())) + { + auto const& names = accessorType.getReturnParameterNames(); + auto const& types = accessorType.getReturnParameterTypes(); + // struct + for (size_t i = 0; i < names.size(); ++i) + { + m_context << eth::Instruction::DUP1 + << structType->getStorageOffsetOfMember(names[i]) + << eth::Instruction::ADD; + m_currentLValue = LValue(m_context, LValue::STORAGE, *types[i]); + m_currentLValue.retrieveValue(types[i], Location(), true); + solAssert(types[i]->getSizeOnStack() == 1, "Returning struct elements with stack size != 1 not yet implemented."); + m_context << eth::Instruction::SWAP1; + retSizeOnStack += types[i]->getSizeOnStack(); + } + m_context << eth::Instruction::POP; + } + else + { + // simple value + solAssert(accessorType.getReturnParameterTypes().size() == 1, ""); + m_currentLValue = LValue(m_context, LValue::STORAGE, *returnType); + m_currentLValue.retrieveValue(returnType, Location(), true); + retSizeOnStack = returnType->getSizeOnStack(); + } + solAssert(retSizeOnStack <= 15, "Stack too deep."); + m_context << eth::dupInstruction(retSizeOnStack + 1) << eth::Instruction::JUMP; } ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType, diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 385495947..e6f1e8e1c 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -643,22 +643,31 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl): { TypePointers params; vector paramNames; - TypePointers retParams; - vector retParamNames; - TypePointer varDeclType = _varDecl.getType(); - auto mappingType = dynamic_cast(varDeclType.get()); - auto returnType = varDeclType; + auto returnType = _varDecl.getType(); - while (mappingType != nullptr) + while (auto mappingType = dynamic_cast(returnType.get())) { params.push_back(mappingType->getKeyType()); paramNames.push_back(""); returnType = mappingType->getValueType(); - mappingType = dynamic_cast(mappingType->getValueType().get()); } - retParams.push_back(returnType); - retParamNames.push_back(""); + TypePointers retParams; + vector retParamNames; + if (auto structType = dynamic_cast(returnType.get())) + { + for (pair const& member: structType->getMembers()) + if (member.second->canLiveOutsideStorage()) + { + retParamNames.push_back(member.first); + retParams.push_back(member.second); + } + } + else + { + retParams.push_back(returnType); + retParamNames.push_back(""); + } swap(params, m_parameterTypes); swap(paramNames, m_parameterNames); diff --git a/test/SolidityEndToEndTest.cpp b/test/SolidityEndToEndTest.cpp index f248a5a07..301cc06ef 100644 --- a/test/SolidityEndToEndTest.cpp +++ b/test/SolidityEndToEndTest.cpp @@ -959,6 +959,24 @@ BOOST_AUTO_TEST_CASE(complex_accessors) BOOST_CHECK(callContractFunction("to_multiple_map(uint256,uint256)", 42, 23) == encodeArgs(31)); } +BOOST_AUTO_TEST_CASE(struct_accessor) +{ + char const* sourceCode = R"( + contract test { + struct Data { uint a; uint8 b; mapping(uint => uint) c; bool d; } + mapping(uint => Data) public data; + function test() { + data[7].a = 1; + data[7].b = 2; + data[7].c[0] = 3; + data[7].d = true; + } + } + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("data(uint256)", 7) == encodeArgs(1, 2, true)); +} + BOOST_AUTO_TEST_CASE(balance) { char const* sourceCode = "contract test {\n"