/* C preprocessor macro expansion for GDB.
- Copyright (C) 2002-2014 Free Software Foundation, Inc.
+ Copyright (C) 2002-2017 Free Software Foundation, Inc.
Contributed by Red Hat, Inc.
This file is part of GDB.
#include "bcache.h"
#include "macrotab.h"
#include "macroexp.h"
-#include "gdb_assert.h"
#include "c-lang.h"
/* Zero if TEXT can be safely realloc'ed (i.e., it's its own malloc
block). Non-zero if TEXT is actually pointing into the middle of
- some other block, and we shouldn't reallocate it. */
- int shared;
+ some other block, or to a string literal, and we shouldn't
+ reallocate it. */
+ bool shared;
/* For detecting token splicing.
else
b->text = NULL;
b->len = 0;
- b->shared = 0;
+ b->shared = false;
b->last_token = -1;
}
/* Set the macro buffer *BUF to refer to the LEN bytes at ADDR, as a
shared substring. */
+
static void
-init_shared_buffer (struct macro_buffer *buf, char *addr, int len)
+init_shared_buffer (struct macro_buffer *buf, const char *addr, int len)
{
- buf->text = addr;
+ /* The function accept a "const char *" addr so that clients can
+ pass in string literals without casts. */
+ buf->text = (char *) addr;
buf->len = len;
- buf->shared = 1;
+ buf->shared = true;
buf->size = 0;
buf->last_token = -1;
}
while (b->size <= n)
b->size *= 2;
- b->text = xrealloc (b->text, b->size);
+ b->text = (char *) xrealloc (b->text, b->size);
}
/* Append the LEN bytes at ADDR to the buffer B. */
static void
-appendmem (struct macro_buffer *b, char *addr, int len)
+appendmem (struct macro_buffer *b, const char *addr, int len)
{
int new_len = b->len + len;
args_len = 0;
args_size = 6;
- args = (struct macro_buffer *) xmalloc (sizeof (*args) * args_size);
+ args = XNEWVEC (struct macro_buffer, args_size);
for (;;)
{
if (args_len >= args_size)
{
args_size *= 2;
- args = xrealloc (args, sizeof (*args) * args_size);
+ args = XRESIZEVEC (struct macro_buffer, args, args_size);
}
/* Initialize the next argument. */
if (args_len >= args_size)
{
args_size++;
- args = xrealloc (args, sizeof (*args) * args_size);
+ args = XRESIZEVEC (struct macro_buffer, args,
+ args_size);
}
arg = &args[args_len++];
set_token (arg, src->text, src->text);
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 (struct macro_buffer *replacement_list,
+ struct macro_buffer *token,
+ char **start,
+ struct macro_buffer *lookahead,
+ 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 ARGC and ARGV, substitute the arguments into the
replacement list, and store the result in DEST.
lexed. */
char *lookahead_rl_start;
- init_shared_buffer (&replacement_list, (char *) def->replacement,
+ init_shared_buffer (&replacement_list, def->replacement,
strlen (def->replacement));
gdb_assert (dest->len == 0);
lookahead_rl_start = replacement_list.text;
lookahead_valid = get_token (&lookahead, &replacement_list);
- for (;;)
+ /* __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
+ && strncmp (tok.text, "__VA_OPT__", 10) == 0);
+
+ 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[argc - 1].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)
if (! substituted)
append_tokens_without_splicing (dest, &tok);
}
-
- if (! lookahead_valid)
- break;
-
- tok = lookahead;
- original_rl_start = lookahead_rl_start;
-
- lookahead_rl_start = replacement_list.text;
- lookahead_valid = get_token (&lookahead, &replacement_list);
}
+
+ if (vaopt_state > 0)
+ error (_("Unterminated __VA_OPT__"));
}
{
struct macro_buffer replacement_list;
- init_shared_buffer (&replacement_list, (char *) def->replacement,
+ init_shared_buffer (&replacement_list, def->replacement,
strlen (def->replacement));
scan (dest, &replacement_list, &new_no_loop, lookup_func, lookup_baton);
substitution parameter is the name of the formal
argument without the "...". */
init_shared_buffer (&va_arg_name,
- (char *) def->argv[def->argc - 1],
+ def->argv[def->argc - 1],
len - 3);
is_varargs = 1;
}
{
/* Make a null-terminated copy of it, since that's what our
lookup function expects. */
- char *id = xmalloc (src_first->len + 1);
+ char *id = (char *) xmalloc (src_first->len + 1);
struct cleanup *back_to = make_cleanup (xfree, id);
memcpy (id, src_first->text, src_first->len);
struct macro_buffer src, dest;
struct cleanup *back_to;
- init_shared_buffer (&src, (char *) source, strlen (source));
+ init_shared_buffer (&src, source, strlen (source));
init_buffer (&dest, 0);
dest.last_token = 0;
struct cleanup *back_to;
/* Set up SRC to refer to the input text, pointed to by *lexptr. */
- init_shared_buffer (&src, (char *) *lexptr, strlen (*lexptr));
+ init_shared_buffer (&src, *lexptr, strlen (*lexptr));
/* Set up DEST to receive the expansion, if there is one. */
init_buffer (&dest, 0);