diff --git a/src/Expr.cc b/src/Expr.cc index 3a4e8add70..92fdf76a7d 100644 --- a/src/Expr.cc +++ b/src/Expr.cc @@ -2808,11 +2808,17 @@ IndexExpr::IndexExpr(Expr* arg_op1, ListExpr* arg_op2) SetError("not an index type"); else if ( ! op1->Type()->YieldType() ) + { + if ( IsString(op1->Type()->Tag()) && + match_type == MATCHES_INDEX_SCALAR ) + SetType(base_type(TYPE_STRING)); + else // It's a set - so indexing it yields void. We don't // directly generate an error message, though, since this // expression might be part of an add/delete statement, // rather than yielding a value. - SetType(base_type(TYPE_VOID)); + SetType(base_type(TYPE_VOID)); + } else if ( match_type == MATCHES_INDEX_SCALAR ) SetType(op1->Type()->YieldType()->Ref()); @@ -2888,6 +2894,9 @@ void IndexExpr::Delete(Frame* f) Expr* IndexExpr::MakeLvalue() { + if ( IsString(op1->Type()->Tag()) ) + ExprError("cannot assign to string index expression"); + return new RefExpr(this); } @@ -2961,10 +2970,34 @@ Val* IndexExpr::Fold(Val* v1, Val* v2) const Val* v = 0; - if ( v1->Type()->Tag() == TYPE_VECTOR ) + switch ( v1->Type()->Tag() ) { + case TYPE_VECTOR: v = v1->AsVectorVal()->Lookup(v2); - else + break; + case TYPE_TABLE: v = v1->AsTableVal()->Lookup(v2); + break; + case TYPE_STRING: + { + const ListVal* lv = v2->AsListVal(); + const BroString* s = v1->AsString(); + int len = s->Len(); + bro_int_t first = lv->Index(0)->AsInt(); + bro_int_t last = lv->Length() > 1 ? lv->Index(1)->AsInt() : first; + + if ( first < 0 ) + first += len; + if ( last < 0 ) + last += len; + + BroString* substring = s->GetSubstring(first, last - first + 1); + return new StringVal(substring ? substring : new BroString("")); + break; + } + default: + Error("type cannot be indexed"); + break; + } if ( v ) return v->Ref(); @@ -2991,14 +3024,22 @@ void IndexExpr::Assign(Frame* f, Val* v, Opcode op) return; } - if ( v1->Type()->Tag() == TYPE_VECTOR ) - { + switch ( v1->Type()->Tag() ) { + case TYPE_VECTOR: if ( ! v1->AsVectorVal()->Assign(v2, v, this, op) ) Internal("assignment failed"); - } - - else if ( ! v1->AsTableVal()->Assign(v2, v, op) ) - Internal("assignment failed"); + break; + case TYPE_TABLE: + if ( ! v1->AsTableVal()->Assign(v2, v, op) ) + Internal("assignment failed"); + break; + case TYPE_STRING: + Internal("assignment via string index accessor not allowed"); + break; + default: + Internal("bad index expression type in assignment"); + break; + } Unref(v1); Unref(v2); diff --git a/src/Type.cc b/src/Type.cc index e9b0949d13..74c0eb05cb 100644 --- a/src/Type.cc +++ b/src/Type.cc @@ -114,8 +114,16 @@ BroType::~BroType() delete [] type_id; } -int BroType::MatchesIndex(ListExpr*& /* index */) const +int BroType::MatchesIndex(ListExpr*& index) const { + if ( Tag() == TYPE_STRING ) + { + if ( index->Exprs().length() != 1 && index->Exprs().length() != 2 ) + return DOES_NOT_MATCH_INDEX; + if ( check_and_promote_exprs_to_type(index, ::base_type(TYPE_INT)) ) + return MATCHES_INDEX_SCALAR; + } + return DOES_NOT_MATCH_INDEX; } diff --git a/testing/btest/Baseline/language.string-indexing/out b/testing/btest/Baseline/language.string-indexing/out new file mode 100644 index 0000000000..3359187d4c --- /dev/null +++ b/testing/btest/Baseline/language.string-indexing/out @@ -0,0 +1,13 @@ +1 +12 +123456 +0123456789 +8 +789 +9 +9 +9 + + +2 +1 diff --git a/testing/btest/core/leaks/string-indexing.bro b/testing/btest/core/leaks/string-indexing.bro new file mode 100644 index 0000000000..40af2317e6 --- /dev/null +++ b/testing/btest/core/leaks/string-indexing.bro @@ -0,0 +1,26 @@ +# Needs perftools support. +# +# @TEST-GROUP: leaks +# +# @TEST-REQUIRES: bro --help 2>&1 | grep -q mem-leaks +# +# @TEST-EXEC: HEAP_CHECK_DUMP_DIRECTORY=. HEAPCHECK=local bro -m -b -r $TRACES/wikipedia.trace %INPUT + + +event new_connection(c: connection) + { + local s = "0123456789"; + print s[1]; + print s[1,2]; + print s[1,6]; + print s[0,20]; + print s[-2]; + print s[-3,-1]; + print s[-1,-10]; + print s[-1,0]; + print s[-1,5]; + print s[20, 23]; + print s[-20, 23]; + print s[0,5][2]; + print s[0,5][1,3][0]; + } diff --git a/testing/btest/language/string-indexing.bro b/testing/btest/language/string-indexing.bro new file mode 100644 index 0000000000..d05c1b6c5e --- /dev/null +++ b/testing/btest/language/string-indexing.bro @@ -0,0 +1,17 @@ +# @TEST-EXEC: bro -b %INPUT >out +# @TEST-EXEC: btest-diff out + +local s = "0123456789"; +print s[1]; +print s[1,2]; +print s[1,6]; +print s[0,20]; +print s[-2]; +print s[-3,-1]; +print s[-1,-10]; +print s[-1,0]; +print s[-1,5]; +print s[20, 23]; +print s[-20, 23]; +print s[0,5][2]; +print s[0,5][1,3][0];