diff options
author | Eric Blake <ebb9@byu.net> | 2007-10-25 22:05:15 -0600 |
---|---|---|
committer | Eric Blake <ebb9@byu.net> | 2007-12-17 09:42:19 -0700 |
commit | b0daa4c96f734aab4d450594f64bc15d4321fd60 (patch) | |
tree | 0d29f40a8ec260b5a0dd1176c8d5f230e8b73870 | |
parent | 687dd577f66622e0b69a8cd03b7e5e76fa546c52 (diff) | |
download | m4-b0daa4c96f734aab4d450594f64bc15d4321fd60.tar.gz |
Stage 8: extend life of references into argv
-rw-r--r-- | doc/m4.texinfo | 47 | ||||
-rw-r--r-- | src/input.c | 18 | ||||
-rw-r--r-- | src/m4.h | 12 | ||||
-rw-r--r-- | src/macro.c | 325 |
4 files changed, 329 insertions, 73 deletions
diff --git a/doc/m4.texinfo b/doc/m4.texinfo index 93b76963..64262f5b 100644 --- a/doc/m4.texinfo +++ b/doc/m4.texinfo @@ -535,6 +535,7 @@ below as taking effect after any files that occurred earlier in the command line. The argument @option{--} is a marker to denote the end of options. +@comment FIXME option -d+f only works on head right now... With short options, options that do not take arguments may be combined into a single command line argument with subsequent options, options with mandatory arguments may be provided either as a single command line @@ -2637,6 +2638,46 @@ ifelse(`foo', `bar', `3', `gnu', `gnats', `6', `7', `8') @ignore @comment Stress tests, not worth documenting. + +@comment Ensure that references compared to strings work regardless of +@comment similar prefixes. +@example +define(`e', `$@@')define(`long', `01234567890123456789') +@result{} +ifelse(long, `01234567890123456789', `yes', `no') +@result{}yes +ifelse(`01234567890123456789', long, `yes', `no') +@result{}yes +ifelse(long, `01234567890123456789-', `yes', `no') +@result{}no +ifelse(`01234567890123456789-', long, `yes', `no') +@result{}no +ifelse(e(long), `01234567890123456789', `yes', `no') +@result{}yes +ifelse(`01234567890123456789', e(long), `yes', `no') +@result{}yes +ifelse(e(long), `01234567890123456789-', `yes', `no') +@result{}no +ifelse(`01234567890123456789-', e(long), `yes', `no') +@result{}no +ifelse(-e(long), `-01234567890123456789', `yes', `no') +@result{}yes +ifelse(-`01234567890123456789', -e(long), `yes', `no') +@result{}yes +ifelse(-e(long), `-01234567890123456789-', `yes', `no') +@result{}no +ifelse(`-01234567890123456789-', -e(long), `yes', `no') +@result{}no +ifelse(-e(long)-, `-01234567890123456789-', `yes', `no') +@result{}yes +ifelse(-`01234567890123456789-', -e(long)-, `yes', `no') +@result{}yes +ifelse(-e(long)-, `-01234567890123456789', `yes', `no') +@result{}no +ifelse(`-01234567890123456789', -e(long)-, `yes', `no') +@result{}no +@end example + @comment It would be nice to pass builtin tokens through ifelse, m4wrap, @comment user macros; hence the fixmes. @example @@ -3234,6 +3275,7 @@ option @option{--nesting-limit} (or @option{-L}, @pxref{Limits control, @option{-t}) can be used to invoke @code{traceon(@var{name})} before parsing input. +@comment The explicit -dp neutralizes the testsuite default of -d. @comment options: -dp -L3 -tifelse @comment status: 1 @example @@ -3396,6 +3438,7 @@ are reset to the default of @samp{aeq}. The expansion of @code{debugmode} is void. @end deffn +@comment The explicit -dp neutralizes the testsuite default of -d. @comment options: -dp @example $ @kbd{m4} @@ -3444,7 +3487,7 @@ The expansion of @code{debugfile} is void. @end deffn @example -$ @kbd{m4} +$ @kbd{m4 -d} traceon(`divnum') @result{} divnum(`extra') @@ -7067,7 +7110,7 @@ repeating the side effects of unquoted list elements will have any detrimental effects. @example -$ @kbd{m4 -I examples} +$ @kbd{m4 -d -I examples} include(`foreach2.m4') @result{} include(`foreachq2.m4') diff --git a/src/input.c b/src/input.c index f2f14e95..2fb5996e 100644 --- a/src/input.c +++ b/src/input.c @@ -215,6 +215,7 @@ make_text_link (struct obstack *obs, token_chain **start, token_chain **end) chain->next = NULL; chain->str = str; chain->len = len; + chain->level = -1; chain->argv = NULL; chain->index = 0; chain->flatten = false; @@ -317,9 +318,10 @@ push_string_init (void) | current_input stack and TOKEN lives in temporary storage. Allows | | gathering input from multiple locations, rather than copying | | everything consecutively onto the input stack. Must be called | -| between push_string_init and push_string_finish. | +| between push_string_init and push_string_finish. Return true only | +| if LEVEL is non-negative, and a reference was created to TOKEN. | `-------------------------------------------------------------------*/ -void +bool push_token (token_data *token, int level) { token_chain *chain; @@ -328,7 +330,7 @@ push_token (token_data *token, int level) // TODO - also accept TOKEN_COMP chains assert (TOKEN_DATA_TYPE (token) == TOKEN_TEXT); if (TOKEN_DATA_LEN (token) == 0) - return; + return false; if (next->type == INPUT_STRING) { @@ -352,9 +354,13 @@ push_token (token_data *token, int level) else chain->str = TOKEN_DATA_TEXT (token); chain->len = TOKEN_DATA_LEN (token); + chain->level = -1; + // chain->level = level; chain->argv = NULL; chain->index = 0; chain->flatten = false; + return false; // no reference exists when text is copied + // return true; } /*-------------------------------------------------------------------. @@ -468,7 +474,9 @@ pop_input (bool cleanup) assert (!"implemented yet"); abort (); } - chain = chain->next; + if (chain->level >= 0) + adjust_refcount (chain->level, false); + isp->u.u_c.chain = chain = chain->next; } break; @@ -759,6 +767,8 @@ next_char_1 (void) assert (!"implemented yet"); abort (); } + if (chain->level >= 0) + adjust_refcount (chain->level, false); isp->u.u_c.chain = chain = chain->next; } break; @@ -88,14 +88,6 @@ typedef struct string STRING; #define obstack_chunk_alloc xmalloc #define obstack_chunk_free free -/* glibc's obstack left out the ability to suspend and resume growth - of an object on the stack. Reopen OBJECT (previously returned by - obstack_alloc or obstack_finish) with SIZE for additional growth, - freeing all objects that occur later in the stack. */ -#define obstack_regrow(OBS, OBJECT, SIZE) \ - (obstack_free (OBS, (char *) (OBJECT) + (SIZE)), \ - (OBS)->object_base = (char *) (OBJECT)) - /* These must come first. */ typedef struct input_block input_block; typedef struct token_data token_data; @@ -286,6 +278,7 @@ struct token_chain token_chain *next; /* Pointer to next link of chain. */ const char *str; /* NUL-terminated string if text, else NULL. */ size_t len; /* Length of str, else 0. */ + int level; /* Expansion level of link content, or -1. */ macro_arguments *argv;/* Reference to earlier $@. */ unsigned int index; /* Argument index within argv. */ bool flatten; /* True to treat builtins as text. */ @@ -350,7 +343,7 @@ void skip_line (const char *); void push_file (FILE *, const char *, bool); void push_macro (builtin_func *); struct obstack *push_string_init (void); -void push_token (token_data *, int); +bool push_token (token_data *, int); const input_block *push_string_finish (void); void push_wrapup (const char *); bool pop_wrapup (void); @@ -460,6 +453,7 @@ macro_arguments *make_argv_ref (macro_arguments *, const char *, size_t, bool, bool); void push_arg (struct obstack *, macro_arguments *, unsigned int); void push_args (struct obstack *, macro_arguments *, bool, bool); +size_t adjust_refcount (int, bool); /* File: builtin.c --- builtins. */ diff --git a/src/macro.c b/src/macro.c index 873e82ca..84d53964 100644 --- a/src/macro.c +++ b/src/macro.c @@ -24,6 +24,10 @@ #include "m4.h" +#ifndef DEBUG_MACRO +# define DEBUG_MACRO 0 +#endif /* DEBUG_MACRO */ + /* Opaque structure describing all arguments to a macro, including the macro name at index 0. */ struct macro_arguments @@ -32,9 +36,9 @@ struct macro_arguments arraylen since the array can refer to multiple arguments via a single $@ reference. */ unsigned int argc; - /* False unless the macro expansion refers to $@, determines whether - this object can be freed at end of macro expansion or must wait - until next byte read from file. */ + /* False unless the macro expansion refers to $@; determines whether + this object can be freed immediately at the end of expand_macro, + or must wait until all recursion has completed. */ bool_bitfield inuse : 1; /* False if all arguments are just text or func, true if this argv refers to another one. */ @@ -51,32 +55,134 @@ struct macro_arguments token_data *array[FLEXIBLE_ARRAY_MEMBER]; }; +/* Internal struct to maintain list of argument stacks. Within a + recursion level, consecutive macros can share a stack, but distinct + recursion levels need different stacks since the nested macro is + interrupting the argument collection of the outer level. Note that + a reference can live as long as the expansion containing the + reference can participate as an argument in a future macro call. + + Therefore, we implement a reference counter for each expansion + level, tracking how many references exist into the obstack, as well + as associate a level with each reference. Of course, expand_macro + is actively using argv, so it increments the refcount on entry and + decrements it on exit. Additionally, any time the input engine is + handed a reference that it does not inline, it increases the + refcount in push_token, then decreases it in pop_input once the + reference has been rescanned. Finally, when the input engine hands + a reference back to expand_argument, the refcount increases, which + is then cleaned up at the end of expand_macro. + + For a running example, consider this input: + + define(a,A)define(b,`a(`$1')')define(c,$*)dnl + define(x,`a(1)`'c($@')define(y,`$@)')dnl + x(a(`b')``a'')y(`b')(`a') + => AAaA + + Assuming all arguments are large enough to exceed the inlining + thresholds of the input engine, the interesting sequence of events + is as follows: + + stacks[0] refs stacks[1] refs + after second dnl ends: `' 0 `' 0 + expand_macro for x, level 0: `' 1 `' 0 + expand_macro for a, level 1: `' 1 `' 1 + after collect_arguments for a: `' 1 `b' 1 + push `A' to input stack: `' 1 `b' 1 + exit expand_macro for a: `' 1 `' 0 + after collect_arguments for x: `A`a'' 1 `' 0 + push `a(1)`'c(' to input stack: `A`a'' 1 `' 0 + push_token saves $@(x) ref: `A`a'' 2 `' 0 + exit expand_macro for x: `A`a'' 1 `' 0 + expand_macro for a, level 0: `A`a'' 2 `' 0 + after collect_arguments for a: `A`a''`1' 2 `' 0 + push `A' to input stack: `A`a''`1' 2 `' 0 + exit expand_macro for a: `A`a'' 1 `' 0 + output `A': `A`a'' 1 `' 0 + expand_macro for c, level 0: `A`a'' 2 `' 0 + expand_argument gets $@(x) ref: `A`a''`$@(x)' 3 `' 0 + pop_input ends $@(x) ref: `A`a''`$@(x)' 2 `' 0 + expand_macro for y, level 1: `A`a''`$@(x)' 2 `' 1 + after collect_arguments for y: `A`a''`$@(x)' 2 `b' 1 + push_token saves $@(y) ref: `A`a''`$@(x)' 2 `b' 2 + push `)' to input stack: `A`a''`$@(x)' 2 `b' 2 + exit expand_macro for y: `A`a''`$@(x)' 2 `b' 1 + expand_argument gets $@(y) ref: `A`a''`$@(x)$@(y)' 2 `b' 2 + pop_input ends $@(y) ref: `A`a''`$@(x)$@(y)' 2 `b' 1 + after collect_arguments for c: `A`a''`$@(x)$@(y)' 2 `b' 1 + push_token saves $*(c) ref: `A`a''`$@(x)$@(y)' 3 `b' 2 + expand_macro frees $@(x) ref: `A`a''`$@(x)$@(y)' 2 `b' 2 + expand_macro frees $@(y) ref: `A`a''`$@(x)$@(y)' 2 `b' 1 + exit expand_macro for c: `A`a''`$@(x)$@(y)' 1 `b' 1 + output `Aa': `A`a''`$@(x)$@(y)' 0 `b' 1 + pop_input ends $*(c)$@(x) ref: `' 0 `b' 1 + expand_macro for b, level 0: `' 1 `b' 1 + pop_input ends $*(c)$@(y) ref: `' 1 `' 0 + after collect_arguments for b: `a' 1 `' 0 + push `a(`' to input stack: `a' 1 `' 0 + push_token saves $1(b) ref: `a' 2 `' 0 + push `')' to input stack: `a' 2 `' 0 + exit expand_macro for b: `a' 1 `' 0 + expand_macro for a, level 0 : `a' 2 `' 0 + expand_argument gets $1(b) ref: `a'`$1(b)' 3 `' 0 + pop_input ends $1(b) ref: `a'`$1(b)' 2 `' 0 + after collect_arguments for a: `a'`$1(b)' 2 `' 0 + push `A' to input stack: `a'`$1(b)' 2 `' 0 + expand_macro frees $1(b) ref: `a'`$1(b)' 1 `' 0 + exit expand_macro for a: `' 0 `' 0 + output `A': `' 0 `' 0 + + An obstack is only completely cleared when its refcount reaches + zero. However, as an optimization, expand_macro also frees + anything that it added to the obstack if no additional references + were added at the current expansion level, to reduce the amount of + memory left on the obstack while waiting for refcounts to drop. +*/ +struct macro_arg_stacks +{ + size_t refcount; /* Number of active $@ references at this level. */ + size_t argcount; /* Number of argv at this level. */ + struct obstack *args; /* Content of arguments. */ + struct obstack *argv; /* Argv pointers into args. */ + void *args_base; /* Location for clearing the args obstack. */ + void *argv_base; /* Location for clearing the argv obstack. */ +}; + +typedef struct macro_arg_stacks macro_arg_stacks; + static void expand_macro (symbol *); static bool expand_token (struct obstack *, token_type, token_data *, int, bool); +/* Array of stacks, one element per macro recursion level. */ +static macro_arg_stacks *stacks; + +/* Current size of stacks array. */ +static size_t stacks_count; + /* Current recursion level in expand_macro (). */ int expansion_level = 0; /* The number of the current call of expand_macro (). */ static int macro_call_id = 0; -/* The shared stack of collected arguments for macro calls; as each - argument is collected, it is finished and its location stored in - argv_stack. This stack can be used simultaneously by multiple - macro calls, using obstack_regrow to handle partial objects - embedded in the stack. */ -static struct obstack arg_stack; - -/* The shared stack of pointers to collected arguments for macro - calls. This stack can be used simultaneously by multiple macro - calls, using obstack_regrow to handle partial objects embedded in - the stack. */ -static struct obstack argv_stack; - /* The empty string token. */ static token_data empty_token; +#if DEBUG_MACRO +/* True if significant changes to stacks should be printed to the + trace stream. Primarily useful for debugging $@ ref memory leaks, + and controlled by M4_DEBUG_MACRO environment variable. */ +static int debug_macro_level; +#else +# define debug_macro_level 0 +#endif /* !DEBUG_MACRO */ +#define PRINT_ARGCOUNT_CHANGES 1 +#define PRINT_REFCOUNT_INCREASE 2 +#define PRINT_REFCOUNT_DECREASE 4 + + /*----------------------------------------------------------------. | This function reads all input, and expands each token, one at a | | time. | @@ -88,9 +194,13 @@ expand_input (void) token_type t; token_data td; int line; + size_t i; - obstack_init (&arg_stack); - obstack_init (&argv_stack); +#if DEBUG_MACRO + const char *s = getenv ("M4_DEBUG_MACRO"); + if (s) + debug_macro_level = strtol (s, NULL, 0); +#endif /* DEBUG_MACRO */ TOKEN_DATA_TYPE (&empty_token) = TOKEN_TEXT; TOKEN_DATA_TEXT (&empty_token) = ""; @@ -102,8 +212,20 @@ expand_input (void) while ((t = next_token (&td, &line, NULL)) != TOKEN_EOF) expand_token ((struct obstack *) NULL, t, &td, line, true); - obstack_free (&arg_stack, NULL); - obstack_free (&argv_stack, NULL); + for (i = 0; i < stacks_count; i++) + { + assert (stacks[i].refcount == 0 && stacks[i].argcount == 0); + if (stacks[i].args) + { + obstack_free (stacks[i].args, NULL); + free (stacks[i].args); + obstack_free (stacks[i].argv, NULL); + free (stacks[i].argv); + } + } + free (stacks); + stacks = NULL; + stacks_count = 0; } @@ -317,7 +439,8 @@ expand_argument (struct obstack *obs, token_data *argp, const char *caller) `-------------------------------------------------------------------------*/ static macro_arguments * -collect_arguments (symbol *sym, struct obstack *arguments) +collect_arguments (symbol *sym, struct obstack *arguments, + struct obstack *argv_stack) { token_data td; token_data *tdp; @@ -333,7 +456,7 @@ collect_arguments (symbol *sym, struct obstack *arguments) args.argv0_len = strlen (args.argv0); args.quote_age = quote_age (); args.arraylen = 0; - obstack_grow (&argv_stack, &args, offsetof (macro_arguments, array)); + obstack_grow (argv_stack, &args, offsetof (macro_arguments, array)); if (peek_token () == TOKEN_OPEN) { @@ -347,7 +470,7 @@ collect_arguments (symbol *sym, struct obstack *arguments) tdp = &empty_token; else tdp = (token_data *) obstack_copy (arguments, &td, sizeof td); - obstack_ptr_grow (&argv_stack, tdp); + obstack_ptr_grow (argv_stack, tdp); args.arraylen++; args.argc++; /* Be conservative - any change in quoting while collecting @@ -360,7 +483,7 @@ collect_arguments (symbol *sym, struct obstack *arguments) } while (more_args); } - argv = (macro_arguments *) obstack_finish (&argv_stack); + argv = (macro_arguments *) obstack_finish (argv_stack); argv->argc = args.argc; if (args.quote_age != quote_age ()) argv->quote_age = 0; @@ -409,15 +532,14 @@ call_macro (symbol *sym, int argc, macro_arguments *argv, static void expand_macro (symbol *sym) { - void *arg_base = NULL; /* Base of arg_stack on entry. */ - void *argv_base = NULL; /* Base of argv_stack on entry. */ - unsigned int arg_size; /* Size of arg_stack on entry. */ - unsigned int argv_size; /* Size of argv_stack on entry. */ - macro_arguments *argv; - struct obstack *expansion; - const input_block *expanded; - bool traced; - int my_call_id; + void *args_base; /* Base of stacks[i].args on entry. */ + void *argv_base; /* Base of stacks[i].argv on entry. */ + macro_arguments *argv; /* Arguments to the called macro. */ + struct obstack *expansion; /* Collects the macro's expansion. */ + const input_block *expanded; /* The resulting expansion, for tracing. */ + bool traced; /* True if this macro is traced. */ + int my_call_id; /* Sequence id for this macro. */ + int level = expansion_level; /* Expansion level of this macro. */ /* Report errors at the location where the open parenthesis (if any) was found, but after expansion, restore global state back to the @@ -430,6 +552,33 @@ expand_macro (symbol *sym) const char *loc_close_file; int loc_close_line; + /* Obstack preparation. */ + if (level >= stacks_count) + { + size_t old_count = stacks_count; + stacks = (macro_arg_stacks *) x2nrealloc (stacks, &stacks_count, + sizeof *stacks); + memset (&stacks[old_count], 0, + sizeof *stacks * (stacks_count - old_count)); + } + if (!stacks[level].args) + { + assert (!stacks[level].refcount); + stacks[level].args = xmalloc (sizeof (struct obstack)); + stacks[level].argv = xmalloc (sizeof (struct obstack)); + obstack_init (stacks[level].args); + obstack_init (stacks[level].argv); + stacks[level].args_base = obstack_finish (stacks[level].args); + stacks[level].argv_base = obstack_finish (stacks[level].argv); + } + assert (obstack_object_size (stacks[level].args) == 0 + && obstack_object_size (stacks[level].argv) == 0); + args_base = obstack_finish (stacks[level].args); + argv_base = obstack_finish (stacks[level].argv); + adjust_refcount (level, true); + stacks[level].argcount++; + + /* Prepare for argument collection. */ SYMBOL_PENDING_EXPANSIONS (sym)++; expansion_level++; if (nesting_limit > 0 && expansion_level > nesting_limit) @@ -441,18 +590,12 @@ expand_macro (symbol *sym) my_call_id = macro_call_id; traced = (debug_level & DEBUG_TRACE_ALL) || SYMBOL_TRACED (sym); - - arg_size = obstack_object_size (&arg_stack); - argv_size = obstack_object_size (&argv_stack); - arg_base = obstack_finish (&arg_stack); - if (0 < argv_size) - argv_base = obstack_finish (&argv_stack); - if (traced && (debug_level & DEBUG_TRACE_CALL)) trace_prepre (SYMBOL_NAME (sym), my_call_id); - argv = collect_arguments (sym, &arg_stack); + argv = collect_arguments (sym, stacks[level].args, stacks[level].argv); + /* The actual macro call. */ loc_close_file = current_file; loc_close_line = current_line; current_file = loc_open_file; @@ -468,6 +611,7 @@ expand_macro (symbol *sym) if (traced) trace_post (SYMBOL_NAME (sym), my_call_id, argv, expanded); + /* Cleanup. */ current_file = loc_close_file; current_line = loc_close_line; @@ -477,15 +621,58 @@ expand_macro (symbol *sym) if (SYMBOL_DELETED (sym)) free_symbol (sym); - // TODO pay attention to argv->inuse, in case someone is depending on $@ - if (0 < arg_size) - obstack_regrow (&arg_stack, arg_base, arg_size); - else - obstack_free (&arg_stack, arg_base); - if (0 < argv_size) - obstack_regrow (&argv_stack, argv_base, argv_size); - else - obstack_free (&argv_stack, argv); + /* If argv contains references, those refcounts can be reduced now. */ + // TODO - support references in argv + + /* We no longer need argv, so reduce the refcount. Additionally, if + no other references to argv were created, we can free our portion + of the obstack, although we must leave earlier content alone. A + refcount of 0 implies that adjust_refcount already freed the + entire stack. */ + if (adjust_refcount (level, false)) + { + if (argv->inuse) + { + if (debug_macro_level & PRINT_ARGCOUNT_CHANGES) + xfprintf (debug, "m4debug: -%d- `%s' in use, level=%d, " + "refcount=%zu, argcount=%zu\n", my_call_id, argv->argv0, + level, stacks[level].refcount, stacks[level].argcount); + } + else + { + obstack_free (stacks[level].args, args_base); + obstack_free (stacks[level].argv, argv_base); + stacks[level].argcount--; + } + } +} + +/* Adjust the refcount of argument stack LEVEL. If INCREASE, then + increase the count, otherwise decrease the count and clear the + entire stack if the new count is zero. Return the new + refcount. */ +size_t +adjust_refcount (int level, bool increase) +{ + assert (level >= 0 && level < stacks_count && stacks[level].args); + assert (increase || stacks[level].refcount); + if (increase) + stacks[level].refcount++; + else if (--stacks[level].refcount == 0) + { + obstack_free (stacks[level].args, stacks[level].args_base); + obstack_free (stacks[level].argv, stacks[level].argv_base); + if ((debug_macro_level & PRINT_ARGCOUNT_CHANGES) + && stacks[level].argcount > 1) + xfprintf (debug, "m4debug: -%d- freeing %zu args, level=%d\n", + macro_call_id, stacks[level].argcount, level); + stacks[level].argcount = 0; + } + if (debug_macro_level + & (increase ? PRINT_REFCOUNT_INCREASE : PRINT_REFCOUNT_DECREASE)) + xfprintf (debug, "m4debug: level %d refcount=%d\n", level, + stacks[level].refcount); + return stacks[level].refcount; } @@ -525,6 +712,23 @@ arg_token (macro_arguments *argv, unsigned int index) return token; } +/* Mark ARGV as being in use, along with any $@ references that it + wraps. */ +static void +arg_mark (macro_arguments *argv) +{ + argv->inuse = true; + if (argv->has_ref) + { + // TODO for now we support only a single-length $@ chain... + assert (argv->arraylen == 1 + && TOKEN_DATA_TYPE (argv->array[0]) == TOKEN_COMP + && !argv->array[0]->u.chain->next + && !argv->array[0]->u.chain->str); + argv->array[0]->u.chain->argv->inuse = true; + } +} + /* Given ARGV, return how many arguments it refers to. */ unsigned int arg_argc (macro_arguments *argv) @@ -661,8 +865,9 @@ make_argv_ref (macro_arguments *argv, const char *argv0, size_t argv0_len, token_data *token; token_chain *chain; unsigned int index = skip ? 2 : 1; + struct obstack *obs = stacks[expansion_level - 1].argv; - assert (obstack_object_size (&argv_stack) == 0); + assert (obstack_object_size (obs) == 0); /* When making a reference through a reference, point to the original if possible. */ if (argv->has_ref) @@ -678,17 +883,17 @@ make_argv_ref (macro_arguments *argv, const char *argv0, size_t argv0_len, if (argv->argc <= index) { new_argv = (macro_arguments *) - obstack_alloc (&argv_stack, offsetof (macro_arguments, array)); + obstack_alloc (obs, offsetof (macro_arguments, array)); new_argv->arraylen = 0; new_argv->has_ref = false; } else { new_argv = (macro_arguments *) - obstack_alloc (&argv_stack, + obstack_alloc (obs, offsetof (macro_arguments, array) + sizeof token); - token = (token_data *) obstack_alloc (&argv_stack, sizeof *token); - chain = (token_chain *) obstack_alloc (&argv_stack, sizeof *chain); + token = (token_data *) obstack_alloc (obs, sizeof *token); + chain = (token_chain *) obstack_alloc (obs, sizeof *chain); new_argv->arraylen = 1; new_argv->array[0] = token; new_argv->has_ref = true; @@ -697,11 +902,11 @@ make_argv_ref (macro_arguments *argv, const char *argv0, size_t argv0_len, chain->next = NULL; chain->str = NULL; chain->len = 0; + chain->level = expansion_level - 1; chain->argv = argv; chain->index = index; chain->flatten = flatten; } - // TODO should argv->inuse be set? new_argv->argc = argv->argc - (index - 1); new_argv->inuse = false; new_argv->argv0 = argv0; @@ -727,7 +932,8 @@ push_arg (struct obstack *obs, macro_arguments *argv, unsigned int index) token = arg_token (argv, index); // TODO handle func tokens? assert (TOKEN_DATA_TYPE (token) == TOKEN_TEXT); - push_token (token, expansion_level - 1); + if (push_token (token, expansion_level - 1)) + arg_mark (argv); } /* Push series of comma-separated arguments from ARGV, which should @@ -741,6 +947,7 @@ push_args (struct obstack *obs, macro_arguments *argv, bool skip, bool quote) token_data sep; unsigned int i = skip ? 2 : 1; bool use_sep = false; + bool inuse = false; static char comma[2] = ","; if (i >= argv->argc) @@ -779,8 +986,10 @@ push_args (struct obstack *obs, macro_arguments *argv, bool skip, bool quote) use_sep = true; // TODO handle func tokens? assert (TOKEN_DATA_TYPE (token) == TOKEN_TEXT); - push_token (token, expansion_level - 1); + inuse |= push_token (token, expansion_level - 1); } if (quote) obstack_grow (obs, rquote.string, rquote.length); + if (inuse) + arg_mark (argv); } |