libcpp: Directly peek for initial line marker
authorNathan Sidwell <nathan@acm.org>
Thu, 8 Oct 2020 19:11:37 +0000 (12:11 -0700)
committerNathan Sidwell <nathan@acm.org>
Thu, 8 Oct 2020 19:16:21 +0000 (12:16 -0700)
Using the tokenizer to sniff for an initial line marker for
preprocessed input is a little brittle, particularly with
-fdirectives-only.  If there is no marker we'll happily munch initial
comments.  This patch directly sniffs the buffer.  This is safe
because the initial line marker was machine generated and must be
right at the beginning of the file.  Anything else is not such a line
marker.  The same is true for the initial directory marker.  For that
tokenizing the string is simplest, but at that point it's either a
regular line marker or a directory marker.  If it's a regular marker,
unwinding tokens is fine.

libcpp/
* internal.h (enum include_type): Rename IT_MAIN_INJECT to
IT_PRE_MAIN.
* init.c (cpp_read_main_file): If there is no line marker, adjust
the initial line marker.
(read_original_filename): Return bool, peek the buffer directly
before trying to tokenize.
(read_original_directory): Likewise.  Directly prod the string
literal.
* files.c (_cpp_stack_file): Adjust for IT_PRE_MAIN change.

libcpp/files.c
libcpp/init.c
libcpp/internal.h

index b890b8ebf1e07c82dbfe1b32166337b6ea8f73e7..5af41364d0a41de4b8ac634ed4c78991409cd051 100644 (file)
@@ -948,10 +948,12 @@ _cpp_stack_file (cpp_reader *pfile, _cpp_file *file, include_type type,
 
   /* Add line map and do callbacks.  */
   _cpp_do_file_change (pfile, LC_ENTER, file->path,
-                      /* With preamble injection, start on line zero, so
-                         the preamble doesn't appear to have been
-                         included from line 1.  */
-                      type == IT_MAIN_INJECT ? 0 : 1, sysp);
+                      /* With preamble injection, start on line zero,
+                         so the preamble doesn't appear to have been
+                         included from line 1.  Likewise when
+                         starting preprocessed, we expect an initial
+                         locating line.  */
+                      type == IT_PRE_MAIN ? 0 : 1, sysp);
 
   return true;
 }
index aba5854d357b6b76f05b4abebb15fe16b2426aef..84c0a9efa74b7d62b3a0a776bab3a3b473b54ed2 100644 (file)
@@ -36,7 +36,7 @@ along with this program; see the file COPYING3.  If not see
 
 static void init_library (void);
 static void mark_named_operators (cpp_reader *, int);
-static void read_original_filename (cpp_reader *);
+static bool read_original_filename (cpp_reader *);
 static void read_original_directory (cpp_reader *);
 static void post_options (cpp_reader *);
 
@@ -681,94 +681,114 @@ cpp_read_main_file (cpp_reader *pfile, const char *fname, bool injecting)
     return NULL;
 
   _cpp_stack_file (pfile, pfile->main_file,
-                  injecting ? IT_MAIN_INJECT : IT_MAIN, 0);
+                  injecting || CPP_OPTION (pfile, preprocessed)
+                  ? IT_PRE_MAIN : IT_MAIN, 0);
 
   /* For foo.i, read the original filename foo.c now, for the benefit
      of the front ends.  */
   if (CPP_OPTION (pfile, preprocessed))
-    read_original_filename (pfile);
+    if (!read_original_filename (pfile))
+      {
+       /* We're on line 1 after all.  */
+       auto *last = linemap_check_ordinary
+         (LINEMAPS_LAST_MAP (pfile->line_table, false));
+       last->to_line = 1;
+       /* Inform of as-if a file change.  */
+       _cpp_do_file_change (pfile, LC_RENAME_VERBATIM, LINEMAP_FILE (last),
+                            LINEMAP_LINE (last), LINEMAP_SYSP (last));
+      }
 
   return ORDINARY_MAP_FILE_NAME (LINEMAPS_LAST_ORDINARY_MAP (pfile->line_table));
 }
 
