[pango/matthiasc/for-main: 38/38] Update the json parser




commit 63718feabe38dd29f88987493a47afb9f93e2b76
Author: Matthias Clasen <mclasen redhat com>
Date:   Sun Dec 5 21:09:44 2021 -0500

    Update the json parser
    
    This includes better error reporting,
    with error locations.

 pango/json/gtkjsonparser.c        | 877 +++++++++++++++++++++++++++++---------
 pango/json/gtkjsonparserprivate.h |  36 +-
 pango/serializer.c                |  89 ++--
 3 files changed, 763 insertions(+), 239 deletions(-)
---
diff --git a/pango/json/gtkjsonparser.c b/pango/json/gtkjsonparser.c
index 91048fec..e82d974d 100644
--- a/pango/json/gtkjsonparser.c
+++ b/pango/json/gtkjsonparser.c
@@ -42,9 +42,12 @@ struct _GtkJsonParser
 {
   GBytes *bytes;
   const guchar *reader; /* current read head, pointing as far as we've read */
+  const guchar *start; /* pointer at start of data, after optional BOM */
   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 +57,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 +181,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 +210,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 (self->start <= 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,37 +256,144 @@ 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 = self->start;
+
   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_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 = self->start;
+
+  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 = self->start;
+
+  va_start (args, format);
+  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);
 }
 
 static gboolean
 gtk_json_parser_is_eof (GtkJsonParser *self)
 {
-  return self->reader >= self->end || *self->reader == '\0';
+  return self->reader >= self->end;
 }
 
 static gsize
@@ -247,6 +404,18 @@ gtk_json_parser_remaining (GtkJsonParser *self)
   return self->end - self->reader;
 }
 
+static void
+gtk_json_parser_skip_bom (GtkJsonParser *self)
+{
+  if (gtk_json_parser_remaining (self) < 3)
+    return;
+
+  if (self->reader[0] == 0xEF &&
+      self->reader[1] == 0xBB &&
+      self->reader[2] == 0xBF)
+    self->reader += 3;
+}
+
 static void
 gtk_json_parser_skip_whitespace (GtkJsonParser *self)
 {
@@ -375,6 +544,58 @@ gtk_json_unescape_char (const guchar *json_escape,
     }
 }
                    
