diff --git a/libsolidity/CompilerUtils.cpp b/libsolidity/CompilerUtils.cpp index 192d66d5c..73be38176 100644 --- a/libsolidity/CompilerUtils.cpp +++ b/libsolidity/CompilerUtils.cpp @@ -184,17 +184,14 @@ void CompilerUtils::copyByteArrayToStorage(ByteArrayType const& _targetType, << eth::Instruction::SWAP1; // stack here: target_ref target_data_end target_data_ref // store length (in bytes) - if (_sourceType.getOffset() == 0) - m_context << eth::Instruction::CALLDATASIZE; - else - m_context << _sourceType.getOffset() << eth::Instruction::CALLDATASIZE << eth::Instruction::SUB; + m_context << eth::Instruction::CALLDATASIZE; m_context << eth::Instruction::DUP1 << eth::Instruction::DUP5 << eth::Instruction::SSTORE; // jump to end if length is zero m_context << eth::Instruction::ISZERO; eth::AssemblyItem copyLoopEnd = m_context.newTag(); m_context.appendConditionalJumpTo(copyLoopEnd); - - m_context << _sourceType.getOffset(); + // store start offset + m_context << u256(0); // stack now: target_ref target_data_end target_data_ref calldata_offset eth::AssemblyItem copyLoopStart = m_context.newTag(); m_context << copyLoopStart diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 63132a124..3dbb4012d 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -504,6 +504,12 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) } BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to " + type.toString())); } + case Type::Category::ByteArray: + { + solAssert(member == "length", "Illegal bytearray member."); + m_context << eth::Instruction::SLOAD; + break; + } default: BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Member access to unknown type.")); } diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index f43a3ffe3..33cc8a1ec 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -58,7 +58,7 @@ TypePointer Type::fromElementaryTypeName(Token::Value _typeToken) else if (Token::String0 <= _typeToken && _typeToken <= Token::String32) return make_shared(int(_typeToken) - int(Token::String0)); else if (_typeToken == Token::Bytes) - return make_shared(ByteArrayType::Location::Storage, 0, 0, true); + return make_shared(ByteArrayType::Location::Storage); else BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " + std::string(Token::toString(_typeToken)) + " to type.")); @@ -515,12 +515,7 @@ TypePointer ContractType::unaryOperatorResult(Token::Value _operator) const bool ByteArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const { - if (*this == _convertTo) - return true; - if (_convertTo.getCategory() != Category::ByteArray) - return false; - auto const& other = dynamic_cast(_convertTo); - return (m_dynamicLength == other.m_dynamicLength || m_length == other.m_length); + return _convertTo.getCategory() == getCategory(); } TypePointer ByteArrayType::unaryOperatorResult(Token::Value _operator) const @@ -535,8 +530,7 @@ bool ByteArrayType::operator==(Type const& _other) const if (_other.getCategory() != getCategory()) return false; ByteArrayType const& other = dynamic_cast(_other); - return other.m_location == m_location && other.m_dynamicLength == m_dynamicLength - && other.m_length == m_length && other.m_offset == m_offset; + return other.m_location == m_location; } unsigned ByteArrayType::getSizeOnStack() const @@ -547,6 +541,8 @@ unsigned ByteArrayType::getSizeOnStack() const return 1; } +const MemberList ByteArrayType::s_byteArrayMemberList = MemberList({{"length", make_shared(256)}}); + bool ContractType::operator==(Type const& _other) const { if (_other.getCategory() != getCategory()) diff --git a/libsolidity/Types.h b/libsolidity/Types.h index 0270773fb..5a0e2c429 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -286,25 +286,19 @@ public: enum class Location { Storage, CallData, Memory }; virtual Category getCategory() const override { return Category::ByteArray; } - explicit ByteArrayType(Location _location, u256 const& _offset = 0, u256 const& _length = 0, - bool _dynamicLength = false): - m_location(_location), m_offset(_offset), m_length(_length), m_dynamicLength(_dynamicLength) {} + explicit ByteArrayType(Location _location): m_location(_location) {} virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual bool operator==(const Type& _other) const override; virtual unsigned getSizeOnStack() const override; virtual std::string toString() const override { return "bytes"; } + virtual MemberList const& getMembers() const override { return s_byteArrayMemberList; } Location getLocation() const { return m_location; } - u256 const& getOffset() const { return m_offset; } - u256 const& getLength() const { return m_length; } - bool hasDynamicLength() const { return m_dynamicLength; } private: Location m_location; - u256 m_offset; - u256 m_length; - bool m_dynamicLength; + static const MemberList s_byteArrayMemberList; }; /** diff --git a/test/SolidityEndToEndTest.cpp b/test/SolidityEndToEndTest.cpp index 6c90ca62c..587d4193f 100644 --- a/test/SolidityEndToEndTest.cpp +++ b/test/SolidityEndToEndTest.cpp @@ -2385,6 +2385,45 @@ BOOST_AUTO_TEST_CASE(copy_removes_bytes_data) BOOST_CHECK(m_state.storage(m_contractAddress).empty()); } +BOOST_AUTO_TEST_CASE(bytes_inside_mappings) +{ + char const* sourceCode = R"( + contract c { + function set(uint key) returns (bool) { data[key] = msg.data; return true; } + function copy(uint from, uint to) returns (bool) { data[to] = data[from]; return true; } + mapping(uint => bytes) data; + } + )"; + compileAndRun(sourceCode); + // store a short byte array at 1 and a longer one at 2 + BOOST_CHECK(callContractFunction("set(uint256)", 1, 2) == encodeArgs(true)); + BOOST_CHECK(callContractFunction("set(uint256)", 2, 2, 3, 4, 5) == encodeArgs(true)); + BOOST_CHECK(!m_state.storage(m_contractAddress).empty()); + // copy shorter to longer + BOOST_CHECK(callContractFunction("copy(uint256,uint256)", 1, 2) == encodeArgs(true)); + BOOST_CHECK(!m_state.storage(m_contractAddress).empty()); + // copy empty to both + BOOST_CHECK(callContractFunction("copy(uint256,uint256)", 99, 1) == encodeArgs(true)); + BOOST_CHECK(!m_state.storage(m_contractAddress).empty()); + BOOST_CHECK(callContractFunction("copy(uint256,uint256)", 99, 2) == encodeArgs(true)); + BOOST_CHECK(m_state.storage(m_contractAddress).empty()); +} + +BOOST_AUTO_TEST_CASE(bytes_length_member) +{ + char const* sourceCode = R"( + contract c { + function set() returns (bool) { data = msg.data; return true; } + function getLength() returns (uint) { return data.length; } + bytes data; + } + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("getLength()") == encodeArgs(0)); + BOOST_CHECK(callContractFunction("set()", 1, 2) == encodeArgs(true)); + BOOST_CHECK(callContractFunction("getLength()") == encodeArgs(4+32+32)); +} + BOOST_AUTO_TEST_SUITE_END() }