mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-01-13 15:56:23 +01:00
Merge branch 'gdbus-marshallers' into 'main'
gdbus-codegen: Generate specific marshallers for each required type Closes #3028 See merge request GNOME/glib!3491
This commit is contained in:
commit
4ef693a457
@ -1138,7 +1138,7 @@ class InterfaceInfoBodyCodeGenerator:
|
||||
"const %s * const %s[] =\n" % (element_type, array_name_lower)
|
||||
)
|
||||
self.outfile.write("{\n")
|
||||
for (_, name) in elements:
|
||||
for _, name in elements:
|
||||
self.outfile.write(" &%s,\n" % name)
|
||||
self.outfile.write(" NULL,\n")
|
||||
self.outfile.write("};\n")
|
||||
@ -1462,6 +1462,7 @@ class CodeGenerator:
|
||||
self.glib_min_required = glib_min_required
|
||||
self.symbol_decoration_define = symbol_decoration_define
|
||||
self.outfile = outfile
|
||||
self.marshallers = set()
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------
|
||||
|
||||
@ -1486,6 +1487,55 @@ class CodeGenerator:
|
||||
"#ifdef G_OS_UNIX\n" "# include <gio/gunixfdlist.h>\n" "#endif\n" "\n"
|
||||
)
|
||||
|
||||
self.outfile.write(
|
||||
"""#ifdef G_ENABLE_DEBUG
|
||||
#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v)
|
||||
#define g_marshal_value_peek_char(v) g_value_get_schar (v)
|
||||
#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v)
|
||||
#define g_marshal_value_peek_int(v) g_value_get_int (v)
|
||||
#define g_marshal_value_peek_uint(v) g_value_get_uint (v)
|
||||
#define g_marshal_value_peek_long(v) g_value_get_long (v)
|
||||
#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v)
|
||||
#define g_marshal_value_peek_int64(v) g_value_get_int64 (v)
|
||||
#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v)
|
||||
#define g_marshal_value_peek_enum(v) g_value_get_enum (v)
|
||||
#define g_marshal_value_peek_flags(v) g_value_get_flags (v)
|
||||
#define g_marshal_value_peek_float(v) g_value_get_float (v)
|
||||
#define g_marshal_value_peek_double(v) g_value_get_double (v)
|
||||
#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v)
|
||||
#define g_marshal_value_peek_param(v) g_value_get_param (v)
|
||||
#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v)
|
||||
#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v)
|
||||
#define g_marshal_value_peek_object(v) g_value_get_object (v)
|
||||
#define g_marshal_value_peek_variant(v) g_value_get_variant (v)
|
||||
#else /* !G_ENABLE_DEBUG */
|
||||
/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
|
||||
* Do not access GValues directly in your code. Instead, use the
|
||||
* g_value_get_*() functions
|
||||
*/
|
||||
#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int
|
||||
#define g_marshal_value_peek_char(v) (v)->data[0].v_int
|
||||
#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint
|
||||
#define g_marshal_value_peek_int(v) (v)->data[0].v_int
|
||||
#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint
|
||||
#define g_marshal_value_peek_long(v) (v)->data[0].v_long
|
||||
#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong
|
||||
#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64
|
||||
#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64
|
||||
#define g_marshal_value_peek_enum(v) (v)->data[0].v_long
|
||||
#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong
|
||||
#define g_marshal_value_peek_float(v) (v)->data[0].v_float
|
||||
#define g_marshal_value_peek_double(v) (v)->data[0].v_double
|
||||
#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer
|
||||
#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer
|
||||
#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer
|
||||
#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer
|
||||
#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer
|
||||
#define g_marshal_value_peek_variant(v) (v)->data[0].v_pointer
|
||||
#endif /* !G_ENABLE_DEBUG */"""
|
||||
"\n\n"
|
||||
)
|
||||
|
||||
self.outfile.write(
|
||||
"typedef struct\n"
|
||||
"{\n"
|
||||
@ -2136,7 +2186,7 @@ class CodeGenerator:
|
||||
" G_STRUCT_OFFSET (%sIface, handle_%s),\n"
|
||||
" g_signal_accumulator_true_handled,\n"
|
||||
" NULL,\n" # accu_data
|
||||
" g_cclosure_marshal_generic,\n"
|
||||
f" {i.name_lower}_method_marshal_{m.name_lower},\n"
|
||||
" G_TYPE_BOOLEAN,\n"
|
||||
" %d,\n"
|
||||
" G_TYPE_DBUS_METHOD_INVOCATION"
|
||||
@ -2187,7 +2237,7 @@ class CodeGenerator:
|
||||
" G_STRUCT_OFFSET (%sIface, %s),\n"
|
||||
" NULL,\n" # accumulator
|
||||
" NULL,\n" # accu_data
|
||||
" g_cclosure_marshal_generic,\n"
|
||||
f" {i.name_lower}_signal_marshal_{s.name_lower},\n"
|
||||
" G_TYPE_NONE,\n"
|
||||
" %d"
|
||||
% (s.name_hyphen, i.camel_name, s.name_lower, len(s.args))
|
||||
@ -2397,7 +2447,6 @@ class CodeGenerator:
|
||||
self.outfile.write("}\n")
|
||||
self.outfile.write("\n")
|
||||
if p.arg.free_func is not None:
|
||||
|
||||
self.outfile.write(
|
||||
self.docbook_gen.expand(
|
||||
"/**\n"
|
||||
@ -2526,6 +2575,159 @@ class CodeGenerator:
|
||||
|
||||
# ---------------------------------------------------------------------------------------------------
|
||||
|
||||
def generate_marshaller(self, func_name, in_args, ret_arg=None):
|
||||
self.generate_marshaller_declaration(func_name, uses_ret=ret_arg is not None)
|
||||
self.outfile.write("{\n")
|
||||
self.generate_marshaller_body(func_name, in_args, ret_arg)
|
||||
self.outfile.write("}\n" "\n")
|
||||
|
||||
def generate_marshaller_wrapper(self, wrapper_name, wrapped_func):
|
||||
self.generate_marshaller_declaration(
|
||||
wrapper_name,
|
||||
uses_ret=True,
|
||||
uses_hint=True,
|
||||
inline=True,
|
||||
)
|
||||
self.outfile.write("{\n")
|
||||
self.outfile.write(
|
||||
f" {wrapped_func} (closure,\n"
|
||||
" return_value, n_param_values, param_values, "
|
||||
"invocation_hint, marshal_data);\n"
|
||||
)
|
||||
self.outfile.write("}\n" "\n")
|
||||
|
||||
def generate_marshaller_declaration(
|
||||
self, func_name, uses_ret=False, uses_hint=False, inline=False
|
||||
):
|
||||
self.outfile.write(
|
||||
f"{'inline ' if inline else ''}static void\n"
|
||||
f"{func_name} (\n"
|
||||
" GClosure *closure,\n"
|
||||
f" GValue *return_value{' G_GNUC_UNUSED' if not uses_ret else ''},\n"
|
||||
" unsigned int n_param_values,\n"
|
||||
" const GValue *param_values,\n"
|
||||
f" void *invocation_hint{' G_GNUC_UNUSED' if not uses_hint else ''},\n"
|
||||
" void *marshal_data)\n"
|
||||
)
|
||||
|
||||
def generate_marshaller_body(self, func_name, in_args=[], ret_arg=None):
|
||||
marshal_func_type = f"{utils.uscore_to_camel_case(func_name)}Func"
|
||||
self.outfile.write(
|
||||
f" typedef {ret_arg.ctype_in if ret_arg else 'void '}(*{marshal_func_type})\n"
|
||||
" (void *data1,\n"
|
||||
+ "".join([f" {a.ctype_in}arg_{a.name},\n" for a in in_args])
|
||||
+ " void *data2);\n"
|
||||
f" {marshal_func_type} callback;\n"
|
||||
" GCClosure *cc = (GCClosure*) closure;\n"
|
||||
f" void *data1, *data2;\n"
|
||||
)
|
||||
|
||||
if ret_arg:
|
||||
self.outfile.write(
|
||||
f" {ret_arg.ctype_in}v_return;\n"
|
||||
"\n"
|
||||
" g_return_if_fail (return_value != NULL);"
|
||||
)
|
||||
|
||||
self.outfile.write(
|
||||
"\n"
|
||||
f" g_return_if_fail (n_param_values == {len(in_args) + 1});\n"
|
||||
"\n"
|
||||
" if (G_CCLOSURE_SWAP_DATA (closure))\n"
|
||||
" {\n"
|
||||
" data1 = closure->data;\n"
|
||||
" data2 = g_value_peek_pointer (param_values + 0);\n"
|
||||
" }\n"
|
||||
" else\n"
|
||||
" {\n"
|
||||
" data1 = g_value_peek_pointer (param_values + 0);\n"
|
||||
" data2 = closure->data;\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
f" callback = ({marshal_func_type})\n"
|
||||
" (marshal_data ? marshal_data : cc->callback);\n"
|
||||
"\n"
|
||||
)
|
||||
|
||||
prefix = ""
|
||||
if ret_arg:
|
||||
self.outfile.write(" v_return =\n")
|
||||
prefix = 2 * " "
|
||||
|
||||
self.outfile.write(
|
||||
f"{prefix} callback (data1,\n"
|
||||
+ "".join(
|
||||
[
|
||||
f"{prefix} {in_args[i].gvalue_get} (param_values + {i+1}),\n"
|
||||
for i in range(len(in_args))
|
||||
]
|
||||
)
|
||||
+ f"{prefix} data2);\n"
|
||||
)
|
||||
|
||||
if ret_arg:
|
||||
self.outfile.write(
|
||||
"\n" f" {ret_arg.gvalue_set} (return_value, v_return);\n"
|
||||
)
|
||||
|
||||
def generic_marshaller_name(self, args=[], ret=None):
|
||||
name = "_g_dbus_codegen_marshal_"
|
||||
name += f"{ret.gvalue_type.upper() if ret else 'VOID'}__"
|
||||
if args:
|
||||
name += "_".join(f"{a.gvalue_type.upper()}" for a in args)
|
||||
else:
|
||||
name += "VOID"
|
||||
return name
|
||||
|
||||
def generic_marshaller_name_for_type(self, t):
|
||||
assert isinstance(t, (dbustypes.Signal, dbustypes.Method))
|
||||
|
||||
if not t.marshaller_ret_arg:
|
||||
if not t.marshaller_in_args:
|
||||
return "g_cclosure_marshal_VOID__VOID"
|
||||
elif (
|
||||
len(t.marshaller_in_args) == 1
|
||||
and t.marshaller_in_args[0].gclosure_marshaller
|
||||
):
|
||||
return t.marshaller_in_args[0].gclosure_marshaller
|
||||
|
||||
return self.generic_marshaller_name(t.marshaller_in_args, t.marshaller_ret_arg)
|
||||
|
||||
def generate_generic_marshallers(self, i):
|
||||
for t in i.signals + i.methods:
|
||||
marshaller_name = self.generic_marshaller_name_for_type(t)
|
||||
if marshaller_name.startswith("g_cclosure_"):
|
||||
self.marshallers.add(marshaller_name)
|
||||
continue
|
||||
|
||||
if marshaller_name in self.marshallers:
|
||||
continue
|
||||
|
||||
self.generate_marshaller(
|
||||
marshaller_name, t.marshaller_in_args, t.marshaller_ret_arg
|
||||
)
|
||||
self.marshallers.add(marshaller_name)
|
||||
|
||||
def generate_marshaller_for_type(self, i, t):
|
||||
assert isinstance(t, (dbustypes.Signal, dbustypes.Method))
|
||||
|
||||
kind_uscore = utils.camel_case_to_uscore(t.__class__.__name__.lower())
|
||||
func_name = f"{i.name_lower}_{kind_uscore}_marshal_{t.name_lower}"
|
||||
marshaller_name = self.generic_marshaller_name_for_type(t)
|
||||
assert marshaller_name in self.marshallers
|
||||
|
||||
self.generate_marshaller_wrapper(func_name, marshaller_name)
|
||||
|
||||
def generate_signal_marshallers(self, i):
|
||||
for s in i.signals:
|
||||
self.generate_marshaller_for_type(i, s)
|
||||
|
||||
def generate_method_marshallers(self, i):
|
||||
for m in i.methods:
|
||||
self.generate_marshaller_for_type(i, m)
|
||||
|
||||
# ---------------------------------------------------------------------------------------------------
|
||||
|
||||
def generate_method_calls(self, i):
|
||||
for m in i.methods:
|
||||
# async begin
|
||||
@ -5246,10 +5448,14 @@ class CodeGenerator:
|
||||
|
||||
def generate(self):
|
||||
self.generate_body_preamble()
|
||||
for i in self.ifaces:
|
||||
self.generate_generic_marshallers(i)
|
||||
for i in self.ifaces:
|
||||
self.generate_interface_intro(i)
|
||||
self.generate_signals_enum_for_interface(i)
|
||||
self.generate_introspection_for_interface(i)
|
||||
self.generate_signal_marshallers(i)
|
||||
self.generate_method_marshallers(i)
|
||||
self.generate_interface(i)
|
||||
self.generate_property_accessors(i)
|
||||
self.generate_signal_emitters(i)
|
||||
|
@ -119,7 +119,7 @@ def apply_annotation(iface_list, iface, method, signal, prop, arg, key, value):
|
||||
|
||||
def apply_annotations(iface_list, annotation_list):
|
||||
# apply annotations given on the command line
|
||||
for (what, key, value) in annotation_list:
|
||||
for what, key, value in annotation_list:
|
||||
pos = what.find("::")
|
||||
if pos != -1:
|
||||
# signal
|
||||
|
@ -83,7 +83,10 @@ class Arg:
|
||||
self.format_in = "@" + self.signature
|
||||
self.format_out = "@" + self.signature
|
||||
self.gvariant_get = "XXX"
|
||||
self.gvalue_get = "g_value_get_variant"
|
||||
self.gvalue_type = "variant"
|
||||
self.gvalue_get = "g_marshal_value_peek_variant"
|
||||
self.gvalue_set = "g_value_take_variant"
|
||||
self.gclosure_marshaller = "g_cclosure_marshal_VOID__VARIANT"
|
||||
self.array_annotation = ""
|
||||
|
||||
if not utils.lookup_annotation(
|
||||
@ -99,7 +102,10 @@ class Arg:
|
||||
self.format_in = "b"
|
||||
self.format_out = "b"
|
||||
self.gvariant_get = "g_variant_get_boolean"
|
||||
self.gvalue_get = "g_value_get_boolean"
|
||||
self.gvalue_type = "boolean"
|
||||
self.gvalue_get = "g_marshal_value_peek_boolean"
|
||||
self.gvalue_set = "g_value_set_boolean"
|
||||
self.gclosure_marshaller = "g_cclosure_marshal_VOID__BOOLEAN"
|
||||
elif self.signature == "y":
|
||||
self.ctype_in_g = "guchar "
|
||||
self.ctype_in = "guchar "
|
||||
@ -110,7 +116,10 @@ class Arg:
|
||||
self.format_in = "y"
|
||||
self.format_out = "y"
|
||||
self.gvariant_get = "g_variant_get_byte"
|
||||
self.gvalue_get = "g_value_get_uchar"
|
||||
self.gvalue_type = "uchar"
|
||||
self.gvalue_get = "g_marshal_value_peek_uchar"
|
||||
self.gvalue_set = "g_value_set_uchar"
|
||||
self.gclosure_marshaller = "g_cclosure_marshal_VOID__UCHAR"
|
||||
elif self.signature == "n":
|
||||
self.ctype_in_g = "gint "
|
||||
self.ctype_in = "gint16 "
|
||||
@ -121,7 +130,10 @@ class Arg:
|
||||
self.format_in = "n"
|
||||
self.format_out = "n"
|
||||
self.gvariant_get = "g_variant_get_int16"
|
||||
self.gvalue_get = "g_value_get_int"
|
||||
self.gvalue_type = "int"
|
||||
self.gvalue_get = "g_marshal_value_peek_int"
|
||||
self.gvalue_set = "g_value_set_int"
|
||||
self.gclosure_marshaller = "g_cclosure_marshal_VOID__INT"
|
||||
elif self.signature == "q":
|
||||
self.ctype_in_g = "guint "
|
||||
self.ctype_in = "guint16 "
|
||||
@ -132,7 +144,10 @@ class Arg:
|
||||
self.format_in = "q"
|
||||
self.format_out = "q"
|
||||
self.gvariant_get = "g_variant_get_uint16"
|
||||
self.gvalue_get = "g_value_get_uint"
|
||||
self.gvalue_type = "uint"
|
||||
self.gvalue_get = "g_marshal_value_peek_uint"
|
||||
self.gvalue_set = "g_value_set_uint"
|
||||
self.gclosure_marshaller = "g_cclosure_marshal_VOID__UINT"
|
||||
elif self.signature == "i":
|
||||
self.ctype_in_g = "gint "
|
||||
self.ctype_in = "gint "
|
||||
@ -143,7 +158,10 @@ class Arg:
|
||||
self.format_in = "i"
|
||||
self.format_out = "i"
|
||||
self.gvariant_get = "g_variant_get_int32"
|
||||
self.gvalue_get = "g_value_get_int"
|
||||
self.gvalue_type = "int"
|
||||
self.gvalue_get = "g_marshal_value_peek_int"
|
||||
self.gvalue_set = "g_value_set_int"
|
||||
self.gclosure_marshaller = "g_cclosure_marshal_VOID__INT"
|
||||
elif self.signature == "u":
|
||||
self.ctype_in_g = "guint "
|
||||
self.ctype_in = "guint "
|
||||
@ -154,7 +172,10 @@ class Arg:
|
||||
self.format_in = "u"
|
||||
self.format_out = "u"
|
||||
self.gvariant_get = "g_variant_get_uint32"
|
||||
self.gvalue_get = "g_value_get_uint"
|
||||
self.gvalue_type = "uint"
|
||||
self.gvalue_get = "g_marshal_value_peek_uint"
|
||||
self.gvalue_set = "g_value_set_uint"
|
||||
self.gclosure_marshaller = "g_cclosure_marshal_VOID__UINT"
|
||||
elif self.signature == "x":
|
||||
self.ctype_in_g = "gint64 "
|
||||
self.ctype_in = "gint64 "
|
||||
@ -165,7 +186,10 @@ class Arg:
|
||||
self.format_in = "x"
|
||||
self.format_out = "x"
|
||||
self.gvariant_get = "g_variant_get_int64"
|
||||
self.gvalue_get = "g_value_get_int64"
|
||||
self.gvalue_type = "int64"
|
||||
self.gvalue_get = "g_marshal_value_peek_int64"
|
||||
self.gvalue_set = "g_value_set_int64"
|
||||
self.gclosure_marshaller = None
|
||||
elif self.signature == "t":
|
||||
self.ctype_in_g = "guint64 "
|
||||
self.ctype_in = "guint64 "
|
||||
@ -176,7 +200,10 @@ class Arg:
|
||||
self.format_in = "t"
|
||||
self.format_out = "t"
|
||||
self.gvariant_get = "g_variant_get_uint64"
|
||||
self.gvalue_get = "g_value_get_uint64"
|
||||
self.gvalue_type = "uint64"
|
||||
self.gvalue_get = "g_marshal_value_peek_uint64"
|
||||
self.gvalue_set = "g_value_set_uint64"
|
||||
self.gclosure_marshaller = None
|
||||
elif self.signature == "d":
|
||||
self.ctype_in_g = "gdouble "
|
||||
self.ctype_in = "gdouble "
|
||||
@ -187,7 +214,10 @@ class Arg:
|
||||
self.format_in = "d"
|
||||
self.format_out = "d"
|
||||
self.gvariant_get = "g_variant_get_double"
|
||||
self.gvalue_get = "g_value_get_double"
|
||||
self.gvalue_type = "double"
|
||||
self.gvalue_get = "g_marshal_value_peek_double"
|
||||
self.gvalue_set = "g_value_set_double"
|
||||
self.gclosure_marshaller = "g_cclosure_marshal_VOID__DOUBLE"
|
||||
elif self.signature == "s":
|
||||
self.ctype_in_g = "const gchar *"
|
||||
self.ctype_in = "const gchar *"
|
||||
@ -199,7 +229,10 @@ class Arg:
|
||||
self.format_in = "s"
|
||||
self.format_out = "s"
|
||||
self.gvariant_get = "g_variant_get_string"
|
||||
self.gvalue_get = "g_value_get_string"
|
||||
self.gvalue_type = "string"
|
||||
self.gvalue_get = "g_marshal_value_peek_string"
|
||||
self.gvalue_set = "g_value_set_string"
|
||||
self.gclosure_marshaller = "g_cclosure_marshal_VOID__STRING"
|
||||
elif self.signature == "o":
|
||||
self.ctype_in_g = "const gchar *"
|
||||
self.ctype_in = "const gchar *"
|
||||
@ -211,7 +244,10 @@ class Arg:
|
||||
self.format_in = "o"
|
||||
self.format_out = "o"
|
||||
self.gvariant_get = "g_variant_get_string"
|
||||
self.gvalue_get = "g_value_get_string"
|
||||
self.gvalue_type = "string"
|
||||
self.gvalue_get = "g_marshal_value_peek_string"
|
||||
self.gvalue_set = "g_value_set_string"
|
||||
self.gclosure_marshaller = "g_cclosure_marshal_VOID__STRING"
|
||||
elif self.signature == "g":
|
||||
self.ctype_in_g = "const gchar *"
|
||||
self.ctype_in = "const gchar *"
|
||||
@ -223,7 +259,10 @@ class Arg:
|
||||
self.format_in = "g"
|
||||
self.format_out = "g"
|
||||
self.gvariant_get = "g_variant_get_string"
|
||||
self.gvalue_get = "g_value_get_string"
|
||||
self.gvalue_type = "string"
|
||||
self.gvalue_get = "g_marshal_value_peek_string"
|
||||
self.gvalue_set = "g_value_set_string"
|
||||
self.gclosure_marshaller = "g_cclosure_marshal_VOID__STRING"
|
||||
elif self.signature == "ay":
|
||||
self.ctype_in_g = "const gchar *"
|
||||
self.ctype_in = "const gchar *"
|
||||
@ -235,7 +274,10 @@ class Arg:
|
||||
self.format_in = "^ay"
|
||||
self.format_out = "^ay"
|
||||
self.gvariant_get = "g_variant_get_bytestring"
|
||||
self.gvalue_get = "g_value_get_string"
|
||||
self.gvalue_type = "string"
|
||||
self.gvalue_get = "g_marshal_value_peek_string"
|
||||
self.gvalue_set = "g_value_set_string"
|
||||
self.gclosure_marshaller = "g_cclosure_marshal_VOID__STRING"
|
||||
elif self.signature == "as":
|
||||
self.ctype_in_g = "const gchar *const *"
|
||||
self.ctype_in = "const gchar *const *"
|
||||
@ -247,7 +289,10 @@ class Arg:
|
||||
self.format_in = "^as"
|
||||
self.format_out = "^as"
|
||||
self.gvariant_get = "g_variant_get_strv"
|
||||
self.gvalue_get = "g_value_get_boxed"
|
||||
self.gvalue_type = "boxed"
|
||||
self.gvalue_get = "g_marshal_value_peek_boxed"
|
||||
self.gvalue_set = "g_value_take_boxed"
|
||||
self.gclosure_marshaller = "g_cclosure_marshal_VOID__BOXED"
|
||||
self.array_annotation = "(array zero-terminated=1)"
|
||||
elif self.signature == "ao":
|
||||
self.ctype_in_g = "const gchar *const *"
|
||||
@ -260,7 +305,10 @@ class Arg:
|
||||
self.format_in = "^ao"
|
||||
self.format_out = "^ao"
|
||||
self.gvariant_get = "g_variant_get_objv"
|
||||
self.gvalue_get = "g_value_get_boxed"
|
||||
self.gvalue_type = "boxed"
|
||||
self.gvalue_get = "g_marshal_value_peek_boxed"
|
||||
self.gvalue_set = "g_value_take_boxed"
|
||||
self.gclosure_marshaller = "g_cclosure_marshal_VOID__BOXED"
|
||||
self.array_annotation = "(array zero-terminated=1)"
|
||||
elif self.signature == "aay":
|
||||
self.ctype_in_g = "const gchar *const *"
|
||||
@ -273,7 +321,10 @@ class Arg:
|
||||
self.format_in = "^aay"
|
||||
self.format_out = "^aay"
|
||||
self.gvariant_get = "g_variant_get_bytestring_array"
|
||||
self.gvalue_get = "g_value_get_boxed"
|
||||
self.gvalue_type = "boxed"
|
||||
self.gvalue_get = "g_marshal_value_peek_boxed"
|
||||
self.gvalue_set = "g_value_take_boxed"
|
||||
self.gclosure_marshaller = "g_cclosure_marshal_VOID__BOXED"
|
||||
self.array_annotation = "(array zero-terminated=1)"
|
||||
|
||||
for a in self.annotations:
|
||||
@ -336,6 +387,24 @@ class Method:
|
||||
if utils.lookup_annotation(self.annotations, "org.gtk.GDBus.C.UnixFD"):
|
||||
self.unix_fd = True
|
||||
|
||||
self.marshaller_ret_arg = Arg("return", "b")
|
||||
self.marshaller_ret_arg.post_process(None, None, None, None, None)
|
||||
|
||||
method_invocation_arg = Arg("method_invocation", None)
|
||||
method_invocation_arg.ctype_in = "GDBusMethodInvocation *"
|
||||
method_invocation_arg.gvalue_type = "object"
|
||||
method_invocation_arg.gvalue_get = "g_marshal_value_peek_object"
|
||||
method_invocation_arg.gclosure_marshaller = None
|
||||
self.marshaller_in_args = [method_invocation_arg] + self.in_args
|
||||
|
||||
if self.unix_fd:
|
||||
fd_list_arg = Arg("fd_list", None)
|
||||
fd_list_arg.ctype_in = "GUnixFDList *"
|
||||
fd_list_arg.gvalue_type = "object"
|
||||
fd_list_arg.gvalue_get = "g_marshal_value_peek_object"
|
||||
fd_list_arg.gclosure_marshaller = None
|
||||
self.marshaller_in_args.insert(0, fd_list_arg)
|
||||
|
||||
for a in self.annotations:
|
||||
a.post_process(interface_prefix, cns, cns_upper, cns_lower, self)
|
||||
|
||||
@ -386,6 +455,9 @@ class Signal:
|
||||
):
|
||||
self.deprecated = True
|
||||
|
||||
self.marshaller_ret_arg = None
|
||||
self.marshaller_in_args = self.args
|
||||
|
||||
for a in self.annotations:
|
||||
a.post_process(interface_prefix, cns, cns_upper, cns_lower, self)
|
||||
|
||||
|
@ -114,6 +114,10 @@ def camel_case_to_uscore(s):
|
||||
return ret
|
||||
|
||||
|
||||
def uscore_to_camel_case(s):
|
||||
return "".join([s[0].upper() + s[1:].lower() if s else "_" for s in s.split("_")])
|
||||
|
||||
|
||||
def is_ugly_case(s):
|
||||
if s and s.find("_") > 0:
|
||||
return True
|
||||
|
@ -60,6 +60,27 @@ class TestCodegen(unittest.TestCase):
|
||||
# Track the cwd, we want to back out to that to clean up our tempdir
|
||||
cwd = ""
|
||||
|
||||
ARGUMENTS_TYPES = {
|
||||
"b": {"value_type": "boolean"},
|
||||
"y": {"value_type": "uchar"},
|
||||
"n": {"value_type": "int"},
|
||||
"q": {"value_type": "uint"},
|
||||
"i": {"value_type": "int"},
|
||||
"u": {"value_type": "uint"},
|
||||
"x": {"value_type": "int64", "lacks_marshaller": True},
|
||||
"t": {"value_type": "uint64", "lacks_marshaller": True},
|
||||
"d": {"value_type": "double"},
|
||||
"s": {"value_type": "string"},
|
||||
"o": {"value_type": "string"},
|
||||
"g": {"value_type": "string"},
|
||||
"h": {"value_type": "variant"},
|
||||
"ay": {"value_type": "string"},
|
||||
"as": {"value_type": "boxed"},
|
||||
"ao": {"value_type": "boxed"},
|
||||
"aay": {"value_type": "boxed"},
|
||||
"asv": {"value_type": "variant", "variant_type": "a{sv}"},
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
self.timeout_seconds = 6 # seconds per test
|
||||
self.tmpdir = tempfile.TemporaryDirectory()
|
||||
@ -126,6 +147,51 @@ class TestCodegen(unittest.TestCase):
|
||||
"#ifdef G_OS_UNIX\n"
|
||||
"# include <gio/gunixfdlist.h>\n"
|
||||
"#endif",
|
||||
"private_gvalues_getters": """#ifdef G_ENABLE_DEBUG
|
||||
#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v)
|
||||
#define g_marshal_value_peek_char(v) g_value_get_schar (v)
|
||||
#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v)
|
||||
#define g_marshal_value_peek_int(v) g_value_get_int (v)
|
||||
#define g_marshal_value_peek_uint(v) g_value_get_uint (v)
|
||||
#define g_marshal_value_peek_long(v) g_value_get_long (v)
|
||||
#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v)
|
||||
#define g_marshal_value_peek_int64(v) g_value_get_int64 (v)
|
||||
#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v)
|
||||
#define g_marshal_value_peek_enum(v) g_value_get_enum (v)
|
||||
#define g_marshal_value_peek_flags(v) g_value_get_flags (v)
|
||||
#define g_marshal_value_peek_float(v) g_value_get_float (v)
|
||||
#define g_marshal_value_peek_double(v) g_value_get_double (v)
|
||||
#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v)
|
||||
#define g_marshal_value_peek_param(v) g_value_get_param (v)
|
||||
#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v)
|
||||
#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v)
|
||||
#define g_marshal_value_peek_object(v) g_value_get_object (v)
|
||||
#define g_marshal_value_peek_variant(v) g_value_get_variant (v)
|
||||
#else /* !G_ENABLE_DEBUG */
|
||||
/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
|
||||
* Do not access GValues directly in your code. Instead, use the
|
||||
* g_value_get_*() functions
|
||||
*/
|
||||
#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int
|
||||
#define g_marshal_value_peek_char(v) (v)->data[0].v_int
|
||||
#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint
|
||||
#define g_marshal_value_peek_int(v) (v)->data[0].v_int
|
||||
#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint
|
||||
#define g_marshal_value_peek_long(v) (v)->data[0].v_long
|
||||
#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong
|
||||
#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64
|
||||
#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64
|
||||
#define g_marshal_value_peek_enum(v) (v)->data[0].v_long
|
||||
#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong
|
||||
#define g_marshal_value_peek_float(v) (v)->data[0].v_float
|
||||
#define g_marshal_value_peek_double(v) (v)->data[0].v_double
|
||||
#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer
|
||||
#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer
|
||||
#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer
|
||||
#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer
|
||||
#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer
|
||||
#define g_marshal_value_peek_variant(v) (v)->data[0].v_pointer
|
||||
#endif /* !G_ENABLE_DEBUG */""",
|
||||
"standard_typedefs_and_helpers": "typedef struct\n"
|
||||
"{\n"
|
||||
" GDBusArgInfo parent_struct;\n"
|
||||
@ -325,6 +391,8 @@ G_END_DECLS
|
||||
|
||||
{standard_header_includes}
|
||||
|
||||
{private_gvalues_getters}
|
||||
|
||||
{standard_typedefs_and_helpers}""".format(
|
||||
**result.subs
|
||||
),
|
||||
@ -665,6 +733,521 @@ G_END_DECLS
|
||||
self.assertEqual(result.out.strip().count("GDBusCallFlags call_flags,"), 2)
|
||||
self.assertEqual(result.out.strip().count("gint timeout_msec,"), 2)
|
||||
|
||||
@unittest.skipIf(on_win32(), "requires /dev/stdout")
|
||||
def test_generate_signal_id_simple_signal(self):
|
||||
"""Test that signals IDs are used to emit signals"""
|
||||
interface_xml = """
|
||||
<node>
|
||||
<interface name="org.project.UsefulInterface">
|
||||
<signal name="SimpleSignal"/>
|
||||
</interface>
|
||||
<interface name="org.project.OtherIface">
|
||||
<signal name="SimpleSignal"/>
|
||||
</interface>
|
||||
</node>"""
|
||||
|
||||
result = self.runCodegenWithInterface(
|
||||
interface_xml, "--output", "/dev/stdout", "--body"
|
||||
)
|
||||
stripped_out = result.out.strip()
|
||||
self.assertFalse(result.err)
|
||||
self.assertIs(stripped_out.count("g_signal_emit_by_name ("), 0)
|
||||
|
||||
for iface in ["USEFUL_INTERFACE", "OTHER_IFACE"]:
|
||||
enum_name = f"_ORG_PROJECT_{iface}_SIGNALS"
|
||||
enum_item = f"_ORG_PROJECT_{iface}_SIMPLE_SIGNAL"
|
||||
self.assertIs(stripped_out.count(f"{enum_item},"), 1)
|
||||
self.assertIs(stripped_out.count(f"{enum_name}[{enum_item}] ="), 1)
|
||||
self.assertIs(
|
||||
stripped_out.count(
|
||||
f" g_signal_emit (object, {enum_name}[{enum_item}], 0);"
|
||||
),
|
||||
1,
|
||||
)
|
||||
|
||||
@unittest.skipIf(on_win32(), "requires /dev/stdout")
|
||||
def test_generate_signal_id_multiple_signals_types(self):
|
||||
"""Test that signals IDs are used to emit signals for all types"""
|
||||
|
||||
signal_template = "<signal name='{}'><arg name='{}' type='{}'/></signal>"
|
||||
generated_signals = [
|
||||
signal_template.format(
|
||||
f"SingleArgSignal{t.upper()}", f"an_{t}", props.get("variant_type", t)
|
||||
)
|
||||
for t, props in self.ARGUMENTS_TYPES.items()
|
||||
]
|
||||
|
||||
interface_xml = f"""
|
||||
<node>
|
||||
<interface name="org.project.SignalingIface">
|
||||
<signal name="NoArgSignal" />
|
||||
{''.join(generated_signals)}
|
||||
</interface>
|
||||
</node>"""
|
||||
|
||||
result = self.runCodegenWithInterface(
|
||||
interface_xml, "--output", "/dev/stdout", "--body"
|
||||
)
|
||||
stripped_out = result.out.strip()
|
||||
self.assertFalse(result.err)
|
||||
self.assertIs(stripped_out.count("g_signal_emit_by_name ("), 0)
|
||||
|
||||
iface = "SIGNALING_IFACE"
|
||||
for t in self.ARGUMENTS_TYPES.keys():
|
||||
enum_name = f"_ORG_PROJECT_{iface}_SIGNALS"
|
||||
enum_item = f"_ORG_PROJECT_{iface}_SINGLE_ARG_SIGNAL_{t.upper()}"
|
||||
self.assertIs(stripped_out.count(f"{enum_item},"), 1)
|
||||
self.assertIs(stripped_out.count(f"{enum_name}[{enum_item}] ="), 1)
|
||||
self.assertIs(
|
||||
stripped_out.count(
|
||||
f" g_signal_emit (object, {enum_name}[{enum_item}], 0, arg_an_{t});"
|
||||
),
|
||||
1,
|
||||
)
|
||||
|
||||
@unittest.skipIf(on_win32(), "requires /dev/stdout")
|
||||
def test_generate_signal_id_multiple_signal_args_types(self):
|
||||
"""Test that signals IDs are used to emit signals for all types"""
|
||||
|
||||
generated_args = [
|
||||
f"<arg name='an_{t}' type='{props.get('variant_type', t)}'/>\n"
|
||||
for t, props in self.ARGUMENTS_TYPES.items()
|
||||
]
|
||||
|
||||
interface_xml = f"""
|
||||
<node>
|
||||
<interface name="org.project.SignalingIface">
|
||||
<signal name="SignalWithManyArgs">
|
||||
{''.join(generated_args)}
|
||||
</signal>
|
||||
</interface>
|
||||
</node>"""
|
||||
|
||||
result = self.runCodegenWithInterface(
|
||||
interface_xml, "--output", "/dev/stdout", "--body"
|
||||
)
|
||||
stripped_out = result.out.strip()
|
||||
self.assertFalse(result.err)
|
||||
self.assertIs(stripped_out.count("g_signal_emit_by_name ("), 0)
|
||||
|
||||
iface = "SIGNALING_IFACE"
|
||||
enum_name = f"_ORG_PROJECT_{iface}_SIGNALS"
|
||||
enum_item = f"_ORG_PROJECT_{iface}_SIGNAL_WITH_MANY_ARGS"
|
||||
self.assertIs(stripped_out.count(f"{enum_item},"), 1)
|
||||
self.assertIs(stripped_out.count(f"{enum_name}[{enum_item}] ="), 1)
|
||||
|
||||
args = ", ".join([f"arg_an_{t}" for t in self.ARGUMENTS_TYPES.keys()])
|
||||
self.assertIs(
|
||||
stripped_out.count(
|
||||
f" g_signal_emit (object, {enum_name}[{enum_item}], 0, {args});"
|
||||
),
|
||||
1,
|
||||
)
|
||||
|
||||
@unittest.skipIf(on_win32(), "requires /dev/stdout")
|
||||
def test_generate_signals_marshaller_simple_signal(self):
|
||||
"""Test that signals marshaller is generated for simple signal"""
|
||||
interface_xml = """
|
||||
<node>
|
||||
<interface name="org.project.SignalingIface">
|
||||
<signal name="SimpleSignal"/>
|
||||
</interface>
|
||||
<interface name="org.project.OtherSignalingIface">
|
||||
<signal name="SimpleSignal"/>
|
||||
</interface>
|
||||
</node>"""
|
||||
|
||||
result = self.runCodegenWithInterface(
|
||||
interface_xml, "--output", "/dev/stdout", "--body"
|
||||
)
|
||||
stripped_out = result.out.strip()
|
||||
self.assertFalse(result.err)
|
||||
self.assertIs(stripped_out.count("g_cclosure_marshal_generic"), 0)
|
||||
|
||||
func_name = "org_project_signaling_iface_signal_marshal_simple_signal"
|
||||
self.assertIs(stripped_out.count(f"{func_name},"), 1)
|
||||
self.assertIs(stripped_out.count(f"{func_name} ("), 1)
|
||||
self.assertIs(stripped_out.count("g_cclosure_marshal_VOID__VOID (closure"), 2)
|
||||
|
||||
func_name = "org_project_other_signaling_iface_signal_marshal_simple_signal"
|
||||
self.assertIs(stripped_out.count(f"{func_name},"), 1)
|
||||
self.assertIs(stripped_out.count(f"{func_name} ("), 1)
|
||||
self.assertIs(stripped_out.count("g_cclosure_marshal_VOID__VOID (closure"), 2)
|
||||
|
||||
@unittest.skipIf(on_win32(), "requires /dev/stdout")
|
||||
def test_generate_signals_marshaller_single_typed_args(self):
|
||||
"""Test that signals marshaller is generated for each known type"""
|
||||
for t, props in self.ARGUMENTS_TYPES.items():
|
||||
camel_type = t[0].upper() + t[1:]
|
||||
interface_xml = f"""
|
||||
<node>
|
||||
<interface name="org.project.SignalingIface">
|
||||
<signal name="SimpleSignal"/>
|
||||
<signal name="SingleArgSignal{camel_type}">
|
||||
<arg name="arg_{t}" type="{props.get("variant_type", t)}"/>
|
||||
</signal>
|
||||
</interface>
|
||||
</node>"""
|
||||
|
||||
result = self.runCodegenWithInterface(
|
||||
interface_xml, "--output", "/dev/stdout", "--body"
|
||||
)
|
||||
stripped_out = result.out.strip()
|
||||
self.assertFalse(result.err)
|
||||
self.assertEqual(stripped_out.count("g_cclosure_marshal_generic"), 0)
|
||||
|
||||
self.assertIs(
|
||||
stripped_out.count("g_cclosure_marshal_VOID__VOID (closure"), 1
|
||||
)
|
||||
|
||||
func_name = (
|
||||
f"org_project_signaling_iface_signal_marshal_single_arg_signal_{t}"
|
||||
)
|
||||
self.assertIs(stripped_out.count(f"{func_name},"), 1)
|
||||
self.assertIs(stripped_out.count(f"{func_name} ("), 1)
|
||||
|
||||
if props.get("lacks_marshaller", False):
|
||||
self.assertIs(
|
||||
stripped_out.count(
|
||||
f"g_marshal_value_peek_{props['value_type']} (param_values + 1)"
|
||||
),
|
||||
1,
|
||||
)
|
||||
else:
|
||||
self.assertIs(
|
||||
stripped_out.count(
|
||||
f"g_cclosure_marshal_VOID__{props['value_type'].upper()} (closure"
|
||||
),
|
||||
1,
|
||||
)
|
||||
|
||||
@unittest.skipIf(on_win32(), "requires /dev/stdout")
|
||||
def test_generate_signals_marshallers_multiple_args(self):
|
||||
"""Test that signals marshallers are generated"""
|
||||
generated_args = [
|
||||
f"<arg name='an_{t}' type='{props.get('variant_type', t)}'/>\n"
|
||||
for t, props in self.ARGUMENTS_TYPES.items()
|
||||
]
|
||||
|
||||
interface_xml = f"""
|
||||
<node>
|
||||
<interface name="org.project.SignalingIface">
|
||||
<signal name="SimpleSignal"/>
|
||||
<signal name="SignalWithManyArgs">
|
||||
{''.join(generated_args)}
|
||||
</signal>
|
||||
</interface>
|
||||
</node>"""
|
||||
|
||||
result = self.runCodegenWithInterface(
|
||||
interface_xml, "--output", "/dev/stdout", "--body"
|
||||
)
|
||||
stripped_out = result.out.strip()
|
||||
self.assertFalse(result.err)
|
||||
self.assertIs(stripped_out.count("g_cclosure_marshal_generic"), 0)
|
||||
|
||||
func_name = f"org_project_signaling_iface_signal_marshal_simple_signal"
|
||||
self.assertIs(stripped_out.count(f"{func_name},"), 1)
|
||||
self.assertIs(stripped_out.count(f"{func_name} ("), 1)
|
||||
|
||||
func_name = f"org_project_signaling_iface_signal_marshal_signal_with_many_args"
|
||||
self.assertIs(stripped_out.count(f"{func_name},"), 1)
|
||||
self.assertIs(stripped_out.count(f"{func_name} ("), 1)
|
||||
|
||||
# Check access to MultipleArgsSignal arguments
|
||||
index = 1
|
||||
for props in self.ARGUMENTS_TYPES.values():
|
||||
self.assertIs(
|
||||
stripped_out.count(
|
||||
f"g_marshal_value_peek_{props['value_type']} (param_values + {index})"
|
||||
),
|
||||
1,
|
||||
)
|
||||
index += 1
|
||||
|
||||
@unittest.skipIf(on_win32(), "requires /dev/stdout")
|
||||
def test_generate_methods_marshaller_simple_method(self):
|
||||
"""Test that methods marshaller is generated for simple method"""
|
||||
interface_xml = """
|
||||
<node>
|
||||
<interface name="org.project.CallableIface">
|
||||
<method name="SimpleMethod"/>
|
||||
</interface>
|
||||
<interface name="org.project.OtherCallableIface">
|
||||
<method name="SimpleMethod"/>
|
||||
</interface>
|
||||
</node>"""
|
||||
|
||||
result = self.runCodegenWithInterface(
|
||||
interface_xml, "--output", "/dev/stdout", "--body"
|
||||
)
|
||||
stripped_out = result.out.strip()
|
||||
self.assertFalse(result.err)
|
||||
self.assertIs(stripped_out.count("g_cclosure_marshal_generic"), 0)
|
||||
|
||||
func_name = "org_project_callable_iface_method_marshal_simple_method"
|
||||
self.assertIs(stripped_out.count(f"{func_name},"), 1)
|
||||
self.assertIs(stripped_out.count(f"{func_name} ("), 1)
|
||||
|
||||
func_name = "org_project_other_callable_iface_method_marshal_simple_method"
|
||||
self.assertIs(stripped_out.count(f"{func_name},"), 1)
|
||||
self.assertIs(stripped_out.count(f"{func_name} ("), 1)
|
||||
|
||||
self.assertIs(
|
||||
stripped_out.count("g_marshal_value_peek_object (param_values + 1)"), 1
|
||||
)
|
||||
self.assertIs(
|
||||
stripped_out.count("g_value_set_boolean (return_value, v_return);"), 1
|
||||
)
|
||||
|
||||
self.assertIs(
|
||||
stripped_out.count(
|
||||
"_g_dbus_codegen_marshal_BOOLEAN__OBJECT (\n GClosure"
|
||||
),
|
||||
1,
|
||||
)
|
||||
self.assertIs(
|
||||
stripped_out.count("_g_dbus_codegen_marshal_BOOLEAN__OBJECT (closure"), 2
|
||||
)
|
||||
|
||||
@unittest.skipIf(on_win32(), "requires /dev/stdout")
|
||||
def test_generate_methods_marshaller_single_typed_in_args(self):
|
||||
"""Test that methods marshallers are generated for each known type"""
|
||||
for t, props in self.ARGUMENTS_TYPES.items():
|
||||
camel_type = t[0].upper() + t[1:]
|
||||
interface_xml = f"""
|
||||
<node>
|
||||
<interface name="org.project.UsefulInterface">
|
||||
<method name="SingleArgMethod{camel_type}">
|
||||
<arg name="arg_{t}" type="{props.get("variant_type", t)}"/>
|
||||
</method>
|
||||
</interface>
|
||||
</node>"""
|
||||
|
||||
result = self.runCodegenWithInterface(
|
||||
interface_xml, "--output", "/dev/stdout", "--body"
|
||||
)
|
||||
stripped_out = result.out.strip()
|
||||
self.assertFalse(result.err)
|
||||
self.assertEqual(stripped_out.count("g_cclosure_marshal_generic"), 0)
|
||||
|
||||
func_name = (
|
||||
f"org_project_useful_interface_method_marshal_single_arg_method_{t}"
|
||||
)
|
||||
self.assertIs(stripped_out.count(f"{func_name},"), 1)
|
||||
self.assertIs(stripped_out.count(f"{func_name} ("), 1)
|
||||
self.assertIs(
|
||||
stripped_out.count("g_marshal_value_peek_object (param_values + 1)"), 1
|
||||
)
|
||||
self.assertIs(
|
||||
stripped_out.count("g_value_set_boolean (return_value, v_return);"), 1
|
||||
)
|
||||
self.assertIs(
|
||||
stripped_out.count(
|
||||
f"g_marshal_value_peek_{props['value_type']} (param_values + 2)"
|
||||
),
|
||||
1,
|
||||
)
|
||||
|
||||
@unittest.skipIf(on_win32(), "requires /dev/stdout")
|
||||
def test_generate_methods_marshaller_single_typed_out_args(self):
|
||||
"""Test that methods marshallers are generated for each known type"""
|
||||
for t, props in self.ARGUMENTS_TYPES.items():
|
||||
camel_type = t[0].upper() + t[1:]
|
||||
interface_xml = f"""
|
||||
<node>
|
||||
<interface name="org.project.UsefulInterface">
|
||||
<method name="SingleArgMethod{camel_type}">
|
||||
<arg name="arg_{t}" type="{props.get("variant_type", t)}" direction="out"/>
|
||||
</method>
|
||||
</interface>
|
||||
</node>"""
|
||||
|
||||
result = self.runCodegenWithInterface(
|
||||
interface_xml, "--output", "/dev/stdout", "--body"
|
||||
)
|
||||
stripped_out = result.out.strip()
|
||||
self.assertFalse(result.err)
|
||||
self.assertEqual(stripped_out.count("g_cclosure_marshal_generic"), 0)
|
||||
|
||||
func_name = (
|
||||
f"org_project_useful_interface_method_marshal_single_arg_method_{t}"
|
||||
)
|
||||
self.assertIs(stripped_out.count(f"{func_name},"), 1)
|
||||
self.assertIs(stripped_out.count(f"{func_name} ("), 1)
|
||||
self.assertIs(
|
||||
stripped_out.count("g_marshal_value_peek_object (param_values + 1)"), 1
|
||||
)
|
||||
self.assertIs(
|
||||
stripped_out.count("g_value_set_boolean (return_value, v_return);"), 1
|
||||
)
|
||||
self.assertIs(stripped_out.count("(param_values + 2)"), 0)
|
||||
|
||||
@unittest.skipIf(on_win32(), "requires /dev/stdout")
|
||||
def test_generate_methods_marshallers_multiple_in_args(self):
|
||||
"""Test that methods marshallers are generated"""
|
||||
generated_args = [
|
||||
f"<arg name='an_{t}' type='{props.get('variant_type', t)}'/>\n"
|
||||
for t, props in self.ARGUMENTS_TYPES.items()
|
||||
]
|
||||
|
||||
interface_xml = f"""
|
||||
<node>
|
||||
<interface name="org.project.CallableIface">
|
||||
<method name="MethodWithManyArgs">
|
||||
{''.join(generated_args)}
|
||||
</method>
|
||||
<method name="SameMethodWithManyArgs">
|
||||
{''.join(generated_args)}
|
||||
</method>
|
||||
</interface>
|
||||
<interface name="org.project.OtherCallableIface">
|
||||
<method name="MethodWithManyArgs">
|
||||
{''.join(generated_args)}
|
||||
</method>
|
||||
</interface>
|
||||
</node>"""
|
||||
|
||||
result = self.runCodegenWithInterface(
|
||||
interface_xml, "--output", "/dev/stdout", "--body"
|
||||
)
|
||||
stripped_out = result.out.strip()
|
||||
self.assertFalse(result.err)
|
||||
self.assertIs(stripped_out.count("g_cclosure_marshal_generic"), 0)
|
||||
|
||||
func_name = f"org_project_callable_iface_method_marshal_method_with_many_args"
|
||||
self.assertIs(stripped_out.count(f"{func_name},"), 1)
|
||||
self.assertIs(stripped_out.count(f"{func_name} ("), 1)
|
||||
|
||||
# Check access to MultipleArgsMethod arguments
|
||||
index = 1
|
||||
self.assertIs(
|
||||
stripped_out.count(f"g_marshal_value_peek_object (param_values + {index})"),
|
||||
1,
|
||||
)
|
||||
index += 1
|
||||
|
||||
for props in self.ARGUMENTS_TYPES.values():
|
||||
self.assertIs(
|
||||
stripped_out.count(
|
||||
f"g_marshal_value_peek_{props['value_type']} (param_values + {index})"
|
||||
),
|
||||
1,
|
||||
)
|
||||
index += 1
|
||||
|
||||
self.assertIs(
|
||||
stripped_out.count("g_value_set_boolean (return_value, v_return);"), 1
|
||||
)
|
||||
func_types = "_".join(
|
||||
[p["value_type"].upper() for p in self.ARGUMENTS_TYPES.values()]
|
||||
)
|
||||
func_name = f"_g_dbus_codegen_marshal_BOOLEAN__OBJECT_{func_types}"
|
||||
self.assertIs(stripped_out.count(f"{func_name} (\n GClosure"), 1)
|
||||
self.assertIs(stripped_out.count(f"{func_name} (closure"), 3)
|
||||
|
||||
func_name = (
|
||||
f"org_project_other_callable_iface_method_marshal_method_with_many_args"
|
||||
)
|
||||
self.assertIs(stripped_out.count(f"{func_name},"), 1)
|
||||
self.assertIs(stripped_out.count(f"{func_name} ("), 1)
|
||||
|
||||
@unittest.skipIf(on_win32(), "requires /dev/stdout")
|
||||
def test_generate_methods_marshallers_multiple_out_args(self):
|
||||
"""Test that methods marshallers are generated"""
|
||||
generated_args = [
|
||||
f"<arg name='an_{t}' type='{props.get('variant_type', t)}' direction='out'/>\n"
|
||||
for t, props in self.ARGUMENTS_TYPES.items()
|
||||
]
|
||||
|
||||
interface_xml = f"""
|
||||
<node>
|
||||
<interface name="org.project.CallableIface">
|
||||
<method name="MethodWithManyArgs">
|
||||
{''.join(generated_args)}
|
||||
</method>
|
||||
</interface>
|
||||
</node>"""
|
||||
|
||||
result = self.runCodegenWithInterface(
|
||||
interface_xml, "--output", "/dev/stdout", "--body"
|
||||
)
|
||||
stripped_out = result.out.strip()
|
||||
self.assertFalse(result.err)
|
||||
self.assertIs(stripped_out.count("g_cclosure_marshal_generic"), 0)
|
||||
|
||||
func_name = f"org_project_callable_iface_method_marshal_method_with_many_args"
|
||||
self.assertIs(stripped_out.count(f"{func_name},"), 1)
|
||||
self.assertIs(stripped_out.count(f"{func_name} ("), 1)
|
||||
|
||||
# Check access to MultipleArgsMethod arguments
|
||||
index = 1
|
||||
self.assertIs(
|
||||
stripped_out.count(f"g_marshal_value_peek_object (param_values + {index})"),
|
||||
1,
|
||||
)
|
||||
index += 1
|
||||
|
||||
for index in range(index, len(self.ARGUMENTS_TYPES)):
|
||||
self.assertIs(stripped_out.count(f"(param_values + {index})"), 0)
|
||||
|
||||
self.assertIs(
|
||||
stripped_out.count("g_value_set_boolean (return_value, v_return);"), 1
|
||||
)
|
||||
|
||||
self.assertIs(
|
||||
stripped_out.count("_g_dbus_codegen_marshal_BOOLEAN__OBJECT (closure"),
|
||||
1,
|
||||
)
|
||||
|
||||
@unittest.skipIf(on_win32(), "requires /dev/stdout")
|
||||
def test_generate_methods_marshallers_with_unix_fds(self):
|
||||
"""Test an interface with `h` arguments"""
|
||||
interface_xml = """
|
||||
<node>
|
||||
<interface name="test.FDPassing">
|
||||
<method name="HelloFD">
|
||||
<annotation name="org.gtk.GDBus.C.UnixFD" value="1"/>
|
||||
<arg name="greeting" direction="in" type="s"/>
|
||||
<arg name="response" direction="out" type="s"/>
|
||||
</method>
|
||||
</interface>
|
||||
</node>"""
|
||||
|
||||
result = self.runCodegenWithInterface(
|
||||
interface_xml, "--output", "/dev/stdout", "--body"
|
||||
)
|
||||
stripped_out = result.out.strip()
|
||||
self.assertFalse(result.err)
|
||||
self.assertIs(stripped_out.count("g_cclosure_marshal_generic"), 0)
|
||||
|
||||
func_name = f"test_fdpassing_method_marshal_hello_fd"
|
||||
self.assertIs(stripped_out.count(f"{func_name},"), 1)
|
||||
self.assertIs(stripped_out.count(f"{func_name} ("), 1)
|
||||
|
||||
index = 1
|
||||
self.assertIs(
|
||||
stripped_out.count(f"g_marshal_value_peek_object (param_values + {index})"),
|
||||
1,
|
||||
)
|
||||
index += 1
|
||||
|
||||
self.assertIs(
|
||||
stripped_out.count(f"g_marshal_value_peek_object (param_values + {index})"),
|
||||
1,
|
||||
)
|
||||
|
||||
index += 1
|
||||
self.assertIs(
|
||||
stripped_out.count(f"g_marshal_value_peek_string (param_values + {index})"),
|
||||
1,
|
||||
)
|
||||
index += 1
|
||||
|
||||
self.assertIs(
|
||||
stripped_out.count("g_value_set_boolean (return_value, v_return);"), 1
|
||||
)
|
||||
|
||||
def test_generate_valid_docbook(self):
|
||||
"""Test the basic functionality of the docbook generator."""
|
||||
xml_contents = """
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "gdbus-tests.h"
|
||||
#include "gstdio.h"
|
||||
|
||||
#if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_64
|
||||
#include "gdbus-test-codegen-generated-min-required-2-64.h"
|
||||
@ -410,6 +411,22 @@ on_handle_check_not_authorized_from_object (FooiGenAuthorize *object,
|
||||
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
on_handle_fdpassing_hello_fd (FooiGenMethodThreads *object,
|
||||
GDBusMethodInvocation *invocation,
|
||||
GUnixFDList *fd_list,
|
||||
const gchar *greeting,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_assert_true (G_IS_UNIX_FD_LIST (fd_list));
|
||||
g_assert_cmpuint (g_unix_fd_list_get_length (fd_list), ==, 2);
|
||||
g_assert_cmpstr (greeting, ==, "Hey fd!");
|
||||
foo_igen_test_fdpassing_complete_hello_fd (FOO_IGEN_TEST_FDPASSING (object),
|
||||
invocation, fd_list,
|
||||
"I love to receive fds!");
|
||||
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
static gboolean
|
||||
@ -430,6 +447,7 @@ static GThread *method_handler_thread = NULL;
|
||||
|
||||
static FooiGenBar *exported_bar_object = NULL;
|
||||
static FooiGenBat *exported_bat_object = NULL;
|
||||
static FooiGenTestFDPassing *exported_fd_passing_object = NULL;
|
||||
static FooiGenAuthorize *exported_authorize_object = NULL;
|
||||
static GDBusObjectSkeleton *authorize_enclosing_object = NULL;
|
||||
static FooiGenMethodThreads *exported_thread_object_1 = NULL;
|
||||
@ -443,6 +461,7 @@ unexport_objects (void)
|
||||
g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (exported_authorize_object));
|
||||
g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (exported_thread_object_1));
|
||||
g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (exported_thread_object_2));
|
||||
g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (exported_fd_passing_object));
|
||||
}
|
||||
|
||||
static void
|
||||
@ -584,6 +603,17 @@ on_bus_acquired (GDBusConnection *connection,
|
||||
|
||||
g_assert_cmpint (g_dbus_interface_skeleton_get_flags (G_DBUS_INTERFACE_SKELETON (exported_thread_object_2)), ==, G_DBUS_INTERFACE_SKELETON_FLAGS_NONE);
|
||||
|
||||
exported_fd_passing_object = foo_igen_test_fdpassing_skeleton_new ();
|
||||
g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (exported_fd_passing_object),
|
||||
connection,
|
||||
"/fdpassing",
|
||||
&error);
|
||||
g_assert_no_error (error);
|
||||
g_signal_connect (exported_fd_passing_object,
|
||||
"handle-hello-fd",
|
||||
G_CALLBACK (on_handle_fdpassing_hello_fd),
|
||||
NULL);
|
||||
|
||||
method_handler_thread = g_thread_self ();
|
||||
}
|
||||
|
||||
@ -1125,6 +1155,45 @@ check_bar_proxy (FooiGenBar *proxy,
|
||||
g_variant_unref (array_of_signatures);
|
||||
}
|
||||
|
||||
static void
|
||||
check_fdpassing_proxy (FooiGenTestFDPassing *proxy)
|
||||
{
|
||||
GError *error = NULL;
|
||||
GUnixFDList *fd_list = g_unix_fd_list_new ();
|
||||
GUnixFDList *ret_fd_list = NULL;
|
||||
char *response = NULL;
|
||||
int fd;
|
||||
|
||||
fd = dup (0);
|
||||
g_assert_cmpint (g_unix_fd_list_append (fd_list, fd, &error), ==, 0);
|
||||
g_assert_no_error (error);
|
||||
g_close (fd, &error);
|
||||
g_assert_no_error (error);
|
||||
|
||||
fd = dup (0);
|
||||
g_assert_cmpint (g_unix_fd_list_append (fd_list, fd, &error), ==, 1);
|
||||
g_assert_no_error (error);
|
||||
g_close (fd, &error);
|
||||
g_assert_no_error (error);
|
||||
|
||||
foo_igen_test_fdpassing_call_hello_fd_sync (proxy, "Hey fd!",
|
||||
#if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_64
|
||||
G_DBUS_CALL_FLAGS_NO_AUTO_START, -1,
|
||||
#endif
|
||||
fd_list,
|
||||
&response, &ret_fd_list, NULL,
|
||||
&error);
|
||||
g_assert_no_error (error);
|
||||
|
||||
g_assert_true (G_IS_UNIX_FD_LIST (ret_fd_list));
|
||||
g_assert_cmpuint (g_unix_fd_list_get_length (fd_list), ==, 2);
|
||||
|
||||
g_assert_cmpstr (response, ==, "I love to receive fds!");
|
||||
g_clear_pointer (&response, g_free);
|
||||
g_clear_object (&fd_list);
|
||||
g_clear_object (&ret_fd_list);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
static void
|
||||
@ -1309,6 +1378,7 @@ check_proxies_in_thread (gpointer user_data)
|
||||
GError *error;
|
||||
FooiGenBar *bar_proxy;
|
||||
FooiGenBat *bat_proxy;
|
||||
FooiGenTestFDPassing *fd_passing_proxy;
|
||||
FooiGenAuthorize *authorize_proxy;
|
||||
FooiGenMethodThreads *thread_proxy_1;
|
||||
FooiGenMethodThreads *thread_proxy_2;
|
||||
@ -1370,6 +1440,16 @@ check_proxies_in_thread (gpointer user_data)
|
||||
g_object_unref (thread_proxy_1);
|
||||
g_object_unref (thread_proxy_2);
|
||||
|
||||
fd_passing_proxy = foo_igen_test_fdpassing_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
|
||||
G_DBUS_PROXY_FLAGS_NONE,
|
||||
"org.gtk.GDBus.BindingsTool.Test",
|
||||
"/fdpassing",
|
||||
NULL, /* GCancellable* */
|
||||
&error);
|
||||
g_assert_no_error (error);
|
||||
check_fdpassing_proxy (fd_passing_proxy);
|
||||
g_object_unref (fd_passing_proxy);
|
||||
|
||||
/* Wait for the proxy signals to all be unsubscribed. */
|
||||
while (g_main_context_iteration (thread_context, FALSE))
|
||||
{
|
||||
@ -2650,53 +2730,53 @@ test_standalone_interface_info (void)
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
static gboolean
|
||||
handle_hello_fd (FooiGenFDPassing *object,
|
||||
handle_hello_fd (FooiGenTestFDPassing *object,
|
||||
GDBusMethodInvocation *invocation,
|
||||
GUnixFDList *fd_list,
|
||||
const gchar *arg_greeting)
|
||||
{
|
||||
foo_igen_fdpassing_complete_hello_fd (object, invocation, fd_list, arg_greeting);
|
||||
foo_igen_test_fdpassing_complete_hello_fd (object, invocation, fd_list, arg_greeting);
|
||||
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
||||
}
|
||||
|
||||
#if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_64
|
||||
static gboolean
|
||||
handle_no_annotation (FooiGenFDPassing *object,
|
||||
handle_no_annotation (FooiGenTestFDPassing *object,
|
||||
GDBusMethodInvocation *invocation,
|
||||
GUnixFDList *fd_list,
|
||||
GVariant *arg_greeting,
|
||||
const gchar *arg_greeting_locale)
|
||||
{
|
||||
foo_igen_fdpassing_complete_no_annotation (object, invocation, fd_list, arg_greeting, arg_greeting_locale);
|
||||
foo_igen_test_fdpassing_complete_no_annotation (object, invocation, fd_list, arg_greeting, arg_greeting_locale);
|
||||
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
handle_no_annotation_nested (FooiGenFDPassing *object,
|
||||
handle_no_annotation_nested (FooiGenTestFDPassing *object,
|
||||
GDBusMethodInvocation *invocation,
|
||||
GUnixFDList *fd_list,
|
||||
GVariant *arg_files)
|
||||
{
|
||||
foo_igen_fdpassing_complete_no_annotation_nested (object, invocation, fd_list);
|
||||
foo_igen_test_fdpassing_complete_no_annotation_nested (object, invocation, fd_list);
|
||||
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
||||
}
|
||||
#else
|
||||
static gboolean
|
||||
handle_no_annotation (FooiGenFDPassing *object,
|
||||
handle_no_annotation (FooiGenTestFDPassing *object,
|
||||
GDBusMethodInvocation *invocation,
|
||||
GVariant *arg_greeting,
|
||||
const gchar *arg_greeting_locale)
|
||||
{
|
||||
foo_igen_fdpassing_complete_no_annotation (object, invocation, arg_greeting, arg_greeting_locale);
|
||||
foo_igen_test_fdpassing_complete_no_annotation (object, invocation, arg_greeting, arg_greeting_locale);
|
||||
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
handle_no_annotation_nested (FooiGenFDPassing *object,
|
||||
handle_no_annotation_nested (FooiGenTestFDPassing *object,
|
||||
GDBusMethodInvocation *invocation,
|
||||
GVariant *arg_files)
|
||||
{
|
||||
foo_igen_fdpassing_complete_no_annotation_nested (object, invocation);
|
||||
foo_igen_test_fdpassing_complete_no_annotation_nested (object, invocation);
|
||||
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
||||
}
|
||||
#endif
|
||||
@ -2709,7 +2789,7 @@ handle_no_annotation_nested (FooiGenFDPassing *object,
|
||||
static void
|
||||
test_unix_fd_list (void)
|
||||
{
|
||||
FooiGenFDPassingIface iface;
|
||||
FooiGenTestFDPassingIface iface;
|
||||
|
||||
g_test_bug ("https://gitlab.gnome.org/GNOME/glib/issues/1726");
|
||||
|
||||
|
@ -170,7 +170,11 @@ test_extra_programs = {
|
||||
|
||||
python_tests = {
|
||||
# FIXME: https://gitlab.gnome.org/GNOME/glib/-/issues/2764
|
||||
'codegen.py' : { 'can_fail' : host_system == 'freebsd' },
|
||||
'codegen.py' : {
|
||||
'can_fail' : host_system == 'freebsd',
|
||||
'suite': ['gdbus-codegen', 'slow'],
|
||||
'timeout': 90,
|
||||
},
|
||||
}
|
||||
|
||||
test_env = environment()
|
||||
@ -458,6 +462,7 @@ if host_machine.system() != 'windows'
|
||||
'gdbus-test-codegen' : {
|
||||
'extra_sources' : [extra_sources, gdbus_test_codegen_generated, gdbus_test_codegen_generated_interface_info],
|
||||
'c_args' : ['-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_32'],
|
||||
'suite': ['gdbus-codegen'],
|
||||
},
|
||||
'gdbus-threading' : {
|
||||
'extra_sources' : extra_sources,
|
||||
@ -476,11 +481,13 @@ if host_machine.system() != 'windows'
|
||||
'extra_sources' : [extra_sources, gdbus_test_codegen_generated, gdbus_test_codegen_generated_interface_info],
|
||||
'c_args' : ['-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_36',
|
||||
'-DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_36'],
|
||||
'suite': ['gdbus-codegen'],
|
||||
},
|
||||
'gdbus-test-codegen-min-required-2-64' : {
|
||||
'source' : 'gdbus-test-codegen.c',
|
||||
'extra_sources' : [extra_sources, gdbus_test_codegen_generated_min_required_2_64, gdbus_test_codegen_generated_interface_info],
|
||||
'c_args' : ['-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_64'],
|
||||
'suite': ['gdbus-codegen'],
|
||||
},
|
||||
'gapplication' : {
|
||||
'extra_sources' : extra_sources,
|
||||
@ -1045,7 +1052,7 @@ endforeach
|
||||
|
||||
foreach test_name, extra_args : python_tests
|
||||
depends = [extra_args.get('depends', [])]
|
||||
suite = ['gio', 'no-valgrind']
|
||||
suite = ['gio', 'no-valgrind'] + extra_args.get('suite', [])
|
||||
|
||||
if extra_args.get('can_fail', false)
|
||||
suite += 'failing'
|
||||
@ -1062,6 +1069,7 @@ foreach test_name, extra_args : python_tests
|
||||
depends: depends,
|
||||
args: ['-B', files(test_name)],
|
||||
env: test_env,
|
||||
timeout: extra_args.get('timeout'),
|
||||
suite: suite,
|
||||
)
|
||||
|
||||
|
@ -475,7 +475,7 @@
|
||||
</interface>
|
||||
<unknownTag/>
|
||||
|
||||
<interface name="FDPassing">
|
||||
<interface name="test.FDPassing">
|
||||
<method name="HelloFD">
|
||||
<annotation name="org.gtk.GDBus.C.UnixFD" value="1"/>
|
||||
<arg name="greeting" direction="in" type="s"/>
|
||||
|
Loading…
Reference in New Issue
Block a user