+typedef struct _JsonStringIter JsonStringIter;
+struct _JsonStringIter
+{
+  char buf[6];
+  const guchar *s;
+  const guchar *next;
+};
+
+static gsize
+json_string_iter_next (JsonStringIter *iter)
+{
+  gsize len;
+
+  iter->s = iter->next;
+  iter->next = json_find_character (iter->s, STRING_MARKER);
+  if (iter->next != iter->s)
+    return iter->next - iter->s;
+  if (*iter->next == '"')
+    return 0;
+  iter->next += gtk_json_unescape_char (iter->next, iter->buf, &len);
+  iter->s = (const guchar *) iter->buf;
+  return len;
+}
+
+/* The escaped string MUST be valid json, so it must begin
+ * with " and end with " and must not contain any invalid
+ * escape codes.
+ * This function is meant to be fast
+ */
+static gsize
+json_string_iter_init (JsonStringIter *iter,
+                       const guchar   *string)
+{
+  g_assert (*string == '"');
+
+  iter->next = string + 1;
+
+  return json_string_iter_next (iter);
+}
+
+static gboolean
+json_string_iter_has_next (JsonStringIter *iter)
+{
+  return *iter->next != '"';
+}
+
+static const char *
+json_string_iter_get (JsonStringIter *iter)
+{
+  return (const char *) iter->s;
+}
+
 /* The escaped string MUST be valid json, so it must begin
  * with " and end with " and must not contain any invalid
  * escape codes.
@@ -383,44 +604,37 @@ gtk_json_unescape_char (const guchar *json_escape,
 static char *
 gtk_json_unescape_string (const guchar *escaped)
 {
-  char buf[6];
-  gsize buf_size;
+  JsonStringIter iter;
   GString *string;
-  const guchar *last, *s;
+  gsize len;
 
+  len = json_string_iter_init (&iter, escaped);
   string = NULL;
 
-  g_assert (*escaped == '"');
-  last = escaped + 1;
-  for (s = json_find_character (last, STRING_MARKER);
-       *s != '"';
-       s = json_find_character (last, STRING_MARKER))
-    {
-      g_assert (*s == '\\');
-      if (string == NULL)
-        string = g_string_new (NULL);
-      g_string_append_len (string, (const char *) last, s - last);
-      last = s + gtk_json_unescape_char (s, buf, &buf_size);
-      g_string_append_len (string, buf, buf_size);
-    }
+  if (!json_string_iter_has_next (&iter))
+    return g_strndup (json_string_iter_get (&iter), len);
 
-  if (string)
-    {
-      g_string_append_len (string, (const char *) last, s - last);
-      return g_string_free (string, FALSE);
-    }
-  else
+  string = g_string_new (NULL);
+
+  do
     {
-      return g_strndup ((const char *) last, s - last);
+      g_string_append_len (string, json_string_iter_get (&iter), len);
     }
+  while ((len = json_string_iter_next (&iter)));
+  
+  return g_string_free (string, FALSE);
 }
 
 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;
     }
 
@@ -430,7 +644,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)
@@ -451,9 +670,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 '\\':
@@ -467,83 +688,127 @@ 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
                         {
                           unichar = 0;
                         }
-                    }
 
-                  if (unichar == 0)
-                    {
-                      gtk_json_parser_syntax_error (self, "Invalid UTF-16 surrogate pair");
-                      return FALSE;
+                      if (unichar == 0)
+                        {
+                          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++;
 
@@ -552,29 +817,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
@@ -614,7 +891,19 @@ 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 + 3;
+      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 if (*self->reader == 0)
+    gtk_json_parser_syntax_error (self, "Unexpected nul byte in document");
+  else
+    gtk_json_parser_syntax_error (self, "Expected a value");
   return FALSE;
 }
 
@@ -689,9 +978,9 @@ gtk_json_parser_new_for_bytes (GBytes *bytes)
   self->block = self->blocks;
   self->block->type = GTK_JSON_BLOCK_TOPLEVEL;
 
-  gtk_json_parser_skip_whitespace (self);
-  self->block->value = self->reader;
-  gtk_json_parser_parse_value (self);
+  gtk_json_parser_skip_bom (self);
+  self->start = self->reader;
+  gtk_json_parser_rewind (self);
 
   return self;
 }
@@ -702,8 +991,6 @@ gtk_json_parser_free (GtkJsonParser *self)
   if (self == NULL)
     return;
 
-  g_clear_error (&self->error);
-
   g_bytes_unref (self->bytes);
 
   if (self->blocks != self->blocks_preallocated)
@@ -715,24 +1002,40 @@ gtk_json_parser_free (GtkJsonParser *self)
 static gboolean
 gtk_json_parser_skip_block (GtkJsonParser *self)
 {
+  gsize depth;
+
   if (self->reader != self->block->value)
     return TRUE;
 
-  if (*self->reader == '{')
-    {
-      return gtk_json_parser_start_object (self) &&
-             gtk_json_parser_end (self);
-    }
-  else if (*self->reader == '[')
-    {
-      return gtk_json_parser_start_array (self) &&
-             gtk_json_parser_end (self);
-    }
-  else
+  depth = gtk_json_parser_get_depth (self);
+  while (TRUE)
     {
-      g_assert_not_reached ();
-      return FALSE;
+      if (*self->reader == '{')
+        {
+          if (!gtk_json_parser_start_object (self))
+            return FALSE;
+        }
+      else if (*self->reader == '[')
+        {
+          if (!gtk_json_parser_start_array (self))
+            return FALSE;
+        }
+    
+      while (self->reader != self->block->value)
+        {
+          /* This should never be reentrant to this function or we might
+           * loop causing stack overflow */
+          if (!gtk_json_parser_next (self))
+            {
+              if (!gtk_json_parser_end (self))
+                return FALSE;
+              if (depth >= gtk_json_parser_get_depth (self))
+                return TRUE;
+            }
+        }
     }
