From 062baefde09483277bbc94574cfc45793779f98e Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Fri, 13 Feb 2015 11:24:32 -0600 Subject: [PATCH] Add 'while' statement to Bro language. --- src/SerialTypes.h | 1 + src/Stmt.cc | 122 +++++++++++++++++++++- src/Stmt.h | 29 +++++ src/StmtEnums.h | 1 + src/parse.y | 6 ++ src/scan.l | 1 + testing/btest/Baseline/language.while/out | 12 +++ testing/btest/core/leaks/while.bro | 80 ++++++++++++++ testing/btest/language/while.bro | 77 ++++++++++++++ 9 files changed, 328 insertions(+), 1 deletion(-) create mode 100644 testing/btest/Baseline/language.while/out create mode 100644 testing/btest/core/leaks/while.bro create mode 100644 testing/btest/language/while.bro diff --git a/src/SerialTypes.h b/src/SerialTypes.h index d2f227838c..e50ec3889f 100644 --- a/src/SerialTypes.h +++ b/src/SerialTypes.h @@ -181,6 +181,7 @@ SERIAL_STMT(INIT_STMT, 17) SERIAL_STMT(NULL_STMT, 18) SERIAL_STMT(WHEN_STMT, 19) SERIAL_STMT(FALLTHROUGH_STMT, 20) +SERIAL_STMT(WHILE_STMT, 21) #define SERIAL_TYPE(name, val) SERIAL_CONST(name, val, BRO_TYPE) SERIAL_TYPE(BRO_TYPE, 1) diff --git a/src/Stmt.cc b/src/Stmt.cc index cb716b3f15..d2f8c48cee 100644 --- a/src/Stmt.cc +++ b/src/Stmt.cc @@ -23,7 +23,7 @@ const char* stmt_name(BroStmtTag t) "print", "event", "expr", "if", "when", "switch", "for", "next", "break", "return", "add", "delete", "list", "bodylist", - "", "fallthrough", + "", "fallthrough", "while", "null", }; @@ -1127,6 +1127,126 @@ bool EventStmt::DoUnserialize(UnserialInfo* info) return event_expr != 0; } +WhileStmt::WhileStmt(Expr* arg_loop_condition, Stmt* arg_body) + : loop_condition(arg_loop_condition), body(arg_body) + { + if ( ! loop_condition->IsError() && + ! IsBool(loop_condition->Type()->Tag()) ) + loop_condition->Error("while conditional must be boolean"); + } + +WhileStmt::~WhileStmt() + { + Unref(loop_condition); + Unref(body); + } + +int WhileStmt::IsPure() const + { + return loop_condition->IsPure() && body->IsPure(); + } + +void WhileStmt::Describe(ODesc* d) const + { + Stmt::Describe(d); + + if ( d->IsReadable() ) + d->Add("("); + + loop_condition->Describe(d); + + if ( d->IsReadable() ) + d->Add(")"); + + d->SP(); + d->PushIndent(); + body->AccessStats(d); + body->Describe(d); + d->PopIndent(); + } + +TraversalCode WhileStmt::Traverse(TraversalCallback* cb) const + { + TraversalCode tc = cb->PreStmt(this); + HANDLE_TC_STMT_PRE(tc); + + tc = loop_condition->Traverse(cb); + HANDLE_TC_STMT_PRE(tc); + + tc = body->Traverse(cb); + HANDLE_TC_STMT_PRE(tc); + + tc = cb->PostStmt(this); + HANDLE_TC_STMT_POST(tc); + } + +Val* WhileStmt::Exec(Frame* f, stmt_flow_type& flow) const + { + RegisterAccess(); + flow = FLOW_NEXT; + Val* rval = 0; + + for ( ; ; ) + { + Val* cond = loop_condition->Eval(f); + + if ( ! cond ) + break; + + bool cont = cond->AsBool(); + Unref(cond); + + if ( ! cont ) + break; + + flow = FLOW_NEXT; + rval = body->Exec(f, flow); + + if ( flow == FLOW_BREAK || flow == FLOW_RETURN ) + break; + } + + if ( flow == FLOW_LOOP || flow == FLOW_BREAK ) + flow = FLOW_NEXT; + + return rval; + } + +Stmt* WhileStmt::Simplify() + { + loop_condition = simplify_expr(loop_condition, SIMPLIFY_GENERAL); + + if ( loop_condition->IsConst() && loop_condition->IsZero() ) + return new NullStmt(); + + body = simplify_stmt(body); + return this; + } + +IMPLEMENT_SERIAL(WhileStmt, SER_WHILE_STMT); + +bool WhileStmt::DoSerialize(SerialInfo* info) const + { + DO_SERIALIZE(SER_WHILE_STMT, Stmt); + + if ( ! loop_condition->Serialize(info) ) + return false; + + return body->Serialize(info); + } + +bool WhileStmt::DoUnserialize(UnserialInfo* info) + { + DO_UNSERIALIZE(Stmt); + loop_condition = Expr::Unserialize(info); + + if ( ! loop_condition ) + return false; + + body = Stmt::Unserialize(info); + return body != 0; + } + ForStmt::ForStmt(id_list* arg_loop_vars, Expr* loop_expr) : ExprStmt(STMT_FOR, loop_expr) { diff --git a/src/Stmt.h b/src/Stmt.h index 32b90b4190..79406fd51b 100644 --- a/src/Stmt.h +++ b/src/Stmt.h @@ -310,6 +310,35 @@ protected: EventExpr* event_expr; }; +class WhileStmt : public Stmt { +public: + + WhileStmt(Expr* loop_condition, Stmt* body); + + ~WhileStmt(); + + int IsPure() const; + + void Describe(ODesc* d) const; + + TraversalCode Traverse(TraversalCallback* cb) const; + +protected: + friend class Stmt; + + DECLARE_SERIAL(WhileStmt); + + WhileStmt() + { loop_condition = 0; body = 0; } + + Val* Exec(Frame* f, stmt_flow_type& flow) const; + + Stmt* Simplify(); + + Expr* loop_condition; + Stmt* body; +}; + class ForStmt : public ExprStmt { public: ForStmt(id_list* loop_vars, Expr* loop_expr); diff --git a/src/StmtEnums.h b/src/StmtEnums.h index d34f642594..ad99c2365a 100644 --- a/src/StmtEnums.h +++ b/src/StmtEnums.h @@ -17,6 +17,7 @@ typedef enum { STMT_LIST, STMT_EVENT_BODY_LIST, STMT_INIT, STMT_FALLTHROUGH, + STMT_WHILE, STMT_NULL #define NUM_STMTS (int(STMT_NULL) + 1) } BroStmtTag; diff --git a/src/parse.y b/src/parse.y index f74880dc13..8054718d45 100644 --- a/src/parse.y +++ b/src/parse.y @@ -16,6 +16,7 @@ %token TOK_REMOVE_FROM TOK_RETURN TOK_SCHEDULE TOK_SET %token TOK_STRING TOK_SUBNET TOK_SWITCH TOK_TABLE %token TOK_TIME TOK_TIMEOUT TOK_TIMER TOK_TYPE TOK_UNION TOK_VECTOR TOK_WHEN +%token TOK_WHILE %token TOK_ATTR_ADD_FUNC TOK_ATTR_ENCRYPT TOK_ATTR_DEFAULT %token TOK_ATTR_OPTIONAL TOK_ATTR_REDEF TOK_ATTR_ROTATE_INTERVAL @@ -1340,6 +1341,11 @@ stmt: $1->AsForStmt()->AddBody($2); } + | TOK_WHILE '(' expr ')' stmt + { + $$ = new WhileStmt($3, $5); + } + | TOK_NEXT ';' opt_no_test { set_location(@1, @2); diff --git a/src/scan.l b/src/scan.l index ae11382fb3..b13215e4b8 100644 --- a/src/scan.l +++ b/src/scan.l @@ -221,6 +221,7 @@ export return TOK_EXPORT; fallthrough return TOK_FALLTHROUGH; file return TOK_FILE; for return TOK_FOR; +while return TOK_WHILE; function return TOK_FUNCTION; global return TOK_GLOBAL; "?$" return TOK_HAS_FIELD; diff --git a/testing/btest/Baseline/language.while/out b/testing/btest/Baseline/language.while/out new file mode 100644 index 0000000000..d37792c0b4 --- /dev/null +++ b/testing/btest/Baseline/language.while/out @@ -0,0 +1,12 @@ +10 +s +ss +sss +{ +7, +1, +9, +5, +3 +} +[number 0, number 1, number 2, number 3, number 4, number 5, number 6, number 7, number 8, number 9, number 10, number 11, number 12] diff --git a/testing/btest/core/leaks/while.bro b/testing/btest/core/leaks/while.bro new file mode 100644 index 0000000000..eac6f2622e --- /dev/null +++ b/testing/btest/core/leaks/while.bro @@ -0,0 +1,80 @@ +# @TEST-GROUP: leaks +# @TEST-REQUIRES: bro --help 2>&1 | grep -q mem-leaks + +# @TEST-EXEC: HEAP_CHECK_DUMP_DIRECTORY=. HEAPCHECK=local btest-bg-run bro bro -m -b -r $TRACES/http/get.trace %INPUT +# @TEST-EXEC: btest-bg-wait 30 + +function test_noop() + { + while ( F ) + print "noooooooooo"; + } + +function test_it() + { + local i = 0; + + while ( i < 10 ) + ++i; + + print i; + } + +function test_break() + { + local s = ""; + + while ( T ) + { + s += "s"; + print s; + + if ( s == "sss" ) + break; + } + } + +function test_next() + { + local s: set[count]; + local i = 0; + + while ( 9 !in s ) + { + ++i; + + if ( i % 2 == 0 ) + next; + + add s[i]; + } + + print s; + } + +function test_return(): vector of string + { + local i = 0; + local rval: vector of string; + + while ( T ) + { + rval[i] = fmt("number %d", i); + ++i; + + if ( i == 13 ) + return rval; + } + + rval[0] = "noooo"; + return rval; + } + +event new_connection(c: connection) + { + test_noop(); + test_it(); + test_break(); + test_next(); + print test_return(); + } diff --git a/testing/btest/language/while.bro b/testing/btest/language/while.bro new file mode 100644 index 0000000000..6828b00b41 --- /dev/null +++ b/testing/btest/language/while.bro @@ -0,0 +1,77 @@ +# @TEST-EXEC: bro -b %INPUT >out +# @TEST-EXEC: btest-diff out + +function test_noop() + { + while ( F ) + print "noooooooooo"; + } + +function test_it() + { + local i = 0; + + while ( i < 10 ) + ++i; + + print i; + } + +function test_break() + { + local s = ""; + + while ( T ) + { + s += "s"; + print s; + + if ( s == "sss" ) + break; + } + } + +function test_next() + { + local s: set[count]; + local i = 0; + + while ( 9 !in s ) + { + ++i; + + if ( i % 2 == 0 ) + next; + + add s[i]; + } + + print s; + } + +function test_return(): vector of string + { + local i = 0; + local rval: vector of string; + + while ( T ) + { + rval[i] = fmt("number %d", i); + ++i; + + if ( i == 13 ) + return rval; + } + + rval[0] = "noooo"; + return rval; + } + +event bro_init() + { + test_noop(); + test_it(); + test_break(); + test_next(); + print test_return(); + }