[gobject-introspection] Handle enumerations with the full range of signed and unsigned values



commit 8916db0a3831cb5e6c3b714125f7596f43d6aa6a
Author: Owen W. Taylor <otaylor fishsoup net>
Date:   Tue Sep 14 11:59:03 2010 -0400

    Handle enumerations with the full range of signed and unsigned values
    
    The C compiler will pick an enumeration type that accomodates the specified
    values for the enumeration, so ignoring 64-bit enumerations, we can
    have enumeration values from MININT32 to MAXUINT32. To handle this properly:
    
     - Use gint64 for holding eumeration values when scanning
     - Add a 'unsigned_value' bit to ValueBlob so we can distinguish the
       int32 vs. uint32 cases in the typelib
     - Change the return value of g_value_info_get_value() to gint64.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=629704

 girepository/gienuminfo.c              |   11 ++++++++---
 girepository/gienuminfo.h              |    2 +-
 girepository/gifieldinfo.c             |    6 +++---
 girepository/girnode.c                 |    3 ++-
 girepository/girnode.h                 |    2 +-
 girepository/girparser.c               |   10 +++++-----
 girepository/girwriter.c               |    7 +++++--
 girepository/gitypelib-internal.h      |    4 +++-
 giscanner/gdumpparser.py               |   32 +++++++++++++++++++++++++-------
 giscanner/giscannermodule.c            |    3 ++-
 giscanner/scannerparser.y              |    6 +++---
 giscanner/sourcescanner.h              |    2 +-
 tests/scanner/Regress-1.0-expected.gir |   26 +++++++++++++++++++++++++-
 tests/scanner/regress.c                |   29 +++++++++++++++++++++++++++++
 tests/scanner/regress.h                |   11 ++++++++++-
 15 files changed, 123 insertions(+), 31 deletions(-)
---
diff --git a/girepository/gienuminfo.c b/girepository/gienuminfo.c
index 9ecbc2a..062f3ab 100644
--- a/girepository/gienuminfo.c
+++ b/girepository/gienuminfo.c
@@ -127,9 +127,11 @@ g_enum_info_get_storage_type (GIEnumInfo *info)
  *
  * Obtain the enumeration value of the #GIValueInfo.
  *
- * Returns: the enumeration value
+ * Returns: the enumeration value. This will always be representable
+ *   as a 32-bit signed or unsigned value. The use of gint64 as the
+ *   return type is to allow both.
  */
-glong
+gint64
 g_value_info_get_value (GIValueInfo *info)
 {
   GIRealInfo *rinfo = (GIRealInfo *)info;
@@ -140,6 +142,9 @@ g_value_info_get_value (GIValueInfo *info)
 
   blob = (ValueBlob *)&rinfo->typelib->data[rinfo->offset];
 
-  return (glong)blob->value;
+  if (blob->unsigned_value)
+    return (gint64)(guint32)blob->value;
+  else
+    return (gint64)blob->value;
 }
 
diff --git a/girepository/gienuminfo.h b/girepository/gienuminfo.h
index 211ea6e..6b24fe7 100644
--- a/girepository/gienuminfo.h
+++ b/girepository/gienuminfo.h
@@ -42,7 +42,7 @@ GIValueInfo  * g_enum_info_get_value         (GIEnumInfo  *info,
 					      gint         n);
 GITypeTag      g_enum_info_get_storage_type  (GIEnumInfo  *info);
 
-glong          g_value_info_get_value        (GIValueInfo *info);
+gint64         g_value_info_get_value        (GIValueInfo *info);
 
 G_END_DECLS
 
