[08/12] Add json-reader.h/cc

Message ID 20220622223447.2462880-9-dmalcolm@redhat.com
State New
Headers
Series RFC: Replay of serialized diagnostics |

Commit Message

David Malcolm June 22, 2022, 10:34 p.m. UTC
  This patch adds classes that better integrate the JSON parser with
GCC's diagnostic subsystem (e.g. line_maps).

gcc/ChangeLog:
	* json-reader.cc: New file.
	* json-reader.h: New file.

Signed-off-by: David Malcolm <dmalcolm@redhat.com>
---
 gcc/json-reader.cc | 122 +++++++++++++++++++++++++++++++++++++++++++++
 gcc/json-reader.h  | 107 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 229 insertions(+)
 create mode 100644 gcc/json-reader.cc
 create mode 100644 gcc/json-reader.h
  

Patch

diff --git a/gcc/json-reader.cc b/gcc/json-reader.cc
new file mode 100644
index 00000000000..e4fbd0db803
--- /dev/null
+++ b/gcc/json-reader.cc
@@ -0,0 +1,122 @@ 
+/* Integration of JSON parsing with GCC diagnostics.
+   Copyright (C) 2022 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "diagnostic.h"
+#include "json-reader.h"
+
+/* Read the contents of PATH into memory, returning a 0-terminated buffer
+   that must be freed by the caller.
+   Issue a fatal error if there are any problems.  */
+
+char *
+read_file (const char *path)
+{
+  FILE *f_in = fopen (path, "r");
+  if (!f_in)
+    fatal_error (UNKNOWN_LOCATION, "unable to open file %qs: %s",
+		 path, xstrerror (errno));
+
+  /* Read content, allocating a buffer for it.  */
+  char *result = NULL;
+  size_t total_sz = 0;
+  size_t alloc_sz = 0;
+  char buf[4096];
+  size_t iter_sz_in;
+
+  while ( (iter_sz_in = fread (buf, 1, sizeof (buf), f_in)) )
+    {
+      gcc_assert (alloc_sz >= total_sz);
+      size_t old_total_sz = total_sz;
+      total_sz += iter_sz_in;
+      /* Allow 1 extra byte for 0-termination.  */
+      if (alloc_sz < (total_sz + 1))
+	{
+	  size_t new_alloc_sz = alloc_sz ? alloc_sz * 2: total_sz + 1;
+	  result = (char *)xrealloc (result, new_alloc_sz);
+	  alloc_sz = new_alloc_sz;
+	}
+      memcpy (result + old_total_sz, buf, iter_sz_in);
+    }
+
+  if (!feof (f_in))
+    fatal_error (UNKNOWN_LOCATION, "error reading from %qs: %s", path,
+		 xstrerror (errno));
+
+  fclose (f_in);
+
+  /* 0-terminate the buffer.  */
+  gcc_assert (total_sz < alloc_sz);
+  result[total_sz] = '\0';
+
+  return result;
+}
+
+/* json_reader's ctor.  */
+
+json_reader::json_reader (const char *filename)
+: m_filename (filename),
+  m_json_loc_map (filename, line_table)
+{
+}
+
+/* Parse UTF8, capturing source location information in m_json_loc_map.
+   If successful, return the top-level value.
+   Otherwise, return NULL and write to *ERR_OUT.  */
+
+json::value *
+json_reader::parse_utf8_string (const char *utf8, bool allow_comments,
+				json::error **err_out)
+{
+  json::value *result
+    = json::parse_utf8_string (utf8, allow_comments, err_out, &m_json_loc_map);
+  return result;
+}
+
+/* Issue an error diagnostic for GMSGID at the location of JV, and exit.  */
+
+void
+json_reader::fatal_error (json::value *jv, const char *gmsgid, ...)
+{
+  location_t loc = m_json_loc_map.get_range_for_value (jv);
+
+  auto_diagnostic_group d;
+  va_list ap;
+  va_start (ap, gmsgid);
+  rich_location richloc (line_table, loc);
+  emit_diagnostic_valist (DK_ERROR, &richloc, NULL, 0, gmsgid, &ap);
+  va_end (ap);
+  exit (1);
+  /* Ideally we'd use ::fatal_error here, but we seem to need to use
+     DK_ERROR for it to be usable from DejaGnu.  */
+}
+
+/* Issue an error diagnostic for ERR, and exit.  */
+
+void
+json_reader::fatal_error (json::error *err)
+{
+  location_t loc = m_json_loc_map.make_location_for_range (err->get_range ());
+  ::error_at (loc, "%s", err->get_msg ());
+  exit (1);
+  /* Ideally we'd use ::fatal_error here, but we seem to need to use
+     DK_ERROR for it to be usable from DejaGnu.  */
+}
diff --git a/gcc/json-reader.h b/gcc/json-reader.h
new file mode 100644
index 00000000000..4537e29cb6a
--- /dev/null
+++ b/gcc/json-reader.h
@@ -0,0 +1,107 @@ 
+/* Integration of JSON parsing with GCC diagnostics.
+   Copyright (C) 2022 David Malcolm <dmalcolm@redhat.com>.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_JSON_READER_H
+#define GCC_JSON_READER_H
+
+#include "json-parsing.h"
+
+/* Concrete implementation of json::location_map that integrates
+   with a line_table, creating location_t values for the locations
+   in the JSON file.  */
+
+class json_line_map : public json::location_map
+{
+public:
+  json_line_map (const char *filename,
+		 line_maps *line_table)
+  : m_filename (filename),
+    m_line_table (line_table)
+  {
+    linemap_add (m_line_table, LC_ENTER, false, xstrdup (m_filename), 0);
+  }
+
+  void record_range_for_value (json::value *jv, const range &r) final override
+  {
+    location_t loc = make_location_for_range (r);
+    m_map.put (jv, loc);
+  }
+
+  void on_finished_parsing () final override
+  {
+    linemap_add (m_line_table, LC_LEAVE, false, NULL, 0);
+  }
+
+  location_t get_range_for_value (json::value *jv)
+  {
+    if (location_t *slot = m_map.get (jv))
+      return *slot;
+    return UNKNOWN_LOCATION;
+  }
+
+  location_t make_location_for_range (const range &r)
+  {
+    location_t start = make_location_for_point (r.m_start);
+    location_t end = make_location_for_point (r.m_end);
+    return make_location (start, start, end);
+  }
+
+private:
+  location_t make_location_for_point (const point &p)
+  {
+    /* json::location_map::point columns are zero-based,
+       whereas libcpp/gcc columns are 1-based.  */
+    const int gcc_column = p.m_column + 1;
+    const int max_column = MAX (1024, gcc_column);
+    linemap_line_start (m_line_table, p.m_line, max_column);
+    return linemap_position_for_column (m_line_table, gcc_column);
+  }
+
+  const char *m_filename;
+  hash_map<json::value *, location_t> m_map;
+  line_maps *m_line_table;
+};
+
+/* Class for reading a JSON file, capturing location_t values for
+   the json::values, and emitting fatal error messages.  */
+
+class json_reader
+{
+public:
+  json_reader (const char *filename);
+  json::value *parse_utf8_string (const char *utf8, bool allow_comments,
+				  json::error **err_out);
+
+  void fatal_error (json::value *jv,
+		    const char *gmsgid, ...)
+    ATTRIBUTE_GCC_DIAG(3,4)
+    ATTRIBUTE_NORETURN;
+
+  void fatal_error (json::error *err)
+    ATTRIBUTE_NORETURN;
+
+protected:
+  const char *m_filename;
+  json_line_map m_json_loc_map;
+};
+
+extern char *read_file (const char *path);
+
+#endif  /* GCC_JSON_READER_H  */