+
+  return TRUE;
 }
 
 gboolean
@@ -757,14 +1060,14 @@ gtk_json_parser_next (GtkJsonParser *self)
       if (gtk_json_parser_is_eof (self))
         {
           self->block->value = NULL;
-          if (gtk_json_parser_remaining (self))
-            {
-              gtk_json_parser_syntax_error (self, "Unexpected nul byte in document");
-            }
+        }
+      else if (*self->reader == 0)
+        {
+          gtk_json_parser_syntax_error (self, "Unexpected nul byte in document");
         }
       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;
 
@@ -772,7 +1075,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;
         }
@@ -788,6 +1094,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))
@@ -809,7 +1120,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;
         }
@@ -839,6 +1153,52 @@ gtk_json_parser_next (GtkJsonParser *self)
   return TRUE;
 }
 
+void
+gtk_json_parser_rewind (GtkJsonParser *self)
+{
+  if (self->error)
+    return;
+
+  switch (self->block->type)
+    {
+    case GTK_JSON_BLOCK_OBJECT:
+      gtk_json_parser_pop_block (self);
+      self->reader = self->block->value;
+      gtk_json_parser_start_object (self);
+      break;
+
+    case GTK_JSON_BLOCK_ARRAY:
+      gtk_json_parser_pop_block (self);
+      self->reader = self->block->value;
+      gtk_json_parser_start_array (self);
+      break;
+
+    case GTK_JSON_BLOCK_TOPLEVEL:
+      self->reader = self->start;
+      gtk_json_parser_skip_whitespace (self);
+      if (gtk_json_parser_is_eof (self))
+        {
+          gtk_json_parser_syntax_error_at (self, self->start, self->reader, "Empty document");
+        }
+      else
+        {
+          self->block->value = self->reader;
+          gtk_json_parser_parse_value (self);
+        }
+      break;
+
+    default:
+      g_assert_not_reached ();
+      return;
+    }
+}
+
+gsize
+gtk_json_parser_get_depth (GtkJsonParser *self)
+{
+  return self->block - self->blocks;
+}
+
 GtkJsonNode
 gtk_json_parser_get_node (GtkJsonParser *self)
 {
@@ -857,43 +1217,214 @@ gtk_json_parser_get_error (GtkJsonParser *self)
   return self->error;
 }
 
-char *
-gtk_json_parser_get_member_name (GtkJsonParser *self)
+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 = self->start;
+  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_supports_member (GtkJsonParser *self)
 {
   if (self->error)
-    return NULL;
+    return FALSE;
 
   if (self->block->type != GTK_JSON_BLOCK_OBJECT)
-    return NULL;
+    return FALSE;
 
   if (self->block->member_name == NULL)
+    return FALSE;
+
+  return TRUE;
+}
+
+char *
+gtk_json_parser_get_member_name (GtkJsonParser *self)
+{
+  if (!gtk_json_parser_supports_member (self))
     return NULL;
 
   return gtk_json_unescape_string (self->block->member_name);
 }
 
