diff options
-rw-r--r-- | libsolidity/analysis/TypeChecker.cpp | 18 | ||||
-rw-r--r-- | libsolidity/ast/Types.cpp | 5 | ||||
-rw-r--r-- | libsolidity/ast/Types.h | 1 | ||||
-rw-r--r-- | libsolidity/codegen/ExpressionCompiler.cpp | 26 | ||||
-rw-r--r-- | test/libsolidity/SolidityEndToEndTest.cpp | 36 | ||||
-rw-r--r-- | test/libsolidity/SolidityNameAndTypeResolution.cpp | 13 |
6 files changed, 99 insertions, 0 deletions
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 0d74ddba..756f0e4f 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1253,6 +1253,8 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) arrayType.isDynamicallySized() ); } + else if (exprType->category() == Type::Category::FixedBytes) + annotation.isLValue = false; return false; } @@ -1317,6 +1319,22 @@ bool TypeChecker::visit(IndexAccess const& _access) } break; } + case Type::Category::FixedBytes: + { + FixedBytesType const& bytesType = dynamic_cast<FixedBytesType const&>(*baseType); + if (!index) + typeError(_access.location(), "Index expression cannot be omitted."); + else + { + expectType(*index, IntegerType(256)); + if (auto integerType = dynamic_cast<IntegerConstantType const*>(type(*index).get())) + if (bytesType.numBytes() <= integerType->literalValue(nullptr)) + typeError(_access.location(), "Out of bounds array access."); + } + resultType = make_shared<FixedBytesType>(1); + isLValue = false; // @todo this heavily depends on how it is embedded + break; + } default: fatalTypeError( _access.baseExpression().location(), diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 79e5bb02..01d1cb37 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -638,6 +638,11 @@ TypePointer FixedBytesType::binaryOperatorResult(Token::Value _operator, TypePoi return TypePointer(); } +MemberList::MemberMap FixedBytesType::nativeMembers(const ContractDefinition*) const +{ + return MemberList::MemberMap{MemberList::Member{"length", make_shared<IntegerType>(8)}}; +} + bool FixedBytesType::operator==(Type const& _other) const { if (_other.category() != category()) diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 723d633b..90a0509b 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -399,6 +399,7 @@ public: virtual bool isValueType() const override { return true; } virtual std::string toString(bool) const override { return "bytes" + dev::toString(m_bytes); } + virtual MemberList::MemberMap nativeMembers(ContractDefinition const*) const override; virtual TypePointer encodingType() const override { return shared_from_this(); } virtual TypePointer interfaceType(bool) const override { return shared_from_this(); } diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 9536c727..58db07b1 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1004,6 +1004,16 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) solAssert(false, "Illegal array member."); break; } + case Type::Category::FixedBytes: + { + auto const& type = dynamic_cast<FixedBytesType const&>(*_memberAccess.expression().annotation().type); + utils().popStackElement(type); + if (member == "length") + m_context << u256(type.numBytes()); + else + solAssert(false, "Illegal fixed bytes member."); + break; + } default: BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Member access to unknown type.")); } @@ -1085,6 +1095,22 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) break; } } + else if (baseType.category() == Type::Category::FixedBytes) + { + FixedBytesType const& fixedBytesType = dynamic_cast<FixedBytesType const&>(baseType); + solAssert(_indexAccess.indexExpression(), "Index expression expected."); + + _indexAccess.indexExpression()->accept(*this); + // stack layout: <value> <index> + // check out-of-bounds access + m_context << u256(fixedBytesType.numBytes()); + m_context << eth::Instruction::DUP2 << eth::Instruction::LT << eth::Instruction::ISZERO; + // out-of-bounds access throws exception + m_context.appendConditionalJumpTo(m_context.errorTag()); + + m_context << eth::Instruction::BYTE; + m_context << (u256(1) << (256 - 8)) << eth::Instruction::MUL; + } else if (baseType.category() == Type::Category::TypeType) { solAssert(baseType.sizeOnStack() == 0, ""); diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 73e2d662..c6e1b10d 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -6394,6 +6394,42 @@ BOOST_AUTO_TEST_CASE(inline_long_string_return) compileAndRun(sourceCode, 0, "C"); BOOST_CHECK(callContractFunction("f()") == encodeDyn(strLong)); } + +BOOST_AUTO_TEST_CASE(fixed_bytes_index_access) +{ + char const* sourceCode = R"( + contract C { + bytes16[] public data; + function f(bytes32 x) returns (byte) { + return x[2]; + } + function g(bytes32 x) returns (uint) { + data = [x[0], x[1], x[2]]; + data[0] = "12345"; + return uint(data[0][4]); + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("f(bytes32)", "789") == encodeArgs("9")); + BOOST_CHECK(callContractFunction("g(bytes32)", "789") == encodeArgs(u256(int('5')))); + BOOST_CHECK(callContractFunction("data(uint256)", u256(1)) == encodeArgs("8")); +} + +BOOST_AUTO_TEST_CASE(fixed_bytes_length_access) +{ + char const* sourceCode = R"( + contract C { + byte a; + function f(bytes32 x) returns (uint, uint, uint) { + return (x.length, bytes16(2).length, a.length + 7); + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("f(bytes32)", "789") == encodeArgs(u256(32), u256(16), u256(8))); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 820fd7d0..d202942c 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -3136,6 +3136,19 @@ BOOST_AUTO_TEST_CASE(conditional_with_all_types) BOOST_CHECK(success(text)); } +BOOST_AUTO_TEST_CASE(index_access_for_bytes) +{ + char const* text = R"( + contract C { + bytes20 x; + function f(bytes16 b) { + b[uint(x[2])]; + } + } + )"; + BOOST_CHECK(success(text)); +} + BOOST_AUTO_TEST_SUITE_END() } |