m4-commit
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[SCM] GNU M4 source repository branch, branch-1_4, updated. branch-cvs-r


From: Eric Blake
Subject: [SCM] GNU M4 source repository branch, branch-1_4, updated. branch-cvs-readonly-34-gc947bf4
Date: Tue, 18 Dec 2007 14:08:17 +0000

This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GNU M4 source repository".

http://git.sv.gnu.org/gitweb/?p=m4.git;a=commitdiff;h=c947bf47f48f9132155bc623596771e4a4e3f732

The branch, branch-1_4 has been updated
       via  c947bf47f48f9132155bc623596771e4a4e3f732 (commit)
      from  da38cc2a2e8272f41366ed87aee5abe64e81c3f9 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit c947bf47f48f9132155bc623596771e4a4e3f732
Author: Eric Blake <address@hidden>
Date:   Thu Oct 25 22:05:15 2007 -0600

    Stage 8: extend life of references into argv.
    
    * src/m4.h (obstack_regrow): Delete, now that it is unused.
    (struct token_chain): Add level field.
    (push_token): Adjust prototype.
    (adjust_refcount): New prototype.
    * src/macro.c [DEBUG_MACRO]: Add debugging hooks for $@ reference
    counting.
    (argc_stack, argv_stack): Delete, replaced by...
    (struct macro_arg_stacks, stacks, stacks_count): ...these new
    structure for tracking $@ references.
    (expand_input): Initialize new structure.
    (collect_arguments): Alter signature.
    (expand_macro): Rework obstack handling, in order to keep $@
    references alive until they have been rescanned.
    (adjust_refcount, arg_mark): New functions.
    (make_argv_ref): Use new field.
    (push_arg, push_args): Update callers.
    * src/input.c (make_text_link): Use new field.
    (push_token): Change signature.
    (pop_input, next_char_1): Call new function.
    * doc/m4.texinfo: Clean up some examples of -d option usage.
    (Ifelse): Add some stress tests.
    
    (cherry picked from commit b0daa4c96f734aab4d450594f64bc15d4321fd60)
    
    Signed-off-by: Eric Blake <address@hidden>

-----------------------------------------------------------------------

Summary of changes:
 ChangeLog      |   25 +++++
 doc/m4.texinfo |   47 ++++++++-
 src/input.c    |   16 ++-
 src/m4.h       |   12 +--
 src/macro.c    |  325 ++++++++++++++++++++++++++++++++++++++++++++++----------
 5 files changed, 352 insertions(+), 73 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 14582ac..2b2a0d0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,28 @@
+2007-12-18  Eric Blake  <address@hidden>
+
+       Stage 8: extend life of references into argv.
+       * src/m4.h (obstack_regrow): Delete, now that it is unused.
+       (struct token_chain): Add level field.
+       (push_token): Adjust prototype.
+       (adjust_refcount): New prototype.
+       * src/macro.c [DEBUG_MACRO]: Add debugging hooks for $@ reference
+       counting.
+       (argc_stack, argv_stack): Delete, replaced by...
+       (struct macro_arg_stacks, stacks, stacks_count): ...these new
+       structure for tracking $@ references.
+       (expand_input): Initialize new structure.
+       (collect_arguments): Alter signature.
+       (expand_macro): Rework obstack handling, in order to keep $@
+       references alive until they have been rescanned.
+       (adjust_refcount, arg_mark): New functions.
+       (make_argv_ref): Use new field.
+       (push_arg, push_args): Update callers.
+       * src/input.c (make_text_link): Use new field.
+       (push_token): Change signature.
+       (pop_input, next_char_1): Call new function.
+       * doc/m4.texinfo: Clean up some examples of -d option usage.
+       (Ifelse): Add some stress tests.
+
 2007-12-13  Eric Blake  <address@hidden>
 
        Yet more rewording.
diff --git a/doc/m4.texinfo b/doc/m4.texinfo
index 576a34c..eaec266 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.
 