+gboolean
+gtk_json_parser_has_member (GtkJsonParser *self,
+                            const char    *name)
+{
+  JsonStringIter iter;
+  gsize found, len;
+
+  if (!gtk_json_parser_supports_member (self))
+    return FALSE;
+
+  found = 0;
+
+  for (len = json_string_iter_init (&iter, self->block->member_name);
+       len > 0;
+       len = json_string_iter_next (&iter))
+    {
+      const char *s = json_string_iter_get (&iter);
+
+      if (strncmp (name + found, s, len) != 0)
+        return FALSE;
+
+      found += len;
+    }
+
+  return TRUE;
+}
+
+gboolean
+gtk_json_parser_find_member (GtkJsonParser *self,
+                             const char    *name)
+{
+  if (!gtk_json_parser_supports_member (self))
+    {
+      while (gtk_json_parser_next (self));
+      return FALSE;
+    }
+
+  gtk_json_parser_rewind (self);
+
+  do
+    {
+      if (gtk_json_parser_has_member (self, name))
+        return TRUE;
+    }
+  while (gtk_json_parser_next (self));
+
+  return FALSE;
+}
+
 gssize
 gtk_json_parser_select_member (GtkJsonParser      *self,
                                const char * const *options)
 {
-  char *member_name;
-  gssize i;
+  JsonStringIter iter;
+  gssize i, j;
+  gsize found, len;
 
-  member_name = gtk_json_parser_get_member_name (self);
-  if (member_name == NULL)
+  if (!gtk_json_parser_supports_member (self))
     return -1;
 
-  for (i = 0; options[i]; i++)
+  if (options[0] == NULL)
+    return -1;
+
+  found = 0;
+  i = 0;
+
+  for (len = json_string_iter_init (&iter, self->block->member_name);
+       len > 0;
+       len = json_string_iter_next (&iter))
     {
-      if (strcmp (member_name, options[i]) == 0)
-        break;
+      const char *s = json_string_iter_get (&iter);
+
+      if (strncmp (options[i] + found, s, len) != 0)
+        {
+          for (j = i + 1; options[j]; j++)
+            {
+              if (strncmp (options[j], options[i], found) == 0 &&
+                  strncmp (options[j] + found, s, len) == 0)
+                {
+                  i = j;
+                  break;
+                }
+            }
+          if (j != i)
+            return -1;
+        }
+      found += len;
     }
-  if (options[i] == NULL)
-    i = -1;
 
-  g_free (member_name);
+  if (options[i][found] == 0)
+    return i;
+
+  for (j = i + 1; options[j]; i++)
+    {
+      if (strncmp (options[j], options[i], found) != 0)
+        continue;
+      if (options[j][found] == 0)
+        return j;
+    }
 
-  return i;
+  return -1;
 }
 
 gboolean
@@ -910,7 +1441,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;
 }
 
@@ -927,7 +1458,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;
     }
 
@@ -957,49 +1488,6 @@ gtk_json_parser_get_number (GtkJsonParser *self)
   return result;
 }
 
-int
-gtk_json_parser_get_int (GtkJsonParser *self)
-{
-  int result;
-
-  if (self->error)
-    return 0;
-
-  if (self->block->value == NULL)
-    return 0;
-
-  if (!strchr ("-0123456789", *self->block->value))
-    {
-      gtk_json_parser_value_error (self, "Expected a number");
-      return 0;
-    }
-
-  errno = 0;
-  if (gtk_json_parser_remaining (self) == 0)
-    {
-      /* need terminated string here */
-      char *s = g_strndup ((const char *) self->block->value, self->reader - self->block->value);
-      result = g_ascii_strtod (s, NULL);
-      g_free (s);
-    }
-  else
-    {
-      result = (int) g_ascii_strtoll ((const char *) self->block->value, NULL, 10);
-    }
-
-  if (errno)
-    {
-      if (errno == ERANGE)
-        gtk_json_parser_value_error (self, "Number out of range");
-      else
-        gtk_json_parser_value_error (self, "%s", g_strerror (errno));
-
-      return 0;
-    }
-
-  return result;
-}
-
 #if 0
 int                     gtk_json_parser_get_int                 (GtkJsonParser          *self);
 guint                   gtk_json_parser_get_uint                (GtkJsonParser          *self);
@@ -1016,7 +1504,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 ("");
     }
 
@@ -1031,7 +1519,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;
     }
 
@@ -1040,11 +1528,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))
@@ -1072,7 +1569,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;
     }
 
@@ -1080,7 +1577,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, ']'))
@@ -1131,12 +1631,3 @@ gtk_json_parser_end (GtkJsonParser *self)
   return TRUE;
 }
 
