From 8916db0a3831cb5e6c3b714125f7596f43d6aa6a Mon Sep 17 00:00:00 2001 From: "Owen W. Taylor" Date: Tue, 14 Sep 2010 11:59:03 -0400 Subject: 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 9ecbc2ab..062f3abf 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 211ea6ef..6b24fe7e 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 21a6db02..1a6b688c 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 46fd3c7d..5b9df585 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 edb94008..0df10088 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 8f8f6f4d..42525ecd 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 8c4fa2c3..e90799c1 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 26fd6bfb..de2f131b 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; /* */ - guint32 reserved :31; + guint32 reserved :30; /* */ guint32 name; gint32 value; diff --git a/giscanner/gdumpparser.py b/giscanner/gdumpparser.py index 1c6adfe4..2f5feb50 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 c639e0f2..abea9858 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 80da1b9b..d32bbcc8 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 500d84c3..b67f037b 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 ba3ce29b..36a7b0d9 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. c:identifier="REGRESS_TEST_VALUE2" glib:nick="value2"/> + + + + + + + + + + + + + + diff --git a/tests/scanner/regress.c b/tests/scanner/regress.c index b83ed981..40562f55 100644 --- a/tests/scanner/regress.c +++ b/tests/scanner/regress.c @@ -1237,6 +1237,22 @@ regress_test_enum_get_type (void) return etype; } +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) { @@ -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 8426a95e..6d729f1e 100644 --- a/tests/scanner/regress.h +++ b/tests/scanner/regress.h @@ -136,9 +136,15 @@ 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, @@ -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 */ -- cgit v1.2.1