diff --git a/girepository/gifieldinfo.c b/girepository/gifieldinfo.c
index 21a6db0..1a6b688 100644
--- a/girepository/gifieldinfo.c
+++ b/girepository/gifieldinfo.c
@@ -269,9 +269,9 @@ g_field_info_get_field (GIFieldInfo *field_info,
 	      case GI_INFO_TYPE_FLAGS:
 		{
 		  /* FIXME: there's a mismatch here between the value->v_int we use
-		   * here and the glong result returned from g_value_info_get_value().
-		   * But to switch this to glong, we'd have to make g_function_info_invoke()
-		   * translate value->v_long to the proper ABI for an enum function
+		   * here and the gint64 result returned from g_value_info_get_value().
+		   * But to switch this to gint64, we'd have to make g_function_info_invoke()
+		   * translate value->v_int64 to the proper ABI for an enum function
 		   * call parameter, which will usually be int, and then fix up language
 		   * bindings.
 		   */
diff --git a/girepository/girnode.c b/girepository/girnode.c
index 46fd3c7..5b9df58 100644
--- a/girepository/girnode.c
+++ b/girepository/girnode.c
@@ -2201,8 +2201,9 @@ g_ir_node_build_typelib (GIrNode         *node,
 
 	blob->deprecated = value->deprecated;
 	blob->reserved = 0;
+	blob->unsigned_value = value->value >= 0 ? 1 : 0;
 	blob->name = write_string (node->name, strings, data, offset2);
-	blob->value = value->value;
+	blob->value = (gint32)value->value;
       }
       break;
 
diff --git a/girepository/girnode.h b/girepository/girnode.h
index edb9400..0df1008 100644
--- a/girepository/girnode.h
+++ b/girepository/girnode.h
@@ -262,7 +262,7 @@ struct _GIrNodeValue
 
   gboolean deprecated;
 
-  gint32 value;
+  gint64 value;
 };
 
 struct _GIrNodeConstant
diff --git a/girepository/girparser.c b/girepository/girparser.c
index 8f8f6f4..42525ec 100644
--- a/girepository/girparser.c
+++ b/girepository/girparser.c
@@ -1440,7 +1440,7 @@ start_property (GMarkupParseContext *context,
   return TRUE;
 }
 
-static gint
+static gint64
 parse_value (const gchar *str)
 {
   gchar *shift_op;
@@ -1450,15 +1450,15 @@ parse_value (const gchar *str)
 
   if (shift_op)
     {
-      gint base, shift;
+      gint64 base, shift;
 
-      base = strtol (str, NULL, 10);
-      shift = strtol (shift_op + 3, NULL, 10);
+      base = g_ascii_strtoll (str, NULL, 10);
+      shift = g_ascii_strtoll (shift_op + 3, NULL, 10);
 
       return base << shift;
     }
   else
-    return strtol (str, NULL, 10);
+    return g_ascii_strtoll (str, NULL, 10);
 
   return 0;
 }
diff --git a/girepository/girwriter.c b/girepository/girwriter.c
index 8c4fa2c..e90799c 100644
--- a/girepository/girwriter.c
+++ b/girepository/girwriter.c
@@ -704,7 +704,8 @@ write_value_info (const gchar *namespace,
 		  Xml         *file)
 {
   const gchar *name;
-  glong value;
+  gint64 value;
+  gchar *value_str;
   gboolean deprecated;
 
   name = g_base_info_get_name ((GIBaseInfo *)info);
@@ -712,7 +713,9 @@ write_value_info (const gchar *namespace,
   deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info);
 
   xml_start_element (file, "member");
-  xml_printf (file, " name=\"%s\" value=\"%ld\"", name, value);
+  value_str = g_strdup_printf ("%" G_GINT64_FORMAT, value);
+  xml_printf (file, " name=\"%s\" value=\"%s\"", name, value_str);
+  g_free (value_str);
 
   if (deprecated)
     xml_printf (file, " deprecated=\"1\"");
diff --git a/girepository/gitypelib-internal.h b/girepository/gitypelib-internal.h
index 26fd6bf..de2f131 100644
--- a/girepository/gitypelib-internal.h
+++ b/girepository/gitypelib-internal.h
@@ -632,6 +632,7 @@ typedef struct {
 /**
  * ValueBlob:
  * @deprecated: Whether this value is deprecated
+ * @unsigned_value: if set, value is a 32-bit unsigned integer cast to gint32
  * @value: The numerical value
  * @name: Name of blob
  *
@@ -639,8 +640,9 @@ typedef struct {
  */
 typedef struct {
   guint32 deprecated : 1;
+  guint32 unsigned_value : 1;
   /* <private> */
-  guint32 reserved   :31;
+  guint32 reserved   :30;
   /* <public> */
   guint32 name;
   gint32 value;
diff --git a/giscanner/gdumpparser.py b/giscanner/gdumpparser.py
index 1c6adfe..2f5feb5 100644
--- a/giscanner/gdumpparser.py
+++ b/giscanner/gdumpparser.py
@@ -254,13 +254,36 @@ blob containing data gleaned from GObject's primitive introspection."""
             raise ValueError("Unhandled introspection XML tag %s", xmlnode.tag)
 
     def _introspect_enum(self, xmlnode):
+        type_name = xmlnode.attrib['name']
+        (get_type, c_symbol_prefix) = self._split_type_and_symbol_prefix(xmlnode)
+        try:
+            enum_name = self._transformer.strip_identifier(type_name)
+        except TransformerException, e:
+            message.fatal(e)
+
+        # The scanned member values are more accurate than the values that the
+        # we dumped from GEnumValue.value because GEnumValue.value has the
+        # values as a 32-bit signed integer, even if they were unsigned
+        # in the source code.
+        previous_values = {}
+        previous = self._namespace.get(enum_name)
+        if isinstance(previous, (ast.Enum, ast.Bitfield)):
+            for member in previous.members:
+                previous_values[member.name] = member.value
+
         members = []
         for member in xmlnode.findall('member'):
             # Keep the name closer to what we'd take from C by default;
             # see http://bugzilla.gnome.org/show_bug.cgi?id=575613
             name = member.attrib['nick'].replace('-', '_')
+
+            if name in previous_values:
+                value = previous_values[name]
+            else:
+                value = member.attrib['value']
+
             members.append(ast.Member(name,
-                                      member.attrib['value'],
+                                      value,
                                       member.attrib['name'],
                                       member.attrib['nick']))
 
@@ -269,12 +292,7 @@ blob containing data gleaned from GObject's primitive introspection."""
             klass = ast.Bitfield
         else:
             klass = ast.Enum
-        type_name = xmlnode.attrib['name']
-        (get_type, c_symbol_prefix) = self._split_type_and_symbol_prefix(xmlnode)
-        try:
-            enum_name = self._transformer.strip_identifier(type_name)
-        except TransformerException, e:
-            message.fatal(e)
+
         node = klass(enum_name, type_name,
                      gtype_name=type_name,
                      c_symbol_prefix=c_symbol_prefix,
diff --git a/giscanner/giscannermodule.c b/giscanner/giscannermodule.c
index c639e0f..abea985 100644
--- a/giscanner/giscannermodule.c
+++ b/giscanner/giscannermodule.c
@@ -155,7 +155,8 @@ symbol_get_const_int (PyGISourceSymbol *self,
       Py_INCREF(Py_None);
       return Py_None;
     }
-  return PyInt_FromLong (self->symbol->const_int);
+
+  return PyLong_FromLongLong ((long long)self->symbol->const_int);
 }
 
 static PyObject *
diff --git a/giscanner/scannerparser.y b/giscanner/scannerparser.y
index 80da1b9..d32bbcc 100644
--- a/giscanner/scannerparser.y
+++ b/giscanner/scannerparser.y
@@ -155,11 +155,11 @@ primary_expression
 		$$ = gi_source_symbol_new (CSYMBOL_TYPE_CONST, lineno);
 		$$->const_int_set = TRUE;
 		if (g_str_has_prefix (yytext, "0x") && strlen (yytext) > 2) {
-			$$->const_int = strtol (yytext + 2, NULL, 16);
+			$$->const_int = g_ascii_strtoll (yytext + 2, NULL, 16);
 		} else if (g_str_has_prefix (yytext, "0") && strlen (yytext) > 1) {
-			$$->const_int = strtol (yytext + 1, NULL, 8);
+			$$->const_int = g_ascii_strtoll (yytext + 1, NULL, 8);
 		} else {
-			$$->const_int = atoi (yytext);
+			$$->const_int = g_ascii_strtoll (yytext, NULL, 10);
 		}
 	  }
 	| CHARACTER
diff --git a/giscanner/sourcescanner.h b/giscanner/sourcescanner.h
index 500d84c..b67f037 100644
--- a/giscanner/sourcescanner.h
+++ b/giscanner/sourcescanner.h
@@ -124,7 +124,7 @@ struct _GISourceSymbol
   GISourceType *base_type;
   gboolean const_int_set;
   gboolean private;
-  int const_int;
+  gint64 const_int; /* 64-bit we can handle signed and unsigned 32-bit values */
   char *const_string;
   gboolean const_double_set;
   double const_double;
diff --git a/tests/scanner/Regress-1.0-expected.gir b/tests/scanner/Regress-1.0-expected.gir
index ba3ce29..36a7b0d 100644
--- a/tests/scanner/Regress-1.0-expected.gir
+++ b/tests/scanner/Regress-1.0-expected.gir
@@ -168,10 +168,23 @@ use it should be.</doc>
               c:identifier="REGRESS_TEST_VALUE2"
               glib:nick="value2"/>
       <member name="value3"
-              value="42"
+              value="-1"
               c:identifier="REGRESS_TEST_VALUE3"
               glib:nick="value3"/>
     </enumeration>
+    <enumeration name="TestEnumUnsigned"
+                 glib:type-name="RegressTestEnumUnsigned"
+                 glib:get-type="regress_test_enum_unsigned_get_type"
+                 c:type="RegressTestEnumUnsigned">
+      <member name="value1"
+              value="1"
+              c:identifier="REGRESS_TEST_UNSIGNED_VALUE1"
+              glib:nick="value1"/>
+      <member name="value2"
+              value="2147483648"
+              c:identifier="REGRESS_TEST_UNSIGNED_VALUE2"
+              glib:nick="value2"/>
+    </enumeration>
     <bitfield name="TestFlags"
               glib:type-name="RegressTestFlags"
               glib:get-type="regress_test_flags_get_type"
@@ -2250,6 +2263,17 @@ call and can be released on return.</doc>
         </parameter>
       </parameters>
     </function>
+    <function name="test_unsigned_enum_param"
+              c:identifier="regress_test_unsigned_enum_param">
+      <return-value transfer-ownership="none">
+        <type name="utf8" c:type="gchar*"/>
+      </return-value>
+      <parameters>
+        <parameter name="e" transfer-ownership="none">
+          <type name="TestEnumUnsigned" c:type="RegressTestEnumUnsigned"/>
+        </parameter>
+      </parameters>
+    </function>
     <function name="test_ushort" c:identifier="regress_test_ushort">
       <return-value transfer-ownership="none">
         <type name="gushort" c:type="gushort"/>
diff --git a/tests/scanner/regress.c b/tests/scanner/regress.c
index b83ed98..40562f5 100644
--- a/tests/scanner/regress.c
+++ b/tests/scanner/regress.c
@@ -1238,6 +1238,22 @@ regress_test_enum_get_type (void)
 }
 
 GType
+regress_test_enum_unsigned_get_type (void)
+{
+    static GType etype = 0;
+    if (G_UNLIKELY(etype == 0)) {
+        static const GEnumValue values[] = {
+            { REGRESS_TEST_UNSIGNED_VALUE1, "REGRESS_TEST_UNSIGNED_VALUE1", "value1" },
+            { REGRESS_TEST_UNSIGNED_VALUE2, "REGRESS_TEST_UNSIGNED_VALUE2", "value2" },
+            { 0, NULL, NULL }
+        };
+        etype = g_enum_register_static (g_intern_static_string ("RegressTestEnumUnsigned"), values);
+    }
+
+    return etype;
+}
+
+GType
 regress_test_flags_get_type (void)
 {
     static GType etype = 0;
@@ -1267,6 +1283,19 @@ regress_test_enum_param(RegressTestEnum e)
   return ev->value_nick;
 }
 
+const gchar *
+regress_test_unsigned_enum_param(RegressTestEnumUnsigned e)
+{
+  GEnumValue *ev;
+  GEnumClass *ec;
+
+  ec = g_type_class_ref (regress_test_enum_unsigned_get_type ());
+  ev = g_enum_get_value (ec, e);
+  g_type_class_unref (ec);
+
+  return ev->value_nick;
+}
+
 /* structures */
 
 /**
diff --git a/tests/scanner/regress.h b/tests/scanner/regress.h
index 8426a95..6d729f1 100644
--- a/tests/scanner/regress.h
+++ b/tests/scanner/regress.h
@@ -136,11 +136,17 @@ typedef enum
 {
   REGRESS_TEST_VALUE1,
   REGRESS_TEST_VALUE2,
-  REGRESS_TEST_VALUE3 = 42
+  REGRESS_TEST_VALUE3 = -1
 } RegressTestEnum;
 
 typedef enum
 {
+  REGRESS_TEST_UNSIGNED_VALUE1 = 1,
+  REGRESS_TEST_UNSIGNED_VALUE2 = 0x80000000
+} RegressTestEnumUnsigned;
+
+typedef enum
+{
   REGRESS_TEST_FLAG1 = 1 << 0,
   REGRESS_TEST_FLAG2 = 1 << 1,
   REGRESS_TEST_FLAG3 = 1 << 2,
@@ -148,10 +154,13 @@ typedef enum
 
 GType regress_test_enum_get_type (void) G_GNUC_CONST;
 #define REGRESS_TEST_TYPE_ENUM (regress_test_enum_get_type ())
+GType regress_test_enum_unsigned_get_type (void) G_GNUC_CONST;
+#define REGRESS_TEST_TYPE_ENUM_UNSIGNED (regress_test_enum_unsigned_get_type ())
 GType regress_test_flags_get_type (void) G_GNUC_CONST;
 #define REGRESS_TEST_TYPE_FLAGS (regress_test_flags_get_type ())
 
 const gchar * regress_test_enum_param(RegressTestEnum e);
+const gchar * regress_test_unsigned_enum_param(RegressTestEnumUnsigned e);
 
 /* constants */
 



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