-void
-gtk_json_parser_set_error (GtkJsonParser *self,
-                           GError        *error)
-{
-  if (self->error)
-    g_error_free (error);
-  else
-    self->error = error;
-}
diff --git a/pango/json/gtkjsonparserprivate.h b/pango/json/gtkjsonparserprivate.h
index 81c001cc..38f508ab 100644
--- a/pango/json/gtkjsonparserprivate.h
+++ b/pango/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);
@@ -44,9 +55,14 @@ GtkJsonParser *         gtk_json_parser_new_for_string          (const char
 void                    gtk_json_parser_free                    (GtkJsonParser          *self);
 
 gboolean                gtk_json_parser_next                    (GtkJsonParser          *self);
+void                    gtk_json_parser_rewind                  (GtkJsonParser          *self);
+gsize                   gtk_json_parser_get_depth               (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);
+gboolean                gtk_json_parser_has_member              (GtkJsonParser          *self,
+                                                                 const char             *name);
+gboolean                gtk_json_parser_find_member             (GtkJsonParser          *self,
+                                                                 const char             *name);
 gssize                  gtk_json_parser_select_member           (GtkJsonParser          *self,
                                                                  const char * const     *options);
 
@@ -60,9 +76,21 @@ gboolean                gtk_json_parser_start_object            (GtkJsonParser
 gboolean                gtk_json_parser_start_array             (GtkJsonParser          *self);
 gboolean                gtk_json_parser_end                     (GtkJsonParser          *self);
 
-void                    gtk_json_parser_set_error               (GtkJsonParser          *self,
-                                                                 GError                 *error);
-
+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/pango/serializer.c b/pango/serializer.c
index 39b9dd6e..385410cc 100644
--- a/pango/serializer.c
+++ b/pango/serializer.c
@@ -644,12 +644,10 @@ parser_get_enum_value (GtkJsonParser  *parser,
         }
     }
 
-  gtk_json_parser_set_error (parser,
-      g_error_new (PANGO_LAYOUT_DESERIALIZE_ERROR,
-                   PANGO_LAYOUT_DESERIALIZE_INVALID_VALUE,
-                   "Could not parse enum value of type %s: %s",
-                   g_type_name (type),
-                   str));
+  gtk_json_parser_value_error (parser,
+                               "Could not parse enum value of type %s: %s",
+                               g_type_name (type),
+                               str);
 
   g_free (str);
 
@@ -663,10 +661,8 @@ parser_get_font_description (GtkJsonParser *parser)
   PangoFontDescription *desc = pango_font_description_from_string (str);
 
   if (!desc)
-    gtk_json_parser_set_error (parser,
-        g_error_new (PANGO_LAYOUT_DESERIALIZE_ERROR,
-                     PANGO_LAYOUT_DESERIALIZE_INVALID_VALUE,
-                     "Failed to parse font: %s", str));
+    gtk_json_parser_value_error (parser,
+                                 "Failed to parse font: %s", str);
   g_free (str);
 
   return desc;
@@ -679,10 +675,8 @@ parser_get_color (GtkJsonParser *parser,
   char *str = gtk_json_parser_get_string (parser);
   if (!pango_color_parse (color, str))
     {
-      gtk_json_parser_set_error (parser,
-          g_error_new (PANGO_LAYOUT_DESERIALIZE_ERROR,
-                       PANGO_LAYOUT_DESERIALIZE_INVALID_VALUE,
-                       "Failed to parse color: %s", str));
+      gtk_json_parser_value_error (parser,
+                                   "Failed to parse color: %s", str);
       color->red = color->green = color->blue = 0;
     }
 
@@ -706,10 +700,7 @@ attr_for_type (GtkJsonParser *parser,
       g_assert_not_reached ();
 
     case PANGO_ATTR_INVALID:
-      gtk_json_parser_set_error (parser,
-          g_error_new (PANGO_LAYOUT_DESERIALIZE_ERROR,
-                       PANGO_LAYOUT_DESERIALIZE_MISSING_VALUE,
-                       "Missing attribute type"));
+      gtk_json_parser_schema_error (parser, "Missing attribute type");
       return NULL;
 
     case PANGO_ATTR_LANGUAGE:
@@ -741,7 +732,7 @@ attr_for_type (GtkJsonParser *parser,
       break;
 
     case PANGO_ATTR_SIZE:
-      attr = pango_attr_size_new (gtk_json_parser_get_int (parser));
+      attr = pango_attr_size_new ((int)gtk_json_parser_get_number (parser));
       break;
 
     case PANGO_ATTR_FONT_DESC:
@@ -769,7 +760,7 @@ attr_for_type (GtkJsonParser *parser,
       break;
 
     case PANGO_ATTR_RISE:
-      attr = pango_attr_rise_new (gtk_json_parser_get_int (parser));
+      attr = pango_attr_rise_new ((int)gtk_json_parser_get_number (parser));
       break;
 
     case PANGO_ATTR_SHAPE:
@@ -786,7 +777,7 @@ attr_for_type (GtkJsonParser *parser,
       break;
 
     case PANGO_ATTR_LETTER_SPACING:
-      attr = pango_attr_letter_spacing_new (gtk_json_parser_get_int (parser));
+      attr = pango_attr_letter_spacing_new ((int)gtk_json_parser_get_number (parser));
       break;
 
     case PANGO_ATTR_UNDERLINE_COLOR:
@@ -800,7 +791,7 @@ attr_for_type (GtkJsonParser *parser,
       break;
 
     case PANGO_ATTR_ABSOLUTE_SIZE:
-      attr = pango_attr_size_new_absolute (gtk_json_parser_get_int (parser));
+      attr = pango_attr_size_new_absolute ((int)gtk_json_parser_get_number (parser));
       break;
 
     case PANGO_ATTR_GRAVITY:
@@ -818,11 +809,11 @@ attr_for_type (GtkJsonParser *parser,
       break;
 
     case PANGO_ATTR_FOREGROUND_ALPHA:
-      attr = pango_attr_foreground_alpha_new (gtk_json_parser_get_int (parser));
+      attr = pango_attr_foreground_alpha_new ((int)gtk_json_parser_get_number (parser));
       break;
 
     case PANGO_ATTR_BACKGROUND_ALPHA:
-      attr = pango_attr_background_alpha_new (gtk_json_parser_get_int (parser));
+      attr = pango_attr_background_alpha_new ((int)gtk_json_parser_get_number (parser));
       break;
 
     case PANGO_ATTR_ALLOW_BREAKS:
@@ -830,11 +821,11 @@ attr_for_type (GtkJsonParser *parser,
       break;
 
     case PANGO_ATTR_SHOW:
-      attr = pango_attr_show_new (gtk_json_parser_get_int (parser));
+      attr = pango_attr_show_new ((int)gtk_json_parser_get_number (parser));
       break;
 
     case PANGO_ATTR_INSERT_HYPHENS:
-      attr = pango_attr_insert_hyphens_new (gtk_json_parser_get_int (parser));
+      attr = pango_attr_insert_hyphens_new ((int)gtk_json_parser_get_number (parser));
       break;
 
     case PANGO_ATTR_OVERLINE:
@@ -851,7 +842,7 @@ attr_for_type (GtkJsonParser *parser,
       break;
 
     case PANGO_ATTR_ABSOLUTE_LINE_HEIGHT:
-      attr = pango_attr_line_height_new_absolute (gtk_json_parser_get_int (parser));
+      attr = pango_attr_line_height_new_absolute ((int)gtk_json_parser_get_number (parser));
       break;
 
     case PANGO_ATTR_TEXT_TRANSFORM:
@@ -911,11 +902,11 @@ json_to_attribute (GtkJsonParser *parser)
       switch (gtk_json_parser_select_member (parser, attr_members))
         {
         case ATTR_START:
-          start = gtk_json_parser_get_int (parser);
+          start = (int)gtk_json_parser_get_number (parser);
           break;
 
         case ATTR_END:
-          end = gtk_json_parser_get_int (parser);
+          end = (int)gtk_json_parser_get_number (parser);
           break;
 
         case ATTR_TYPE:
@@ -933,10 +924,7 @@ json_to_attribute (GtkJsonParser *parser)
   while (gtk_json_parser_next (parser));
 
   if (!attr && !gtk_json_parser_get_error (parser))
-    gtk_json_parser_set_error (parser,
-        g_error_new (PANGO_LAYOUT_DESERIALIZE_ERROR,
-                     PANGO_LAYOUT_DESERIALIZE_MISSING_VALUE,
-                     "Attribute missing \"value\""));
+    gtk_json_parser_schema_error (parser, "Attribute missing \"value\"");
 
   gtk_json_parser_end (parser);
 
@@ -996,7 +984,7 @@ json_parser_fill_tabs (GtkJsonParser *parser,
               switch (gtk_json_parser_select_member (parser, tab_members))
                 {
                 case TAB_POSITION:
-                  pos = gtk_json_parser_get_int (parser);
+                  pos = (int) gtk_json_parser_get_number (parser);
                   break;
 
                 case TAB_ALIGNMENT:
@@ -1004,7 +992,7 @@ json_parser_fill_tabs (GtkJsonParser *parser,
                   break;
 
                 case TAB_DECIMAL_POINT:
-                  ch = gtk_json_parser_get_int (parser);
+                  ch = (int) gtk_json_parser_get_number (parser);
                   break;
 
                 default:
@@ -1016,7 +1004,7 @@ json_parser_fill_tabs (GtkJsonParser *parser,
           gtk_json_parser_end (parser);
         }
       else
-        pos = gtk_json_parser_get_int (parser);
+        pos = (int) gtk_json_parser_get_number (parser);
 
       pango_tab_array_set_tab (tabs, index, align, pos);
       pango_tab_array_set_decimal_point (tabs, index, ch);
@@ -1289,19 +1277,19 @@ json_parser_fill_layout (GtkJsonParser               *parser,
           break;
 
         case LAYOUT_WIDTH:
-          pango_layout_set_width (layout, gtk_json_parser_get_int (parser));
+          pango_layout_set_width (layout, (int) gtk_json_parser_get_number (parser));
           break;
 
         case LAYOUT_HEIGHT:
-          pango_layout_set_height (layout, gtk_json_parser_get_int (parser));
+          pango_layout_set_height (layout, (int) gtk_json_parser_get_number (parser));
           break;
 
         case LAYOUT_INDENT:
-          pango_layout_set_indent (layout, gtk_json_parser_get_int (parser));
+          pango_layout_set_indent (layout, (int) gtk_json_parser_get_number (parser));
           break;
 
         case LAYOUT_SPACING:
-          pango_layout_set_spacing (layout, gtk_json_parser_get_int (parser));
+          pango_layout_set_spacing (layout, (int) gtk_json_parser_get_number (parser));
           break;
 
         case LAYOUT_LINE_SPACING:
@@ -1482,6 +1470,7 @@ pango_layout_deserialize (PangoContext                 *context,
 {
   PangoLayout *layout;
   GtkJsonParser *parser;
+  const GError *parser_error;
 
   g_return_val_if_fail (PANGO_IS_CONTEXT (context), NULL);
 
@@ -1490,9 +1479,25 @@ pango_layout_deserialize (PangoContext                 *context,
   parser = gtk_json_parser_new_for_bytes (bytes);
   json_parser_fill_layout (parser, layout, flags);
 
-  if (gtk_json_parser_get_error (parser))
+  parser_error = gtk_json_parser_get_error (parser);
+
+  if (parser_error)
     {
-      g_propagate_error (error, g_error_copy (gtk_json_parser_get_error (parser)));
+      gsize start, end;
+      int code;
+
+      gtk_json_parser_get_error_offset (parser, &start, &end);
+
+      if (g_error_matches (parser_error, GTK_JSON_ERROR, GTK_JSON_ERROR_VALUE))
+        code = PANGO_LAYOUT_DESERIALIZE_INVALID_VALUE;
+      else if (g_error_matches (parser_error, GTK_JSON_ERROR, GTK_JSON_ERROR_SCHEMA))
+        code = PANGO_LAYOUT_DESERIALIZE_MISSING_VALUE;
+      else
+        code = PANGO_LAYOUT_DESERIALIZE_INVALID;
+
+      g_set_error (error, PANGO_LAYOUT_DESERIALIZE_ERROR, code,
+                   "%ld:%ld: %s", start, end, parser_error->message);
+
       g_clear_object (&layout);
     }
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]