[gtk/wip/otte/json: 1/11] jsonparser: Improve error handling
- From: Benjamin Otte <otte src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/wip/otte/json: 1/11] jsonparser: Improve error handling
- Date: Thu, 9 Dec 2021 15:25:47 +0000 (UTC)
commit 58972ce3c49a81469c75738d32ac00cba2935029
Author: Benjamin Otte <otte redhat com>
Date: Sat Dec 4 20:51:11 2021 +0100
jsonparser: Improve error handling
* Add a custom GError domain with our own error values
* Add API so external code can emit errors
* Add location information to errors
* Allow API to query errors
gtk/json/gtkjsonparser.c | 502 +++++++++++++++++++++++++++++++++-------
gtk/json/gtkjsonparserprivate.h | 27 ++-
tools/gtk-json-format.c | 42 +++-
tools/gtk-json-validate.c | 37 ++-
4 files changed, 518 insertions(+), 90 deletions(-)
---
diff --git a/gtk/json/gtkjsonparser.c b/gtk/json/gtkjsonparser.c
index 38a90b4d67..4e71b336da 100644
--- a/gtk/json/gtkjsonparser.c
+++ b/gtk/json/gtkjsonparser.c
@@ -45,6 +45,8 @@ struct _GtkJsonParser
const guchar *end; /* pointer after end of data we're reading */
GError *error; /* if an error has happened, it's stored here. Errors aren't recoverable. */
+ const guchar *error_start; /* start of error location */
+ const guchar *error_end; /* end of error location */
GtkJsonBlock *block; /* current block */
GtkJsonBlock *blocks; /* blocks array */
@@ -54,16 +56,17 @@ struct _GtkJsonParser
typedef enum {
WHITESPACE = (1 << 4),
- STRING_ELEMENT = (1 << 5),
- STRING_MARKER = (1 << 6),
+ NEWLINE = (1 << 5),
+ STRING_ELEMENT = (1 << 6),
+ STRING_MARKER = (1 << 7),
} JsonCharacterType;
#define JSON_CHARACTER_NODE_MASK ((1 << 4) - 1)
static const guchar json_character_table[256] = {
['\t'] = WHITESPACE,
- ['\r'] = WHITESPACE,
- ['\n'] = WHITESPACE,
+ ['\r'] = WHITESPACE | NEWLINE,
+ ['\n'] = WHITESPACE | NEWLINE,
[' '] = WHITESPACE | STRING_ELEMENT,
['!'] = STRING_ELEMENT,
['"'] = GTK_JSON_STRING | STRING_MARKER,
@@ -177,6 +180,21 @@ json_skip_characters (const guchar *start,
return s;
}
+static const guchar *
+json_skip_characters_until (const guchar *start,
+ const guchar *end,
+ JsonCharacterType type)
+{
+ const guchar *s;
+
+ for (s = start; s < end; s++)
+ {
+ if (json_character_table[*s] & type)
+ break;
+ }
+ return s;
+}
+
static const guchar *
json_find_character (const guchar *start,
JsonCharacterType type)
@@ -191,14 +209,45 @@ json_find_character (const guchar *start,
return s;
}
+GQuark
+gtk_json_error_quark (void)
+{
+ return g_quark_from_static_string ("gtk-json-error-quark");
+}
+
static void
-gtk_json_parser_value_error (GtkJsonParser *self,
- const char *format,
- ...) G_GNUC_PRINTF(2, 3);
+gtk_json_parser_take_error (GtkJsonParser *self,
+ const guchar *start_location,
+ const guchar *end_location,
+ GError *error)
+{
+ g_assert (start_location <= end_location);
+ g_assert (g_bytes_get_data (self->bytes, NULL) <= (gconstpointer) start_location);
+ g_assert (end_location <= self->end);
+
+ if (self->error)
+ {
+ g_error_free (error);
+ return;
+ }
+
+ self->error = error;
+ self->error_start = start_location;
+ self->error_end = end_location;
+}
+
static void
-gtk_json_parser_value_error (GtkJsonParser *self,
- const char *format,
- ...)
+gtk_json_parser_syntax_error_at (GtkJsonParser *self,
+ const guchar *error_start,
+ const guchar *error_end,
+ const char *format,
+ ...) G_GNUC_PRINTF(4, 5);
+static void
+gtk_json_parser_syntax_error_at (GtkJsonParser *self,
+ const guchar *error_start,
+ const guchar *error_end,
+ const char *format,
+ ...)
{
va_list args;
@@ -206,30 +255,137 @@ gtk_json_parser_value_error (GtkJsonParser *self,
return;
va_start (args, format);
- self->error = g_error_new_valist (G_FILE_ERROR,
- G_FILE_ERROR_FAILED,
- format, args);
+ gtk_json_parser_take_error (self,
+ error_start,
+ error_end,
+ g_error_new_valist (GTK_JSON_ERROR,
+ GTK_JSON_ERROR_SYNTAX,
+ format, args));
va_end (args);
}
static void
gtk_json_parser_syntax_error (GtkJsonParser *self,
- const char *format,
+ const char *format,
...) G_GNUC_PRINTF(2, 3);
static void
gtk_json_parser_syntax_error (GtkJsonParser *self,
- const char *format,
+ const char *format,
+ ...)
+{
+ va_list args;
+ const guchar *error_end;
+
+ if (self->error)
+ return;
+
+ va_start (args, format);
+ for (error_end = self->reader;
+ error_end < self->end && g_ascii_isalnum (*error_end);
+ error_end++)
+ ;
+ if (error_end == self->reader &&
+ g_utf8_get_char_validated ((const char *) error_end, self->end - error_end) < (gunichar) -2)
+ {
+ error_end = (const guchar *) g_utf8_next_char (error_end);
+ }
+
+ gtk_json_parser_take_error (self,
+ self->reader,
+ error_end,
+ g_error_new_valist (GTK_JSON_ERROR,
+ GTK_JSON_ERROR_SYNTAX,
+ format, args));
+ va_end (args);
+}
+
+static void
+gtk_json_parser_type_error (GtkJsonParser *self,
+ const char *format,
+ ...) G_GNUC_PRINTF(2, 3);
+static void
+gtk_json_parser_type_error (GtkJsonParser *self,
+ const char *format,
+ ...)
+{
+ const guchar *start_location;
+ va_list args;
+
+ if (self->error)
+ return;
+
+ if (self->block->value)
+ start_location = self->block->value;
+ else if (self->block != self->blocks)
+ start_location = self->block[-1].value;
+ else
+ start_location = g_bytes_get_data (self->bytes, NULL);
+
+ va_start (args, format);
+ gtk_json_parser_take_error (self,
+ start_location,
+ self->reader,
+ g_error_new_valist (GTK_JSON_ERROR,
+ GTK_JSON_ERROR_TYPE,
+ format, args));
+ va_end (args);
+}
+
+void
+gtk_json_parser_value_error (GtkJsonParser *self,
+ const char *format,
+ ...)
+{
+ const guchar *start_location;
+ va_list args;
+
+ if (self->error)
+ return;
+
+ if (self->block->value)
+ start_location = self->block->value;
+ else if (self->block != self->blocks)
+ start_location = self->block[-1].value;
+ else
+ start_location = g_bytes_get_data (self->bytes, NULL);
+
+ va_start (args, format);
+ gtk_json_parser_take_error (self,
+ start_location,
+ self->reader,
+ g_error_new_valist (GTK_JSON_ERROR,
+ GTK_JSON_ERROR_VALUE,
+ format, args));
+ va_end (args);
+}
+
+void
+gtk_json_parser_schema_error (GtkJsonParser *self,
+ const char *format,
...)
{
+ const guchar *start_location;
va_list args;
if (self->error)
return;
+ if (self->block->member_name)
+ start_location = self->block->member_name;
+ if (self->block->value)
+ start_location = self->block->value;
+ else if (self->block != self->blocks)
+ start_location = self->block[-1].value;
+ else
+ start_location = g_bytes_get_data (self->bytes, NULL);
+
va_start (args, format);
- self->error = g_error_new_valist (G_FILE_ERROR,
- G_FILE_ERROR_FAILED,
- format, args);
+ gtk_json_parser_take_error (self,
+ start_location,
+ self->reader,
+ g_error_new_valist (GTK_JSON_ERROR,
+ GTK_JSON_ERROR_SCHEMA,
+ format, args));
va_end (args);
}
@@ -459,9 +615,13 @@ gtk_json_unescape_string (const guchar *escaped)
static gboolean
gtk_json_parser_parse_string (GtkJsonParser *self)
{
+ const guchar *start;
+
+ start = self->reader;
+
if (!gtk_json_parser_try_char (self, '"'))
{
- gtk_json_parser_syntax_error (self, "Not a string");
+ gtk_json_parser_type_error (self, "Not a string");
return FALSE;
}
@@ -471,7 +631,12 @@ gtk_json_parser_parse_string (GtkJsonParser *self)
{
if (*self->reader < 0x20)
{
- gtk_json_parser_syntax_error (self, "Disallowed control character in string literal");
+ if (*self->reader == '\r' || *self->reader == '\n')
+ gtk_json_parser_syntax_error (self, "Newlines in strings are not allowed");
+ else if (*self->reader == '\t')
+ gtk_json_parser_syntax_error (self, "Tabs not allowed in strings");
+ else
+ gtk_json_parser_syntax_error (self, "Disallowed control character in string literal");
return FALSE;
}
else if (*self->reader > 127)
@@ -492,9 +657,11 @@ gtk_json_parser_parse_string (GtkJsonParser *self)
else if (*self->reader == '\\')
{
if (gtk_json_parser_remaining (self) < 2)
- goto end;
- self->reader++;
- switch (*self->reader)
+ {
+ self->reader = self->end;
+ goto end;
+ }
+ switch (self->reader[1])
{
case '"':
case '\\':
@@ -508,42 +675,50 @@ gtk_json_parser_parse_string (GtkJsonParser *self)
case 'u':
/* lots of work necessary to validate the unicode escapes here */
- if (gtk_json_parser_remaining (self) < 5 ||
- !g_ascii_isxdigit (self->reader[1]) ||
+ if (gtk_json_parser_remaining (self) < 6 ||
!g_ascii_isxdigit (self->reader[2]) ||
!g_ascii_isxdigit (self->reader[3]) ||
- !g_ascii_isxdigit (self->reader[4]))
+ !g_ascii_isxdigit (self->reader[4]) ||
+ !g_ascii_isxdigit (self->reader[5]))
{
- gtk_json_parser_syntax_error (self, "Invalid Unicode escape sequence");
+ const guchar *end;
+ for (end = self->reader + 2;
+ end < self->reader + 6 && end < self->end;
+ end++)
+ {
+ if (!g_ascii_isxdigit (*end))
+ break;
+ }
+ gtk_json_parser_syntax_error_at (self, self->reader, end, "Invalid Unicode escape
sequence");
return FALSE;
}
else
{
- gunichar unichar = (g_ascii_xdigit_value (self->reader[1]) << 12) |
- (g_ascii_xdigit_value (self->reader[2]) << 8) |
- (g_ascii_xdigit_value (self->reader[3]) << 4) |
- (g_ascii_xdigit_value (self->reader[4]));
+ gsize escape_size = 6;
+ gunichar unichar = (g_ascii_xdigit_value (self->reader[2]) << 12) |
+ (g_ascii_xdigit_value (self->reader[3]) << 8) |
+ (g_ascii_xdigit_value (self->reader[4]) << 4) |
+ (g_ascii_xdigit_value (self->reader[5]));
- self->reader += 4;
/* resolve UTF-16 surrogates for Unicode characters not in the BMP,
* as per ECMA 404, ยง 9, "String"
*/
if (g_unichar_type (unichar) == G_UNICODE_SURROGATE)
{
- if (gtk_json_parser_remaining (self) >= 7 &&
- self->reader[1] == '\\' &&
- self->reader[2] == 'u' &&
- g_ascii_isxdigit (self->reader[3]) &&
- g_ascii_isxdigit (self->reader[4]) &&
- g_ascii_isxdigit (self->reader[5]) &&
- g_ascii_isxdigit (self->reader[6]))
+ if (gtk_json_parser_remaining (self) >= 12 &&
+ self->reader[6] == '\\' &&
+ self->reader[7] == 'u' &&
+ g_ascii_isxdigit (self->reader[8]) &&
+ g_ascii_isxdigit (self->reader[9]) &&
+ g_ascii_isxdigit (self->reader[10]) &&
+ g_ascii_isxdigit (self->reader[11]))
{
unichar = decode_utf16_surrogate_pair (unichar,
- (g_ascii_xdigit_value (self->reader[3]) <<
12) |
- (g_ascii_xdigit_value (self->reader[4]) <<
8) |
- (g_ascii_xdigit_value (self->reader[5]) <<
4) |
- (g_ascii_xdigit_value (self->reader[6])));
- self->reader += 6;
+ (g_ascii_xdigit_value (self->reader[8]) <<
12) |
+ (g_ascii_xdigit_value (self->reader[9]) <<
8) |
+ (g_ascii_xdigit_value (self->reader[10]) <<
4) |
+ (g_ascii_xdigit_value (self->reader[11])));
+ escape_size += 6;
}
else
{
@@ -552,39 +727,75 @@ gtk_json_parser_parse_string (GtkJsonParser *self)
if (unichar == 0)
{
- gtk_json_parser_syntax_error (self, "Invalid UTF-16 surrogate pair");
+ gtk_json_parser_syntax_error_at (self, self->reader, self->reader + escape_size,
"Invalid UTF-16 surrogate pair");
return FALSE;
}
+
+ self->reader += escape_size - 2;
}
}
break;
default:
- gtk_json_parser_syntax_error (self, "Unknown escape sequence");
+ if (g_utf8_get_char_validated ((const char *) self->reader + 1, self->end - self->reader - 1)
< (gunichar) -2)
+ gtk_json_parser_syntax_error_at (self, self->reader, (const guchar *) g_utf8_next_char
(self->reader + 1), "Unknown escape sequence");
+ else
+ gtk_json_parser_syntax_error_at (self, self->reader, self->reader + 1, "Unknown escape
sequence");
return FALSE;
}
- self->reader++;
+ self->reader += 2;
}
self->reader = json_skip_characters (self->reader, self->end, STRING_ELEMENT);
}
end:
- gtk_json_parser_syntax_error (self, "Unterminated string literal");
+ gtk_json_parser_syntax_error_at (self, start, self->reader, "Unterminated string literal");
return FALSE;
}
static gboolean
gtk_json_parser_parse_number (GtkJsonParser *self)
{
+ const guchar *start = self->reader;
+ gboolean have_sign;
+
/* sign */
- gtk_json_parser_try_char (self, '-');
+ have_sign = gtk_json_parser_try_char (self, '-');
/* integer part */
- if (!gtk_json_parser_try_char (self, '0'))
+ if (gtk_json_parser_try_char (self, '0'))
+ {
+ /* Technically, "01" in the JSON grammar would be 2 numbers:
+ * "0" followed by "1".
+ * Practically, nobody understands that it's 2 numbers, so we
+ * special-purpose an error message for it, because 2 numbers
+ * can never follow each other.
+ */
+ if (!gtk_json_parser_is_eof (self) &&
+ g_ascii_isdigit (*self->reader))
+ {
+ do
+ {
+ self->reader++;
+ }
+ while (!gtk_json_parser_is_eof (self) &&
+ g_ascii_isdigit (*self->reader));
+
+ gtk_json_parser_syntax_error_at (self, start, self->reader, "Numbers may not start with leading
0s");
+ return FALSE;
+ }
+ }
+ else
{
if (gtk_json_parser_is_eof (self) ||
!g_ascii_isdigit (*self->reader))
- goto out;
+ {
+ if (have_sign)
+ gtk_json_parser_syntax_error_at (self, start, self->reader, "Expected a number after '-'
character");
+ else
+ gtk_json_parser_type_error (self, "Not a number");
+ return FALSE;
+ }
self->reader++;
@@ -593,29 +804,41 @@ gtk_json_parser_parse_number (GtkJsonParser *self)
}
/* fractional part */
- if (gtk_json_parser_remaining (self) >= 2 && *self->reader == '.' && g_ascii_isdigit (self->reader[1]))
+ if (gtk_json_parser_try_char (self, '.'))
{
- self->reader += 2;
+ if (!g_ascii_isdigit (*self->reader))
+ {
+ gtk_json_parser_syntax_error_at (self, start, self->reader, "Expected a digit after '.'");
+ return FALSE;
+ }
- while (!gtk_json_parser_is_eof (self) && g_ascii_isdigit (*self->reader))
- self->reader++;
+ do
+ {
+ self->reader++;
+ }
+ while (!gtk_json_parser_is_eof (self) && g_ascii_isdigit (*self->reader));
}
/* exponent */
- if (gtk_json_parser_remaining (self) >= 2 && (self->reader[0] == 'e' || self->reader[0] == 'E') &&
- (g_ascii_isdigit (self->reader[1]) ||
- (gtk_json_parser_remaining (self) >= 3 && (self->reader[1] == '+' || self->reader[1] == '-') &&
g_ascii_isdigit (self->reader[2]))))
+ if (gtk_json_parser_try_char (self, 'e') ||
+ gtk_json_parser_try_char (self, 'E'))
{
- self->reader += 2;
+ if (!gtk_json_parser_try_char (self, '-'))
+ gtk_json_parser_try_char (self, '+');
- while (!gtk_json_parser_is_eof (self) && g_ascii_isdigit (*self->reader))
- self->reader++;
+ if (!g_ascii_isdigit (*self->reader))
+ {
+ gtk_json_parser_syntax_error_at (self, start, self->reader, "Expected a digit in exponent");
+ return FALSE;
+ }
+
+ do
+ {
+ self->reader++;
+ }
+ while (!gtk_json_parser_is_eof (self) && g_ascii_isdigit (*self->reader));
}
return TRUE;
-
-out:
- gtk_json_parser_syntax_error (self, "Not a valid number");
- return FALSE;
}
static gboolean
@@ -655,7 +878,17 @@ gtk_json_parser_parse_value (GtkJsonParser *self)
break;
}
- gtk_json_parser_syntax_error (self, "Expected a value");
+ if (gtk_json_parser_remaining (self) >= 2 &&
+ (self->block->value[0] == '.' || self->block->value[0] == '+') &&
+ g_ascii_isdigit (self->block->value[1]))
+ {
+ const guchar *end = self->block->value + 2;
+ while (end < self->end && g_ascii_isalnum (*end))
+ end++;
+ gtk_json_parser_syntax_error_at (self, self->block->value, end, "Numbers may not start with '%c'",
*self->block->value);
+ }
+ else
+ gtk_json_parser_syntax_error (self, "Expected a value");
return FALSE;
}
@@ -731,8 +964,15 @@ gtk_json_parser_new_for_bytes (GBytes *bytes)
self->block->type = GTK_JSON_BLOCK_TOPLEVEL;
gtk_json_parser_skip_whitespace (self);
- self->block->value = self->reader;
- gtk_json_parser_parse_value (self);
+ if (gtk_json_parser_is_eof (self))
+ {
+ gtk_json_parser_syntax_error_at (self, g_bytes_get_data (self->bytes, NULL), self->reader, "Empty
document");
+ }
+ else
+ {
+ self->block->value = self->reader;
+ gtk_json_parser_parse_value (self);
+ }
return self;
}
@@ -806,7 +1046,7 @@ gtk_json_parser_next (GtkJsonParser *self)
}
else
{
- gtk_json_parser_syntax_error (self, "Data at end of document");
+ gtk_json_parser_syntax_error_at (self, self->reader, self->end, "Data at end of document");
}
return FALSE;
@@ -814,7 +1054,10 @@ gtk_json_parser_next (GtkJsonParser *self)
gtk_json_parser_skip_whitespace (self);
if (gtk_json_parser_is_eof (self))
{
- gtk_json_parser_syntax_error (self, "Unexpected end of document");
+ gtk_json_parser_syntax_error_at (self,
+ self->block[-1].value,
+ self->reader,
+ "Unterminated object");
self->block->member_name = NULL;
self->block->value = NULL;
}
@@ -830,6 +1073,11 @@ gtk_json_parser_next (GtkJsonParser *self)
return FALSE;
}
gtk_json_parser_skip_whitespace (self);
+ if (!gtk_json_parser_has_char (self, '"'))
+ {
+ gtk_json_parser_syntax_error (self, "Expected a string for object member name");
+ return FALSE;
+ }
self->block->member_name = self->reader;
if (!gtk_json_parser_parse_string (self))
@@ -851,7 +1099,10 @@ gtk_json_parser_next (GtkJsonParser *self)
gtk_json_parser_skip_whitespace (self);
if (gtk_json_parser_is_eof (self))
{
- gtk_json_parser_syntax_error (self, "Unexpected end of document");
+ gtk_json_parser_syntax_error_at (self,
+ self->block[-1].value,
+ self->reader,
+ "Unterminated array");
self->block->member_name = NULL;
self->block->value = NULL;
}
@@ -899,6 +1150,89 @@ gtk_json_parser_get_error (GtkJsonParser *self)
return self->error;
}
+void
+gtk_json_parser_get_error_offset (GtkJsonParser *self,
+ gsize *start,
+ gsize *end)
+{
+ const guchar *data;
+
+ if (self->error == NULL)
+ {
+ if (start)
+ *start = 0;
+ if (end)
+ *end = 0;
+ return;
+ }
+
+ data = g_bytes_get_data (self->bytes, NULL);
+ if (start)
+ *start = self->error_start - data;
+ if (end)
+ *end = self->error_end - data;
+}
+
+void
+gtk_json_parser_get_error_location (GtkJsonParser *self,
+ gsize *start_line,
+ gsize *start_line_bytes,
+ gsize *end_line,
+ gsize *end_line_bytes)
+{
+ const guchar *s, *line_start;
+ gsize lines;
+
+ if (self->error == NULL)
+ {
+ if (start_line)
+ *start_line = 0;
+ if (start_line_bytes)
+ *start_line_bytes = 0;
+ if (end_line)
+ *end_line = 0;
+ if (end_line_bytes)
+ *end_line_bytes = 0;
+ return;
+ }
+
+ line_start = g_bytes_get_data (self->bytes, NULL);
+ lines = 0;
+
+ for (s = json_skip_characters_until (line_start, self->error_start, NEWLINE);
+ s < self->error_start;
+ s = json_skip_characters_until (line_start, self->error_start, NEWLINE))
+ {
+ if (s[0] == '\r' && s + 1 < self->error_start && s[1] == '\n')
+ s++;
+ lines++;
+ line_start = s + 1;
+ }
+
+ if (start_line)
+ *start_line = lines;
+ if (start_line_bytes)
+ *start_line_bytes = s - line_start;
+
+ if (end_line == NULL && end_line_bytes == NULL)
+ return;
+
+ for (s = json_skip_characters_until (s, self->error_end, NEWLINE);
+ s < self->error_end;
+ s = json_skip_characters_until (line_start, self->error_end, NEWLINE))
+ {
+ if (s[0] == '\r' && s + 1 < self->error_start && s[1] == '\n')
+ s++;
+ lines++;
+ line_start = s + 1;
+ }
+
+ if (end_line)
+ *end_line = lines;
+ if (end_line_bytes)
+ *end_line_bytes = s - line_start;
+}
+
static gboolean
gtk_json_parser_has_member (GtkJsonParser *self)
{
@@ -991,7 +1325,7 @@ gtk_json_parser_get_boolean (GtkJsonParser *self)
else if (*self->block->value == 'f')
return FALSE;
- gtk_json_parser_value_error (self, "Expected a boolean value");
+ gtk_json_parser_type_error (self, "Expected a boolean value");
return FALSE;
}
@@ -1008,7 +1342,7 @@ gtk_json_parser_get_number (GtkJsonParser *self)
if (!strchr ("-0123456789", *self->block->value))
{
- gtk_json_parser_value_error (self, "Expected a number");
+ gtk_json_parser_type_error (self, "Expected a number");
return 0;
}
@@ -1054,7 +1388,7 @@ gtk_json_parser_get_string (GtkJsonParser *self)
if (*self->block->value != '"')
{
- gtk_json_parser_value_error (self, "Expected a string");
+ gtk_json_parser_type_error (self, "Expected a string");
return g_strdup ("");
}
@@ -1069,7 +1403,7 @@ gtk_json_parser_start_object (GtkJsonParser *self)
if (!gtk_json_parser_try_char (self, '{'))
{
- gtk_json_parser_value_error (self, "Expected an object");
+ gtk_json_parser_type_error (self, "Expected an object");
return FALSE;
}
@@ -1078,11 +1412,20 @@ gtk_json_parser_start_object (GtkJsonParser *self)
gtk_json_parser_skip_whitespace (self);
if (gtk_json_parser_is_eof (self))
{
- gtk_json_parser_syntax_error (self, "Unexpected end of document");
+ gtk_json_parser_syntax_error_at (self,
+ self->block[-1].value,
+ self->reader,
+ "Unterminated object");
return FALSE;
}
if (gtk_json_parser_has_char (self, '}'))
return TRUE;
+
+ if (!gtk_json_parser_has_char (self, '"'))
+ {
+ gtk_json_parser_syntax_error (self, "Expected a string for object member name");
+ return FALSE;
+ }
self->block->member_name = self->reader;
if (!gtk_json_parser_parse_string (self))
@@ -1110,7 +1453,7 @@ gtk_json_parser_start_array (GtkJsonParser *self)
if (!gtk_json_parser_try_char (self, '['))
{
- gtk_json_parser_value_error (self, "Expected an array");
+ gtk_json_parser_type_error (self, "Expected an array");
return FALSE;
}
@@ -1118,7 +1461,10 @@ gtk_json_parser_start_array (GtkJsonParser *self)
gtk_json_parser_skip_whitespace (self);
if (gtk_json_parser_is_eof (self))
{
- gtk_json_parser_syntax_error (self, "Unexpected end of document");
+ gtk_json_parser_syntax_error_at (self,
+ self->block[-1].value,
+ self->reader,
+ "Unterminated array");
return FALSE;
}
if (gtk_json_parser_has_char (self, ']'))
diff --git a/gtk/json/gtkjsonparserprivate.h b/gtk/json/gtkjsonparserprivate.h
index f3ff43fd88..fe737fb719 100644
--- a/gtk/json/gtkjsonparserprivate.h
+++ b/gtk/json/gtkjsonparserprivate.h
@@ -35,8 +35,19 @@ typedef enum {
GTK_JSON_ARRAY
} GtkJsonNode;
+typedef enum {
+ GTK_JSON_ERROR_FAILED,
+ GTK_JSON_ERROR_SYNTAX,
+ GTK_JSON_ERROR_TYPE,
+ GTK_JSON_ERROR_VALUE,
+ GTK_JSON_ERROR_SCHEMA,
+} GtkJsonError;
+
typedef struct _GtkJsonParser GtkJsonParser;
+#define GTK_JSON_ERROR (gtk_json_error_quark ())
+GQuark gtk_json_error_quark (void);
+
GtkJsonParser * gtk_json_parser_new_for_bytes (GBytes *bytes);
GtkJsonParser * gtk_json_parser_new_for_string (const char *string,
gssize size);
@@ -45,7 +56,6 @@ void gtk_json_parser_free (GtkJsonParser
gboolean gtk_json_parser_next (GtkJsonParser *self);
GtkJsonNode gtk_json_parser_get_node (GtkJsonParser *self);
-const GError * gtk_json_parser_get_error (GtkJsonParser *self) G_GNUC_PURE;
char * gtk_json_parser_get_member_name (GtkJsonParser *self);
gssize gtk_json_parser_select_member (GtkJsonParser *self,
const char * const *options);
@@ -60,6 +70,21 @@ gboolean gtk_json_parser_start_object (GtkJsonParser
gboolean gtk_json_parser_start_array (GtkJsonParser *self);
gboolean gtk_json_parser_end (GtkJsonParser *self);
+const GError * gtk_json_parser_get_error (GtkJsonParser *self) G_GNUC_PURE;
+void gtk_json_parser_get_error_offset (GtkJsonParser *self,
+ gsize *start,
+ gsize *end);
+void gtk_json_parser_get_error_location (GtkJsonParser *self,
+ gsize *start_line,
+ gsize *start_line_bytes,
+ gsize *end_line,
+ gsize *end_line_bytes);
+void gtk_json_parser_value_error (GtkJsonParser *self,
+ const char *format,
+ ...) G_GNUC_PRINTF(2, 3);
+void gtk_json_parser_schema_error (GtkJsonParser *self,
+ const char *format,
+ ...) G_GNUC_PRINTF(2, 3);
G_END_DECLS
diff --git a/tools/gtk-json-format.c b/tools/gtk-json-format.c
index 9187b08039..2ea4f13014 100644
--- a/tools/gtk-json-format.c
+++ b/tools/gtk-json-format.c
@@ -221,24 +221,52 @@ format (GtkJsonPrinter *printer,
}
parser = gtk_json_parser_new_for_bytes (bytes);
- g_bytes_unref (bytes);
parse_and_print (parser, printer);
+
if (gtk_json_parser_get_error (parser))
{
- char *uri = g_file_get_uri (file);
- const GError *parser_error = gtk_json_parser_get_error (parser);
-
+ char *uri;
+ const GError *parser_error;
+ const char *data, *start, *end;
+ gsize start_line, start_bytes, start_offset, end_line, end_bytes, end_offset;
+ GString *string;
+
+ uri = g_file_get_uri (file);
+ parser_error = gtk_json_parser_get_error (parser);
+ data = g_bytes_get_data (bytes, NULL);
+ gtk_json_parser_get_error_offset (parser, &start_offset, &end_offset);
+ start = data + start_offset;
+ end = data + end_offset;
+ gtk_json_parser_get_error_location (parser,
+ &start_line, &start_bytes,
+ &end_line, &end_bytes);
+
+ string = g_string_new (NULL);
+ g_string_append_printf (string, "%zu:%lu",
+ start_line + 1,
+ g_utf8_pointer_to_offset (start - start_bytes, start) + 1);
+ if (start_line != end_line || start_bytes != end_bytes)
+ {
+ g_string_append (string, "-");
+ if (start_line != end_line)
+ g_string_append_printf (string, "%zu:", end_line + 1);
+ g_string_append_printf (string, "%lu", g_utf8_pointer_to_offset (end - end_bytes, end) + 1);
+ }
/* Translators: the first %s is the program name, the second one
- * is the URI of the file, the third is the error message.
+ * is the URI of the file, the third is the file location and the
+ * final one the error message.
*/
- g_printerr (_("%s: %s: error parsing file: %s\n"),
- g_get_prgname (), uri, parser_error->message);
+ g_printerr (_("%s: %s: error parsing file: %s: %s\n"),
+ g_get_prgname (), uri, string->str, parser_error->message);
+ g_string_free (string, TRUE);
g_free (uri);
gtk_json_parser_free (parser);
+ g_bytes_unref (bytes);
return FALSE;
}
gtk_json_parser_free (parser);
+ g_bytes_unref (bytes);
return TRUE;
}
diff --git a/tools/gtk-json-validate.c b/tools/gtk-json-validate.c
index 73e94990e6..23c049631a 100644
--- a/tools/gtk-json-validate.c
+++ b/tools/gtk-json-validate.c
@@ -60,23 +60,52 @@ validate (GFile *file)
}
parser = gtk_json_parser_new_for_bytes (bytes);
- g_bytes_unref (bytes);
while (gtk_json_parser_next (parser));
json_error = gtk_json_parser_get_error (parser);
if (json_error)
{
+ char *uri;
+ const char *data, *start, *end;
+ gsize start_line, start_bytes, start_offset, end_line, end_bytes, end_offset;
+ GString *string;
+
+ uri = g_file_get_uri (file);
+ data = g_bytes_get_data (bytes, NULL);
+ gtk_json_parser_get_error_offset (parser, &start_offset, &end_offset);
+ start = data + start_offset;
+ end = data + end_offset;
+ gtk_json_parser_get_error_location (parser,
+ &start_line, &start_bytes,
+ &end_line, &end_bytes);
+
+ string = g_string_new (NULL);
+ g_string_append_printf (string, "%zu:%lu",
+ start_line + 1,
+ g_utf8_pointer_to_offset (start - start_bytes, start) + 1);
+ if (start_line != end_line || start_bytes != end_bytes)
+ {
+ g_string_append (string, "-");
+ if (start_line != end_line)
+ g_string_append_printf (string, "%zu:", end_line + 1);
+ g_string_append_printf (string, "%lu", g_utf8_pointer_to_offset (end - end_bytes, end) + 1);
+ }
/* Translators: the first %s is the program name, the second one
- * is the URI of the file, the third is the error message.
+ * is the URI of the file, the third is the file location and the
+ * final one the error message.
*/
- g_printerr (_("%s: %s: error parsing file: %s\n"),
- g_get_prgname (), g_file_get_uri (file), json_error->message);
+ g_printerr (_("%s: %s: error parsing file: %s: %s\n"),
+ g_get_prgname (), uri, string->str, json_error->message);
+ g_string_free (string, TRUE);
+ g_free (uri);
gtk_json_parser_free (parser);
+ g_bytes_unref (bytes);
return FALSE;
}
gtk_json_parser_free (parser);
+ g_bytes_unref (bytes);
return TRUE;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]