diff options
Diffstat (limited to 'gcc/pretty-print-format-impl.h')
-rw-r--r-- | gcc/pretty-print-format-impl.h | 407 |
1 files changed, 397 insertions, 10 deletions
diff --git a/gcc/pretty-print-format-impl.h b/gcc/pretty-print-format-impl.h index e05ad38..cffdd46 100644 --- a/gcc/pretty-print-format-impl.h +++ b/gcc/pretty-print-format-impl.h @@ -23,6 +23,308 @@ along with GCC; see the file COPYING3. If not see #include "pretty-print.h" +/* A struct representing a pending item to be printed within + pp_format. + + These can represent: + - a run of text within one of the output_buffers's obstacks + - begin/end named color + - open/close quote + - begin/end URL + - custom data (for the formatter, for the pretty_printer, + or the output format) + + These are built into pp_token_list instances. + + Doing so allows for interaction between: + + - pretty_printer formatting codes (such as C++'s %H and %I, + which can't be printed until we've seen both) + + - output formats, such as text vs SARIF (so each can handle URLs + and event IDs it its own way) + + - optimization records, where we want to stash data into the + formatted messages + + - urlifiers: these can be run in phase 3 of formatting + + without needing lots of fragile logic on char pointers. + + To avoid needing lots of heap allocation/deallocation, pp_token + instances are allocated in the pretty_printer's chunk_obstack: + they must not outlive phase 3 of formatting of the given + chunk_info level. */ + +struct pp_token +{ +public: + enum class kind + { + text, + + begin_color, + end_color, + + begin_quote, + end_quote, + + begin_url, + end_url, + + custom_data, + + NUM_KINDS + }; + + pp_token (enum kind k); + + pp_token (const pp_token &) = delete; + pp_token (pp_token &&) = delete; + + virtual ~pp_token () = default; + + pp_token &operator= (const pp_token &) = delete; + pp_token &operator= (pp_token &&) = delete; + + void dump (FILE *out) const; + void DEBUG_FUNCTION dump () const { dump (stderr); } + + static void *operator new (size_t sz, obstack &s); + static void operator delete (void *); + + enum kind m_kind; + + // Intrusive doubly-linked list + pp_token *m_prev; + pp_token *m_next; +}; + +/* Subclasses of pp_token for the various kinds of token. */ + +struct pp_token_text : public pp_token +{ + pp_token_text (label_text &&value) + : pp_token (kind::text), + m_value (std::move (value)) + { + gcc_assert (m_value.get ()); + } + + label_text m_value; +}; + +template <> +template <> +inline bool +is_a_helper <pp_token_text *>::test (pp_token *tok) +{ + return tok->m_kind == pp_token::kind::text; +} + +template <> +template <> +inline bool +is_a_helper <const pp_token_text *>::test (const pp_token *tok) +{ + return tok->m_kind == pp_token::kind::text; +} + +struct pp_token_begin_color : public pp_token +{ + pp_token_begin_color (label_text &&value) + : pp_token (kind::begin_color), + m_value (std::move (value)) + { + gcc_assert (m_value.get ()); + } + + label_text m_value; +}; + +template <> +template <> +inline bool +is_a_helper <pp_token_begin_color *>::test (pp_token *tok) +{ + return tok->m_kind == pp_token::kind::begin_color; +} + +template <> +template <> +inline bool +is_a_helper <const pp_token_begin_color *>::test (const pp_token *tok) +{ + return tok->m_kind == pp_token::kind::begin_color; +} + +struct pp_token_end_color : public pp_token +{ + pp_token_end_color () + : pp_token (kind::end_color) + { + } +}; + +struct pp_token_begin_quote : public pp_token +{ + pp_token_begin_quote () + : pp_token (kind::begin_quote) + { + } +}; + +struct pp_token_end_quote : public pp_token +{ + pp_token_end_quote () + : pp_token (kind::end_quote) + { + } +}; + +struct pp_token_begin_url : public pp_token +{ + pp_token_begin_url (label_text &&value) + : pp_token (kind::begin_url), + m_value (std::move (value)) + { + gcc_assert (m_value.get ()); + } + + label_text m_value; +}; + +template <> +template <> +inline bool +is_a_helper <pp_token_begin_url*>::test (pp_token *tok) +{ + return tok->m_kind == pp_token::kind::begin_url; +} + +template <> +template <> +inline bool +is_a_helper <const pp_token_begin_url*>::test (const pp_token *tok) +{ + return tok->m_kind == pp_token::kind::begin_url; +} + +struct pp_token_end_url : public pp_token +{ + pp_token_end_url () + : pp_token (kind::end_url) + { + } +}; + +struct pp_token_custom_data : public pp_token +{ + class value + { + public: + virtual ~value () {} + virtual void dump (FILE *out) const = 0; + + /* Hook for lowering a custom_data token to standard tokens. + Return true and write to OUT if possible. + Return false for custom_data that is to be handled by + the token_printer. */ + virtual bool as_standard_tokens (pp_token_list &out) = 0; + }; + + pp_token_custom_data (std::unique_ptr<value> val) + : pp_token (kind::custom_data), + m_value (std::move (val)) + { + gcc_assert (m_value.get ()); + } + + std::unique_ptr<value> m_value; +}; + +template <> +template <> +inline bool +is_a_helper <pp_token_custom_data *>::test (pp_token *tok) +{ + return tok->m_kind == pp_token::kind::custom_data; +} + +template <> +template <> +inline bool +is_a_helper <const pp_token_custom_data *>::test (const pp_token *tok) +{ + return tok->m_kind == pp_token::kind::custom_data; +} + +/* A list of pp_token, with ownership of the tokens, using + a particular obstack to allocate its tokens. These are + also allocated on the obstack during formatting (or, occasionally, + the stack). */ + +class pp_token_list +{ +public: + // Allocate a new pp_token_list within S. + static pp_token_list *make (obstack &s) + { + return new (s) pp_token_list (s); + } + static void *operator new (size_t sz, obstack &s); + static void operator delete (void *); + + pp_token_list (obstack &s); + pp_token_list (const pp_token_list &) = delete; + pp_token_list (pp_token_list &&); + + ~pp_token_list (); + + pp_token &operator= (const pp_token_list &) = delete; + pp_token &operator= (pp_token_list &&) = delete; + +/* Make a pp_token of the given subclass, using the relevant obstack to provide + the memory. The pp_token must therefore not outlive the current chunk_info + level during formatting. */ + template<typename Subclass, typename... Args> + std::unique_ptr<pp_token> + make_token (Args&&... args) + { + return std::unique_ptr<pp_token> + (new (m_obstack) Subclass (std::forward<Args> (args)...)); + } + + template<typename Subclass, typename... Args> + void push_back (Args&&... args) + { + auto tok = make_token<Subclass> (std::forward<Args> (args)...); + push_back (std::move (tok)); + } + void push_back_text (label_text &&text); + void push_back (std::unique_ptr<pp_token> tok); + void push_back_list (pp_token_list &&list); + + std::unique_ptr<pp_token> pop_front (); + + std::unique_ptr<pp_token> remove_token (pp_token *tok); + + void insert_after (std::unique_ptr<pp_token> new_tok, + pp_token *relative_tok); + + void replace_custom_tokens (); + void merge_consecutive_text_tokens (); + void apply_urlifier (const urlifier &urlifier); + + void dump (FILE *out) const; + void DEBUG_FUNCTION dump () const { dump (stderr); } + + obstack &m_obstack; + + pp_token *m_first; + pp_token *m_end; +}; + /* The chunk_info data structure forms a stack of the results from the first phase of formatting (pp_format) which have not yet been output (pp_output_formatted_text). A stack is necessary because @@ -34,13 +336,15 @@ class chunk_info friend class pp_markup::context; public: - const char * const *get_args () const { return m_args; } - quoting_info *get_quoting_info () const { return m_quotes; } + pp_token_list * const * get_token_lists () const { return m_args; } - void append_formatted_chunk (const char *content); + void append_formatted_chunk (obstack &s, const char *content); void pop_from_output_buffer (output_buffer &buf); + void dump (FILE *out) const; + void DEBUG_FUNCTION dump () const { dump (stderr); } + private: void on_begin_quote (const output_buffer &buf, unsigned chunk_idx, @@ -54,17 +358,100 @@ private: /* Pointer to previous chunk on the stack. */ chunk_info *m_prev; - /* Array of chunks to output. Each chunk is a NUL-terminated string. + /* Array of chunks to output. Each chunk is a doubly-linked list of + pp_token. + + The chunks can be printed via chunk_info::dump (). + In the first phase of formatting, even-numbered chunks are to be output verbatim, odd-numbered chunks are format specifiers. + For example, given: + pp_format (pp, + "foo: %i, bar: %s, opt: %qs", + 42, "baz", "-foption"); + + after phase 1 we might have: + (gdb) call buffer->cur_chunk_array->dump() + 0: [TEXT("foo: ")] + 1: [TEXT("i")] + 2: [TEXT(", bar: ")] + 3: [TEXT("s")] + 4: [TEXT(", opt: ")] + 5: [TEXT("qs")] + The second phase replaces all odd-numbered chunks with formatted - text, and the third phase simply emits all the chunks in sequence - with appropriate line-wrapping. */ - const char *m_args[PP_NL_ARGMAX * 2]; + token lists. In the above example, after phase 2 we might have: + (gdb) call pp->m_buffer->cur_chunk_array->dump() + 0: [TEXT("foo: ")] + 1: [TEXT("42")] + 2: [TEXT(", bar: ")] + 3: [TEXT("baz")] + 4: [TEXT(", opt: ")] + 5: [BEGIN_QUOTE, TEXT("-foption"), END_QUOTE] + For example the %qs has become the three tokens: + [BEGIN_QUOTE, TEXT("-foption"), END_QUOTE] + + The third phase (in pp_output_formatted_text): + + (1) merges the tokens from all the chunks into one list, + giving e.g. + (gdb) call tokens.dump() + [TEXT("foo: "), TEXT("42"), TEXT(", bar: "), TEXT("baz"), + TEXT(", opt: "), BEGIN_QUOTE, TEXT("-foption"), END_QUOTE] + + (2) lowers some custom tokens into non-custom tokens + + (3) merges consecutive text tokens, giving e.g.: + (gdb) call tokens.dump() + [TEXT("foo: 42, bar: baz, option: "), + BEGIN_QUOTE, TEXT("-foption"), END_QUOTE] + + (4) if provided with a urlifier, tries to apply it to quoted text, + giving e.g: + (gdb) call tokens.dump() + [TEXT("foo: 42, bar: baz, option: "), BEGIN_QUOTE, + BEGIN_URL("http://example.com"), TEXT("-foption"), END_URL, END_QUOTE] + + (5) emits all tokens in sequence with appropriate line-wrapping. This + can be overridded via the pretty_printer's token_printer, allowing for + output formats to e.g. override how URLs are handled, or to handle + custom_data that wasn't lowered in (2) above, e.g. for handling JSON + output of optimization records. */ + pp_token_list *m_args[PP_NL_ARGMAX * 2]; + + /* The pp_tokens, pp_token_lists, and the accumulated text buffers are + allocated within the output_buffer's chunk_obstack. In the above + example, the in-memory layout of the chunk_obstack might look like + this after phase 1: + + + pp_token_list for chunk 0 (m_first: *) <--- START of chunk_info level + | | + + "foo: \0" <-------------\ | + | | | + + pp_token_text (borrowed: *) <-------/ + | + + pp_token_list for chunk 1 + | + + "i\0" <------------------\ + | | + + pp_token_text (borrowed: *) + | + + ...etc for chunks 2 to 4... + | + + pp_token_list for chunk 5 + | + + "qs\0" <-----------------\ + | | + + pp_token_text (borrowed: *) + | + | + V + obstack grows this way - /* If non-null, information on quoted text runs within the chunks - for use by a urlifier. */ - quoting_info *m_quotes; + At each stage, allocation of additional text buffers, tokens, and lists + grow forwards in the obstack (though the internal pointers in linked + lists might point backwards to earlier objects within the same + chunk_info level). */ }; #endif /* GCC_PRETTY_PRINT_FORMAT_IMPL_H */ |