|  | /* C preprocessor macro expansion for GDB. | 
|  | Copyright (C) 2002-2025 Free Software Foundation, Inc. | 
|  | Contributed by Red Hat, Inc. | 
|  |  | 
|  | This file is part of GDB. | 
|  |  | 
|  | This program 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 of the License, or | 
|  | (at your option) any later version. | 
|  |  | 
|  | This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.  */ | 
|  |  | 
|  | #include "gdbsupport/gdb_obstack.h" | 
|  | #include "macrotab.h" | 
|  | #include "macroexp.h" | 
|  | #include "macroscope.h" | 
|  | #include "c-lang.h" | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  | /* A string type that we can use to refer to substrings of other | 
|  | strings.  */ | 
|  |  | 
|  | struct shared_macro_buffer | 
|  | { | 
|  | /* An array of characters.  This buffer is a pointer into some | 
|  | larger string and thus we can't assume in that the text is | 
|  | null-terminated.  */ | 
|  | const char *text; | 
|  |  | 
|  | /* The number of characters in the string.  */ | 
|  | int len; | 
|  |  | 
|  | /* For detecting token splicing. | 
|  |  | 
|  | This is the index in TEXT of the first character of the token | 
|  | that abuts the end of TEXT.  If TEXT contains no tokens, then we | 
|  | set this equal to LEN.  If TEXT ends in whitespace, then there is | 
|  | no token abutting the end of TEXT (it's just whitespace), and | 
|  | again, we set this equal to LEN.  We set this to -1 if we don't | 
|  | know the nature of TEXT.  */ | 
|  | int last_token = -1; | 
|  |  | 
|  | /* If this buffer is holding the result from get_token, then this | 
|  | is non-zero if it is an identifier token, zero otherwise.  */ | 
|  | int is_identifier = 0; | 
|  |  | 
|  | shared_macro_buffer () | 
|  | : text (NULL), | 
|  | len (0) | 
|  | { | 
|  | } | 
|  |  | 
|  | /* Set the macro buffer to refer to the LEN bytes at ADDR, as a | 
|  | shared substring.  */ | 
|  | shared_macro_buffer (const char *addr, int len) | 
|  | { | 
|  | set_shared (addr, len); | 
|  | } | 
|  |  | 
|  | /* Set the macro buffer to refer to the LEN bytes at ADDR, as a | 
|  | shared substring.  */ | 
|  | void set_shared (const char *addr, int len_) | 
|  | { | 
|  | text = addr; | 
|  | len = len_; | 
|  | } | 
|  | }; | 
|  |  | 
|  | /* A string type that we can resize and quickly append to.  */ | 
|  |  | 
|  | struct growable_macro_buffer | 
|  | { | 
|  | /* An array of characters.  The first LEN bytes are the real text, | 
|  | but there are SIZE bytes allocated to the array.  */ | 
|  | char *text; | 
|  |  | 
|  | /* The number of characters in the string.  */ | 
|  | int len; | 
|  |  | 
|  | /* The number of characters allocated to the string.  */ | 
|  | int size; | 
|  |  | 
|  | /* For detecting token splicing. | 
|  |  | 
|  | This is the index in TEXT of the first character of the token | 
|  | that abuts the end of TEXT.  If TEXT contains no tokens, then we | 
|  | set this equal to LEN.  If TEXT ends in whitespace, then there is | 
|  | no token abutting the end of TEXT (it's just whitespace), and | 
|  | again, we set this equal to LEN.  We set this to -1 if we don't | 
|  | know the nature of TEXT.  */ | 
|  | int last_token = -1; | 
|  |  | 
|  | /* Set the macro buffer to the empty string, guessing that its | 
|  | final contents will fit in N bytes.  (It'll get resized if it | 
|  | doesn't, so the guess doesn't have to be right.)  Allocate the | 
|  | initial storage with xmalloc.  */ | 
|  | explicit growable_macro_buffer (int n) | 
|  | : len (0), | 
|  | size (n) | 
|  | { | 
|  | if (n > 0) | 
|  | text = (char *) xmalloc (n); | 
|  | else | 
|  | text = NULL; | 
|  | } | 
|  |  | 
|  | DISABLE_COPY_AND_ASSIGN (growable_macro_buffer); | 
|  |  | 
|  | ~growable_macro_buffer () | 
|  | { | 
|  | xfree (text); | 
|  | } | 
|  |  | 
|  | /* Release the text of the buffer to the caller.  */ | 
|  | gdb::unique_xmalloc_ptr<char> release () | 
|  | { | 
|  | gdb_assert (size); | 
|  | char *result = text; | 
|  | text = NULL; | 
|  | return gdb::unique_xmalloc_ptr<char> (result); | 
|  | } | 
|  |  | 
|  | /* Resize the buffer to be at least N bytes long.  */ | 
|  | void resize_buffer (int n) | 
|  | { | 
|  | if (size == 0) | 
|  | size = n; | 
|  | else | 
|  | while (size <= n) | 
|  | size *= 2; | 
|  |  | 
|  | text = (char *) xrealloc (text, size); | 
|  | } | 
|  |  | 
|  | /* Append the character C to the buffer.  */ | 
|  | void appendc (int c) | 
|  | { | 
|  | int new_len = len + 1; | 
|  |  | 
|  | if (new_len > size) | 
|  | resize_buffer (new_len); | 
|  |  | 
|  | text[len] = c; | 
|  | len = new_len; | 
|  | } | 
|  |  | 
|  | /* Append the COUNT bytes at ADDR to the buffer.  */ | 
|  | void appendmem (const char *addr, int count) | 
|  | { | 
|  | int new_len = len + count; | 
|  |  | 
|  | if (new_len > size) | 
|  | resize_buffer (new_len); | 
|  |  | 
|  | memcpy (text + len, addr, count); | 
|  | len = new_len; | 
|  | } | 
|  | }; | 
|  |  | 
|  |  | 
|  |  | 
|  | /* Recognizing preprocessor tokens.  */ | 
|  |  | 
|  |  | 
|  | int | 
|  | macro_is_whitespace (int c) | 
|  | { | 
|  | return (c == ' ' | 
|  | || c == '\t' | 
|  | || c == '\n' | 
|  | || c == '\v' | 
|  | || c == '\f'); | 
|  | } | 
|  |  | 
|  |  | 
|  | int | 
|  | macro_is_digit (int c) | 
|  | { | 
|  | return ('0' <= c && c <= '9'); | 
|  | } | 
|  |  | 
|  |  | 
|  | int | 
|  | macro_is_identifier_nondigit (int c) | 
|  | { | 
|  | return (c == '_' | 
|  | || ('a' <= c && c <= 'z') | 
|  | || ('A' <= c && c <= 'Z')); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void | 
|  | set_token (shared_macro_buffer *tok, const char *start, const char *end) | 
|  | { | 
|  | tok->set_shared (start, end - start); | 
|  | tok->last_token = 0; | 
|  |  | 
|  | /* Presumed; get_identifier may overwrite this.  */ | 
|  | tok->is_identifier = 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int | 
|  | get_comment (shared_macro_buffer *tok, const char *p, const char *end) | 
|  | { | 
|  | if (p + 2 > end) | 
|  | return 0; | 
|  | else if (p[0] == '/' | 
|  | && p[1] == '*') | 
|  | { | 
|  | const char *tok_start = p; | 
|  |  | 
|  | p += 2; | 
|  |  | 
|  | for (; p < end; p++) | 
|  | if (p + 2 <= end | 
|  | && p[0] == '*' | 
|  | && p[1] == '/') | 
|  | { | 
|  | p += 2; | 
|  | set_token (tok, tok_start, p); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | error (_("Unterminated comment in macro expansion.")); | 
|  | } | 
|  | else if (p[0] == '/' | 
|  | && p[1] == '/') | 
|  | { | 
|  | const char *tok_start = p; | 
|  |  | 
|  | p += 2; | 
|  | for (; p < end; p++) | 
|  | if (*p == '\n') | 
|  | break; | 
|  |  | 
|  | set_token (tok, tok_start, p); | 
|  | return 1; | 
|  | } | 
|  | else | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int | 
|  | get_identifier (shared_macro_buffer *tok, const char *p, const char *end) | 
|  | { | 
|  | if (p < end | 
|  | && macro_is_identifier_nondigit (*p)) | 
|  | { | 
|  | const char *tok_start = p; | 
|  |  | 
|  | while (p < end | 
|  | && (macro_is_identifier_nondigit (*p) | 
|  | || macro_is_digit (*p))) | 
|  | p++; | 
|  |  | 
|  | set_token (tok, tok_start, p); | 
|  | tok->is_identifier = 1; | 
|  | return 1; | 
|  | } | 
|  | else | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int | 
|  | get_pp_number (shared_macro_buffer *tok, const char *p, const char *end) | 
|  | { | 
|  | if (p < end | 
|  | && (macro_is_digit (*p) | 
|  | || (*p == '.' | 
|  | && p + 2 <= end | 
|  | && macro_is_digit (p[1])))) | 
|  | { | 
|  | const char *tok_start = p; | 
|  |  | 
|  | while (p < end) | 
|  | { | 
|  | if (p + 2 <= end | 
|  | && strchr ("eEpP", *p) | 
|  | && (p[1] == '+' || p[1] == '-')) | 
|  | p += 2; | 
|  | else if (macro_is_digit (*p) | 
|  | || macro_is_identifier_nondigit (*p) | 
|  | || *p == '.') | 
|  | p++; | 
|  | else | 
|  | break; | 
|  | } | 
|  |  | 
|  | set_token (tok, tok_start, p); | 
|  | return 1; | 
|  | } | 
|  | else | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | /* If the text starting at P going up to (but not including) END | 
|  | starts with a character constant, set *TOK to point to that | 
|  | character constant, and return 1.  Otherwise, return zero. | 
|  | Signal an error if it contains a malformed or incomplete character | 
|  | constant.  */ | 
|  | static int | 
|  | get_character_constant (shared_macro_buffer *tok, | 
|  | const char *p, const char *end) | 
|  | { | 
|  | /* ISO/IEC 9899:1999 (E)  Section 6.4.4.4  paragraph 1 | 
|  | But of course, what really matters is that we handle it the same | 
|  | way GDB's C/C++ lexer does.  So we call parse_escape in utils.c | 
|  | to handle escape sequences.  */ | 
|  | if ((p + 1 <= end && *p == '\'') | 
|  | || (p + 2 <= end | 
|  | && (p[0] == 'L' || p[0] == 'u' || p[0] == 'U') | 
|  | && p[1] == '\'')) | 
|  | { | 
|  | const char *tok_start = p; | 
|  | int char_count = 0; | 
|  |  | 
|  | if (*p == '\'') | 
|  | p++; | 
|  | else if (*p == 'L' || *p == 'u' || *p == 'U') | 
|  | p += 2; | 
|  | else | 
|  | gdb_assert_not_reached ("unexpected character constant"); | 
|  |  | 
|  | for (;;) | 
|  | { | 
|  | if (p >= end) | 
|  | error (_("Unmatched single quote.")); | 
|  | else if (*p == '\'') | 
|  | { | 
|  | if (!char_count) | 
|  | error (_("A character constant must contain at least one " | 
|  | "character.")); | 
|  | p++; | 
|  | break; | 
|  | } | 
|  | else if (*p == '\\') | 
|  | { | 
|  | const char *s, *o; | 
|  |  | 
|  | s = o = ++p; | 
|  | char_count += c_parse_escape (&s, NULL); | 
|  | p += s - o; | 
|  | } | 
|  | else | 
|  | { | 
|  | p++; | 
|  | char_count++; | 
|  | } | 
|  | } | 
|  |  | 
|  | set_token (tok, tok_start, p); | 
|  | return 1; | 
|  | } | 
|  | else | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* If the text starting at P going up to (but not including) END | 
|  | starts with a string literal, set *TOK to point to that string | 
|  | literal, and return 1.  Otherwise, return zero.  Signal an error if | 
|  | it contains a malformed or incomplete string literal.  */ | 
|  | static int | 
|  | get_string_literal (shared_macro_buffer *tok, const char *p, const char *end) | 
|  | { | 
|  | if ((p + 1 <= end | 
|  | && *p == '"') | 
|  | || (p + 2 <= end | 
|  | && (p[0] == 'L' || p[0] == 'u' || p[0] == 'U') | 
|  | && p[1] == '"')) | 
|  | { | 
|  | const char *tok_start = p; | 
|  |  | 
|  | if (*p == '"') | 
|  | p++; | 
|  | else if (*p == 'L' || *p == 'u' || *p == 'U') | 
|  | p += 2; | 
|  | else | 
|  | gdb_assert_not_reached ("unexpected string literal"); | 
|  |  | 
|  | for (;;) | 
|  | { | 
|  | if (p >= end) | 
|  | error (_("Unterminated string in expression.")); | 
|  | else if (*p == '"') | 
|  | { | 
|  | p++; | 
|  | break; | 
|  | } | 
|  | else if (*p == '\n') | 
|  | error (_("Newline characters may not appear in string " | 
|  | "constants.")); | 
|  | else if (*p == '\\') | 
|  | { | 
|  | const char *s, *o; | 
|  |  | 
|  | s = o = ++p; | 
|  | c_parse_escape (&s, NULL); | 
|  | p += s - o; | 
|  | } | 
|  | else | 
|  | p++; | 
|  | } | 
|  |  | 
|  | set_token (tok, tok_start, p); | 
|  | return 1; | 
|  | } | 
|  | else | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int | 
|  | get_punctuator (shared_macro_buffer *tok, const char *p, const char *end) | 
|  | { | 
|  | /* Here, speed is much less important than correctness and clarity.  */ | 
|  |  | 
|  | /* ISO/IEC 9899:1999 (E)  Section 6.4.6  Paragraph 1. | 
|  | Note that this table is ordered in a special way.  A punctuator | 
|  | which is a prefix of another punctuator must appear after its | 
|  | "extension".  Otherwise, the wrong token will be returned.  */ | 
|  | static const char * const punctuators[] = { | 
|  | "[", "]", "(", ")", "{", "}", "?", ";", ",", "~", | 
|  | "...", ".", | 
|  | "->", "--", "-=", "-", | 
|  | "++", "+=", "+", | 
|  | "*=", "*", | 
|  | "!=", "!", | 
|  | "&&", "&=", "&", | 
|  | "/=", "/", | 
|  | "%>", "%:%:", "%:", "%=", "%", | 
|  | "^=", "^", | 
|  | "##", "#", | 
|  | ":>", ":", | 
|  | "||", "|=", "|", | 
|  | "<<=", "<<", "<=", "<:", "<%", "<", | 
|  | ">>=", ">>", ">=", ">", | 
|  | "==", "=", | 
|  | 0 | 
|  | }; | 
|  |  | 
|  | int i; | 
|  |  | 
|  | if (p + 1 <= end) | 
|  | { | 
|  | for (i = 0; punctuators[i]; i++) | 
|  | { | 
|  | const char *punctuator = punctuators[i]; | 
|  |  | 
|  | if (p[0] == punctuator[0]) | 
|  | { | 
|  | int len = strlen (punctuator); | 
|  |  | 
|  | if (p + len <= end | 
|  | && ! memcmp (p, punctuator, len)) | 
|  | { | 
|  | set_token (tok, p, p + len); | 
|  | return 1; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Peel the next preprocessor token off of SRC, and put it in TOK. | 
|  | Mutate TOK to refer to the first token in SRC, and mutate SRC to | 
|  | refer to the text after that token.  The resulting TOK will point | 
|  | into the same string SRC does.  Initialize TOK's last_token field. | 
|  | Return non-zero if we succeed, or 0 if we didn't find any more | 
|  | tokens in SRC.  */ | 
|  |  | 
|  | static int | 
|  | get_token (shared_macro_buffer *tok, shared_macro_buffer *src) | 
|  | { | 
|  | const char *p = src->text; | 
|  | const char *end = p + src->len; | 
|  |  | 
|  | /* From the ISO C standard, ISO/IEC 9899:1999 (E), section 6.4: | 
|  |  | 
|  | preprocessing-token: | 
|  | header-name | 
|  | identifier | 
|  | pp-number | 
|  | character-constant | 
|  | string-literal | 
|  | punctuator | 
|  | each non-white-space character that cannot be one of the above | 
|  |  | 
|  | We don't have to deal with header-name tokens, since those can | 
|  | only occur after a #include, which we will never see.  */ | 
|  |  | 
|  | while (p < end) | 
|  | if (macro_is_whitespace (*p)) | 
|  | p++; | 
|  | else if (get_comment (tok, p, end)) | 
|  | p += tok->len; | 
|  | else if (get_pp_number (tok, p, end) | 
|  | || get_character_constant (tok, p, end) | 
|  | || get_string_literal (tok, p, end) | 
|  | /* Note: the grammar in the standard seems to be | 
|  | ambiguous: L'x' can be either a wide character | 
|  | constant, or an identifier followed by a normal | 
|  | character constant.  By trying `get_identifier' after | 
|  | we try get_character_constant and get_string_literal, | 
|  | we give the wide character syntax precedence.  Now, | 
|  | since GDB doesn't handle wide character constants | 
|  | anyway, is this the right thing to do?  */ | 
|  | || get_identifier (tok, p, end) | 
|  | || get_punctuator (tok, p, end)) | 
|  | { | 
|  | /* How many characters did we consume, including whitespace?  */ | 
|  | int consumed = p - src->text + tok->len; | 
|  |  | 
|  | src->text += consumed; | 
|  | src->len -= consumed; | 
|  | return 1; | 
|  | } | 
|  | else | 
|  | { | 
|  | /* We have found a "non-whitespace character that cannot be | 
|  | one of the above."  Make a token out of it.  */ | 
|  | int consumed; | 
|  |  | 
|  | set_token (tok, p, p + 1); | 
|  | consumed = p - src->text + tok->len; | 
|  | src->text += consumed; | 
|  | src->len -= consumed; | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | /* Appending token strings, with and without splicing  */ | 
|  |  | 
|  |  | 
|  | /* Append the macro buffer SRC to the end of DEST, and ensure that | 
|  | doing so doesn't splice the token at the end of SRC with the token | 
|  | at the beginning of DEST.  SRC and DEST must have their last_token | 
|  | fields set.  Upon return, DEST's last_token field is set correctly. | 
|  |  | 
|  | For example: | 
|  |  | 
|  | If DEST is "(" and SRC is "y", then we can return with | 
|  | DEST set to "(y" --- we've simply appended the two buffers. | 
|  |  | 
|  | However, if DEST is "x" and SRC is "y", then we must not return | 
|  | with DEST set to "xy" --- that would splice the two tokens "x" and | 
|  | "y" together to make a single token "xy".  However, it would be | 
|  | fine to return with DEST set to "x y".  Similarly, "<" and "<" must | 
|  | yield "< <", not "<<", etc.  */ | 
|  | static void | 
|  | append_tokens_without_splicing (growable_macro_buffer *dest, | 
|  | shared_macro_buffer *src) | 
|  | { | 
|  | int original_dest_len = dest->len; | 
|  | shared_macro_buffer dest_tail, new_token; | 
|  |  | 
|  | gdb_assert (src->last_token != -1); | 
|  | gdb_assert (dest->last_token != -1); | 
|  |  | 
|  | /* First, just try appending the two, and call get_token to see if | 
|  | we got a splice.  */ | 
|  | dest->appendmem (src->text, src->len); | 
|  |  | 
|  | /* If DEST originally had no token abutting its end, then we can't | 
|  | have spliced anything, so we're done.  */ | 
|  | if (dest->last_token == original_dest_len) | 
|  | { | 
|  | dest->last_token = original_dest_len + src->last_token; | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* Set DEST_TAIL to point to the last token in DEST, followed by | 
|  | all the stuff we just appended.  */ | 
|  | dest_tail.set_shared (dest->text + dest->last_token, | 
|  | dest->len - dest->last_token); | 
|  |  | 
|  | /* Re-parse DEST's last token.  We know that DEST used to contain | 
|  | at least one token, so if it doesn't contain any after the | 
|  | append, then we must have spliced "/" and "*" or "/" and "/" to | 
|  | make a comment start.  (Just for the record, I got this right | 
|  | the first time.  This is not a bug fix.)  */ | 
|  | if (get_token (&new_token, &dest_tail) | 
|  | && (new_token.text + new_token.len | 
|  | == dest->text + original_dest_len)) | 
|  | { | 
|  | /* No splice, so we're done.  */ | 
|  | dest->last_token = original_dest_len + src->last_token; | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* Okay, a simple append caused a splice.  Let's chop dest back to | 
|  | its original length and try again, but separate the texts with a | 
|  | space.  */ | 
|  | dest->len = original_dest_len; | 
|  | dest->appendc (' '); | 
|  | dest->appendmem (src->text, src->len); | 
|  |  | 
|  | dest_tail.set_shared (dest->text + dest->last_token, | 
|  | dest->len - dest->last_token); | 
|  |  | 
|  | /* Try to re-parse DEST's last token, as above.  */ | 
|  | if (get_token (&new_token, &dest_tail) | 
|  | && (new_token.text + new_token.len | 
|  | == dest->text + original_dest_len)) | 
|  | { | 
|  | /* No splice, so we're done.  */ | 
|  | dest->last_token = original_dest_len + 1 + src->last_token; | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* As far as I know, there's no case where inserting a space isn't | 
|  | enough to prevent a splice.  */ | 
|  | internal_error (_("unable to avoid splicing tokens during macro expansion")); | 
|  | } | 
|  |  | 
|  | /* Stringify an argument, and insert it into DEST.  ARG is the text to | 
|  | stringify; it is LEN bytes long.  */ | 
|  |  | 
|  | static void | 
|  | stringify (growable_macro_buffer *dest, const char *arg, int len) | 
|  | { | 
|  | /* Trim initial whitespace from ARG.  */ | 
|  | while (len > 0 && macro_is_whitespace (*arg)) | 
|  | { | 
|  | ++arg; | 
|  | --len; | 
|  | } | 
|  |  | 
|  | /* Trim trailing whitespace from ARG.  */ | 
|  | while (len > 0 && macro_is_whitespace (arg[len - 1])) | 
|  | --len; | 
|  |  | 
|  | /* Insert the string.  */ | 
|  | dest->appendc ('"'); | 
|  | while (len > 0) | 
|  | { | 
|  | /* We could try to handle strange cases here, like control | 
|  | characters, but there doesn't seem to be much point.  */ | 
|  | if (macro_is_whitespace (*arg)) | 
|  | { | 
|  | /* Replace a sequence of whitespace with a single space.  */ | 
|  | dest->appendc (' '); | 
|  | while (len > 1 && macro_is_whitespace (arg[1])) | 
|  | { | 
|  | ++arg; | 
|  | --len; | 
|  | } | 
|  | } | 
|  | else if (*arg == '\\' || *arg == '"') | 
|  | { | 
|  | dest->appendc ('\\'); | 
|  | dest->appendc (*arg); | 
|  | } | 
|  | else | 
|  | dest->appendc (*arg); | 
|  | ++arg; | 
|  | --len; | 
|  | } | 
|  | dest->appendc ('"'); | 
|  | dest->last_token = dest->len; | 
|  | } | 
|  |  | 
|  | /* See macroexp.h.  */ | 
|  |  | 
|  | gdb::unique_xmalloc_ptr<char> | 
|  | macro_stringify (const char *str) | 
|  | { | 
|  | int len = strlen (str); | 
|  | growable_macro_buffer buffer (len); | 
|  |  | 
|  | stringify (&buffer, str, len); | 
|  | buffer.appendc ('\0'); | 
|  |  | 
|  | return buffer.release (); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Expanding macros!  */ | 
|  |  | 
|  |  | 
|  | /* A singly-linked list of the names of the macros we are currently | 
|  | expanding --- for detecting expansion loops.  */ | 
|  | struct macro_name_list { | 
|  | const char *name; | 
|  | struct macro_name_list *next; | 
|  | }; | 
|  |  | 
|  |  | 
|  | /* Return non-zero if we are currently expanding the macro named NAME, | 
|  | according to LIST; otherwise, return zero. | 
|  |  | 
|  | You know, it would be possible to get rid of all the NO_LOOP | 
|  | arguments to these functions by simply generating a new lookup | 
|  | function and baton which refuses to find the definition for a | 
|  | particular macro, and otherwise delegates the decision to another | 
|  | function/baton pair.  But that makes the linked list of excluded | 
|  | macros chained through untyped baton pointers, which will make it | 
|  | harder to debug.  :(  */ | 
|  | static int | 
|  | currently_rescanning (struct macro_name_list *list, const char *name) | 
|  | { | 
|  | for (; list; list = list->next) | 
|  | if (strcmp (name, list->name) == 0) | 
|  | return 1; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Gather the arguments to a macro expansion. | 
|  |  | 
|  | NAME is the name of the macro being invoked.  (It's only used for | 
|  | printing error messages.) | 
|  |  | 
|  | Assume that SRC is the text of the macro invocation immediately | 
|  | following the macro name.  For example, if we're processing the | 
|  | text foo(bar, baz), then NAME would be foo and SRC will be (bar, | 
|  | baz). | 
|  |  | 
|  | If SRC doesn't start with an open paren ( token at all, return | 
|  | false, leave SRC unchanged, and don't set *ARGS_PTR to anything. | 
|  |  | 
|  | If SRC doesn't contain a properly terminated argument list, then | 
|  | raise an error. | 
|  |  | 
|  | For a variadic macro, NARGS holds the number of formal arguments to | 
|  | the macro.  For a GNU-style variadic macro, this should be the | 
|  | number of named arguments.  For a non-variadic macro, NARGS should | 
|  | be -1. | 
|  |  | 
|  | Otherwise, return true and set *ARGS_PTR to a vector of macro | 
|  | buffers referring to the argument texts.  The macro buffers share | 
|  | their text with SRC, and their last_token fields are initialized. | 
|  |  | 
|  | NOTE WELL: if SRC starts with a open paren ( token followed | 
|  | immediately by a close paren ) token (e.g., the invocation looks | 
|  | like "foo()"), we treat that as one argument, which happens to be | 
|  | the empty list of tokens.  The caller should keep in mind that such | 
|  | a sequence of tokens is a valid way to invoke one-parameter | 
|  | function-like macros, but also a valid way to invoke zero-parameter | 
|  | function-like macros.  Eeew. | 
|  |  | 
|  | Consume the tokens from SRC; after this call, SRC contains the text | 
|  | following the invocation.  */ | 
|  |  | 
|  | static bool | 
|  | gather_arguments (const char *name, shared_macro_buffer *src, int nargs, | 
|  | std::vector<shared_macro_buffer> *args_ptr) | 
|  | { | 
|  | shared_macro_buffer tok; | 
|  | std::vector<shared_macro_buffer> args; | 
|  |  | 
|  | /* Does SRC start with an opening paren token?  Read from a copy of | 
|  | SRC, so SRC itself is unaffected if we don't find an opening | 
|  | paren.  */ | 
|  | { | 
|  | shared_macro_buffer temp (src->text, src->len); | 
|  |  | 
|  | if (! get_token (&tok, &temp) | 
|  | || tok.len != 1 | 
|  | || tok.text[0] != '(') | 
|  | return false; | 
|  | } | 
|  |  | 
|  | /* Consume SRC's opening paren.  */ | 
|  | get_token (&tok, src); | 
|  |  | 
|  | for (;;) | 
|  | { | 
|  | int depth; | 
|  |  | 
|  | /* Initialize the next argument.  */ | 
|  | shared_macro_buffer *arg = &args.emplace_back (); | 
|  | set_token (arg, src->text, src->text); | 
|  |  | 
|  | /* Gather the argument's tokens.  */ | 
|  | depth = 0; | 
|  | for (;;) | 
|  | { | 
|  | if (! get_token (&tok, src)) | 
|  | error (_("Malformed argument list for macro `%s'."), name); | 
|  |  | 
|  | /* Is tok an opening paren?  */ | 
|  | if (tok.len == 1 && tok.text[0] == '(') | 
|  | depth++; | 
|  |  | 
|  | /* Is tok is a closing paren?  */ | 
|  | else if (tok.len == 1 && tok.text[0] == ')') | 
|  | { | 
|  | /* If it's a closing paren at the top level, then that's | 
|  | the end of the argument list.  */ | 
|  | if (depth == 0) | 
|  | { | 
|  | /* In the varargs case, the last argument may be | 
|  | missing.  Add an empty argument in this case.  */ | 
|  | if (nargs != -1 && args.size () == nargs - 1) | 
|  | { | 
|  | arg = &args.emplace_back (); | 
|  | set_token (arg, src->text, src->text); | 
|  | } | 
|  |  | 
|  | *args_ptr = std::move (args); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | depth--; | 
|  | } | 
|  |  | 
|  | /* If tok is a comma at top level, then that's the end of | 
|  | the current argument.  However, if we are handling a | 
|  | variadic macro and we are computing the last argument, we | 
|  | want to include the comma and remaining tokens.  */ | 
|  | else if (tok.len == 1 && tok.text[0] == ',' && depth == 0 | 
|  | && (nargs == -1 || args.size () < nargs)) | 
|  | break; | 
|  |  | 
|  | /* Extend the current argument to enclose this token.  If | 
|  | this is the current argument's first token, leave out any | 
|  | leading whitespace, just for aesthetics.  */ | 
|  | if (arg->len == 0) | 
|  | { | 
|  | arg->text = tok.text; | 
|  | arg->len = tok.len; | 
|  | arg->last_token = 0; | 
|  | } | 
|  | else | 
|  | { | 
|  | arg->len = (tok.text + tok.len) - arg->text; | 
|  | arg->last_token = tok.text - arg->text; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | /* The `expand' and `substitute_args' functions both invoke `scan' | 
|  | recursively, so we need a forward declaration somewhere.  */ | 
|  | static void scan (growable_macro_buffer *dest, | 
|  | shared_macro_buffer *src, | 
|  | struct macro_name_list *no_loop, | 
|  | const macro_scope &scope); | 
|  |  | 
|  | /* A helper function for substitute_args. | 
|  |  | 
|  | ARGV is a vector of all the arguments; ARGC is the number of | 
|  | arguments.  IS_VARARGS is true if the macro being substituted is a | 
|  | varargs macro; in this case VA_ARG_NAME is the name of the | 
|  | "variable" argument.  VA_ARG_NAME is ignored if IS_VARARGS is | 
|  | false. | 
|  |  | 
|  | If the token TOK is the name of a parameter, return the parameter's | 
|  | index.  If TOK is not an argument, return -1.  */ | 
|  |  | 
|  | static int | 
|  | find_parameter (const shared_macro_buffer *tok, | 
|  | int is_varargs, const shared_macro_buffer *va_arg_name, | 
|  | int argc, const char * const *argv) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | if (! tok->is_identifier) | 
|  | return -1; | 
|  |  | 
|  | for (i = 0; i < argc; ++i) | 
|  | if (tok->len == strlen (argv[i]) | 
|  | && !memcmp (tok->text, argv[i], tok->len)) | 
|  | return i; | 
|  |  | 
|  | if (is_varargs && tok->len == va_arg_name->len | 
|  | && ! memcmp (tok->text, va_arg_name->text, tok->len)) | 
|  | return argc - 1; | 
|  |  | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | /* Helper function for substitute_args that gets the next token and | 
|  | updates the passed-in state variables.  */ | 
|  |  | 
|  | static void | 
|  | get_next_token_for_substitution (shared_macro_buffer *replacement_list, | 
|  | shared_macro_buffer *token, | 
|  | const char **start, | 
|  | shared_macro_buffer *lookahead, | 
|  | const char **lookahead_start, | 
|  | int *lookahead_valid, | 
|  | bool *keep_going) | 
|  | { | 
|  | if (!*lookahead_valid) | 
|  | *keep_going = false; | 
|  | else | 
|  | { | 
|  | *keep_going = true; | 
|  | *token = *lookahead; | 
|  | *start = *lookahead_start; | 
|  | *lookahead_start = replacement_list->text; | 
|  | *lookahead_valid = get_token (lookahead, replacement_list); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Given the macro definition DEF, being invoked with the actual | 
|  | arguments given by ARGV, substitute the arguments into the | 
|  | replacement list, and store the result in DEST. | 
|  |  | 
|  | IS_VARARGS should be true if DEF is a varargs macro.  In this case, | 
|  | VA_ARG_NAME should be the name of the "variable" argument -- either | 
|  | __VA_ARGS__ for c99-style varargs, or the final argument name, for | 
|  | GNU-style varargs.  If IS_VARARGS is false, this parameter is | 
|  | ignored. | 
|  |  | 
|  | If it is necessary to expand macro invocations in one of the | 
|  | arguments, use LOOKUP_FUNC and LOOKUP_BATON to find the macro | 
|  | definitions, and don't expand invocations of the macros listed in | 
|  | NO_LOOP.  */ | 
|  |  | 
|  | static void | 
|  | substitute_args (growable_macro_buffer *dest, | 
|  | struct macro_definition *def, | 
|  | int is_varargs, const shared_macro_buffer *va_arg_name, | 
|  | const std::vector<shared_macro_buffer> &argv, | 
|  | struct macro_name_list *no_loop, | 
|  | const macro_scope &scope) | 
|  | { | 
|  | /* The token we are currently considering.  */ | 
|  | shared_macro_buffer tok; | 
|  | /* The replacement list's pointer from just before TOK was lexed.  */ | 
|  | const char *original_rl_start; | 
|  | /* We have a single lookahead token to handle token splicing.  */ | 
|  | shared_macro_buffer lookahead; | 
|  | /* The lookahead token might not be valid.  */ | 
|  | int lookahead_valid; | 
|  | /* The replacement list's pointer from just before LOOKAHEAD was | 
|  | lexed.  */ | 
|  | const char *lookahead_rl_start; | 
|  |  | 
|  | /* A macro buffer for the macro's replacement list.  */ | 
|  | shared_macro_buffer replacement_list (def->replacement, | 
|  | strlen (def->replacement)); | 
|  |  | 
|  | gdb_assert (dest->len == 0); | 
|  | dest->last_token = 0; | 
|  |  | 
|  | original_rl_start = replacement_list.text; | 
|  | if (! get_token (&tok, &replacement_list)) | 
|  | return; | 
|  | lookahead_rl_start = replacement_list.text; | 
|  | lookahead_valid = get_token (&lookahead, &replacement_list); | 
|  |  | 
|  | /* __VA_OPT__ state variable.  The states are: | 
|  | 0 - nothing happening | 
|  | 1 - saw __VA_OPT__ | 
|  | >= 2 in __VA_OPT__, the value encodes the parenthesis depth.  */ | 
|  | unsigned vaopt_state = 0; | 
|  |  | 
|  | for (bool keep_going = true; | 
|  | keep_going; | 
|  | get_next_token_for_substitution (&replacement_list, | 
|  | &tok, | 
|  | &original_rl_start, | 
|  | &lookahead, | 
|  | &lookahead_rl_start, | 
|  | &lookahead_valid, | 
|  | &keep_going)) | 
|  | { | 
|  | bool token_is_vaopt = (tok.len == 10 | 
|  | && startswith (tok.text, "__VA_OPT__")); | 
|  |  | 
|  | if (vaopt_state > 0) | 
|  | { | 
|  | if (token_is_vaopt) | 
|  | error (_("__VA_OPT__ cannot appear inside __VA_OPT__")); | 
|  | else if (tok.len == 1 && tok.text[0] == '(') | 
|  | { | 
|  | ++vaopt_state; | 
|  | /* We just entered __VA_OPT__, so don't emit this | 
|  | token.  */ | 
|  | continue; | 
|  | } | 
|  | else if (vaopt_state == 1) | 
|  | error (_("__VA_OPT__ must be followed by an open parenthesis")); | 
|  | else if (tok.len == 1 && tok.text[0] == ')') | 
|  | { | 
|  | --vaopt_state; | 
|  | if (vaopt_state == 1) | 
|  | { | 
|  | /* Done with __VA_OPT__.  */ | 
|  | vaopt_state = 0; | 
|  | /* Don't emit.  */ | 
|  | continue; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* If __VA_ARGS__ is empty, then drop the contents of | 
|  | __VA_OPT__.  */ | 
|  | if (argv.back ().len == 0) | 
|  | continue; | 
|  | } | 
|  | else if (token_is_vaopt) | 
|  | { | 
|  | if (!is_varargs) | 
|  | error (_("__VA_OPT__ is only valid in a variadic macro")); | 
|  | vaopt_state = 1; | 
|  | /* Don't emit this token.  */ | 
|  | continue; | 
|  | } | 
|  |  | 
|  | /* Just for aesthetics.  If we skipped some whitespace, copy | 
|  | that to DEST.  */ | 
|  | if (tok.text > original_rl_start) | 
|  | { | 
|  | dest->appendmem (original_rl_start, tok.text - original_rl_start); | 
|  | dest->last_token = dest->len; | 
|  | } | 
|  |  | 
|  | /* Is this token the stringification operator?  */ | 
|  | if (tok.len == 1 | 
|  | && tok.text[0] == '#') | 
|  | { | 
|  | int arg; | 
|  |  | 
|  | if (!lookahead_valid) | 
|  | error (_("Stringification operator requires an argument.")); | 
|  |  | 
|  | arg = find_parameter (&lookahead, is_varargs, va_arg_name, | 
|  | def->argc, def->argv); | 
|  | if (arg == -1) | 
|  | error (_("Argument to stringification operator must name " | 
|  | "a macro parameter.")); | 
|  |  | 
|  | stringify (dest, argv[arg].text, argv[arg].len); | 
|  |  | 
|  | /* Read one token and let the loop iteration code handle the | 
|  | rest.  */ | 
|  | lookahead_rl_start = replacement_list.text; | 
|  | lookahead_valid = get_token (&lookahead, &replacement_list); | 
|  | } | 
|  | /* Is this token the splicing operator?  */ | 
|  | else if (tok.len == 2 | 
|  | && tok.text[0] == '#' | 
|  | && tok.text[1] == '#') | 
|  | error (_("Stray splicing operator")); | 
|  | /* Is the next token the splicing operator?  */ | 
|  | else if (lookahead_valid | 
|  | && lookahead.len == 2 | 
|  | && lookahead.text[0] == '#' | 
|  | && lookahead.text[1] == '#') | 
|  | { | 
|  | int finished = 0; | 
|  | int prev_was_comma = 0; | 
|  |  | 
|  | /* Note that GCC warns if the result of splicing is not a | 
|  | token.  In the debugger there doesn't seem to be much | 
|  | benefit from doing this.  */ | 
|  |  | 
|  | /* Insert the first token.  */ | 
|  | if (tok.len == 1 && tok.text[0] == ',') | 
|  | prev_was_comma = 1; | 
|  | else | 
|  | { | 
|  | int arg = find_parameter (&tok, is_varargs, va_arg_name, | 
|  | def->argc, def->argv); | 
|  |  | 
|  | if (arg != -1) | 
|  | dest->appendmem (argv[arg].text, argv[arg].len); | 
|  | else | 
|  | dest->appendmem (tok.text, tok.len); | 
|  | } | 
|  |  | 
|  | /* Apply a possible sequence of ## operators.  */ | 
|  | for (;;) | 
|  | { | 
|  | if (! get_token (&tok, &replacement_list)) | 
|  | error (_("Splicing operator at end of macro")); | 
|  |  | 
|  | /* Handle a comma before a ##.  If we are handling | 
|  | varargs, and the token on the right hand side is the | 
|  | varargs marker, and the final argument is empty or | 
|  | missing, then drop the comma.  This is a GNU | 
|  | extension.  There is one ambiguous case here, | 
|  | involving pedantic behavior with an empty argument, | 
|  | but we settle that in favor of GNU-style (GCC uses an | 
|  | option).  If we aren't dealing with varargs, we | 
|  | simply insert the comma.  */ | 
|  | if (prev_was_comma) | 
|  | { | 
|  | if (! (is_varargs | 
|  | && tok.len == va_arg_name->len | 
|  | && !memcmp (tok.text, va_arg_name->text, tok.len) | 
|  | && argv.back ().len == 0)) | 
|  | dest->appendmem (",", 1); | 
|  | prev_was_comma = 0; | 
|  | } | 
|  |  | 
|  | /* Insert the token.  If it is a parameter, insert the | 
|  | argument.  If it is a comma, treat it specially.  */ | 
|  | if (tok.len == 1 && tok.text[0] == ',') | 
|  | prev_was_comma = 1; | 
|  | else | 
|  | { | 
|  | int arg = find_parameter (&tok, is_varargs, va_arg_name, | 
|  | def->argc, def->argv); | 
|  |  | 
|  | if (arg != -1) | 
|  | dest->appendmem (argv[arg].text, argv[arg].len); | 
|  | else | 
|  | dest->appendmem (tok.text, tok.len); | 
|  | } | 
|  |  | 
|  | /* Now read another token.  If it is another splice, we | 
|  | loop.  */ | 
|  | original_rl_start = replacement_list.text; | 
|  | if (! get_token (&tok, &replacement_list)) | 
|  | { | 
|  | finished = 1; | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (! (tok.len == 2 | 
|  | && tok.text[0] == '#' | 
|  | && tok.text[1] == '#')) | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (prev_was_comma) | 
|  | { | 
|  | /* We saw a comma.  Insert it now.  */ | 
|  | dest->appendmem (",", 1); | 
|  | } | 
|  |  | 
|  | dest->last_token = dest->len; | 
|  | if (finished) | 
|  | lookahead_valid = 0; | 
|  | else | 
|  | { | 
|  | /* Set up for the loop iterator.  */ | 
|  | lookahead = tok; | 
|  | lookahead_rl_start = original_rl_start; | 
|  | lookahead_valid = 1; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | /* Is this token an identifier?  */ | 
|  | int substituted = 0; | 
|  | int arg = find_parameter (&tok, is_varargs, va_arg_name, | 
|  | def->argc, def->argv); | 
|  |  | 
|  | if (arg != -1) | 
|  | { | 
|  | /* Expand any macro invocations in the argument text, | 
|  | and append the result to dest.  Remember that scan | 
|  | mutates its source, so we need to scan a new buffer | 
|  | referring to the argument's text, not the argument | 
|  | itself.  */ | 
|  | shared_macro_buffer arg_src (argv[arg].text, argv[arg].len); | 
|  | scan (dest, &arg_src, no_loop, scope); | 
|  | substituted = 1; | 
|  | } | 
|  |  | 
|  | /* If it wasn't a parameter, then just copy it across.  */ | 
|  | if (! substituted) | 
|  | append_tokens_without_splicing (dest, &tok); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (vaopt_state > 0) | 
|  | error (_("Unterminated __VA_OPT__")); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Expand a call to a macro named ID, whose definition is DEF.  Append | 
|  | its expansion to DEST.  SRC is the input text following the ID | 
|  | token.  We are currently rescanning the expansions of the macros | 
|  | named in NO_LOOP; don't re-expand them.  Use LOOKUP_FUNC and | 
|  | LOOKUP_BATON to find definitions for any nested macro references. | 
|  |  | 
|  | Return 1 if we decided to expand it, zero otherwise.  (If it's a | 
|  | function-like macro name that isn't followed by an argument list, | 
|  | we don't expand it.)  If we return zero, leave SRC unchanged.  */ | 
|  | static int | 
|  | expand (const char *id, | 
|  | struct macro_definition *def, | 
|  | growable_macro_buffer *dest, | 
|  | shared_macro_buffer *src, | 
|  | struct macro_name_list *no_loop, | 
|  | const macro_scope &scope) | 
|  | { | 
|  | struct macro_name_list new_no_loop; | 
|  |  | 
|  | /* Create a new node to be added to the front of the no-expand list. | 
|  | This list is appropriate for re-scanning replacement lists, but | 
|  | it is *not* appropriate for scanning macro arguments; invocations | 
|  | of the macro whose arguments we are gathering *do* get expanded | 
|  | there.  */ | 
|  | new_no_loop.name = id; | 
|  | new_no_loop.next = no_loop; | 
|  |  | 
|  | /* What kind of macro are we expanding?  */ | 
|  | if (def->kind == macro_object_like) | 
|  | { | 
|  | shared_macro_buffer replacement_list (def->replacement, | 
|  | strlen (def->replacement)); | 
|  |  | 
|  | scan (dest, &replacement_list, &new_no_loop, scope); | 
|  | return 1; | 
|  | } | 
|  | else if (def->kind == macro_function_like) | 
|  | { | 
|  | shared_macro_buffer va_arg_name; | 
|  | int is_varargs = 0; | 
|  |  | 
|  | if (def->argc >= 1) | 
|  | { | 
|  | if (strcmp (def->argv[def->argc - 1], "...") == 0) | 
|  | { | 
|  | /* In C99-style varargs, substitution is done using | 
|  | __VA_ARGS__.  */ | 
|  | va_arg_name.set_shared ("__VA_ARGS__", strlen ("__VA_ARGS__")); | 
|  | is_varargs = 1; | 
|  | } | 
|  | else | 
|  | { | 
|  | int len = strlen (def->argv[def->argc - 1]); | 
|  |  | 
|  | if (len > 3 | 
|  | && strcmp (def->argv[def->argc - 1] + len - 3, "...") == 0) | 
|  | { | 
|  | /* In GNU-style varargs, the name of the | 
|  | substitution parameter is the name of the formal | 
|  | argument without the "...".  */ | 
|  | va_arg_name.set_shared (def->argv[def->argc - 1], len - 3); | 
|  | is_varargs = 1; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | std::vector<shared_macro_buffer> argv; | 
|  | /* If we couldn't find any argument list, then we don't expand | 
|  | this macro.  */ | 
|  | if (!gather_arguments (id, src, is_varargs ? def->argc : -1, | 
|  | &argv)) | 
|  | return 0; | 
|  |  | 
|  | /* Check that we're passing an acceptable number of arguments for | 
|  | this macro.  */ | 
|  | if (argv.size () != def->argc) | 
|  | { | 
|  | if (is_varargs && argv.size () >= def->argc - 1) | 
|  | { | 
|  | /* Ok.  */ | 
|  | } | 
|  | /* Remember that a sequence of tokens like "foo()" is a | 
|  | valid invocation of a macro expecting either zero or one | 
|  | arguments.  */ | 
|  | else if (! (argv.size () == 1 | 
|  | && argv[0].len == 0 | 
|  | && def->argc == 0)) | 
|  | error (_("Wrong number of arguments to macro `%s' " | 
|  | "(expected %d, got %d)."), | 
|  | id, def->argc, int (argv.size ())); | 
|  | } | 
|  |  | 
|  | /* Note that we don't expand macro invocations in the arguments | 
|  | yet --- we let subst_args take care of that.  Parameters that | 
|  | appear as operands of the stringifying operator "#" or the | 
|  | splicing operator "##" don't get macro references expanded, | 
|  | so we can't really tell whether it's appropriate to macro- | 
|  | expand an argument until we see how it's being used.  */ | 
|  | growable_macro_buffer substituted (0); | 
|  | substitute_args (&substituted, def, is_varargs, &va_arg_name, | 
|  | argv, no_loop, scope); | 
|  |  | 
|  | /* Now `substituted' is the macro's replacement list, with all | 
|  | argument values substituted into it properly.	Re-scan it for | 
|  | macro references, but don't expand invocations of this macro. | 
|  |  | 
|  | We create a new buffer, `substituted_src', which points into | 
|  | `substituted', and scan that.	We can't scan `substituted' | 
|  | itself, since the tokenization process moves the buffer's | 
|  | text pointer around, and we still need to be able to find | 
|  | `substituted's original text buffer after scanning it so we | 
|  | can free it.  */ | 
|  | shared_macro_buffer substituted_src (substituted.text, substituted.len); | 
|  | scan (dest, &substituted_src, &new_no_loop, scope); | 
|  |  | 
|  | return 1; | 
|  | } | 
|  | else | 
|  | internal_error (_("bad macro definition kind")); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* If the single token in SRC_FIRST followed by the tokens in SRC_REST | 
|  | constitute a macro invocation not forbidden in NO_LOOP, append its | 
|  | expansion to DEST and return non-zero.  Otherwise, return zero, and | 
|  | leave DEST unchanged. | 
|  |  | 
|  | SRC_FIRST must be a string built by get_token.  */ | 
|  | static int | 
|  | maybe_expand (growable_macro_buffer *dest, | 
|  | shared_macro_buffer *src_first, | 
|  | shared_macro_buffer *src_rest, | 
|  | struct macro_name_list *no_loop, | 
|  | const macro_scope &scope) | 
|  | { | 
|  | /* Is this token an identifier?  */ | 
|  | if (src_first->is_identifier) | 
|  | { | 
|  | /* Make a null-terminated copy of it, since that's what our | 
|  | lookup function expects.  */ | 
|  | std::string id (src_first->text, src_first->len); | 
|  |  | 
|  | /* If we're currently re-scanning the result of expanding | 
|  | this macro, don't expand it again.  */ | 
|  | if (! currently_rescanning (no_loop, id.c_str ())) | 
|  | { | 
|  | /* Does this identifier have a macro definition in scope?  */ | 
|  | macro_definition *def = standard_macro_lookup (id.c_str (), scope); | 
|  |  | 
|  | if (def && expand (id.c_str (), def, dest, src_rest, no_loop, scope)) | 
|  | return 1; | 
|  | } | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Expand macro references in SRC, appending the results to DEST. | 
|  | Assume we are re-scanning the result of expanding the macros named | 
|  | in NO_LOOP, and don't try to re-expand references to them.  */ | 
|  |  | 
|  | static void | 
|  | scan (growable_macro_buffer *dest, | 
|  | shared_macro_buffer *src, | 
|  | struct macro_name_list *no_loop, | 
|  | const macro_scope &scope) | 
|  | { | 
|  |  | 
|  | for (;;) | 
|  | { | 
|  | shared_macro_buffer tok; | 
|  | const char *original_src_start = src->text; | 
|  |  | 
|  | /* Find the next token in SRC.  */ | 
|  | if (! get_token (&tok, src)) | 
|  | break; | 
|  |  | 
|  | /* Just for aesthetics.  If we skipped some whitespace, copy | 
|  | that to DEST.  */ | 
|  | if (tok.text > original_src_start) | 
|  | { | 
|  | dest->appendmem (original_src_start, tok.text - original_src_start); | 
|  | dest->last_token = dest->len; | 
|  | } | 
|  |  | 
|  | if (! maybe_expand (dest, &tok, src, no_loop, scope)) | 
|  | /* We didn't end up expanding tok as a macro reference, so | 
|  | simply append it to dest.  */ | 
|  | append_tokens_without_splicing (dest, &tok); | 
|  | } | 
|  |  | 
|  | /* Just for aesthetics.  If there was any trailing whitespace in | 
|  | src, copy it to dest.  */ | 
|  | if (src->len) | 
|  | { | 
|  | dest->appendmem (src->text, src->len); | 
|  | dest->last_token = dest->len; | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | gdb::unique_xmalloc_ptr<char> | 
|  | macro_expand (const char *source, const macro_scope &scope) | 
|  | { | 
|  | shared_macro_buffer src (source, strlen (source)); | 
|  |  | 
|  | growable_macro_buffer dest (0); | 
|  | dest.last_token = 0; | 
|  |  | 
|  | scan (&dest, &src, 0, scope); | 
|  |  | 
|  | dest.appendc ('\0'); | 
|  |  | 
|  | return dest.release (); | 
|  | } | 
|  |  | 
|  |  | 
|  | gdb::unique_xmalloc_ptr<char> | 
|  | macro_expand_once (const char *source, const macro_scope &scope) | 
|  | { | 
|  | error (_("Expand-once not implemented yet.")); | 
|  | } | 
|  |  | 
|  | gdb::unique_xmalloc_ptr<char> | 
|  | macro_expand_next (const char **lexptr, const macro_scope &scope) | 
|  | { | 
|  | shared_macro_buffer tok; | 
|  |  | 
|  | /* Set up SRC to refer to the input text, pointed to by *lexptr.  */ | 
|  | shared_macro_buffer src (*lexptr, strlen (*lexptr)); | 
|  |  | 
|  | /* Set up DEST to receive the expansion, if there is one.  */ | 
|  | growable_macro_buffer dest (0); | 
|  | dest.last_token = 0; | 
|  |  | 
|  | /* Get the text's first preprocessing token.  */ | 
|  | if (! get_token (&tok, &src)) | 
|  | return nullptr; | 
|  |  | 
|  | /* If it's a macro invocation, expand it.  */ | 
|  | if (maybe_expand (&dest, &tok, &src, 0, scope)) | 
|  | { | 
|  | /* It was a macro invocation!  Package up the expansion as a | 
|  | null-terminated string and return it.  Set *lexptr to the | 
|  | start of the next token in the input.  */ | 
|  | dest.appendc ('\0'); | 
|  | *lexptr = src.text; | 
|  | return dest.release (); | 
|  | } | 
|  | else | 
|  | { | 
|  | /* It wasn't a macro invocation.  */ | 
|  | return nullptr; | 
|  | } | 
|  | } |