diff --git a/CMakeLists.txt b/CMakeLists.txt index b42b6e5c0..0abd188c1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,7 +27,6 @@ endif() target_link_libraries(${EXECUTABLE} ${JSONCPP_LIBRARIES}) target_link_libraries(${EXECUTABLE} evmcore) -target_link_libraries(${EXECUTABLE} devcore) target_link_libraries(${EXECUTABLE} devcrypto) install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) diff --git a/CompilerContext.h b/CompilerContext.h index 67dd3c94b..53d0a89a4 100644 --- a/CompilerContext.h +++ b/CompilerContext.h @@ -83,6 +83,7 @@ public: /// Converts an offset relative to the current stack height to a value that can be used later /// with baseToCurrentStackOffset to point to the same stack element. unsigned currentToBaseStackOffset(unsigned _offset) const; + /// @returns pair of slot and byte offset of the value inside this slot. std::pair getStorageLocationOfVariable(Declaration const& _declaration) const; /// Appends a JUMPI instruction to a new tag and @returns the tag diff --git a/CompilerUtils.cpp b/CompilerUtils.cpp index 511254fa5..454951147 100644 --- a/CompilerUtils.cpp +++ b/CompilerUtils.cpp @@ -157,8 +157,12 @@ void CompilerUtils::copyToStackTop(unsigned _stackDepth, unsigned _itemSize) void CompilerUtils::popStackElement(Type const& _type) { - unsigned const size = _type.getSizeOnStack(); - for (unsigned i = 0; i < size; ++i) + popStackSlots(_type.getSizeOnStack()); +} + +void CompilerUtils::popStackSlots(size_t _amount) +{ + for (size_t i = 0; i < _amount; ++i) m_context << eth::Instruction::POP; } diff --git a/CompilerUtils.h b/CompilerUtils.h index 043de41dd..5b809beac 100644 --- a/CompilerUtils.h +++ b/CompilerUtils.h @@ -79,6 +79,8 @@ public: void copyToStackTop(unsigned _stackDepth, unsigned _itemSize); /// Removes the current value from the top of the stack. void popStackElement(Type const& _type); + /// Removes element from the top of the stack _amount times. + void popStackSlots(size_t _amount); template static unsigned getSizeOnStack(std::vector const& _variables); diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index 8c8c3ee0d..8877d9c05 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -60,57 +60,82 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& CompilerContext::LocationSetter locationSetter(m_context, _varDecl); FunctionType accessorType(_varDecl); - unsigned length = 0; TypePointers const& paramTypes = accessorType.getParameterTypes(); - // move arguments to memory - for (TypePointer const& paramType: boost::adaptors::reverse(paramTypes)) - length += CompilerUtils(m_context).storeInMemory(length, *paramType, true); // retrieve the position of the variable auto const& location = m_context.getStorageLocationOfVariable(_varDecl); - m_context << location.first; + m_context << location.first << u256(location.second); TypePointer returnType = _varDecl.getType(); - for (TypePointer const& paramType: paramTypes) + + for (size_t i = 0; i < paramTypes.size(); ++i) { - // move offset to memory - CompilerUtils(m_context).storeInMemory(length); - unsigned argLen = paramType->getCalldataEncodedSize(); - length -= argLen; - m_context << u256(argLen + 32) << u256(length) << eth::Instruction::SHA3; - - returnType = dynamic_cast(*returnType).getValueType(); + if (auto mappingType = dynamic_cast(returnType.get())) + { + // pop offset + m_context << eth::Instruction::POP; + // move storage offset to memory. + CompilerUtils(m_context).storeInMemory(32); + // move key to memory. + CompilerUtils(m_context).copyToStackTop(paramTypes.size() - i, 1); + CompilerUtils(m_context).storeInMemory(0); + m_context << u256(64) << u256(0) << eth::Instruction::SHA3; + // push offset + m_context << u256(0); + returnType = mappingType->getValueType(); + } + else if (auto arrayType = dynamic_cast(returnType.get())) + { + // pop offset + m_context << eth::Instruction::POP; + CompilerUtils(m_context).copyToStackTop(paramTypes.size() - i + 1, 1); + ArrayUtils(m_context).accessIndex(*arrayType); + returnType = arrayType->getBaseType(); + } + else + solAssert(false, "Index access is allowed only for \"mapping\" and \"array\" types."); + } + // remove index arguments. + if (paramTypes.size() == 1) + m_context << eth::Instruction::SWAP2 << eth::Instruction::POP << eth::Instruction::SWAP1; + else if (paramTypes.size() >= 2) + { + m_context << eth::swapInstruction(paramTypes.size()); + m_context << eth::Instruction::POP; + m_context << eth::swapInstruction(paramTypes.size()); + CompilerUtils(m_context).popStackSlots(paramTypes.size() - 1); } - unsigned retSizeOnStack = 0; solAssert(accessorType.getReturnParameterTypes().size() >= 1, ""); if (StructType const* structType = dynamic_cast(returnType.get())) { + // remove offset + m_context << eth::Instruction::POP; auto const& names = accessorType.getReturnParameterNames(); auto const& types = accessorType.getReturnParameterTypes(); // struct for (size_t i = 0; i < names.size(); ++i) { - if (types[i]->getCategory() == Type::Category::Mapping) + if (types[i]->getCategory() == Type::Category::Mapping || types[i]->getCategory() == Type::Category::Array) continue; pair const& offsets = structType->getStorageOffsetsOfMember(names[i]); m_context << eth::Instruction::DUP1 << u256(offsets.first) << eth::Instruction::ADD << u256(offsets.second); StorageItem(m_context, *types[i]).retrieveValue(SourceLocation(), true); - solAssert(types[i]->getSizeOnStack() == 1, "Returning struct elements with stack size != 1 not yet implemented."); + solAssert(types[i]->getSizeOnStack() == 1, "Returning struct elements with stack size != 1 is not yet implemented."); m_context << eth::Instruction::SWAP1; retSizeOnStack += types[i]->getSizeOnStack(); } + // remove slot m_context << eth::Instruction::POP; } else { // simple value solAssert(accessorType.getReturnParameterTypes().size() == 1, ""); - m_context << u256(location.second); StorageItem(m_context, *returnType).retrieveValue(SourceLocation(), true); retSizeOnStack = returnType->getSizeOnStack(); } - solAssert(retSizeOnStack <= 15, "Stack too deep."); + solAssert(retSizeOnStack <= 15, "Stack is too deep."); m_context << eth::dupInstruction(retSizeOnStack + 1); m_context.appendJump(eth::AssemblyItem::JumpType::OutOfFunction); } @@ -758,7 +783,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) appendTypeMoveToMemory(IntegerType(256)); m_context << u256(0) << eth::Instruction::SHA3; m_context << u256(0); - setLValueToStorageItem( _indexAccess); + setLValueToStorageItem(_indexAccess); } else if (baseType.getCategory() == Type::Category::Array) { @@ -1038,7 +1063,7 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio else // send all gas except the amount needed to execute "SUB" and "CALL" // @todo this retains too much gas for now, needs to be fine-tuned. - m_context << u256(50 + (_functionType.valueSet() ? 9000 : 0)) << eth::Instruction::GAS << eth::Instruction::SUB; + m_context << u256(50 + (_functionType.valueSet() ? 9000 : 0) + 25000) << eth::Instruction::GAS << eth::Instruction::SUB; m_context << eth::Instruction::CALL; auto tag = m_context.appendConditionalJump(); m_context << eth::Instruction::STOP << tag; // STOP if CALL leaves 0. diff --git a/Types.cpp b/Types.cpp index e35dc0b6f..48796fd05 100644 --- a/Types.cpp +++ b/Types.cpp @@ -1000,15 +1000,26 @@ FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal FunctionType::FunctionType(VariableDeclaration const& _varDecl): m_location(Location::External), m_isConstant(true), m_declaration(&_varDecl) { - TypePointers params; + TypePointers paramTypes; vector paramNames; auto returnType = _varDecl.getType(); - while (auto mappingType = dynamic_cast(returnType.get())) + while (true) { - params.push_back(mappingType->getKeyType()); - paramNames.push_back(""); - returnType = mappingType->getValueType(); + if (auto mappingType = dynamic_cast(returnType.get())) + { + paramTypes.push_back(mappingType->getKeyType()); + paramNames.push_back(""); + returnType = mappingType->getValueType(); + } + else if (auto arrayType = dynamic_cast(returnType.get())) + { + returnType = arrayType->getBaseType(); + paramNames.push_back(""); + paramTypes.push_back(make_shared(256)); + } + else + break; } TypePointers retParams; @@ -1016,7 +1027,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl): if (auto structType = dynamic_cast(returnType.get())) { for (auto const& member: structType->getMembers()) - if (member.type->canLiveOutsideStorage()) + if (member.type->getCategory() != Category::Mapping && member.type->getCategory() != Category::Array) { retParamNames.push_back(member.name); retParams.push_back(member.type); @@ -1028,7 +1039,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl): retParamNames.push_back(""); } - swap(params, m_parameterTypes); + swap(paramTypes, m_parameterTypes); swap(paramNames, m_parameterNames); swap(retParams, m_returnParameterTypes); swap(retParamNames, m_returnParameterNames);