diff --git a/src/Expr.cc b/src/Expr.cc index 12d3d72304..32fc9b5acc 100644 --- a/src/Expr.cc +++ b/src/Expr.cc @@ -3320,12 +3320,20 @@ bool HasFieldExpr::DoUnserialize(UnserialInfo* info) return UNSERIALIZE(¬_used) && UNSERIALIZE_STR(&field_name, 0) && UNSERIALIZE(&field); } -RecordConstructorExpr::RecordConstructorExpr(ListExpr* constructor_list) +RecordConstructorExpr::RecordConstructorExpr(ListExpr* constructor_list, + BroType* arg_type) : UnaryExpr(EXPR_RECORD_CONSTRUCTOR, constructor_list) { if ( IsError() ) return; + if ( arg_type && arg_type->Tag() != TYPE_RECORD ) + { + Error("bad record constructor type", arg_type); + SetError(); + return; + } + // Spin through the list, which should be comprised of // either record's or record-field-assign, and build up a // record type to associate with this constructor. @@ -3365,7 +3373,17 @@ RecordConstructorExpr::RecordConstructorExpr(ListExpr* constructor_list) } } - SetType(new RecordType(record_types)); + ctor_type = new RecordType(record_types); + + if ( arg_type ) + SetType(arg_type->Ref()); + else + SetType(ctor_type->Ref()); + } + +RecordConstructorExpr::~RecordConstructorExpr() + { + Unref(ctor_type); } Val* RecordConstructorExpr::InitVal(const BroType* t, Val* aggr) const @@ -3391,7 +3409,7 @@ Val* RecordConstructorExpr::InitVal(const BroType* t, Val* aggr) const Val* RecordConstructorExpr::Fold(Val* v) const { ListVal* lv = v->AsListVal(); - RecordType* rt = type->AsRecordType(); + RecordType* rt = ctor_type->AsRecordType(); if ( lv->Length() != rt->NumFields() ) Internal("inconsistency evaluating record constructor"); @@ -3401,6 +3419,19 @@ Val* RecordConstructorExpr::Fold(Val* v) const for ( int i = 0; i < lv->Length(); ++i ) rv->Assign(i, lv->Index(i)->Ref()); + if ( ! same_type(rt, type) ) + { + RecordVal* new_val = rv->CoerceTo(type->AsRecordType()); + + if ( new_val ) + { + Unref(rv); + rv = new_val; + } + else + Internal("record constructor coercion failed"); + } + return rv; } @@ -3416,12 +3447,16 @@ IMPLEMENT_SERIAL(RecordConstructorExpr, SER_RECORD_CONSTRUCTOR_EXPR); bool RecordConstructorExpr::DoSerialize(SerialInfo* info) const { DO_SERIALIZE(SER_RECORD_CONSTRUCTOR_EXPR, UnaryExpr); + SERIALIZE_OPTIONAL(ctor_type); return true; } bool RecordConstructorExpr::DoUnserialize(UnserialInfo* info) { DO_UNSERIALIZE(UnaryExpr); + BroType* t = 0; + UNSERIALIZE_OPTIONAL(t, RecordType::Unserialize(info)); + ctor_type = t->AsRecordType(); return true; } diff --git a/src/Expr.h b/src/Expr.h index bb7526d502..edc1ced6e5 100644 --- a/src/Expr.h +++ b/src/Expr.h @@ -57,6 +57,7 @@ extern const char* expr_name(BroExprTag t); class Stmt; class Frame; class ListExpr; +class NameExpr; class CallExpr; class EventExpr; @@ -165,6 +166,17 @@ public: return (ListExpr*) this; } + const NameExpr* AsNameExpr() const + { + CHECK_TAG(tag, EXPR_NAME, "ExprVal::AsNameExpr", expr_name) + return (const NameExpr*) this; + } + NameExpr* AsNameExpr() + { + CHECK_TAG(tag, EXPR_NAME, "ExprVal::AsNameExpr", expr_name) + return (NameExpr*) this; + } + void Describe(ODesc* d) const; bool Serialize(SerialInfo* info) const; @@ -729,7 +741,8 @@ protected: class RecordConstructorExpr : public UnaryExpr { public: - RecordConstructorExpr(ListExpr* constructor_list); + RecordConstructorExpr(ListExpr* constructor_list, BroType* arg_type = 0); + ~RecordConstructorExpr(); protected: friend class Expr; @@ -741,6 +754,8 @@ protected: void ExprDescribe(ODesc* d) const; DECLARE_SERIAL(RecordConstructorExpr); + + RecordType* ctor_type; // type inferred from the ctor expression list args }; class TableConstructorExpr : public UnaryExpr { diff --git a/src/Type.h b/src/Type.h index 0b7620cd68..bad51776d9 100644 --- a/src/Type.h +++ b/src/Type.h @@ -217,6 +217,11 @@ public: return tag == TYPE_TABLE && (YieldType() == 0); } + int IsTable() const + { + return tag == TYPE_TABLE && (YieldType() != 0); + } + BroType* Ref() { ::Ref(this); return this; } virtual void Describe(ODesc* d) const; diff --git a/src/parse.y b/src/parse.y index 7ce1174595..bfaf282c6a 100644 --- a/src/parse.y +++ b/src/parse.y @@ -522,10 +522,39 @@ expr: $$ = new VectorConstructorExpr($3); } - | expr '(' opt_expr_list ')' + | expr '(' { - set_location(@1, @4); - $$ = new CallExpr($1, $3, in_hook > 0); + if ( $1->Tag() == EXPR_NAME && $1->Type()->IsTable() ) + ++in_init; + } + opt_expr_list + { + if ( $1->Tag() == EXPR_NAME && $1->Type()->IsTable() ) + --in_init; + } + ')' + { + set_location(@1, @6); + + BroType* ctor_type = 0; + + if ( $1->Tag() == EXPR_NAME && + (ctor_type = $1->AsNameExpr()->Id()->AsType()) ) + { + switch ( ctor_type->Tag() ) { + case TYPE_RECORD: + $$ = new RecordConstructorExpr($4, ctor_type); + break; + case TYPE_TABLE: + case TYPE_VECTOR: + default: + $1->Error("constructor type not implemented"); + YYERROR; + } + } + + else + $$ = new CallExpr($1, $4, in_hook > 0); } | TOK_HOOK { ++in_hook; } expr diff --git a/testing/btest/Baseline/language.named-ctors/out b/testing/btest/Baseline/language.named-ctors/out new file mode 100644 index 0000000000..39b2ed7c0b --- /dev/null +++ b/testing/btest/Baseline/language.named-ctors/out @@ -0,0 +1,2 @@ +[min=, max=2] +[min=7, max=42] diff --git a/testing/btest/language/named-ctors.bro b/testing/btest/language/named-ctors.bro new file mode 100644 index 0000000000..7f04b9d4b0 --- /dev/null +++ b/testing/btest/language/named-ctors.bro @@ -0,0 +1,12 @@ +# @TEST-EXEC: bro -b %INPUT >out +# @TEST-EXEC: btest-diff out + +type MyRec: record { + min: count &optional; + max: count; +}; + +local myrec: MyRec = MyRec($max=2); +print myrec; +myrec = MyRec($min=7, $max=42); +print myrec;