address@hidden 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
@@ -2640,6 +2641,46 @@ ifelse(`foo', `bar', `3', `gnu', `gnats', `6', `7', `8')
 
 @ignore
 @comment Stress tests, not worth documenting.
+
address@hidden Ensure that references compared to strings work regardless of
address@hidden similar prefixes.
address@hidden
+define(`e', `$@@')define(`long', `01234567890123456789')
address@hidden
+ifelse(long, `01234567890123456789', `yes', `no')
address@hidden
+ifelse(`01234567890123456789', long, `yes', `no')
address@hidden
+ifelse(long, `01234567890123456789-', `yes', `no')
address@hidden
+ifelse(`01234567890123456789-', long, `yes', `no')
address@hidden
+ifelse(e(long), `01234567890123456789', `yes', `no')
address@hidden
+ifelse(`01234567890123456789', e(long), `yes', `no')
address@hidden
+ifelse(e(long), `01234567890123456789-', `yes', `no')
address@hidden
+ifelse(`01234567890123456789-', e(long), `yes', `no')
address@hidden
+ifelse(-e(long), `-01234567890123456789', `yes', `no')
address@hidden
+ifelse(-`01234567890123456789', -e(long), `yes', `no')
address@hidden
+ifelse(-e(long), `-01234567890123456789-', `yes', `no')
address@hidden
+ifelse(`-01234567890123456789-', -e(long), `yes', `no')
address@hidden
+ifelse(-e(long)-, `-01234567890123456789-', `yes', `no')
address@hidden
+ifelse(-`01234567890123456789-', -e(long)-, `yes', `no')
address@hidden
+ifelse(-e(long)-, `-01234567890123456789', `yes', `no')
address@hidden
+ifelse(`-01234567890123456789', -e(long)-, `yes', `no')
address@hidden
address@hidden example
+
 @comment It would be nice to pass builtin tokens through ifelse, m4wrap,
 @comment user macros; hence the fixmes.
 @example
@@ -3237,6 +3278,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.
 
address@hidden The explicit -dp neutralizes the testsuite default of -d.
 @comment options: -dp -L3 -tifelse
 @comment status: 1
 @example
@@ -3399,6 +3441,7 @@ are reset to the default of @samp{aeq}.
 The expansion of @code{debugmode} is void.
 @end deffn
 
address@hidden The explicit -dp neutralizes the testsuite default of -d.
 @comment options: -dp
 @example
 $ @kbd{m4}
@@ -3447,7 +3490,7 @@ The expansion of @code{debugfile} is void.
 @end deffn
 
 @example
-$ @kbd{m4}
+$ @kbd{m4 -d}
 traceon(`divnum')
 @result{}
 divnum(`extra')
@@ -7070,7 +7113,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 5c87217..8386061 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)
     {
@@ -353,9 +355,11 @@ push_token (token_data *token, int level)
   else
     chain->str = TOKEN_DATA_TEXT (token);
   chain->len = TOKEN_DATA_LEN (token);
+  chain->level = -1;
   chain->argv = NULL;
   chain->index = 0;
   chain->flatten = false;
+  return false; /* No reference exists when text is copied.  */
 }
 
 /*-------------------------------------------------------------------.
@@ -469,7 +473,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;
 
@@ -760,6 +766,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;
diff --git a/src/m4.h b/src/m4.h
index 111f167..854f28d 100644
--- a/src/m4.h
+++ b/src/m4.h
@@ -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 address@hidden  */
   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 1e4b271..63d957d 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 argc_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 (&argc_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 (&argc_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 *argc_base = NULL;      /* Base of argc_stack on entry.  */
-  void *argv_base = NULL;      /* Base of argv_stack on entry.  */
-  unsigned int argc_size;      /* Size of argc_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);
-
-  argc_size = obstack_object_size (&argc_stack);
-  argv_size = obstack_object_size (&argv_stack);
-  argc_base = obstack_finish (&argc_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, &argc_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 
address@hidden  */
-  if (0 < argc_size)
-    obstack_regrow (&argc_stack, argc_base, argc_size);
-  else
-    obstack_free (&argc_stack, argc_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);
 }


hooks/post-receive
--
GNU M4 source repository




reply via email to

[Prev in Thread] Current Thread [Next in Thread]