diff --git a/src/Expr.cc b/src/Expr.cc index 58963579fd..8f003de030 100644 --- a/src/Expr.cc +++ b/src/Expr.cc @@ -32,7 +32,7 @@ const char* expr_name(BroExprTag t) "$=", "in", "<<>>", "()", "event", "schedule", "coerce", "record_coerce", "table_coerce", - "sizeof", "flatten", "cast", "is" + "sizeof", "flatten", "cast", "is", "[:]=" }; if ( int(t) >= NUM_EXPRS ) @@ -2905,8 +2905,46 @@ bool AssignExpr::DoUnserialize(UnserialInfo* info) return UNSERIALIZE(&is_init); } -IndexExpr::IndexExpr(Expr* arg_op1, ListExpr* arg_op2, bool is_slice) -: BinaryExpr(EXPR_INDEX, arg_op1, arg_op2) +IndexSliceAssignExpr::IndexSliceAssignExpr(Expr* op1, Expr* op2, int is_init) + : AssignExpr(op1, op2, is_init) + { + } + +Val* IndexSliceAssignExpr::Eval(Frame* f) const + { + if ( is_init ) + { + RuntimeError("illegal assignment in initialization"); + return 0; + } + + Val* v = op2->Eval(f); + + if ( v ) + { + op1->Assign(f, v); + Unref(v); + } + + return 0; + } + +IMPLEMENT_SERIAL(IndexSliceAssignExpr, SER_INDEX_SLICE_ASSIGN_EXPR); + +bool IndexSliceAssignExpr::DoSerialize(SerialInfo* info) const + { + DO_SERIALIZE(SER_INDEX_SLICE_ASSIGN_EXPR, AssignExpr); + return true; + } + +bool IndexSliceAssignExpr::DoUnserialize(UnserialInfo* info) + { + DO_UNSERIALIZE(AssignExpr); + return true; + } + +IndexExpr::IndexExpr(Expr* arg_op1, ListExpr* arg_op2, bool arg_is_slice) +: BinaryExpr(EXPR_INDEX, arg_op1, arg_op2), is_slice(arg_is_slice) { if ( IsError() ) return; @@ -3302,13 +3340,13 @@ IMPLEMENT_SERIAL(IndexExpr, SER_INDEX_EXPR); bool IndexExpr::DoSerialize(SerialInfo* info) const { DO_SERIALIZE(SER_INDEX_EXPR, BinaryExpr); - return true; + return SERIALIZE(is_slice); } bool IndexExpr::DoUnserialize(UnserialInfo* info) { DO_UNSERIALIZE(BinaryExpr); - return true; + return UNSERIALIZE(&is_slice); } FieldExpr::FieldExpr(Expr* arg_op, const char* arg_field_name) @@ -5747,6 +5785,8 @@ Expr* get_assign_expr(Expr* op1, Expr* op2, int is_init) if ( op1->Type()->Tag() == TYPE_RECORD && op2->Type()->Tag() == TYPE_LIST ) return new RecordAssignExpr(op1, op2, is_init); + else if ( op1->Tag() == EXPR_INDEX && op1->AsIndexExpr()->IsSlice() ) + return new IndexSliceAssignExpr(op1, op2, is_init); else return new AssignExpr(op1, op2, is_init); } diff --git a/src/Expr.h b/src/Expr.h index e268f07648..111cc20e60 100644 --- a/src/Expr.h +++ b/src/Expr.h @@ -49,7 +49,8 @@ typedef enum { EXPR_FLATTEN, EXPR_CAST, EXPR_IS, -#define NUM_EXPRS (int(EXPR_IS) + 1) + EXPR_INDEX_SLICE_ASSIGN, +#define NUM_EXPRS (int(EXPR_INDEX_SLICE_ASSIGN) + 1) } BroExprTag; extern const char* expr_name(BroExprTag t); @@ -58,6 +59,7 @@ class Stmt; class Frame; class ListExpr; class NameExpr; +class IndexExpr; class AssignExpr; class CallExpr; class EventExpr; @@ -187,6 +189,18 @@ public: return (AssignExpr*) this; } + const IndexExpr* AsIndexExpr() const + { + CHECK_TAG(tag, EXPR_INDEX, "ExprVal::AsIndexExpr", expr_name) + return (const IndexExpr*) this; + } + + IndexExpr* AsIndexExpr() + { + CHECK_TAG(tag, EXPR_INDEX, "ExprVal::AsIndexExpr", expr_name) + return (IndexExpr*) this; + } + void Describe(ODesc* d) const override; bool Serialize(SerialInfo* info) const; @@ -663,6 +677,18 @@ protected: Val* val; // optional }; +class IndexSliceAssignExpr : public AssignExpr { +public: + IndexSliceAssignExpr(Expr* op1, Expr* op2, int is_init); + Val* Eval(Frame* f) const override; + +protected: + friend class Expr; + IndexSliceAssignExpr() {} + + DECLARE_SERIAL(IndexSliceAssignExpr); +}; + class IndexExpr : public BinaryExpr { public: IndexExpr(Expr* op1, ListExpr* op2, bool is_slice = false); @@ -682,6 +708,8 @@ public: TraversalCode Traverse(TraversalCallback* cb) const override; + bool IsSlice() const { return is_slice; } + protected: friend class Expr; IndexExpr() { } @@ -691,6 +719,8 @@ protected: void ExprDescribe(ODesc* d) const override; DECLARE_SERIAL(IndexExpr); + + bool is_slice; }; class FieldExpr : public UnaryExpr { diff --git a/src/SerialTypes.h b/src/SerialTypes.h index 029048a80f..a0b3f4231b 100644 --- a/src/SerialTypes.h +++ b/src/SerialTypes.h @@ -166,6 +166,7 @@ SERIAL_EXPR(CAST_EXPR, 45) SERIAL_EXPR(IS_EXPR_, 46) // Name conflict with internal SER_IS_EXPR constant. SERIAL_EXPR(BIT_EXPR, 47) SERIAL_EXPR(COMPLEMENT_EXPR, 48) +SERIAL_EXPR(INDEX_SLICE_ASSIGN_EXPR, 49) #define SERIAL_STMT(name, val) SERIAL_CONST(name, val, STMT) SERIAL_STMT(STMT, 1) diff --git a/src/parse.y b/src/parse.y index 4c39ee191d..48c8143f35 100644 --- a/src/parse.y +++ b/src/parse.y @@ -5,7 +5,7 @@ // Switching parser table type fixes ambiguity problems. %define lr.type ielr -%expect 103 +%expect 104 %token TOK_ADD TOK_ADD_TO TOK_ADDR TOK_ANY %token TOK_ATENDIF TOK_ATELSE TOK_ATIF TOK_ATIFDEF TOK_ATIFNDEF @@ -57,7 +57,7 @@ %type init_class %type opt_init %type TOK_CONSTANT -%type expr opt_expr init anonymous_function +%type expr opt_expr init anonymous_function index_slice %type event %type stmt stmt_list func_body for_head %type type opt_type enum_body @@ -464,6 +464,12 @@ expr: | expr '=' expr { set_location(@1, @3); + + if ( $1->Tag() == EXPR_INDEX && $1->AsIndexExpr()->IsSlice() ) + reporter->Error("index slice assignment may not be used" + " in arbitrary expression contexts, only" + " as a statement"); + $$ = get_assign_expr($1, $3, in_init); } @@ -479,19 +485,7 @@ expr: $$ = new IndexExpr($1, $3); } - | expr '[' opt_expr ':' opt_expr ']' - { - set_location(@1, @6); - Expr* low = $3 ? $3 : new ConstExpr(val_mgr->GetCount(0)); - Expr* high = $5 ? $5 : new SizeExpr($1); - - if ( ! IsIntegral(low->Type()->Tag()) || ! IsIntegral(high->Type()->Tag()) ) - reporter->Error("slice notation must have integral values as indexes"); - - ListExpr* le = new ListExpr(low); - le->Append(high); - $$ = new IndexExpr($1, le, true); - } + | index_slice | expr '$' TOK_ID { @@ -1271,6 +1265,21 @@ init: | expr ; +index_slice: + expr '[' opt_expr ':' opt_expr ']' + { + set_location(@1, @6); + Expr* low = $3 ? $3 : new ConstExpr(val_mgr->GetCount(0)); + Expr* high = $5 ? $5 : new SizeExpr($1); + + if ( ! IsIntegral(low->Type()->Tag()) || ! IsIntegral(high->Type()->Tag()) ) + reporter->Error("slice notation must have integral values as indexes"); + + ListExpr* le = new ListExpr(low); + le->Append(high); + $$ = new IndexExpr($1, le, true); + } + opt_attr: attr_list | @@ -1474,6 +1483,15 @@ stmt: brofiler.DecIgnoreDepth(); } + | index_slice '=' expr ';' opt_no_test + { + set_location(@1, @4); + $$ = new ExprStmt(get_assign_expr($1, $3, in_init)); + + if ( ! $5 ) + brofiler.AddStmt($$); + } + | expr ';' opt_no_test { set_location(@1, @2); diff --git a/testing/btest/core/leaks/vector-indexing.zeek b/testing/btest/core/leaks/vector-indexing.zeek new file mode 100644 index 0000000000..540913bd08 --- /dev/null +++ b/testing/btest/core/leaks/vector-indexing.zeek @@ -0,0 +1,30 @@ +# Needs perftools support. +# +# @TEST-GROUP: leaks +# +# @TEST-REQUIRES: zeek --help 2>&1 | grep -q mem-leaks +# +# @TEST-EXEC: HEAP_CHECK_DUMP_DIRECTORY=. HEAPCHECK=local btest-bg-run zeek zeek -m -b -r $TRACES/wikipedia.trace %INPUT +# @TEST-EXEC: btest-bg-wait 60 + +global did_it = F; + +event new_connection(c: connection) + { + if ( did_it ) + return; + + did_it = T; + + # Slicing tests. + local v17 = vector( 1, 2, 3, 4, 5 ); + print v17[0:2]; + print v17[-3:-1]; + print v17[:2]; + print v17[2:]; + print v17[:]; + v17[0:1] = vector(6); + v17[2:4] = vector(7, 8); + v17[2:4] = vector(9, 10, 11); + v17[2:5] = vector(9); + }