Merge pull request #382 from chriseth/bytesIndex

Index access for bytesXX.
This commit is contained in:
chriseth 2016-02-11 14:09:53 +01:00
commit c6c3c78327
7 changed files with 104 additions and 0 deletions

View File

@ -110,6 +110,11 @@ Operators:
* Comparisons: `<=`, `<`, `==`, `!=`, `>=`, `>` (evaluate to `bool`)
* Bit operators: `&`, `|`, `^` (bitwise exclusive or), `~` (bitwise negation)
* Index access: If `x` is of type `bytesI`, then `x[k]` for `0 <= k < I` returns the `k` th byte (read-only).
Members:
* `.length` yields the fixed length of the byte array (read-only).
Dynamically-sized byte array
----------------------------

View File

@ -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(),

View File

@ -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())

View File

@ -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(); }

View File

@ -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, "");

View File

@ -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()
}

View File

@ -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()
}