diff mbox series

libcpp: Directly peek for initial line marker

Message ID 1a000e0b-b05e-f352-e68f-8aee53d10d2b@acm.org
State New
Headers show
Series libcpp: Directly peek for initial line marker | expand

Commit Message

Nathan Sidwell Oct. 8, 2020, 7:16 p.m. UTC
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.

pushing to trunk,

nathan
diff mbox series

Patch

diff --git i/libcpp/files.c w/libcpp/files.c
index b890b8ebf1e..5af41364d0a 100644
--- i/libcpp/files.c
+++ w/libcpp/files.c
@@ -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;
 }
diff --git i/libcpp/init.c w/libcpp/init.c
index aba5854d357..84c0a9efa74 100644
--- i/libcpp/init.c
+++ w/libcpp/init.c
@@ -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
diff --git i/libcpp/internal.h w/libcpp/internal.h
index 4bafe1cf353..b728df74562 100644
--- i/libcpp/internal.h
+++ w/libcpp/internal.h
@@ -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.  */