Add support for enum with explicit enumerator values.

* Adding support for enums with explicit enumerator values (see doc
  below) to bifcl and policy layer.

* Bifcl: remove (partially written) output files on error and
  do a nice exit(1) instead of harsh abort() on parse errors.

* CMakeText: if bifcl fails, remove output files (failsafe,
  in case bifcl fails to clean up after itself).

Enum description
----------------

Enum's are supported in .bif and .bro scripts.
An enum in a bif will become available in the event engine and
the policy layer.

Enums are "C-style". The first element in an enum will have a
value of 0, the next value will be 1, etc.
It is possible to assign an enumerator value to an element. If
next element does not have an explicit value, its values will be
the value of the last element + 1

Example::
    type foo: enum {
        BAR_A,      # value will be  0
        BAR_B,      # value will be  1
        BAR_C = 10, # value will be 10
        BAR_D,      # value will be 11
    };

Enumerator values can only by positive integer literals.
The literals can be specified in (0x....), but not in octal (bro policy
layer limitation). So, do not use 0123 as value in bifs!

Each enumerator value can only be used once per enum (C allows
to use the same value multiple times). This makes reverse mapping from
value to name (e.g., in %s format strings) unambigious. This is enforced
in by the policy script.

Enums can be redef'ed, i.e., extended. Enumerator values will continue
to increment. If there are multiple redefs in different policy scripts,
then name <-> value mappings will obviously depend on the order in
which scripts are loaded (which might not be obvious).

Example::

    redef enum foo += {
        BAR_E,      # value will be 12
        BAR_F = 5,  # value will be  5
        BAR_G,      # value will be  6
    };
This commit is contained in:
Gregor Maier 2010-12-09 18:29:47 -08:00
parent 2f7fa3470b
commit 72454c230b
6 changed files with 188 additions and 103 deletions

View file

@ -101,7 +101,7 @@ macro(BIF_TARGET bifInput)
get_bif_output_files(${bifInput} bifOutputs) get_bif_output_files(${bifInput} bifOutputs)
add_custom_command(OUTPUT ${bifOutputs} add_custom_command(OUTPUT ${bifOutputs}
COMMAND bifcl COMMAND bifcl
ARGS ${CMAKE_CURRENT_SOURCE_DIR}/${bifInput} ARGS ${CMAKE_CURRENT_SOURCE_DIR}/${bifInput} || (rm -f ${bifOutputs} && exit 1)
DEPENDS ${bifInput} DEPENDS ${bifInput}
COMMENT "[BIFCL] Processing ${bifInput}" COMMENT "[BIFCL] Processing ${bifInput}"
) )

View file