-/* For preprocessed files, if the first tokens are of the form # NUM.
-   handle the directive so we know the original file name.  This will
-   generate file_change callbacks, which the front ends must handle
-   appropriately given their state of initialization.  */
-static void
+/* For preprocessed files, if the very first characters are
+   '#<SPACE>[01]<SPACE>', then handle a line directive so we know the
+   original file name.  This will generate file_change callbacks,
+   which the front ends must handle appropriately given their state of
+   initialization.  We peek directly into the character buffer, so
+   that we're not confused by otherwise-skipped white space &
+   comments.  We can be very picky, because this should have been
+   machine-generated text (by us, no less).  This way we do not
+   interfere with the module directive state machine.  */
+
+static bool
 read_original_filename (cpp_reader *pfile)
 {
-  const cpp_token *token, *token1;
-
-  /* Lex ahead; if the first tokens are of the form # NUM, then
-     process the directive, otherwise back up.  */
-  token = _cpp_lex_direct (pfile);
-  if (token->type == CPP_HASH)
+  auto *buf = pfile->buffer->next_line;
+
+  if (pfile->buffer->rlimit - buf > 4
+      && buf[0] == '#'
+      && buf[1] == ' '
+      // Also permit '1', as that's what used to be here
+      && (buf[2] == '0' || buf[2] == '1')
+      && buf[3] == ' ')
     {
-      pfile->state.in_directive = 1;
-      token1 = _cpp_lex_direct (pfile);
-      _cpp_backup_tokens (pfile, 1);
-      pfile->state.in_directive = 0;
-
-      /* If it's a #line directive, handle it.  */
-      if (token1->type == CPP_NUMBER
-         && _cpp_handle_directive (pfile, token->flags & PREV_WHITE))
+      const cpp_token *token = _cpp_lex_direct (pfile);
+      gcc_checking_assert (token->type == CPP_HASH);
+      if (_cpp_handle_directive (pfile, token->flags & PREV_WHITE))
        {
          read_original_directory (pfile);
-         return;
+         return true;
        }
     }
 
-  /* Backup as if nothing happened.  */
-  _cpp_backup_tokens (pfile, 1);
+  return false;
 }
 
 /* For preprocessed files, if the tokens following the first filename
    line is of the form # <line> "/path/name//", handle the
-   directive so we know the original current directory.  */
+   directive so we know the original current directory.
+
+   As with the first line peeking, we can do this without lexing by
+   being picky.  */
 static void
 read_original_directory (cpp_reader *pfile)
 {
-  const cpp_token *hash, *token;
-
-  /* Lex ahead; if the first tokens are of the form # NUM, then
-     process the directive, otherwise back up.  */
-  hash = _cpp_lex_direct (pfile);
-  if (hash->type != CPP_HASH)
+  auto *buf = pfile->buffer->next_line;
+
+  if (pfile->buffer->rlimit - buf > 4
+      && buf[0] == '#'
+      && buf[1] == ' '
+      // Also permit '1', as that's what used to be here
+      && (buf[2] == '0' || buf[2] == '1')
+      && buf[3] == ' ')
     {
-      _cpp_backup_tokens (pfile, 1);
-      return;
-    }
-
-  token = _cpp_lex_direct (pfile);
+      const cpp_token *hash = _cpp_lex_direct (pfile);
+      gcc_checking_assert (hash->type == CPP_HASH);
+      pfile->state.in_directive = 1;
+      const cpp_token *number = _cpp_lex_direct (pfile);
+      gcc_checking_assert (number->type == CPP_NUMBER);
+      const cpp_token *string = _cpp_lex_direct (pfile);
+      pfile->state.in_directive = 0;
 
-  if (token->type != CPP_NUMBER)
-    {
-      _cpp_backup_tokens (pfile, 2);
-      return;
-    }
+      const unsigned char *text = nullptr;
+      size_t len = 0;
+      if (string->type == CPP_STRING)
+       {
+         /* The string value includes the quotes.  */
+         text = string->val.str.text;
+         len = string->val.str.len;
+       }
+      if (len < 5
+         || !IS_DIR_SEPARATOR (text[len - 2])
+         || !IS_DIR_SEPARATOR (text[len - 3]))
+       {
+         /* That didn't work out, back out.   */
+         _cpp_backup_tokens (pfile, 3);
+         return;
+       }
 
-  token = _cpp_lex_direct (pfile);
+      if (pfile->cb.dir_change)
+       {
+         /* Smash the string directly, it's dead at this point  */
+         char *smashy = (char *)text;
+         smashy[len - 3] = 0;
+         
+         pfile->cb.dir_change (pfile, smashy + 1);
+       }
 
-  if (token->type != CPP_STRING
-      || ! (token->val.str.len >= 5
-           && IS_DIR_SEPARATOR (token->val.str.text[token->val.str.len-2])
-           && IS_DIR_SEPARATOR (token->val.str.text[token->val.str.len-3])))
-    {
-      _cpp_backup_tokens (pfile, 3);
-      return;
+      /* We should be at EOL.  */
     }
-
-  if (pfile->cb.dir_change)
-    {
-      char *debugdir = (char *) alloca (token->val.str.len - 3);
-
-      memcpy (debugdir, (const char *) token->val.str.text + 1,
-             token->val.str.len - 4);
-      debugdir[token->val.str.len - 4] = '\0';
-
-      pfile->cb.dir_change (pfile, debugdir);
-    }      
 }
 
 /* This is called at the end of preprocessing.  It pops the last
index 4bafe1cf353513ad4f52a5ab50d153e284b10700..b728df7456265f98ee6e11a3142178de6efa2a73 100644 (file)
@@ -124,8 +124,8 @@ enum include_type
    IT_CMDLINE,  /* -include */
    IT_DEFAULT,  /* forced header  */
    IT_MAIN,     /* main, start on line 1 */
-   IT_MAIN_INJECT,  /* main, but there will be an injected preamble
-                      before line 1 */
+   IT_PRE_MAIN,  /* main, but there will be a preamble before line
+                   1 */
 
    IT_DIRECTIVE_HWM = IT_IMPORT + 1,  /* Directives below this.  */
    IT_HEADER_HWM = IT_DEFAULT + 1     /* Header files below this.  */