diff options
Diffstat (limited to 'gtk/json')
-rw-r--r-- | gtk/json/gtkjsonparser.c | 502 | ||||
-rw-r--r-- | gtk/json/gtkjsonparserprivate.h | 27 |
2 files changed, 450 insertions, 79 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, @@ -178,6 +181,21 @@ json_skip_characters (const guchar *start, } 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 |