@ -1082,10 +1082,9 @@ bool FileType::DoUnserialize(UnserialInfo* info)
return yield != 0; return yield != 0;
} }
EnumType::EnumType(bool arg_is_export) EnumType::EnumType()
: BroType(TYPE_ENUM) : BroType(TYPE_ENUM)
{ {
is_export = arg_is_export;
counter = 0; counter = 0;
} }
@ -1095,9 +1094,18 @@ EnumType::~EnumType()
delete [] iter->first; delete [] iter->first;
} }
int EnumType::AddName(const string& module_name, const char* name) bro_int_t EnumType::AddName(const string& module_name, const char* name, bool is_export)
{ {
ID* id = lookup_ID(name, module_name.c_str()); return AddName(module_name, name, counter, is_export);
}
bro_int_t EnumType::AddName(const string& module_name, const char* name, bro_int_t val, bool is_export)
{
ID *id;
if ( Lookup(val) )
return -1;
id = lookup_ID(name, module_name.c_str());
if ( ! id ) if ( ! id )
{ {
id = install_ID(name, module_name.c_str(), true, is_export); id = install_ID(name, module_name.c_str(), true, is_export);
@ -1105,32 +1113,15 @@ int EnumType::AddName(const string& module_name, const char* name)
id->SetEnumConst(); id->SetEnumConst();
} }
else else
{ return -1;
debug_msg("identifier already exists: %s\n", name);
return -1;
}
string fullname = make_full_var_name(module_name.c_str(), name); string fullname = make_full_var_name(module_name.c_str(), name);
names[copy_string(fullname.c_str())] = counter; names[copy_string(fullname.c_str())] = val;
return counter++; counter = val + 1;
return val;
} }
int EnumType::AddNamesFrom(const string& module_name, EnumType* et) bro_int_t EnumType::Lookup(const string& module_name, const char* name)
{
int last_added = counter;
for ( NameMap::iterator iter = et->names.begin();
iter != et->names.end(); ++iter )
{
ID* id = lookup_ID(iter->first, module_name.c_str());
id->SetType(this->Ref());
names[copy_string(id->Name())] = counter;
last_added = counter++;
}
return last_added;
}
int EnumType::Lookup(const string& module_name, const char* name)
{ {
NameMap::iterator pos = NameMap::iterator pos =
names.find(make_full_var_name(module_name.c_str(), name).c_str()); names.find(make_full_var_name(module_name.c_str(), name).c_str());
@ -1141,7 +1132,7 @@ int EnumType::Lookup(const string& module_name, const char* name)
return pos->second; return pos->second;
} }
const char* EnumType::Lookup(int value) const char* EnumType::Lookup(bro_int_t value)
{ {
for ( NameMap::iterator iter = names.begin(); for ( NameMap::iterator iter = names.begin();
iter != names.end(); ++iter ) iter != names.end(); ++iter )
@ -1157,9 +1148,7 @@ bool EnumType::DoSerialize(SerialInfo* info) const
{ {
DO_SERIALIZE(SER_ENUM_TYPE, BroType); DO_SERIALIZE(SER_ENUM_TYPE, BroType);
// I guess we don't really need both ... if ( ! (SERIALIZE(counter) && SERIALIZE((unsigned int) names.size())) )
if ( ! (SERIALIZE(counter) && SERIALIZE((unsigned int) names.size()) &&
SERIALIZE(is_export)) )
return false; return false;
for ( NameMap::const_iterator iter = names.begin(); for ( NameMap::const_iterator iter = names.begin();
@ -1178,14 +1167,13 @@ bool EnumType::DoUnserialize(UnserialInfo* info)
unsigned int len; unsigned int len;
if ( ! UNSERIALIZE(&counter) || if ( ! UNSERIALIZE(&counter) ||
! UNSERIALIZE(&len) || ! UNSERIALIZE(&len) )
! UNSERIALIZE(&is_export) )
return false; return false;
while ( len-- ) while ( len-- )
{ {
const char* name; const char* name;
int val; bro_int_t val;
if ( ! (UNSERIALIZE_STR(&name, 0) && UNSERIALIZE(&val)) ) if ( ! (UNSERIALIZE_STR(&name, 0) && UNSERIALIZE(&val)) )
return false; return false;

View file

@ -452,31 +452,30 @@ protected:
class EnumType : public BroType { class EnumType : public BroType {
public: public:
EnumType(bool arg_is_export); EnumType();
~EnumType(); ~EnumType();
// The value of this name is next counter value, which is returned. // The value of this name is next counter value, which is returned.
// A return value of -1 means that the identifier already existed // A return value of -1 means that the identifier or the counter values
// (and thus could not be used). // already existed (and thus could not be used).
int AddName(const string& module_name, const char* name); bro_int_t AddName(const string& module_name, const char* name, bool is_export);
// Add in names from the suppled EnumType; the return value is // The value of this name is set to val, which is return. The counter will
// the value of the last enum added. // be updated, so the next name (without val) will have val+1
int AddNamesFrom(const string& module_name, EnumType* et); // A return value of -1 means that the identifier or val
// already existed (and thus could not be used).
bro_int_t AddName(const string& module_name, const char* name, bro_int_t val, bool is_export);
// -1 indicates not found. // -1 indicates not found.
int Lookup(const string& module_name, const char* name); bro_int_t Lookup(const string& module_name, const char* name);
const char* Lookup(int value); // Returns 0 if not found const char* Lookup(bro_int_t value); // Returns 0 if not found
protected: protected:
EnumType() {}
DECLARE_SERIAL(EnumType) DECLARE_SERIAL(EnumType)
typedef std::map< const char*, int, ltstr > NameMap; typedef std::map< const char*, bro_int_t, ltstr > NameMap;
NameMap names; NameMap names;
int counter; bro_int_t counter;
bool is_export;
}; };
class VectorType : public BroType { class VectorType : public BroType {

View file

@ -2,6 +2,7 @@
// $Id: builtin-func.l 6015 2008-07-23 05:42:37Z vern $ // $Id: builtin-func.l 6015 2008-07-23 05:42:37Z vern $
#include <string.h> #include <string.h>
#include <unistd.h>
#include "bif_arg.h" #include "bif_arg.h"
#include "bif_parse.h" #include "bif_parse.h"
@ -29,6 +30,7 @@ int check_c_mode(int t)
WS [ \t]+ WS [ \t]+
ID [A-Za-z_][A-Za-z_0-9]* ID [A-Za-z_][A-Za-z_0-9]*
ESCSEQ (\\([^\n]|[0-7]+|x[[:xdigit:]]+)) ESCSEQ (\\([^\n]|[0-7]+|x[[:xdigit:]]+))
INT [[:digit:]]+
%option nodefault %option nodefault
@ -78,6 +80,11 @@ ESCSEQ (\\([^\n]|[0-7]+|x[[:xdigit:]]+))
"T" yylval.val = 1; return TOK_BOOL; "T" yylval.val = 1; return TOK_BOOL;
"F" yylval.val = 0; return TOK_BOOL; "F" yylval.val = 0; return TOK_BOOL;
{INT} {
yylval.str = copy_string(yytext);
return TOK_INT;
}
{ID} { {ID} {
yylval.str = copy_string(yytext); yylval.str = copy_string(yytext);
return TOK_ID; return TOK_ID;
@ -111,6 +118,7 @@ ESCSEQ (\\([^\n]|[0-7]+|x[[:xdigit:]]+))
} }
%% %%
int yywrap() int yywrap()
{ {
yy_delete_buffer(YY_CURRENT_BUFFER); yy_delete_buffer(YY_CURRENT_BUFFER);
@ -120,13 +128,21 @@ int yywrap()
extern int yyparse(); extern int yyparse();
char* input_filename = 0; char* input_filename = 0;
FILE* fp_bro_init; FILE* fp_bro_init = 0;
FILE* fp_func_def; FILE* fp_func_def = 0;
FILE* fp_func_h; FILE* fp_func_h = 0;
FILE* fp_func_init; FILE* fp_func_init = 0;
FILE* fp_netvar_h; FILE* fp_netvar_h = 0;
FILE* fp_netvar_def; FILE* fp_netvar_def = 0;
FILE* fp_netvar_init; FILE* fp_netvar_init = 0;
void remove_file(const char *surfix);
void err_exit(void);
FILE* open_output_file(const char* surfix);
void close_if_open(FILE **fpp);
void close_all_output_files(void);
FILE* open_output_file(const char* surfix) FILE* open_output_file(const char* surfix)
{ {
@ -137,12 +153,13 @@ FILE* open_output_file(const char* surfix)
if ( (fp = fopen(fn, "w")) == NULL ) if ( (fp = fopen(fn, "w")) == NULL )
{ {
fprintf(stderr, "Error: cannot open file: %s\n", fn); fprintf(stderr, "Error: cannot open file: %s\n", fn);
exit(1); err_exit();
} }
return fp; return fp;
} }
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
for ( int i = 1; i < argc; i++ ) for ( int i = 1; i < argc; i++ )
@ -156,6 +173,7 @@ int main(int argc, char* argv[])
if ( (fp_input = fopen(input_filename, "r")) == NULL ) if ( (fp_input = fopen(input_filename, "r")) == NULL )
{ {
fprintf(stderr, "Error: cannot open file: %s\n", input_filename); fprintf(stderr, "Error: cannot open file: %s\n", input_filename);
/* no output files open. can simply exit */
exit(1); exit(1);
} }
@ -174,12 +192,48 @@ int main(int argc, char* argv[])
yyparse(); yyparse();
fclose(fp_input); fclose(fp_input);
fclose(fp_bro_init); close_all_output_files();
fclose(fp_func_h);
fclose(fp_func_def);
fclose(fp_func_init);
fclose(fp_netvar_h);
fclose(fp_netvar_def);
fclose(fp_netvar_init);
} }
} }
void close_if_open(FILE **fpp)
{
if (*fpp)
fclose(*fpp);
*fpp = NULL;
}
void close_all_output_files(void)
{
close_if_open(&fp_bro_init);
close_if_open(&fp_func_h);
close_if_open(&fp_func_def);
close_if_open(&fp_func_init);
close_if_open(&fp_netvar_h);
close_if_open(&fp_netvar_def);
close_if_open(&fp_netvar_init);
}
void remove_file(const char *surfix)
{
char fn[1024];
snprintf(fn, sizeof(fn), "%s.%s", input_filename, surfix);
unlink(fn);
}
void err_exit(void)
{
close_all_output_files();
/* clean up. remove all output files we've generated so far */
remove_file("bro");
remove_file("func_h");
remove_file("func_def");
remove_file("func_init");
remove_file("netvar_h");
remove_file("netvar_def");
remove_file("netvar_init");
exit(1);
}

View file

@ -158,11 +158,11 @@ void print_event_c_body(FILE *fp)
%token TOK_WRITE TOK_PUSH TOK_EOF TOK_TRACE %token TOK_WRITE TOK_PUSH TOK_EOF TOK_TRACE
%token TOK_ARGS TOK_ARG TOK_ARGC %token TOK_ARGS TOK_ARG TOK_ARGC
%token TOK_ID TOK_ATTR TOK_CSTR TOK_LF TOK_WS TOK_COMMENT %token TOK_ID TOK_ATTR TOK_CSTR TOK_LF TOK_WS TOK_COMMENT
%token TOK_ATOM TOK_C_TOKEN %token TOK_ATOM TOK_INT TOK_C_TOKEN
%left ',' ':' %left ',' ':'
%type <str> TOK_C_TOKEN TOK_ID TOK_CSTR TOK_WS TOK_COMMENT TOK_ATTR opt_ws %type <str> TOK_C_TOKEN TOK_ID TOK_CSTR TOK_WS TOK_COMMENT TOK_ATTR TOK_INT opt_ws
%type <val> TOK_ATOM TOK_BOOL %type <val> TOK_ATOM TOK_BOOL
%union { %union {
@ -257,6 +257,11 @@ enum_list: enum_list TOK_ID opt_ws ',' opt_ws
fprintf(fp_bro_init, "%s%s,%s", $2, $3, $5); fprintf(fp_bro_init, "%s%s,%s", $2, $3, $5);
fprintf(fp_netvar_h, "\t%s,\n", $2); fprintf(fp_netvar_h, "\t%s,\n", $2);
} }
| enum_list TOK_ID opt_ws '=' opt_ws TOK_INT opt_ws ',' opt_ws
{
fprintf(fp_bro_init, "%s = %s%s,%s", $2, $6, $7, $9);
fprintf(fp_netvar_h, "\t%s = %s,\n", $2, $6);
}
| /* nothing */ | /* nothing */
; ;
@ -543,6 +548,9 @@ c_atom: TOK_ID
{ fprintf(fp_func_def, "%s", $1); } { fprintf(fp_func_def, "%s", $1); }
| TOK_ATOM | TOK_ATOM
{ fprintf(fp_func_def, "%c", $1); } { fprintf(fp_func_def, "%c", $1); }
| TOK_INT
{ fprintf(fp_func_def, "%s", $1); }
; ;
opt_ws: opt_ws TOK_WS opt_ws: opt_ws TOK_WS
@ -566,6 +574,7 @@ extern char* yytext;
extern char* input_filename; extern char* input_filename;
extern int line_number; extern int line_number;
const char* decl_name; const char* decl_name;
void err_exit(void);
void print_msg(const char msg[]) void print_msg(const char msg[])
{ {
@ -605,7 +614,6 @@ int yyerror(const char msg[])
{ {
print_msg(msg); print_msg(msg);
abort(); err_exit();
exit(1);
return 0; return 0;
} }

View file

@ -51,7 +51,7 @@
%type <expr> expr init anonymous_function %type <expr> expr init anonymous_function
%type <event_expr> event %type <event_expr> event
%type <stmt> stmt stmt_list func_body for_head %type <stmt> stmt stmt_list func_body for_head
%type <type> type opt_type refined_type enum_id_list %type <type> type opt_type refined_type enum_body
%type <func_type> func_hdr func_params %type <func_type> func_hdr func_params
%type <type_l> type_list %type <type_l> type_list
%type <type_decl> type_decl formal_args_decl %type <type_decl> type_decl formal_args_decl
@ -104,6 +104,29 @@ bool in_debug = false;
bool resolving_global_ID = false; bool resolving_global_ID = false;
ID* func_id = 0; ID* func_id = 0;
EnumType *cur_enum_type = 0;
static void parser_new_enum (void)
{
/* starting a new enum definition. */
assert(cur_enum_type == NULL);
cur_enum_type = new EnumType();
}
static void parser_redef_enum (ID *id)
{
/* redef an enum. id points to the enum to be redefined.
let cur_enum_type point to it */
assert(cur_enum_type == NULL);
if ( ! id->Type() )
id->Error("unknown identifier");
else
{
cur_enum_type = id->Type()->AsEnumType();
if ( ! cur_enum_type )
id->Error("not an enum");
}
}
%} %}
%union { %union {
@ -546,27 +569,52 @@ single_pattern:
{ $$ = $3; } { $$ = $3; }
; ;
enum_id_list: enum_body:
TOK_ID enum_body_list opt_comma
{ {
set_location(@1); $$ = cur_enum_type;
cur_enum_type = NULL;
EnumType* et = new EnumType(is_export);
if ( et->AddName(current_module, $1) < 0 )
error("identifier in enumerated type definition already exists");
$$ = et;
}
| enum_id_list ',' TOK_ID
{
set_location(@1, @3);
if ( $1->AsEnumType()->AddName(current_module, $3) < 1 )
error("identifier in enumerated type definition already exists");
$$ = $1;
} }
; ;
enum_body_list:
enum_body_elem /* No action */
| enum_body_list ',' enum_body_elem /* no action */
;
enum_body_elem:
/* TODO: We could also define this as TOK_ID '=' expr, (or
TOK_ID '=' = TOK_ID) so that we can return more descriptive
error messages if someboy tries to use constant variables as
enumerator.
*/
TOK_ID '=' TOK_CONSTANT
{
set_location(@1, @3);
assert(cur_enum_type);
if ($3->Type()->Tag() != TYPE_COUNT)
error("enumerator is not a count constant");
if ( cur_enum_type->AddName(current_module, $1, $3->InternalUnsigned(), is_export) < 0 )
error("identifier or enumerator value in enumerated type definition already exists");
}
| TOK_ID '=' '-' TOK_CONSTANT
{
/* We only accept counts as enumerator, but we want to return a nice
error message if users tries to use a negative integer (will also
catch other cases, but that's fine)
*/
error("enumerator is not a count constant");
}
| TOK_ID
{
set_location(@1);
assert(cur_enum_type);
if ( cur_enum_type->AddName(current_module, $1, is_export) < 0 )
error("identifier or enumerator value in enumerated type definition already exists");
}
;
type: type:
TOK_BOOL { TOK_BOOL {
set_location(@1); set_location(@1);
@ -668,10 +716,10 @@ type:
$$ = 0; $$ = 0;
} }
| TOK_ENUM '{' enum_id_list opt_comma '}' | TOK_ENUM '{' { parser_new_enum(); } enum_body '}'
{ {
set_location(@1, @4); set_location(@1, @5);
$$ = $3; $$ = $4;
} }
| TOK_LIST | TOK_LIST
@ -801,21 +849,9 @@ decl:
| TOK_REDEF global_id opt_type init_class opt_init opt_attr ';' | TOK_REDEF global_id opt_type init_class opt_init opt_attr ';'
{ add_global($2, $3, $4, $5, $6, VAR_REDEF); } { add_global($2, $3, $4, $5, $6, VAR_REDEF); }
| TOK_REDEF TOK_ENUM global_id TOK_ADD_TO | TOK_REDEF TOK_ENUM global_id TOK_ADD_TO
'{' enum_id_list opt_comma '}' ';' '{' { parser_redef_enum($3); } enum_body '}' ';'
{ { /* no action */ }
if ( ! $3->Type() )
$3->Error("unknown identifier");
else
{
EnumType* add_to = $3->Type()->AsEnumType();
if ( ! add_to )
$3->Error("not an enum");
else
add_to->AddNamesFrom(current_module,
$6->AsEnumType());
}
}
| TOK_TYPE global_id ':' refined_type opt_attr ';' | TOK_TYPE global_id ':' refined_type opt_attr ';'
{ {