diff --git a/lib/asn1/asn1parse.y b/lib/asn1/asn1parse.y index b70b01cee..b26bfb2ce 100644 --- a/lib/asn1/asn1parse.y +++ b/lib/asn1/asn1parse.y @@ -240,7 +240,6 @@ static int default_tag_env = TE_EXPLICIT; ModuleDefinition: IDENTIFIER objid_opt kw_DEFINITIONS TagDefault ExtensionDefault EEQUAL kw_BEGIN ModuleBody kw_END { - checkundefined(); } ; @@ -320,11 +319,14 @@ referencenames : IDENTIFIER ',' referencenames TypeAssignment : IDENTIFIER EEQUAL Type { - Symbol *s = addsym ($1); + Symbol *s = addsym($1); s->stype = Stype; s->type = $3; fix_labels(s); - generate_type (s); + if (original_order) + generate_type(s); + else + generate_type_header_forwards(s); } ; diff --git a/lib/asn1/gen.c b/lib/asn1/gen.c index b2b559f6f..2676c12e8 100644 --- a/lib/asn1/gen.c +++ b/lib/asn1/gen.c @@ -752,9 +752,12 @@ define_type (int level, const char *name, const char *basename, Type *t, int typ switch (t->type) { case TType: space(level); - fprintf (headerfile, "%s %s;\n", t->symbol->gen_name, name); + fprintf(headerfile, "%s %s;\n", t->symbol->gen_name, name); break; case TInteger: + if (t->symbol && t->symbol->emitted_definition) + break; + space(level); if(t->members) { Member *m; @@ -768,11 +771,11 @@ define_type (int level, const char *name, const char *basename, Type *t, int typ label_prefix, label_prefix_sep, m->gen_name, m->val, last_member_p(m)); } - fprintf (headerfile, "} %s;\n", name); + fprintf(headerfile, "} %s;\n", name); } else if (t->range == NULL) { - fprintf (headerfile, "heim_integer %s;\n", name); + fprintf(headerfile, "heim_integer %s;\n", name); } else if (t->range->min < INT_MIN && t->range->max <= INT64_MAX) { - fprintf (headerfile, "int64_t %s;\n", name); + fprintf(headerfile, "int64_t %s;\n", name); } else if (t->range->min >= 0 && t->range->max > UINT_MAX) { fprintf (headerfile, "uint64_t %s;\n", name); } else if (t->range->min >= INT_MIN && t->range->max <= INT_MAX) { @@ -798,6 +801,10 @@ define_type (int level, const char *name, const char *basename, Type *t, int typ size_t max_memno = 0; size_t bitset_size; + if (t->symbol && t->symbol->emitted_definition) + break; + memset(&i, 0, sizeof(i)); + /* * range.max implies the size of the base unsigned integer used for the * bitfield members. If it's less than or equal to UINT_MAX, then that @@ -827,7 +834,7 @@ define_type (int level, const char *name, const char *basename, Type *t, int typ fprintf (headerfile, "heim_bit_string %s;\n", name); else { int pos = 0; - getnewbasename(&newbasename, typedefp, basename, name); + getnewbasename(&newbasename, typedefp || level == 0, basename, name); fprintf (headerfile, "struct %s {\n", newbasename); HEIM_TAILQ_FOREACH(m, t->members, members) { @@ -876,6 +883,9 @@ define_type (int level, const char *name, const char *basename, Type *t, int typ case TEnumerated: { Member *m; + if (t->symbol && t->symbol->emitted_definition) + break; + label_prefix = prefix_enum ? name : (enum_prefix ? enum_prefix : ""); label_prefix_sep = prefix_enum ? "_" : ""; space(level); @@ -897,7 +907,7 @@ define_type (int level, const char *name, const char *basename, Type *t, int typ case TSequence: { Member *m; - getnewbasename(&newbasename, typedefp, basename, name); + getnewbasename(&newbasename, typedefp || level == 0, basename, name); space(level); fprintf (headerfile, "struct %s {\n", newbasename); @@ -927,12 +937,11 @@ define_type (int level, const char *name, const char *basename, Type *t, int typ Type i; struct range range = { 0, UINT_MAX }; - getnewbasename(&newbasename, typedefp, basename, name); + getnewbasename(&newbasename, typedefp || level == 0, basename, name); + memset(&i, 0, sizeof(i)); i.type = TInteger; i.range = ⦥ - i.members = NULL; - i.constraint = NULL; space(level); fprintf (headerfile, "struct %s {\n", newbasename); @@ -955,13 +964,13 @@ define_type (int level, const char *name, const char *basename, Type *t, int typ fprintf (headerfile, "heim_general_string %s;\n", name); break; case TTag: - define_type (level, name, basename, t->subtype, typedefp, preservep); + define_type (level, name, basename, t->subtype, typedefp, preservep); break; case TChoice: { int first = 1; Member *m; - getnewbasename(&newbasename, typedefp, basename, name); + getnewbasename(&newbasename, typedefp || level == 0, basename, name); space(level); fprintf (headerfile, "struct %s {\n", newbasename); @@ -1050,24 +1059,229 @@ define_type (int level, const char *name, const char *basename, Type *t, int typ default: abort (); } - if (newbasename) - free(newbasename); + free(newbasename); +} + +static void +declare_type(const Symbol *s, Type *t, int typedefp) +{ + char *newbasename = NULL; + + if (typedefp) + fprintf(headerfile, "typedef "); + + switch (t->type) { + case TType: + define_type(0, s->gen_name, s->gen_name, s->type, TRUE, TRUE); + if (template_flag) + generate_template_type_forward(s->gen_name); + emitted_declaration(s); + return; + case TInteger: + case TBoolean: + case TOctetString: + case TBitString: + case TEnumerated: + case TGeneralizedTime: + case TGeneralString: + case TTeletexString: + case TUTCTime: + case TUTF8String: + case TPrintableString: + case TIA5String: + case TBMPString: + case TUniversalString: + case TVisibleString: + case TOID : + case TNull: + define_type(0, s->gen_name, s->gen_name, s->type, TRUE, TRUE); + if (template_flag) + generate_template_type_forward(s->gen_name); + emitted_declaration(s); + emitted_definition(s); + return; + case TTag: + declare_type(s, t->subtype, FALSE); + emitted_declaration(s); + return; + default: + break; + } + + switch (t->type) { + case TSet: + case TSequence: + getnewbasename(&newbasename, TRUE, s->gen_name, s->gen_name); + fprintf(headerfile, "struct %s %s;\n", newbasename, s->gen_name); + break; + case TSetOf: + case TSequenceOf: + getnewbasename(&newbasename, TRUE, s->gen_name, s->gen_name); + fprintf(headerfile, "struct %s %s;\n", newbasename, s->gen_name); + break; + case TChoice: + getnewbasename(&newbasename, TRUE, s->gen_name, s->gen_name); + fprintf(headerfile, "struct %s %s;\n", newbasename, s->gen_name); + break; + default: + abort (); + } + free(newbasename); + emitted_declaration(s); +} + +static void generate_subtypes_header_helper(const Member *m); +static void generate_type_header(const Symbol *); + +static void +generate_subtypes_header_helper(const Member *m) +{ + Member *sm; + Symbol *s; + + if (m->ellipsis) + return; + if (m->type->symbol && (s = getsym(m->type->symbol->name)) && + !s->emitted_definition) { + /* A field of some named type; recurse */ + if (!m->optional && !m->defval) + generate_type_header(s); + return; + } + if (!m->type->subtype && !m->type->members) + return; + if (m->type->type == TTag && + m->type->subtype && m->type->subtype->symbol && + (s = getsym(m->type->subtype->symbol->name))) { + if (!m->optional && !m->defval) + generate_type_header(s); + return; + } + if (m->type->subtype) { + switch (m->type->subtype->type) { + case TSet: + case TSequence: + case TChoice: + break; + default: + return; + } + /* A field of some anonymous (inlined) structured type */ + HEIM_TAILQ_FOREACH(sm, m->type->subtype->members, members) { + generate_subtypes_header_helper(sm); + } + } + if (m->type->members) { + HEIM_TAILQ_FOREACH(sm, m->type->members, members) { + generate_subtypes_header_helper(sm); + } + } +} + +static void +generate_subtypes_header(const Symbol *s) +{ + Type *t = s->type; + Member *m; + + /* + * Recurse down structured types to make sure top-level types get + * defined before they are referenced. + * + * We'll take care to skip OPTIONAL member fields of constructed types so + * that we can have circular types like: + * + * Foo ::= SEQUENCE { + * bar Bar OPTIONAL + * } + * + * Bar ::= SEQUENCE { + * foo Foo OPTIONAL + * } + * + * not unlike XDR, which uses `*' to mean "optional", except in XDR it's + * called a "pointer". With some care we should be able to eventually + * support the silly XDR linked list example: + * + * ListOfFoo ::= SEQUENCE { + * someField SomeType, + * next ListOfFoo OPTIONAL + * } + * + * Not that anyone needs it -- just use a SEQUENCE OF and be done. + */ + + while (t->type == TTag && t->subtype) { + switch (t->subtype->type) { + case TTag: + case TSet: + case TSequence: + case TChoice: + t = t->subtype; + continue; + default: + break; + } + break; + } + + switch (t->type) { + default: return; + case TType: + if (t->symbol && (s = getsym(t->symbol->name))) + generate_type_header(s); + return; + case TSet: + case TSequence: + case TChoice: + break; + } + + HEIM_TAILQ_FOREACH(m, t->members, members) { + generate_subtypes_header_helper(m); + } } static void generate_type_header (const Symbol *s) { - int preservep = preserve_type(s->name) ? TRUE : FALSE; - fprintf (headerfile, "/*\n"); - fprintf (headerfile, "%s ::= ", s->name); + /* + * Recurse down the types of member fields of `s' to make sure that + * referenced types have had their definitions emitted already if the + * member fields are not OPTIONAL/DEFAULTed. + */ + if (s->type) + generate_subtypes_header(s); + + if (!s->type) + return; + + fprintf(headerfile, "/*\n"); + fprintf(headerfile, "%s ::= ", s->name); define_asn1 (0, s->type); - fprintf (headerfile, "\n*/\n\n"); + fprintf(headerfile, "\n*/\n\n"); - fprintf (headerfile, "typedef "); - define_type (0, s->gen_name, s->gen_name, s->type, TRUE, preservep); + if (s->emitted_definition) + return; - fprintf (headerfile, "\n"); + fprintf(headerfile, "typedef "); + define_type(0, s->gen_name, s->gen_name, s->type, TRUE, + preserve_type(s->name) ? TRUE : FALSE); + + fprintf(headerfile, "\n"); + + if (template_flag) + generate_template_type_forward(s->gen_name); + + emitted_definition(s); +} + +void +generate_type_header_forwards(const Symbol *s) +{ + declare_type(s, s->type, TRUE); + fprintf(headerfile, "\n"); } void diff --git a/lib/asn1/gen_locl.h b/lib/asn1/gen_locl.h index 61e728886..064b7ba26 100644 --- a/lib/asn1/gen_locl.h +++ b/lib/asn1/gen_locl.h @@ -103,13 +103,16 @@ typedef struct asn1_module { /* CLI options and flags needed everywhere: */ getarg_strings preserve; getarg_strings seq; + const char *enum_prefix; unsigned int one_code_file:1; unsigned int support_ber:1; unsigned int parse_units_flag:1; + unsigned int prefix_enum:1; /* Should be a getarg_strings of bitrsting types to do this for */ unsigned int rfc1510_bitstring:1; /* Should be a getarg_strings of bitrsting types to do this for */ } *asn1_module; void generate_type (const Symbol *); +void generate_type_header_forwards(const Symbol *); void generate_constant (const Symbol *); void generate_type_encode (const Symbol *); void generate_type_decode (const Symbol *); @@ -143,6 +146,7 @@ void close_codefile(void); int is_template_compat (const Symbol *); void generate_template(const Symbol *); +void generate_template_type_forward(const char *); void gen_template_import(const Symbol *); @@ -152,6 +156,7 @@ extern int support_ber; extern int template_flag; extern int rfc1510_bitstring; extern int one_code_file; +extern int original_order; extern int parse_units_flag; extern char *type_file_string; diff --git a/lib/asn1/gen_template.c b/lib/asn1/gen_template.c index a5b9b6e12..7e5329bc8 100644 --- a/lib/asn1/gen_template.c +++ b/lib/asn1/gen_template.c @@ -879,6 +879,12 @@ gen_template_import(const Symbol *s) gen_extern_stubs(f, s->gen_name); } +void +generate_template_type_forward(const char *name) +{ + fprintf(get_code_file(), "extern const struct asn1_template asn1_%s[];\n", name); +} + static void generate_template_type(const char *varname, const char **dupname, @@ -900,8 +906,6 @@ generate_template_type(const char *varname, if (type->type == TTag) implicit = (type->tag.tagenv == TE_IMPLICIT); - fprintf(get_code_file(), "extern const struct asn1_template asn1_%s[];\n", tl->name); - template_members(&tl->template, basetype, name, type, optional, implicit, isstruct, need_offset); /* if its a sequence or set type, check if there is a ellipsis */ diff --git a/lib/asn1/main.c b/lib/asn1/main.c index 752a582a8..dfdd43a4f 100644 --- a/lib/asn1/main.c +++ b/lib/asn1/main.c @@ -71,6 +71,7 @@ int one_code_file; char *option_file; int parse_units_flag = 1; char *type_file_string = "krb5-types.h"; +int original_order; int version_flag; int help_flag; struct getargs args[] = { @@ -88,7 +89,15 @@ struct getargs args[] = { { "preserve-binary", 0, arg_strings, &preserve, NULL, NULL }, { "sequence", 0, arg_strings, &seq, NULL, NULL }, { "one-code-file", 0, arg_flag, &one_code_file, NULL, NULL }, - { "option-file", 0, arg_string, &option_file, NULL, NULL }, + { "gen-name", 0, arg_string, &name, + "Name of generated module", "NAME" }, + { "option-file", 0, arg_string, &option_file, + "File with additional compiler CLI options", "FILE" }, + { "original-order", 0, arg_flag, &original_order, + "Define C types and functions in the order in which they appear in " + "the ASN.1 module instead of topologically sorting types. This " + "is useful for comparing output to earlier compiler versions.", + NULL }, { "parse-units", 0, arg_negative_flag, &parse_units_flag, NULL, NULL }, { "type-file", 0, arg_string, &type_file_string, NULL, NULL }, { "version", 0, arg_flag, &version_flag, NULL, NULL }, @@ -116,11 +125,11 @@ main(int argc, char **argv) int len = 0, i; setprogname(argv[0]); - if(getarg(args, num_args, argc, argv, &optidx)) + if (getarg(args, num_args, argc, argv, &optidx)) usage(1); - if(help_flag) + if (help_flag) usage(0); - if(version_flag) { + if (version_flag) { print_version(NULL); exit(0); } @@ -204,7 +213,7 @@ main(int argc, char **argv) } - init_generate (file, name); + init_generate(file, name); if (one_code_file) generate_header_of_codefile(name); @@ -213,6 +222,8 @@ main(int argc, char **argv) ret = yyparse (); if(ret != 0 || error_flag != 0) exit(1); + if (!original_order) + generate_types(); close_generate (); if (argc != optidx) fclose(yyin); diff --git a/lib/asn1/symbol.c b/lib/asn1/symbol.c index a090ee95c..99b67d545 100644 --- a/lib/asn1/symbol.c +++ b/lib/asn1/symbol.c @@ -33,9 +33,12 @@ #include "gen_locl.h" #include "lex.h" +#include "lex.h" static Hashtab *htab; +struct symhead symbols; + static int cmp(void *a, void *b) { @@ -84,10 +87,28 @@ addsym(char *name) output_name(s->gen_name); s->stype = SUndefined; hashtabadd(htab, s); + //HEIM_TAILQ_INSERT_TAIL(&symbols, s, symlist); + do { + if (((s)->symlist.tqe_next = (&symbols)->tqh_first) != NULL) + (&symbols)->tqh_first->symlist.tqe_prev = &(s)->symlist.tqe_next; + else + (&symbols)->tqh_last = &(s)->symlist.tqe_next; + (&symbols)->tqh_first = (s); + (s)->symlist.tqe_prev = &(&symbols)->tqh_first; + } while (0); } return s; } +Symbol * +getsym(char *name) +{ + Symbol key; + + key.name = name; + return (Symbol *) hashtabsearch(htab, (void *) &key); +} + static int checkfunc(void *ptr, void *arg) { @@ -106,3 +127,41 @@ checkundefined(void) hashtabforeach(htab, checkfunc, &f); return f; } + +#if 0 +static int +generate_1type(void *ptr, void *arg) +{ + Symbol *s = ptr; + + if (s->stype == Stype && s->type) + generate_type(s); + return 0; +} +#endif + +void +generate_types(void) +{ + Symbol *s; + + if (checkundefined()) + errx(1, "Some types are undefined"); + HEIM_TAILQ_FOREACH_REVERSE(s, &symbols, symhead, symlist) { + if (s->stype == Stype && s->type) + generate_type(s); + } + //hashtabforeach(htab, generate_1type, NULL); +} + +void +emitted_declaration(const Symbol *s) +{ + ((Symbol *)(uintptr_t)s)->emitted_declaration = 1; +} + +void +emitted_definition(const Symbol *s) +{ + ((Symbol *)(uintptr_t)s)->emitted_definition = 1; +} diff --git a/lib/asn1/symbol.h b/lib/asn1/symbol.h index f97faf2b0..b4238550f 100644 --- a/lib/asn1/symbol.h +++ b/lib/asn1/symbol.h @@ -156,12 +156,27 @@ struct symbol { enum { SUndefined, SValue, Stype } stype; struct value *value; Type *type; + HEIM_TAILQ_ENTRY(symbol) symlist; + unsigned int emitted_declaration:1; + unsigned int emitted_definition:1; }; typedef struct symbol Symbol; +//HEIM_TAILQ_HEAD(symhead, symbol); +struct symhead { + struct symbol *tqh_first; + struct symbol **tqh_last; +}; + +extern struct symhead symbols; + void initsym (void); Symbol *addsym (char *); +Symbol *getsym(char *name); void output_name (char *); int checkundefined(void); +void generate_types(void); +void emitted_declaration(const Symbol *); +void emitted_definition(const Symbol *); #endif diff --git a/lib/asn1/test.asn1 b/lib/asn1/test.asn1 index 7546ed744..07e2852fe 100644 --- a/lib/asn1/test.asn1 +++ b/lib/asn1/test.asn1 @@ -6,6 +6,33 @@ BEGIN IMPORTS heim_any FROM heim; +-- Check that we handle out of order definitions. +-- The compiler should emit the definition of TESTOutOfOrderBar before that of +-- TESTOutOfOrderFoo. +TESTOutOfOrderFoo ::= SEQUENCE { + bar TESTOutOfOrderBar +} + +TESTOutOfOrderBar ::= SEQUENCE { + aMember INTEGER +} + +-- Check that we can handle rpc.mountd style "lists". This is unnecessarily +-- inefficient in its encoding, and there's no point to using this over +-- SEQUENCE OF (arrays), but it's neat that we can do this now that we can do +-- out of order definitions. +-- +-- This could be useful if we ever extend asn1_compile to also handle XDR, +-- which we well might since XDR's syntax is a dual of a strict subset of +-- ASN.1, and since XDR the encoding is fairly straightforward. +-- +-- Note that the `next' member has to be OPTIONAL or DEFAULTed for this to +-- work. +TESTCircular ::= SEQUENCE { + name UTF8String, + next TESTCircular OPTIONAL +} + TESTuint32 ::= INTEGER (0..4294967295) TESTuint64 ::= INTEGER(0..9223372036854775807) TESTint64 ::= INTEGER(-9223372036854775808..9223372036854775807)