/* Gengtype persistent state serialization & de-serialization. Useful for gengtype in plugin mode. Copyright (C) 2010-2014 Free Software Foundation, Inc. This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see . Contributed by Jeremie Salvucci and Basile Starynkevitch */ #ifdef GENERATOR_FILE #include "bconfig.h" #else #include "config.h" #endif #include "system.h" #include "errors.h" /* For fatal. */ #include "double-int.h" #include "hashtab.h" #include "version.h" /* For version_string & pkgversion_string. */ #include "obstack.h" #include "gengtype.h" /* Gives the file location of a type, if any. */ static inline struct fileloc* type_lineloc (const_type_p ty) { if (!ty) return NULL; switch (ty->kind) { case TYPE_NONE: gcc_unreachable (); case TYPE_STRUCT: case TYPE_UNION: case TYPE_LANG_STRUCT: case TYPE_USER_STRUCT: case TYPE_UNDEFINED: return CONST_CAST (struct fileloc*, &ty->u.s.line); case TYPE_PARAM_STRUCT: return CONST_CAST (struct fileloc*, &ty->u.param_struct.line); case TYPE_SCALAR: case TYPE_STRING: case TYPE_POINTER: case TYPE_ARRAY: return NULL; default: gcc_unreachable (); } } /* The state file has simplistic lispy lexical tokens. Its lexer gives a linked list of struct state_token_st, through the peek_state_token function. Lexical tokens are consumed with next_state_tokens. */ /* The lexical kind of each lispy token. */ enum state_token_en { STOK_NONE, /* Never used. */ STOK_INTEGER, /* Integer token. */ STOK_STRING, /* String token. */ STOK_LEFTPAR, /* Left opening parenthesis. */ STOK_RIGHTPAR, /* Right closing parenthesis. */ STOK_NAME /* hash-consed name or identifier. */ }; /* Structure and hash-table used to share identifiers or names. */ struct state_ident_st { /* TODO: We could improve the parser by reserving identifiers for state keywords and adding a keyword number for them. That would mean adding another field in this state_ident_st struct. */ char stid_name[1]; /* actually bigger & null terminated */ }; static htab_t state_ident_tab; /* The state_token_st structure is for lexical tokens in the read state file. The stok_kind field discriminates the union. Tokens are allocated by peek_state_token which calls read_a_state_token which allocate them. Tokens are freed by calls to next_state_tokens. Token are organized in a FIFO look-ahead queue filled by peek_state_token. */ struct state_token_st { enum state_token_en stok_kind; /* the lexical kind discriminates the stok_un union */ int stok_line; /* the line number */ int stok_col; /* the column number */ const char *stok_file; /* the file path */ struct state_token_st *stok_next; /* the next token in the queue, when peeked */ union /* discriminated by stok_kind! */ { int stok_num; /* when STOK_INTEGER */ char stok_string[1]; /* when STOK_STRING, actual size is bigger and null terminated */ struct state_ident_st *stok_ident; /* when STOK_IDENT */ void *stok_ptr; /* null otherwise */ } stok_un; }; #define NULL_STATE_TOKEN (struct state_token_st*)0 /* the state_token pointer contains the leftmost current token. The tokens are organized in a linked queue, using stok_next, for token look-ahead. */ struct state_token_st *state_token = NULL_STATE_TOKEN; /* Used by the reading lexer. */ static FILE *state_file; static const char *state_path = NULL; static int state_line = 0; static long state_bol = 0; /* offset of beginning of line */ /* A class for writing out s-expressions, keeping track of newlines and nested indentation. */ class s_expr_writer { public: s_expr_writer (); void write_new_line (); void write_any_indent (int leading_spaces); void begin_s_expr (const char *tag); void end_s_expr (); private: int m_indent_amount; int m_had_recent_newline; }; // class s_expr_writer /* A class for writing out "gtype.state". */ class state_writer : public s_expr_writer { public: state_writer (); private: void write_state_fileloc (struct fileloc *floc); void write_state_fields (pair_p fields); void write_state_a_string (const char *s); void write_state_string_option (options_p current); void write_state_type_option (options_p current); void write_state_nested_option (options_p current); void write_state_option (options_p current); void write_state_options (options_p opt); void write_state_lang_bitmap (lang_bitmap bitmap); void write_state_version (const char *version); void write_state_scalar_type (type_p current); void write_state_string_type (type_p current); void write_state_undefined_type (type_p current); void write_state_struct_union_type (type_p current, const char *kindstr); void write_state_struct_type (type_p current); void write_state_user_struct_type (type_p current); void write_state_union_type (type_p current); void write_state_lang_struct_type (type_p current); void write_state_param_struct_type (type_p current); void write_state_pointer_type (type_p current); void write_state_array_type (type_p current); void write_state_gc_used (enum gc_used_enum gus); void write_state_common_type_content (type_p current); void write_state_type (type_p current); void write_state_pair (pair_p current); int write_state_pair_list (pair_p list); void write_state_typedefs (void); void write_state_structures (void); void write_state_param_structs (void); void write_state_variables (void); void write_state_srcdir (void); void write_state_files_list (void); void write_state_languages (void); friend void write_state (const char *state_path); private: /* Counter of written types. */ int m_state_written_type_count; }; // class state_writer /* class s_expr_writer's trivial constructor. */ s_expr_writer::s_expr_writer () : m_indent_amount (0), m_had_recent_newline (0) { } /* Write a newline to the output file, merging adjacent newlines. */ void s_expr_writer::write_new_line (void) { /* Don't add a newline if we've just had one. */ if (!m_had_recent_newline) { fprintf (state_file, "\n"); m_had_recent_newline = 1; } } /* If we've just had a newline, write the indentation amount, potentially omitting some spaces. LEADING_SPACES exists to support code that writes strings with leading spaces (e.g " foo") which might occur within a line, or could be the first thing on a line. By passing leading_spaces == 1, when such a string is the first thing on a line, write_any_indent () swallows the successive leading spaces into the indentation so that the "foo" begins at the expected column. */ void s_expr_writer::write_any_indent (int leading_spaces) { int i; int amount = m_indent_amount - leading_spaces; if (m_had_recent_newline) for (i = 0; i < amount; i++) fprintf (state_file, " "); m_had_recent_newline = 0; } /* Write the beginning of a new s-expresion e.g. "(!foo " The writer automatically adds whitespace to show the hierarchical structure of the expressions, so each one starts on a new line, and any within it will be at an increased indentation level. */ void s_expr_writer::begin_s_expr (const char *tag) { write_new_line (); write_any_indent (0); fprintf (state_file, "(!%s ", tag); m_indent_amount++; } /* Write out the end of an s-expression: any necssessary indentation, a closing parenthesis, and a new line. */ void s_expr_writer::end_s_expr (void) { m_indent_amount--; write_any_indent (0); fprintf (state_file, ")"); write_new_line (); } /* class state_writer's trivial constructor. */ state_writer::state_writer () : s_expr_writer (), m_state_written_type_count (0) { } /* Fatal error messages when reading the state. They are extremely unlikely, and only appear when this gengtype-state.c file is buggy, or when reading a gengtype state which was not generated by the same version of gengtype or GCC. */ /* Fatal message while reading state. */ static inline void fatal_reading_state (struct state_token_st* tok, const char*msg) { if (tok) fatal ("%s:%d:%d: Invalid state file; %s", tok->stok_file, tok->stok_line, tok->stok_col, msg); else fatal ("%s:%d: Invalid state file; %s", state_path, state_line, msg); } /* Fatal printf-like message while reading state. This can't be a function, because there is no way to pass a va_arg to a variant of fatal. */ #define fatal_reading_state_printf(Tok,Fmt,...) do { \ struct state_token_st* badtok = Tok; \ if (badtok) \ fatal ("%s:%d:%d: Invalid state file; " Fmt, \ badtok->stok_file, \ badtok->stok_line, \ badtok->stok_col, __VA_ARGS__); \ else \ fatal ("%s:%d: Invalid state file; " Fmt, \ state_path, state_line, __VA_ARGS__); \ } while (0) /* Find or allocate an identifier in our name hash table. */ static struct state_ident_st * state_ident_by_name (const char *name, enum insert_option optins) { PTR *slot = NULL; int namlen = 0; struct state_ident_st *stid = NULL; if (!name || !name[0]) return NULL; slot = htab_find_slot (state_ident_tab, name, optins); if (!slot) return NULL; namlen = strlen (name); stid = (struct state_ident_st *) xmalloc (sizeof (struct state_ident_st) + namlen); memset (stid, 0, sizeof (struct state_ident_st) + namlen); strcpy (stid->stid_name, name); *slot = stid; return stid; } /* Our token lexer is heavily inspired by MELT's lexer, and share some code with the file gcc/melt-runtime.c of the GCC MELT branch! We really want the gengtype state to be easily parsable by MELT. This is a usual lispy lexing routine, dealing with spaces and comments, numbers, parenthesis, names, strings. */ static struct state_token_st * read_a_state_token (void) { int c = 0; long curoff = 0; struct state_token_st *tk = NULL; again: /* Read again, e.g. after a comment or spaces. */ c = getc (state_file); if (c == EOF) return NULL; /* Handle spaces, count lines. */ if (c == '\n') { state_line++; state_bol = curoff = ftell (state_file); goto again; }; if (ISSPACE (c)) goto again; /* Skip comments starting with semi-colon. */ if (c == ';') { do { c = getc (state_file); } while (c > 0 && c != '\n'); if (c == '\n') { state_line++; state_bol = curoff = ftell (state_file); } goto again; }; /* Read signed numbers. */ if (ISDIGIT (c) || c == '-' || c == '+') { /* number */ int n = 0; ungetc (c, state_file); curoff = ftell (state_file); if (fscanf (state_file, "%d", &n) <= 0) fatal_reading_state (NULL_STATE_TOKEN, "Lexical error in number"); tk = XCNEW (struct state_token_st); tk->stok_kind = STOK_INTEGER; tk->stok_line = state_line; tk->stok_col = curoff - state_bol; tk->stok_file = state_path; tk->stok_next = NULL; tk->stok_un.stok_num = n; return tk; } /* Read an opening left parenthesis. */ else if (c == '(') { curoff = ftell (state_file); tk = XCNEW (struct state_token_st); tk->stok_kind = STOK_LEFTPAR; tk->stok_line = state_line; tk->stok_col = curoff - state_bol; tk->stok_file = state_path; tk->stok_next = NULL; return tk; } /* Read an closing right parenthesis. */ else if (c == ')') { curoff = ftell (state_file); tk = XCNEW (struct state_token_st); tk->stok_kind = STOK_RIGHTPAR; tk->stok_line = state_line; tk->stok_col = curoff - state_bol; tk->stok_file = state_path; tk->stok_next = NULL; return tk; } /* Read identifiers, using an obstack. */ else if (ISALPHA (c) || c == '_' || c == '$' || c == '!' || c == '#') { struct obstack id_obstack; struct state_ident_st *sid = NULL; char *ids = NULL; obstack_init (&id_obstack); curoff = ftell (state_file); while (ISALNUM (c) || c == '_' || c == '$' || c == '!' || c == '#') { obstack_1grow (&id_obstack, c); c = getc (state_file); if (c < 0) break; }; if (c >= 0) ungetc (c, state_file); obstack_1grow (&id_obstack, (char) 0); ids = XOBFINISH (&id_obstack, char *); sid = state_ident_by_name (ids, INSERT); obstack_free (&id_obstack, NULL); ids = NULL; tk = XCNEW (struct state_token_st); tk->stok_kind = STOK_NAME; tk->stok_line = state_line; tk->stok_col = curoff - state_bol; tk->stok_file = state_path; tk->stok_next = NULL; tk->stok_un.stok_ident = sid; return tk; } /* Read a string, dealing with escape sequences a la C! */ else if (c == '"') { char *cstr = NULL; int cslen = 0; struct obstack bstring_obstack; obstack_init (&bstring_obstack); curoff = ftell (state_file); while ((c = getc (state_file)) != '"' && c >= 0) { if (ISPRINT (c) && c != '\\') obstack_1grow (&bstring_obstack, (char) c); else if (ISSPACE (c) && c != '\n') obstack_1grow (&bstring_obstack, (char) c); else if (c == '\\') { c = getc (state_file); switch (c) { case 'a': obstack_1grow (&bstring_obstack, '\a'); c = getc (state_file); break; case 'b': obstack_1grow (&bstring_obstack, '\b'); c = getc (state_file); break; case 't': obstack_1grow (&bstring_obstack, '\t'); c = getc (state_file); break; case 'n': obstack_1grow (&bstring_obstack, '\n'); c = getc (state_file); break; case 'v': obstack_1grow (&bstring_obstack, '\v'); c = getc (state_file); break; case 'f': obstack_1grow (&bstring_obstack, '\f'); c = getc (state_file); break; case 'r': obstack_1grow (&bstring_obstack, '\r'); c = getc (state_file); break; case '"': obstack_1grow (&bstring_obstack, '\"'); c = getc (state_file); break; case '\\': obstack_1grow (&bstring_obstack, '\\'); c = getc (state_file); break; case ' ': obstack_1grow (&bstring_obstack, ' '); c = getc (state_file); break; case 'x': { unsigned int cx = 0; if (fscanf (state_file, "%02x", &cx) > 0 && cx > 0) obstack_1grow (&bstring_obstack, cx); else fatal_reading_state (NULL_STATE_TOKEN, "Lexical error in string hex escape"); c = getc (state_file); break; } default: fatal_reading_state (NULL_STATE_TOKEN, "Lexical error - unknown string escape"); } } else fatal_reading_state (NULL_STATE_TOKEN, "Lexical error..."); }; if (c != '"') fatal_reading_state (NULL_STATE_TOKEN, "Unterminated string"); obstack_1grow (&bstring_obstack, '\0'); cstr = XOBFINISH (&bstring_obstack, char *); cslen = strlen (cstr); tk = (struct state_token_st *) xcalloc (sizeof (struct state_token_st) + cslen, 1); tk->stok_kind = STOK_STRING; tk->stok_line = state_line; tk->stok_col = curoff - state_bol; tk->stok_file = state_path; tk->stok_next = NULL; strcpy (tk->stok_un.stok_string, cstr); obstack_free (&bstring_obstack, NULL); return tk; } /* Got an unexpected character. */ fatal_reading_state_printf (NULL_STATE_TOKEN, "Lexical error at offset %ld - bad character \\%03o = '%c'", ftell (state_file), c, c); } /* Used for lexical look-ahead. Retrieves the lexical token of rank DEPTH, starting with 0 when reading the state file. Gives null on end of file. */ static struct state_token_st * peek_state_token (int depth) { int remdepth = depth; struct state_token_st **ptoken = &state_token; struct state_token_st *tok = NULL; while (remdepth >= 0) { if (*ptoken == NULL) { *ptoken = tok = read_a_state_token (); if (tok == NULL) return NULL; } tok = *ptoken; ptoken = &((*ptoken)->stok_next); remdepth--; } return tok; } /* Consume the next DEPTH tokens and free them. */ static void next_state_tokens (int depth) { struct state_token_st *n; while (depth > 0) { if (state_token != NULL) { n = state_token->stok_next; free (state_token); state_token = n; } else fatal_reading_state (NULL_STATE_TOKEN, "Tokens stack empty"); depth--; } } /* Safely retrieve the lexical kind of a token. */ static inline enum state_token_en state_token_kind (struct state_token_st *p) { if (p == NULL) return STOK_NONE; else return p->stok_kind; } /* Test if a token is a given name i.e. an identifier. */ static inline bool state_token_is_name (struct state_token_st *p, const char *name) { if (p == NULL) return false; if (p->stok_kind != STOK_NAME) return false; return !strcmp (p->stok_un.stok_ident->stid_name, name); } /* Following routines are useful for serializing datas. * * We want to serialize : * - typedefs list * - structures list * - param_structs list * - variables list * * So, we have one routine for each kind of data. The main writing * routine is write_state. The main reading routine is * read_state. Most writing routines write_state_FOO have a * corresponding reading routine read_state_FOO. Reading is done in a * recursive descending way, and any read error is fatal. */ /* When reading the state, we need to remember the previously seen types by their state_number, since GTY-ed types are usually shared. */ static htab_t state_seen_types; /* Return the length of a linked list made of pairs. */ static int pair_list_length (pair_p list); /* Compute the length of a list of pairs, starting from the first one. */ static int pair_list_length (pair_p list) { int nbpair = 0; pair_p l = NULL; for (l = list; l; l = l->next) nbpair++; return nbpair; } /* Write a file location. Files relative to $(srcdir) are quite frequent and are handled specially. This ensures that two gengtype state file-s produced by gengtype on the same GCC source tree are very similar and can be reasonably compared with diff, even if the two GCC source trees have different absolute paths. */ void state_writer::write_state_fileloc (struct fileloc *floc) { if (floc != NULL && floc->line > 0) { const char *srcrelpath = NULL; gcc_assert (floc->file != NULL); /* Most of the files are inside $(srcdir) so it is worth to handle them specially. */ srcrelpath = get_file_srcdir_relative_path (floc->file); if (srcrelpath != NULL) { begin_s_expr ("srcfileloc"); write_state_a_string (srcrelpath); } else { begin_s_expr ("fileloc"); write_state_a_string (get_input_file_name (floc->file)); } fprintf (state_file, " %d", floc->line); end_s_expr (); } else fprintf (state_file, "nil "); } /* Write a list of fields. */ void state_writer::write_state_fields (pair_p fields) { int nbfields = pair_list_length (fields); int nbpairs = 0; begin_s_expr ("fields"); fprintf (state_file, "%d ", nbfields); nbpairs = write_state_pair_list (fields); gcc_assert (nbpairs == nbfields); end_s_expr (); } /* Write a null-terminated string in our lexical convention, very similar to the convention of C. */ void state_writer::write_state_a_string (const char *s) { char c; write_any_indent (1); fputs (" \"", state_file); for (; *s != 0; s++) { c = *s; switch (c) { case '\a': fputs ("\\a", state_file); break; case '\b': fputs ("\\b", state_file); break; case '\t': fputs ("\\t", state_file); break; case '\n': fputs ("\\n", state_file); break; case '\v': fputs ("\\v", state_file); break; case '\f': fputs ("\\f", state_file); break; case '\r': fputs ("\\r", state_file); break; case '\"': fputs ("\\\"", state_file); break; case '\\': fputs ("\\\\", state_file); break; default: if (ISPRINT (c)) putc (c, state_file); else fprintf (state_file, "\\x%02x", (unsigned) c); } } fputs ("\"", state_file); } /* Our option-s have three kinds, each with its writer. */ void state_writer::write_state_string_option (options_p current) { write_any_indent (0); fprintf (state_file, "string "); if (current->info.string != NULL) write_state_a_string (current->info.string); else fprintf (state_file, " nil "); } void state_writer::write_state_type_option (options_p current) { write_any_indent (0); fprintf (state_file, "type "); write_state_type (current->info.type); } void state_writer::write_state_nested_option (options_p current) { write_any_indent (0); fprintf (state_file, "nested "); write_state_type (current->info.nested->type); if (current->info.nested->convert_from != NULL) write_state_a_string (current->info.nested->convert_from); else { write_any_indent (1); fprintf (state_file, " nil "); } if (current->info.nested->convert_to != NULL) write_state_a_string (current->info.nested->convert_to); else { write_any_indent (1); fprintf (state_file, " nil "); } } void state_writer::write_state_option (options_p current) { begin_s_expr ("option"); write_any_indent (0); if (current->name != NULL) fprintf (state_file, "%s ", current->name); else fprintf (state_file, "nil "); switch (current->kind) { case OPTION_STRING: write_state_string_option (current); break; case OPTION_TYPE: write_state_type_option (current); break; case OPTION_NESTED: write_state_nested_option (current); break; default: fatal ("Option tag unknown"); } /* Terminate the "option" s-expression. */ end_s_expr (); } /* Write a list of GTY options. */ void state_writer::write_state_options (options_p opt) { options_p current; if (opt == NULL) { write_any_indent (0); fprintf (state_file, "nil "); return; } begin_s_expr ("options"); for (current = opt; current != NULL; current = current->next) write_state_option (current); end_s_expr (); } /* Write a bitmap representing a set of GCC front-end languages. */ void state_writer::write_state_lang_bitmap (lang_bitmap bitmap) { write_any_indent (0); fprintf (state_file, "%d ", (int) bitmap); } /* Write version information. */ void state_writer::write_state_version (const char *version) { begin_s_expr ("version"); write_state_a_string (version); end_s_expr (); } /* Write a scalar type. We have only two of these. */ void state_writer::write_state_scalar_type (type_p current) { write_any_indent (0); if (current == &scalar_nonchar) fprintf (state_file, "scalar_nonchar "); else if (current == &scalar_char) fprintf (state_file, "scalar_char "); else fatal ("Unexpected type in write_state_scalar_type"); write_state_common_type_content (current); } /* Write the string type. There is only one such thing! */ void state_writer::write_state_string_type (type_p current) { if (current == &string_type) { write_any_indent (0); fprintf (state_file, "string "); write_state_common_type_content (current); } else fatal ("Unexpected type in write_state_string_type"); } /* Write an undefined type. */ void state_writer::write_state_undefined_type (type_p current) { DBGPRINTF ("undefined type @ %p #%d '%s'", (void *) current, current->state_number, current->u.s.tag); write_any_indent (0); fprintf (state_file, "undefined "); gcc_assert (current->gc_used == GC_UNUSED); write_state_common_type_content (current); if (current->u.s.tag != NULL) write_state_a_string (current->u.s.tag); else { write_any_indent (0); fprintf (state_file, "nil"); } write_state_fileloc (type_lineloc (current)); } /* Common code to write structure like types. */ void state_writer::write_state_struct_union_type (type_p current, const char *kindstr) { DBGPRINTF ("%s type @ %p #%d '%s'", kindstr, (void *) current, current->state_number, current->u.s.tag); write_any_indent (0); fprintf (state_file, "%s ", kindstr); write_state_common_type_content (current); if (current->u.s.tag != NULL) write_state_a_string (current->u.s.tag); else { write_any_indent (0); fprintf (state_file, "nil"); } write_state_fileloc (type_lineloc (current)); write_state_fields (current->u.s.fields); write_state_options (current->u.s.opt); write_state_lang_bitmap (current->u.s.bitmap); } /* Write a GTY struct type. */ void state_writer::write_state_struct_type (type_p current) { write_state_struct_union_type (current, "struct"); write_state_type (current->u.s.lang_struct); write_state_type (current->u.s.base_class); } /* Write a GTY user-defined struct type. */ void state_writer::write_state_user_struct_type (type_p current) { DBGPRINTF ("user_struct type @ %p #%d '%s'", (void *) current, current->state_number, current->u.s.tag); write_any_indent (0); fprintf (state_file, "user_struct "); write_state_common_type_content (current); if (current->u.s.tag != NULL) write_state_a_string (current->u.s.tag); else { write_any_indent (0); fprintf (state_file, "nil"); } write_state_fileloc (type_lineloc (current)); write_state_fields (current->u.s.fields); } /* write a GTY union type. */ void state_writer::write_state_union_type (type_p current) { write_state_struct_union_type (current, "union"); write_state_type (current->u.s.lang_struct); } /* Write a lang_struct type. This is tricky and was painful to debug, we deal with the next field specifically within their lang_struct subfield, which points to a linked list of homonumous types. Change this function with extreme care, see also read_state_lang_struct_type. */ void state_writer::write_state_lang_struct_type (type_p current) { int nbhomontype = 0; type_p hty = NULL; const char *homoname = 0; write_state_struct_union_type (current, "lang_struct"); /* lang_struct-ures are particularly tricky, since their u.s.lang_struct field gives a list of homonymous struct-s or union-s! */ DBGPRINTF ("lang_struct @ %p #%d", (void *) current, current->state_number); for (hty = current->u.s.lang_struct; hty != NULL; hty = hty->next) { nbhomontype++; DBGPRINTF ("homonymous #%d hty @ %p #%d '%s'", nbhomontype, (void *) hty, hty->state_number, hty->u.s.tag); /* Every member of the homonymous list should have the same tag. */ gcc_assert (union_or_struct_p (hty)); gcc_assert (hty->u.s.lang_struct == current); if (!homoname) homoname = hty->u.s.tag; gcc_assert (strcmp (homoname, hty->u.s.tag) == 0); } begin_s_expr ("homotypes"); fprintf (state_file, "%d", nbhomontype); for (hty = current->u.s.lang_struct; hty != NULL; hty = hty->next) write_state_type (hty); end_s_expr (); } /* Write a parametrized structure GTY type. */ void state_writer::write_state_param_struct_type (type_p current) { int i; write_any_indent (0); fprintf (state_file, "param_struct "); write_state_common_type_content (current); write_state_type (current->u.param_struct.stru); for (i = 0; i < NUM_PARAM; i++) { if (current->u.param_struct.param[i] != NULL) write_state_type (current->u.param_struct.param[i]); else { write_any_indent (0); fprintf (state_file, "nil "); } } write_state_fileloc (¤t->u.param_struct.line); } /* Write a pointer type. */ void state_writer::write_state_pointer_type (type_p current) { write_any_indent (0); fprintf (state_file, "pointer "); write_state_common_type_content (current); write_state_type (current->u.p); } /* Write an array type. */ void state_writer::write_state_array_type (type_p current) { write_any_indent (0); fprintf (state_file, "array "); write_state_common_type_content (current); if (current->u.a.len != NULL) write_state_a_string (current->u.a.len); else { write_any_indent (1); fprintf (state_file, " nil"); } write_any_indent (1); fprintf (state_file, " "); write_state_type (current->u.a.p); } /* Write the gc_used information. */ void state_writer::write_state_gc_used (enum gc_used_enum gus) { write_any_indent (1); switch (gus) { case GC_UNUSED: fprintf (state_file, " gc_unused"); break; case GC_USED: fprintf (state_file, " gc_used"); break; case GC_MAYBE_POINTED_TO: fprintf (state_file, " gc_maybe_pointed_to"); break; case GC_POINTED_TO: fprintf (state_file, " gc_pointed_to"); break; default: gcc_unreachable (); } } /* Utility routine to write the common content of all types. Notice that the next field is *not* written on purpose. */ void state_writer::write_state_common_type_content (type_p current) { write_any_indent (0); fprintf (state_file, "%d ", current->state_number); /* We do not write the next type, because list of types are explicitly written. However, lang_struct are special in that respect. See function write_state_lang_struct_type for more. */ write_state_type (current->pointer_to); write_state_gc_used (current->gc_used); } /* The important and recursive routine writing GTY types as understood by gengtype. Types which have a positive state_number have already been seen and written. */ void state_writer::write_state_type (type_p current) { write_any_indent (0); if (current == NULL) { fprintf (state_file, "nil "); return; } begin_s_expr ("type"); if (current->state_number > 0) { write_any_indent (0); fprintf (state_file, "already_seen %d", current->state_number); } else { m_state_written_type_count++; DBGPRINTF ("writing type #%d @%p old number %d", m_state_written_type_count, (void *) current, current->state_number); current->state_number = m_state_written_type_count; switch (current->kind) { case TYPE_NONE: gcc_unreachable (); case TYPE_UNDEFINED: write_state_undefined_type (current); break; case TYPE_STRUCT: write_state_struct_type (current); break; case TYPE_USER_STRUCT: write_state_user_struct_type (current); break; case TYPE_UNION: write_state_union_type (current); break; case TYPE_POINTER: write_state_pointer_type (current); break; case TYPE_ARRAY: write_state_array_type (current); break; case TYPE_LANG_STRUCT: write_state_lang_struct_type (current); break; case TYPE_PARAM_STRUCT: write_state_param_struct_type (current); break; case TYPE_SCALAR: write_state_scalar_type (current); break; case TYPE_STRING: write_state_string_type (current); break; } } /* Terminate the "type" s-expression. */ end_s_expr (); } /* Write a pair. */ void state_writer::write_state_pair (pair_p current) { if (current == NULL) { write_any_indent (0); fprintf (state_file, "nil)"); return; } begin_s_expr ("pair"); if (current->name != NULL) write_state_a_string (current->name); else write_state_a_string ("nil"); write_state_type (current->type); write_state_fileloc (&(current->line)); write_state_options (current->opt); /* Terminate the "pair" s-expression. */ end_s_expr (); } /* Write a pair list and return the number of pairs written. */ int state_writer::write_state_pair_list (pair_p list) { int nbpair = 0; pair_p current; for (current = list; current != NULL; current = current->next) { write_state_pair (current); nbpair++; } return nbpair; } /* When writing imported linked lists, like typedefs, structures, param_structs, ... we count their length first and write it. These eases the reading, and enables an extra verification on the number of actually read items. */ /* Write our typedefs. */ void state_writer::write_state_typedefs (void) { int nbtypedefs = pair_list_length (typedefs); int nbpairs = 0; begin_s_expr ("typedefs"); fprintf (state_file, "%d", nbtypedefs); nbpairs = write_state_pair_list (typedefs); gcc_assert (nbpairs == nbtypedefs); end_s_expr (); if (verbosity_level >= 2) printf ("%s wrote %d typedefs\n", progname, nbtypedefs); } /* Write our structures. */ void state_writer::write_state_structures (void) { int nbstruct = 0; type_p current; for (current = structures; current != NULL; current = current->next) nbstruct++; begin_s_expr ("structures"); fprintf (state_file, "%d", nbstruct); for (current = structures; current != NULL; current = current->next) { write_new_line (); write_state_type (current); } /* Terminate the "structures" s-expression. */ end_s_expr (); if (verbosity_level >= 2) printf ("%s wrote %d structures in state\n", progname, nbstruct); } /* Write our param_struct-s. */ void state_writer::write_state_param_structs (void) { int nbparamstruct = 0; type_p current; for (current = param_structs; current != NULL; current = current->next) nbparamstruct++; begin_s_expr ("param_structs"); fprintf (state_file, "%d", nbparamstruct); for (current = param_structs; current != NULL; current = current->next) write_state_type (current); end_s_expr (); } /* Write our variables. */ void state_writer::write_state_variables (void) { int nbvars = pair_list_length (variables); int nbpairs = 0; begin_s_expr ("variables"); fprintf (state_file, "%d", nbvars); nbpairs = write_state_pair_list (variables); gcc_assert (nbpairs == nbvars); end_s_expr (); if (verbosity_level >= 2) printf ("%s wrote %d variables.\n", progname, nbvars); } /* Write the source directory. File locations within the source directory have been written specifically. */ void state_writer::write_state_srcdir (void) { begin_s_expr ("srcdir"); write_state_a_string (srcdir); end_s_expr (); } /* Count and write the list of our files. */ void state_writer::write_state_files_list (void) { int i = 0; /* Write the list of files with their lang_bitmap. */ begin_s_expr ("fileslist"); fprintf (state_file, "%d", (int) num_gt_files); for (i = 0; i < (int) num_gt_files; i++) { const char *cursrcrelpath = NULL; const input_file *curfil = gt_files[i]; /* Most of the files are inside $(srcdir) so it is worth to handle them specially. */ cursrcrelpath = get_file_srcdir_relative_path (curfil); if (cursrcrelpath) { begin_s_expr ("srcfile"); fprintf (state_file, "%d ", get_lang_bitmap (curfil)); write_state_a_string (cursrcrelpath); } else { begin_s_expr ("file"); fprintf (state_file, "%d ", get_lang_bitmap (curfil)); write_state_a_string (get_input_file_name (curfil)); } /* Terminate the inner s-expression (either "srcfile" or "file"). */ end_s_expr (); } /* Terminate the "fileslist" s-expression. */ end_s_expr (); } /* Write the list of GCC front-end languages. */ void state_writer::write_state_languages (void) { int i = 0; begin_s_expr ("languages"); fprintf (state_file, "%d", (int) num_lang_dirs); for (i = 0; i < (int) num_lang_dirs; i++) { /* Languages names are identifiers, we expect only letters or underscores or digits in them. In particular, C++ is not a valid language name, but cp is valid. */ fprintf (state_file, " %s", lang_dir_names[i]); } end_s_expr (); } /* Write the trailer. */ static void write_state_trailer (void) { /* This test should probably catch IO errors like disk full... */ if (fputs ("\n(!endfile)\n", state_file) == EOF) fatal ("failed to write state trailer [%s]", xstrerror (errno)); } /* The write_state routine is the only writing routine called by main in gengtype.c. To avoid messing the state if gengtype is interrupted or aborted, we write a temporary file and rename it after having written it in totality. */ void write_state (const char *state_path) { long statelen = 0; time_t now = 0; char *temp_state_path = NULL; char tempsuffix[40]; time (&now); /* We write a unique temporary file which is renamed when complete * only. So even if gengtype is interrupted, the written state file * won't be partially written, since the temporary file is not yet * renamed in that case. */ memset (tempsuffix, 0, sizeof (tempsuffix)); snprintf (tempsuffix, sizeof (tempsuffix) - 1, "-%ld-%d.tmp", (long) now, (int) getpid ()); temp_state_path = concat (state_path, tempsuffix, NULL); state_file = fopen (temp_state_path, "w"); if (state_file == NULL) fatal ("Failed to open file %s for writing state: %s", temp_state_path, xstrerror (errno)); if (verbosity_level >= 3) printf ("%s writing state file %s temporarily in %s\n", progname, state_path, temp_state_path); /* This is the first line of the state. Perhaps the file utility could know about that, so don't change it often. */ fprintf (state_file, ";;;;@@@@ GCC gengtype state\n"); /* Output a few comments for humans. */ fprintf (state_file, ";;; DON'T EDIT THIS FILE, since generated by GCC's gengtype\n"); fprintf (state_file, ";;; The format of this file is tied to a particular version of GCC.\n"); fprintf (state_file, ";;; Don't parse this file wihout knowing GCC gengtype internals.\n"); fprintf (state_file, ";;; This file should be parsed by the same %s which wrote it.\n", progname); state_writer sw; /* The first non-comment significant line gives the version string. */ sw.write_state_version (version_string); sw.write_state_srcdir (); sw.write_state_languages (); sw.write_state_files_list (); sw.write_state_structures (); sw.write_state_typedefs (); sw.write_state_param_structs (); sw.write_state_variables (); write_state_trailer (); statelen = ftell (state_file); if (ferror (state_file)) fatal ("output error when writing state file %s [%s]", temp_state_path, xstrerror (errno)); if (fclose (state_file)) fatal ("failed to close state file %s [%s]", temp_state_path, xstrerror (errno)); if (rename (temp_state_path, state_path)) fatal ("failed to rename %s to state file %s [%s]", temp_state_path, state_path, xstrerror (errno)); free (temp_state_path); if (verbosity_level >= 1) printf ("%s wrote state file %s of %ld bytes with %d GTY-ed types\n", progname, state_path, statelen, sw.m_state_written_type_count); } /** End of writing routines! The corresponding reading routines follow. **/ /* Forward declarations, since some read_state_* functions are recursive! */ static void read_state_fileloc (struct fileloc *line); static void read_state_options (options_p *opt); static void read_state_type (type_p *current); static void read_state_pair (pair_p *pair); /* Return the number of pairs actually read. */ static int read_state_pair_list (pair_p *list); static void read_state_fields (pair_p *fields); static void read_state_common_type_content (type_p current); /* Record into the state_seen_types hash-table a type which we are reading, to enable recursive or circular references to it. */ static void record_type (type_p type) { PTR *slot; slot = htab_find_slot (state_seen_types, type, INSERT); gcc_assert (slot); *slot = type; } /* Read an already seen type. */ static void read_state_already_seen_type (type_p *type) { struct state_token_st *t0 = peek_state_token (0); if (state_token_kind (t0) == STOK_INTEGER) { PTR *slot = NULL; struct type loctype = { TYPE_SCALAR, 0, 0, 0, GC_UNUSED, {0} }; loctype.state_number = t0->stok_un.stok_num; slot = htab_find_slot (state_seen_types, &loctype, NO_INSERT); if (slot == NULL) { fatal_reading_state (t0, "Unknown type"); } next_state_tokens (1); *type = (type_p) *slot; } else { fatal_reading_state (t0, "Bad seen type"); } } /* Read the scalar_nonchar type. */ static void read_state_scalar_nonchar_type (type_p *type) { *type = &scalar_nonchar; read_state_common_type_content (*type); } /* Read the scalar_char type. */ static void read_state_scalar_char_type (type_p *type) { *type = &scalar_char; read_state_common_type_content (*type); } /* Read the string_type. */ static void read_state_string_type (type_p *type) { *type = &string_type; read_state_common_type_content (*type); } /* Read a lang_bitmap representing a set of GCC front-end languages. */ static void read_state_lang_bitmap (lang_bitmap *bitmap) { struct state_token_st *t; t = peek_state_token (0); if (state_token_kind (t) == STOK_INTEGER) { *bitmap = t->stok_un.stok_num; next_state_tokens (1); } else { fatal_reading_state (t, "Bad syntax for bitmap"); } } /* Read an undefined type. */ static void read_state_undefined_type (type_p type) { struct state_token_st *t0; type->kind = TYPE_UNDEFINED; read_state_common_type_content (type); t0 = peek_state_token (0); if (state_token_kind (t0) == STOK_STRING) { if (state_token_is_name (t0, "nil")) { type->u.s.tag = NULL; DBGPRINTF ("read anonymous undefined type @%p #%d", (void *) type, type->state_number); } else { type->u.s.tag = xstrdup (t0->stok_un.stok_string); DBGPRINTF ("read undefined type @%p #%d '%s'", (void *) type, type->state_number, type->u.s.tag); } next_state_tokens (1); read_state_fileloc (&(type->u.s.line)); } else { fatal_reading_state (t0, "Bad tag in undefined type"); } } /* Read a GTY-ed struct type. */ static void read_state_struct_type (type_p type) { struct state_token_st *t0; type->kind = TYPE_STRUCT; read_state_common_type_content (type); t0 = peek_state_token (0); if (state_token_kind (t0) == STOK_STRING) { if (state_token_is_name (t0, "nil")) { type->u.s.tag = NULL; DBGPRINTF ("read anonymous struct type @%p #%d", (void *) type, type->state_number); } else { type->u.s.tag = xstrdup (t0->stok_un.stok_string); DBGPRINTF ("read struct type @%p #%d '%s'", (void *) type, type->state_number, type->u.s.tag); } next_state_tokens (1); read_state_fileloc (&(type->u.s.line)); read_state_fields (&(type->u.s.fields)); read_state_options (&(type->u.s.opt)); read_state_lang_bitmap (&(type->u.s.bitmap)); read_state_type (&(type->u.s.lang_struct)); read_state_type (&(type->u.s.base_class)); if (type->u.s.base_class) add_subclass (type->u.s.base_class, type); } else { fatal_reading_state (t0, "Bad tag in struct type"); } } /* Read a GTY-ed user-provided struct TYPE. */ static void read_state_user_struct_type (type_p type) { struct state_token_st *t0; type->kind = TYPE_USER_STRUCT; read_state_common_type_content (type); t0 = peek_state_token (0); if (state_token_kind (t0) == STOK_STRING) { if (state_token_is_name (t0, "nil")) { type->u.s.tag = NULL; DBGPRINTF ("read anonymous struct type @%p #%d", (void *) type, type->state_number); } else { type->u.s.tag = xstrdup (t0->stok_un.stok_string); DBGPRINTF ("read struct type @%p #%d '%s'", (void *) type, type->state_number, type->u.s.tag); } next_state_tokens (1); read_state_fileloc (&(type->u.s.line)); read_state_fields (&(type->u.s.fields)); } else { fatal_reading_state (t0, "Bad tag in user-struct type"); } } /* Read a GTY-ed union type. */ static void read_state_union_type (type_p type) { struct state_token_st *t0; type->kind = TYPE_UNION; read_state_common_type_content (type); t0 = peek_state_token (0); if (state_token_kind (t0) == STOK_STRING) { if (state_token_is_name (t0, "nil")) { type->u.s.tag = NULL; DBGPRINTF ("read anonymous union type @%p #%d", (void *) type, type->state_number); } else { type->u.s.tag = xstrdup (t0->stok_un.stok_string); DBGPRINTF ("read union type @%p #%d '%s'", (void *) type, type->state_number, type->u.s.tag); } next_state_tokens (1); read_state_fileloc (&(type->u.s.line)); read_state_fields (&(type->u.s.fields)); read_state_options (&(type->u.s.opt)); read_state_lang_bitmap (&(type->u.s.bitmap)); read_state_type (&(type->u.s.lang_struct)); } else fatal_reading_state (t0, "Bad tag in union type"); } /* Read a GTY-ed pointer type. */ static void read_state_pointer_type (type_p type) { type->kind = TYPE_POINTER; read_state_common_type_content (type); DBGPRINTF ("read pointer type @%p #%d", (void *) type, type->state_number); read_state_type (&(type->u.p)); } /* Read a GTY-ed array type. */ static void read_state_array_type (type_p type) { struct state_token_st *t0; type->kind = TYPE_ARRAY; read_state_common_type_content (type); t0 = peek_state_token (0); if (state_token_kind (t0) == STOK_STRING) { type->u.a.len = xstrdup (t0->stok_un.stok_string); DBGPRINTF ("read array type @%p #%d length '%s'", (void *) type, type->state_number, type->u.a.len); next_state_tokens (1); } else if (state_token_is_name (t0, "nil")) { type->u.a.len = NULL; DBGPRINTF ("read array type @%p #%d without length", (void *) type, type->state_number); next_state_tokens (1); } else fatal_reading_state (t0, "Bad array name type"); read_state_type (&(type->u.a.p)); } /* Read a lang_struct type for GTY-ed struct-s which depends upon GCC front-end languages. This is a tricky function and it was painful to debug. Change it with extreme care. See also write_state_lang_struct_type. */ static void read_state_lang_struct_type (type_p type) { struct state_token_st *t0 = NULL; struct state_token_st *t1 = NULL; struct state_token_st *t2 = NULL; type->kind = TYPE_LANG_STRUCT; read_state_common_type_content (type); t0 = peek_state_token (0); if (state_token_kind (t0) == STOK_STRING) { if (state_token_is_name (t0, "nil")) { DBGPRINTF ("read anonymous lang_struct type @%p #%d", (void *) type, type->state_number); type->u.s.tag = NULL; } else { type->u.s.tag = xstrdup (t0->stok_un.stok_string); DBGPRINTF ("read lang_struct type @%p #%d '%s'", (void *) type, type->state_number, type->u.s.tag); } next_state_tokens (1); } else fatal_reading_state (t0, "Bad tag in lang struct type"); read_state_fileloc (&(type->u.s.line)); read_state_fields (&(type->u.s.fields)); read_state_options (&(type->u.s.opt)); read_state_lang_bitmap (&(type->u.s.bitmap)); /* Within lang_struct-ures, the lang_struct field is a linked list of homonymous types! */ t0 = peek_state_token (0); t1 = peek_state_token (1); t2 = peek_state_token (2); /* Parse (!homotypes .... ) */ if (state_token_kind (t0) == STOK_LEFTPAR && state_token_is_name (t1, "!homotypes") && state_token_kind (t2) == STOK_INTEGER) { type_p *prevty = &type->u.s.lang_struct; int nbhomotype = t2->stok_un.stok_num; int i = 0; t0 = t1 = t2 = NULL; next_state_tokens (3); for (i = 0; i < nbhomotype; i++) { read_state_type (prevty); t0 = peek_state_token (0); if (*prevty) prevty = &(*prevty)->next; else fatal_reading_state (t0, "expecting type in homotype list for lang_struct"); }; if (state_token_kind (t0) != STOK_RIGHTPAR) fatal_reading_state (t0, "expecting ) in homotype list for lang_struct"); next_state_tokens (1); } else fatal_reading_state (t0, "expecting !homotypes for lang_struct"); } /* Read a param_struct type for GTY parametrized structures. */ static void read_state_param_struct_type (type_p type) { int i; struct state_token_st *t0; type->kind = TYPE_PARAM_STRUCT; read_state_common_type_content (type); DBGPRINTF ("read param_struct type @%p #%d", (void *) type, type->state_number); read_state_type (&(type->u.param_struct.stru)); for (i = 0; i < NUM_PARAM; i++) { t0 = peek_state_token (0); if (state_token_is_name (t0, "nil")) { type->u.param_struct.param[i] = NULL; next_state_tokens (1); } else read_state_type (&(type->u.param_struct.param[i])); } read_state_fileloc (&(type->u.param_struct.line)); } /* Read the gc used information. */ static void read_state_gc_used (enum gc_used_enum *pgus) { struct state_token_st *t0 = peek_state_token (0); if (state_token_is_name (t0, "gc_unused")) *pgus = GC_UNUSED; else if (state_token_is_name (t0, "gc_used")) *pgus = GC_USED; else if (state_token_is_name (t0, "gc_maybe_pointed_to")) *pgus = GC_MAYBE_POINTED_TO; else if (state_token_is_name (t0, "gc_pointed_to")) *pgus = GC_POINTED_TO; else fatal_reading_state (t0, "invalid gc_used information"); next_state_tokens (1); } /* Utility function to read the common content of types. */ static void read_state_common_type_content (type_p current) { struct state_token_st *t0 = peek_state_token (0); if (state_token_kind (t0) == STOK_INTEGER) { current->state_number = t0->stok_un.stok_num; next_state_tokens (1); record_type (current); } else fatal_reading_state_printf (t0, "Expected integer for state_number line %d", state_line); /* We don't read the next field of the type. */ read_state_type (¤t->pointer_to); read_state_gc_used (¤t->gc_used); } /* Read a GTY-ed type. */ void read_state_type (type_p *current) { struct state_token_st *t0 = peek_state_token (0); struct state_token_st *t1 = peek_state_token (1); if (state_token_kind (t0) == STOK_LEFTPAR && state_token_is_name (t1, "!type")) { next_state_tokens (2); t0 = peek_state_token (0); if (state_token_is_name (t0, "already_seen")) { next_state_tokens (1); read_state_already_seen_type (current); } else { t0 = peek_state_token (0); if (state_token_is_name (t0, "scalar_nonchar")) { next_state_tokens (1); read_state_scalar_nonchar_type (current); } else if (state_token_is_name (t0, "scalar_char")) { next_state_tokens (1); read_state_scalar_char_type (current); } else if (state_token_is_name (t0, "string")) { next_state_tokens (1); read_state_string_type (current); } else if (state_token_is_name (t0, "undefined")) { *current = XCNEW (struct type); next_state_tokens (1); read_state_undefined_type (*current); } else if (state_token_is_name (t0, "struct")) { *current = XCNEW (struct type); next_state_tokens (1); read_state_struct_type (*current); } else if (state_token_is_name (t0, "union")) { *current = XCNEW (struct type); next_state_tokens (1); read_state_union_type (*current); } else if (state_token_is_name (t0, "lang_struct")) { *current = XCNEW (struct type); next_state_tokens (1); read_state_lang_struct_type (*current); } else if (state_token_is_name (t0, "param_struct")) { *current = XCNEW (struct type); next_state_tokens (1); read_state_param_struct_type (*current); } else if (state_token_is_name (t0, "pointer")) { *current = XCNEW (struct type); next_state_tokens (1); read_state_pointer_type (*current); } else if (state_token_is_name (t0, "array")) { *current = XCNEW (struct type); next_state_tokens (1); read_state_array_type (*current); } else if (state_token_is_name (t0, "user_struct")) { *current = XCNEW (struct type); next_state_tokens (1); read_state_user_struct_type (*current); } else fatal_reading_state (t0, "bad type in (!type"); } t0 = peek_state_token (0); if (state_token_kind (t0) != STOK_RIGHTPAR) fatal_reading_state (t0, "missing ) in type"); next_state_tokens (1); } else if (state_token_is_name (t0, "nil")) { next_state_tokens (1); *current = NULL; } else fatal_reading_state (t0, "bad type syntax"); } /* Read a file location. Files within the source directory are dealt with specifically. */ void read_state_fileloc (struct fileloc *floc) { bool issrcfile = false; struct state_token_st *t0 = peek_state_token (0); struct state_token_st *t1 = peek_state_token (1); gcc_assert (floc != NULL); gcc_assert (srcdir != NULL); if (state_token_kind (t0) == STOK_LEFTPAR && (state_token_is_name (t1, "!fileloc") || (issrcfile = state_token_is_name (t1, "!srcfileloc")))) { next_state_tokens (2); t0 = peek_state_token (0); t1 = peek_state_token (1); if (state_token_kind (t0) == STOK_STRING && state_token_kind (t1) == STOK_INTEGER) { char *path = t0->stok_un.stok_string; if (issrcfile) { static const char dirsepstr[2] = { DIR_SEPARATOR, (char) 0 }; char *fullpath = concat (srcdir, dirsepstr, path, NULL); floc->file = input_file_by_name (fullpath); free (fullpath); } else floc->file = input_file_by_name (path); floc->line = t1->stok_un.stok_num; next_state_tokens (2); } else fatal_reading_state (t0, "Bad fileloc syntax, expected path string and line"); t0 = peek_state_token (0); if (state_token_kind (t0) != STOK_RIGHTPAR) fatal_reading_state (t0, "Bad fileloc syntax, expected )"); next_state_tokens (1); } else if (state_token_is_name (t0, "nil")) { next_state_tokens (1); floc->file = NULL; floc->line = 0; } else fatal_reading_state (t0, "Bad fileloc syntax"); } /* Read the fields of a GTY-ed type. */ void read_state_fields (pair_p *fields) { pair_p tmp = NULL; struct state_token_st *t0 = peek_state_token (0); struct state_token_st *t1 = peek_state_token (1); struct state_token_st *t2 = peek_state_token (2); if (state_token_kind (t0) == STOK_LEFTPAR && state_token_is_name (t1, "!fields") && state_token_kind (t2) == STOK_INTEGER) { int nbfields = t2->stok_un.stok_num; int nbpairs = 0; next_state_tokens (3); nbpairs = read_state_pair_list (&tmp); t0 = peek_state_token (0); if (nbpairs != nbfields) fatal_reading_state_printf (t0, "Mismatched fields number, expected %d got %d", nbpairs, nbfields); if (state_token_kind (t0) == STOK_RIGHTPAR) next_state_tokens (1); else fatal_reading_state (t0, "Bad fields expecting )"); } *fields = tmp; } /* Read a string option. */ static void read_state_string_option (options_p opt) { struct state_token_st *t0 = peek_state_token (0); opt->kind = OPTION_STRING; if (state_token_kind (t0) == STOK_STRING) { opt->info.string = xstrdup (t0->stok_un.stok_string); next_state_tokens (1); } else if (state_token_is_name (t0, "nil")) { opt->info.string = NULL; next_state_tokens (1); } else fatal_reading_state (t0, "Missing name in string option"); } /* Read a type option. */ static void read_state_type_option (options_p opt) { opt->kind = OPTION_TYPE; read_state_type (&(opt->info.type)); } /* Read a nested option. */ static void read_state_nested_option (options_p opt) { struct state_token_st *t0; opt->info.nested = XCNEW (struct nested_ptr_data); opt->kind = OPTION_NESTED; read_state_type (&(opt->info.nested->type)); t0 = peek_state_token (0); if (state_token_kind (t0) == STOK_STRING) { opt->info.nested->convert_from = xstrdup (t0->stok_un.stok_string); next_state_tokens (1); } else if (state_token_is_name (t0, "nil")) { opt->info.nested->convert_from = NULL; next_state_tokens (1); } else fatal_reading_state (t0, "Bad nested convert_from option"); t0 = peek_state_token (0); if (state_token_kind (t0) == STOK_STRING) { opt->info.nested->convert_to = xstrdup (t0->stok_un.stok_string); next_state_tokens (1); } else if (state_token_is_name (t0, "nil")) { opt->info.nested->convert_to = NULL; next_state_tokens (1); } else fatal_reading_state (t0, "Bad nested convert_from option"); } /* Read an GTY option. */ static void read_state_option (options_p *opt) { struct state_token_st *t0 = peek_state_token (0); struct state_token_st *t1 = peek_state_token (1); if (state_token_kind (t0) == STOK_LEFTPAR && state_token_is_name (t1, "!option")) { next_state_tokens (2); t0 = peek_state_token (0); if (state_token_kind (t0) == STOK_NAME) { *opt = XCNEW (struct options); if (state_token_is_name (t0, "nil")) (*opt)->name = NULL; else (*opt)->name = t0->stok_un.stok_ident->stid_name; next_state_tokens (1); t0 = peek_state_token (0); if (state_token_kind (t0) == STOK_NAME) { if (state_token_is_name (t0, "string")) { next_state_tokens (1); read_state_string_option (*opt); } else if (state_token_is_name (t0, "type")) { next_state_tokens (1); read_state_type_option (*opt); } else if (state_token_is_name (t0, "nested")) { next_state_tokens (1); read_state_nested_option (*opt); } else fatal_reading_state (t0, "Bad option type"); t0 = peek_state_token (0); if (state_token_kind (t0) != STOK_RIGHTPAR) fatal_reading_state (t0, "Bad syntax in option, expecting )"); next_state_tokens (1); } else fatal_reading_state (t0, "Missing option type"); } else fatal_reading_state (t0, "Bad name for option"); } else fatal_reading_state (t0, "Bad option, waiting for )"); } /* Read a list of options. */ void read_state_options (options_p *opt) { options_p head = NULL; options_p previous = NULL; options_p current_option = NULL; struct state_token_st *t0 = peek_state_token (0); struct state_token_st *t1 = peek_state_token (1); if (state_token_kind (t0) == STOK_LEFTPAR && state_token_is_name (t1, "!options")) { next_state_tokens (2); t0 = peek_state_token (0); while (state_token_kind (t0) != STOK_RIGHTPAR) { read_state_option (¤t_option); if (head == NULL) { head = current_option; previous = head; } else { previous->next = current_option; previous = current_option; } t0 = peek_state_token (0); } next_state_tokens (1); } else if (state_token_is_name (t0, "nil")) { next_state_tokens (1); } else fatal_reading_state (t0, "Bad options syntax"); *opt = head; } /* Read a version, and check against the version of the gengtype. */ static void read_state_version (const char *version_string) { struct state_token_st *t0 = peek_state_token (0); struct state_token_st *t1 = peek_state_token (1); if (state_token_kind (t0) == STOK_LEFTPAR && state_token_is_name (t1, "!version")) { next_state_tokens (2); t0 = peek_state_token (0); t1 = peek_state_token (1); if (state_token_kind (t0) == STOK_STRING && state_token_kind (t1) == STOK_RIGHTPAR) { /* Check that the read version string is the same as current version. */ if (strcmp (version_string, t0->stok_un.stok_string)) fatal_reading_state_printf (t0, "version string mismatch; expecting %s but got %s", version_string, t0->stok_un.stok_string); next_state_tokens (2); } else fatal_reading_state (t0, "Missing version or right parenthesis"); } else fatal_reading_state (t0, "Bad version syntax"); } /* Read a pair. */ void read_state_pair (pair_p *current) { struct state_token_st *t0 = peek_state_token (0); struct state_token_st *t1 = peek_state_token (1); if (state_token_kind (t0) == STOK_LEFTPAR && state_token_is_name (t1, "!pair")) { *current = XCNEW (struct pair); next_state_tokens (2); t0 = peek_state_token (0); if (state_token_kind (t0) == STOK_STRING) { if (strcmp (t0->stok_un.stok_string, "nil") == 0) { (*current)->name = NULL; } else { (*current)->name = xstrdup (t0->stok_un.stok_string); } next_state_tokens (1); read_state_type (&((*current)->type)); read_state_fileloc (&((*current)->line)); read_state_options (&((*current)->opt));; t0 = peek_state_token (0); if (state_token_kind (t0) == STOK_RIGHTPAR) { next_state_tokens (1); } else { fatal_reading_state (t0, "Bad syntax for pair, )"); } } else { fatal_reading_state (t0, "Bad name for pair"); } } else if (state_token_kind (t0) == STOK_NAME && state_token_is_name (t0, "nil")) { next_state_tokens (1); *current = NULL; } else fatal_reading_state_printf (t0, "Bad syntax for pair, (!pair %d", state_token->stok_kind); } /* Return the number of pairs actually read. */ int read_state_pair_list (pair_p *list) { int nbpair = 0; pair_p head = NULL; pair_p previous = NULL; pair_p tmp = NULL; struct state_token_st *t0 = peek_state_token (0); while (t0 && state_token_kind (t0) != STOK_RIGHTPAR) { read_state_pair (&tmp); if (head == NULL) { head = tmp; previous = head; } else { previous->next = tmp; previous = tmp; } t0 = peek_state_token (0); nbpair++; } /* don't consume the ); the caller will eat it. */ *list = head; return nbpair; } /* Read the typedefs. */ static void read_state_typedefs (pair_p *typedefs) { int nbtypedefs = 0; pair_p list = NULL; struct state_token_st *t0 = peek_state_token (0); struct state_token_st *t1 = peek_state_token (1); struct state_token_st *t2 = peek_state_token (2); if (state_token_kind (t0) == STOK_LEFTPAR && state_token_is_name (t1, "!typedefs") && state_token_kind (t2) == STOK_INTEGER) { int nbpairs = 0; nbtypedefs = t2->stok_un.stok_num; next_state_tokens (3); nbpairs = read_state_pair_list (&list); t0 = peek_state_token (0); if (nbpairs != nbtypedefs) fatal_reading_state_printf (t0, "invalid number of typedefs, expected %d but got %d", nbtypedefs, nbpairs); if (state_token_kind (t0) == STOK_RIGHTPAR) next_state_tokens (1); else fatal_reading_state (t0, "Bad typedefs syntax )"); } else fatal_reading_state (t0, "Bad typedefs syntax (!typedefs"); if (verbosity_level >= 2) printf ("%s read %d typedefs from state\n", progname, nbtypedefs); *typedefs = list; } /* Read the structures. */ static void read_state_structures (type_p *structures) { type_p head = NULL; type_p previous = NULL; type_p tmp; int nbstruct = 0, countstruct = 0; struct state_token_st *t0 = peek_state_token (0); struct state_token_st *t1 = peek_state_token (1); struct state_token_st *t2 = peek_state_token (2); if (state_token_kind (t0) == STOK_LEFTPAR && state_token_is_name (t1, "!structures") && state_token_kind (t2) == STOK_INTEGER) { nbstruct = t2->stok_un.stok_num; next_state_tokens (3); t0 = peek_state_token (0); while (t0 && state_token_kind (t0) != STOK_RIGHTPAR) { tmp = NULL; read_state_type (&tmp); countstruct++; if (head == NULL) { head = tmp; previous = head; } else { previous->next = tmp; previous = tmp; } t0 = peek_state_token (0); } next_state_tokens (1); } else fatal_reading_state (t0, "Bad structures syntax"); if (countstruct != nbstruct) fatal_reading_state_printf (NULL_STATE_TOKEN, "expected %d structures but got %d", nbstruct, countstruct); if (verbosity_level >= 2) printf ("%s read %d structures from state\n", progname, nbstruct); *structures = head; } /* Read the param_struct-s. */ static void read_state_param_structs (type_p *param_structs) { int nbparamstructs = 0; int countparamstructs = 0; type_p head = NULL; type_p previous = NULL; type_p tmp; struct state_token_st *t0 = peek_state_token (0); struct state_token_st *t1 = peek_state_token (1); struct state_token_st *t2 = peek_state_token (2); if (state_token_kind (t0) == STOK_LEFTPAR && state_token_is_name (t1, "!param_structs") && state_token_kind (t2) == STOK_INTEGER) { nbparamstructs = t2->stok_un.stok_num; next_state_tokens (3); t0 = t1 = t2 = NULL; t0 = peek_state_token (0); while (state_token_kind (t0) != STOK_RIGHTPAR) { tmp = NULL; read_state_type (&tmp); if (head == NULL) { head = tmp; previous = head; } else { previous->next = tmp; previous = tmp; } t0 = peek_state_token (0); countparamstructs++; } next_state_tokens (1); } else fatal_reading_state (t0, "Bad param_structs syntax"); t0 = peek_state_token (0); if (countparamstructs != nbparamstructs) fatal_reading_state_printf (t0, "invalid number of param_structs expected %d got %d", nbparamstructs, countparamstructs); *param_structs = head; } /* Read the variables. */ static void read_state_variables (pair_p *variables) { pair_p list = NULL; int nbvars = 0; struct state_token_st *t0 = peek_state_token (0); struct state_token_st *t1 = peek_state_token (1); struct state_token_st *t2 = peek_state_token (2); if (state_token_kind (t0) == STOK_LEFTPAR && state_token_is_name (t1, "!variables") && state_token_kind (t2) == STOK_INTEGER) { int nbpairs = 0; nbvars = t2->stok_un.stok_num; next_state_tokens (3); nbpairs = read_state_pair_list (&list); t0 = peek_state_token (0); if (nbpairs != nbvars) fatal_reading_state_printf (t0, "Invalid number of variables, expected %d but got %d", nbvars, nbpairs); if (state_token_kind (t0) == STOK_RIGHTPAR) next_state_tokens (1); else fatal_reading_state (t0, "Waiting for ) in variables"); } else fatal_reading_state (t0, "Bad variables syntax"); *variables = list; if (verbosity_level >= 2) printf ("%s read %d variables from state\n", progname, nbvars); } /* Read the source directory. */ static void read_state_srcdir (void) { struct state_token_st *t0 = peek_state_token (0); struct state_token_st *t1 = peek_state_token (1); if (state_token_kind (t0) == STOK_LEFTPAR && state_token_is_name (t1, "!srcdir")) { next_state_tokens (2); t0 = peek_state_token (0); t1 = peek_state_token (1); if (state_token_kind (t0) == STOK_STRING && state_token_kind (t1) == STOK_RIGHTPAR) { srcdir = xstrdup (t0->stok_un.stok_string); srcdir_len = strlen (srcdir); next_state_tokens (2); return; } } fatal_reading_state (t0, "Bad srcdir in state_file"); } /* Read the sequence of GCC front-end languages. */ static void read_state_languages (void) { struct state_token_st *t0 = peek_state_token (0); struct state_token_st *t1 = peek_state_token (1); struct state_token_st *t2 = peek_state_token (2); if (state_token_kind (t0) == STOK_LEFTPAR && state_token_is_name (t1, "!languages") && state_token_kind (t2) == STOK_INTEGER) { int i = 0; num_lang_dirs = t2->stok_un.stok_num; lang_dir_names = XCNEWVEC (const char *, num_lang_dirs); next_state_tokens (3); t0 = t1 = t2 = NULL; for (i = 0; i < (int) num_lang_dirs; i++) { t0 = peek_state_token (0); if (state_token_kind (t0) != STOK_NAME) fatal_reading_state (t0, "expecting language name in state file"); lang_dir_names[i] = t0->stok_un.stok_ident->stid_name; next_state_tokens (1); } t0 = peek_state_token (0); if (state_token_kind (t0) != STOK_RIGHTPAR) fatal_reading_state (t0, "missing ) in languages list of state file"); next_state_tokens (1); } else fatal_reading_state (t0, "expecting languages list in state file"); } /* Read the sequence of files. */ static void read_state_files_list (void) { struct state_token_st *t0 = peek_state_token (0); struct state_token_st *t1 = peek_state_token (1); struct state_token_st *t2 = peek_state_token (2); if (state_token_kind (t0) == STOK_LEFTPAR && state_token_is_name (t1, "!fileslist") && state_token_kind (t2) == STOK_INTEGER) { int i = 0; num_gt_files = t2->stok_un.stok_num; next_state_tokens (3); t0 = t1 = t2 = NULL; gt_files = XCNEWVEC (const input_file *, num_gt_files); for (i = 0; i < (int) num_gt_files; i++) { bool issrcfile = FALSE; t0 = t1 = t2 = NULL; t0 = peek_state_token (0); t1 = peek_state_token (1); t2 = peek_state_token (2); if (state_token_kind (t0) == STOK_LEFTPAR && (state_token_is_name (t1, "!file") || (issrcfile = state_token_is_name (t1, "!srcfile"))) && state_token_kind (t2) == STOK_INTEGER) { lang_bitmap bmap = t2->stok_un.stok_num; next_state_tokens (3); t0 = t1 = t2 = NULL; t0 = peek_state_token (0); t1 = peek_state_token (1); if (state_token_kind (t0) == STOK_STRING && state_token_kind (t1) == STOK_RIGHTPAR) { const char *fnam = t0->stok_un.stok_string; /* Allocate & fill a gt_file entry with space for the lang_bitmap before! */ input_file *curgt = NULL; if (issrcfile) { static const char dirsepstr[2] = { DIR_SEPARATOR, (char) 0 }; char *fullpath = concat (srcdir, dirsepstr, fnam, NULL); curgt = input_file_by_name (fullpath); free (fullpath); } else curgt = input_file_by_name (fnam); set_lang_bitmap (curgt, bmap); gt_files[i] = curgt; next_state_tokens (2); } else fatal_reading_state (t0, "bad file in !fileslist of state file"); } else fatal_reading_state (t0, "expecting file in !fileslist of state file"); }; t0 = peek_state_token (0); if (state_token_kind (t0) != STOK_RIGHTPAR) fatal_reading_state (t0, "missing ) for !fileslist in state file"); next_state_tokens (1); } else fatal_reading_state (t0, "missing !fileslist in state file"); } /* Read the trailer. */ static void read_state_trailer (void) { struct state_token_st *t0 = peek_state_token (0); struct state_token_st *t1 = peek_state_token (1); struct state_token_st *t2 = peek_state_token (2); if (state_token_kind (t0) == STOK_LEFTPAR && state_token_is_name (t1, "!endfile") && state_token_kind (t2) == STOK_RIGHTPAR) next_state_tokens (3); else fatal_reading_state (t0, "missing !endfile in state file"); } /* Utility functions for the state_seen_types hash table. */ static unsigned hash_type_number (const void *ty) { const struct type *type = (const struct type *) ty; return type->state_number; } static int equals_type_number (const void *ty1, const void *ty2) { const struct type *type1 = (const struct type *) ty1; const struct type *type2 = (const struct type *) ty2; return type1->state_number == type2->state_number; } static int string_eq (const void *a, const void *b) { const char *a0 = (const char *)a; const char *b0 = (const char *)b; return (strcmp (a0, b0) == 0); } /* The function reading the state, called by main from gengtype.c. */ void read_state (const char *path) { state_file = fopen (path, "r"); if (state_file == NULL) fatal ("Failed to open state file %s for reading [%s]", path, xstrerror (errno)); state_path = path; state_line = 1; if (verbosity_level >= 1) { printf ("%s reading state file %s;", progname, state_path); if (verbosity_level >= 2) putchar ('\n'); fflush (stdout); } state_seen_types = htab_create (2017, hash_type_number, equals_type_number, NULL); state_ident_tab = htab_create (4027, htab_hash_string, string_eq, NULL); read_state_version (version_string); read_state_srcdir (); read_state_languages (); read_state_files_list (); read_state_structures (&structures); if (ferror (state_file)) fatal_reading_state_printf (NULL_STATE_TOKEN, "input error while reading state [%s]", xstrerror (errno)); read_state_typedefs (&typedefs); read_state_param_structs (¶m_structs); read_state_variables (&variables); read_state_trailer (); if (verbosity_level >= 1) { printf ("%s read %ld bytes.\n", progname, ftell (state_file)); fflush (stdout); }; if (fclose (state_file)) fatal ("failed to close read state file %s [%s]", path, xstrerror (errno)); state_file = NULL; state_path = NULL; } /* End of file gengtype-state.c. */