diff --git a/gio/gdbus-2.0/codegen/codegen.py b/gio/gdbus-2.0/codegen/codegen.py index a5fd98772..88e08196b 100644 --- a/gio/gdbus-2.0/codegen/codegen.py +++ b/gio/gdbus-2.0/codegen/codegen.py @@ -2136,7 +2136,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" @@ -2525,34 +2525,44 @@ class CodeGenerator: # --------------------------------------------------------------------------------------------------- - def generate_marshaller(self, func_name, in_args): - self.generate_marshaller_declaration(func_name) + 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) + self.generate_marshaller_body(func_name, in_args, ret_arg) self.outfile.write("}\n" "\n") - def generate_marshaller_declaration(self, func_name): + def generate_marshaller_declaration(self, func_name, uses_ret=False): self.outfile.write( "static void\n" f"{func_name} (\n" " GClosure *closure,\n" - " GValue *return_value G_GNUC_UNUSED,\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" " void *invocation_hint G_GNUC_UNUSED,\n" " void *marshal_data)\n" ) - def generate_marshaller_body(self, func_name, in_args=[]): + 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 void (*{marshal_func_type})\n" + 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" @@ -2570,30 +2580,48 @@ class CodeGenerator: f" callback = ({marshal_func_type})\n" " (marshal_data ? marshal_data : cc->callback);\n" "\n" - " callback (data1,\n" + ) + + prefix = "" + if ret_arg: + self.outfile.write(" v_return =\n") + prefix = 2 * " " + + self.outfile.write( + f"{prefix} callback (data1,\n" + "".join( [ - f" {in_args[i].gvalue_get} (param_values + {i+1}),\n" + f"{prefix} {in_args[i].gvalue_get} (param_values + {i+1}),\n" for i in range(len(in_args)) ] ) - + " data2);\n" + + f"{prefix} data2);\n" ) + if ret_arg: + self.outfile.write( + "\n" f" {ret_arg.gvalue_set} (return_value, v_return);\n" + ) + def generate_marshaller_for_type(self, i, t): - assert isinstance(t, dbustypes.Signal) + assert isinstance(t, (dbustypes.Signal, dbustypes.Method)) kind_uscore = utils.camel_case_to_uscore(t.__class__.__name__.lower()) self.generate_marshaller( func_name=f"{i.name_lower}_{kind_uscore}_marshal_{t.name_lower}", - in_args=t.args, + in_args=t.marshaller_in_args, + ret_arg=t.marshaller_ret_arg, ) 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): @@ -5321,6 +5349,7 @@ class CodeGenerator: 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) diff --git a/gio/gdbus-2.0/codegen/dbustypes.py b/gio/gdbus-2.0/codegen/dbustypes.py index d5b671e0a..dcc0be9db 100644 --- a/gio/gdbus-2.0/codegen/dbustypes.py +++ b/gio/gdbus-2.0/codegen/dbustypes.py @@ -84,6 +84,7 @@ class Arg: self.format_out = "@" + self.signature self.gvariant_get = "XXX" self.gvalue_get = "g_value_get_variant" + self.gvalue_set = "g_value_take_variant" self.array_annotation = "" if not utils.lookup_annotation( @@ -100,6 +101,7 @@ class Arg: self.format_out = "b" self.gvariant_get = "g_variant_get_boolean" self.gvalue_get = "g_value_get_boolean" + self.gvalue_set = "g_value_set_boolean" elif self.signature == "y": self.ctype_in_g = "guchar " self.ctype_in = "guchar " @@ -111,6 +113,7 @@ class Arg: self.format_out = "y" self.gvariant_get = "g_variant_get_byte" self.gvalue_get = "g_value_get_uchar" + self.gvalue_set = "g_value_set_uchar" elif self.signature == "n": self.ctype_in_g = "gint " self.ctype_in = "gint16 " @@ -122,6 +125,7 @@ class Arg: self.format_out = "n" self.gvariant_get = "g_variant_get_int16" self.gvalue_get = "g_value_get_int" + self.gvalue_set = "g_value_set_int" elif self.signature == "q": self.ctype_in_g = "guint " self.ctype_in = "guint16 " @@ -133,6 +137,7 @@ class Arg: self.format_out = "q" self.gvariant_get = "g_variant_get_uint16" self.gvalue_get = "g_value_get_uint" + self.gvalue_set = "g_value_set_uint" elif self.signature == "i": self.ctype_in_g = "gint " self.ctype_in = "gint " @@ -144,6 +149,7 @@ class Arg: self.format_out = "i" self.gvariant_get = "g_variant_get_int32" self.gvalue_get = "g_value_get_int" + self.gvalue_set = "g_value_set_int" elif self.signature == "u": self.ctype_in_g = "guint " self.ctype_in = "guint " @@ -155,6 +161,7 @@ class Arg: self.format_out = "u" self.gvariant_get = "g_variant_get_uint32" self.gvalue_get = "g_value_get_uint" + self.gvalue_set = "g_value_set_uint" elif self.signature == "x": self.ctype_in_g = "gint64 " self.ctype_in = "gint64 " @@ -166,6 +173,7 @@ class Arg: self.format_out = "x" self.gvariant_get = "g_variant_get_int64" self.gvalue_get = "g_value_get_int64" + self.gvalue_set = "g_value_set_int64" elif self.signature == "t": self.ctype_in_g = "guint64 " self.ctype_in = "guint64 " @@ -177,6 +185,7 @@ class Arg: self.format_out = "t" self.gvariant_get = "g_variant_get_uint64" self.gvalue_get = "g_value_get_uint64" + self.gvalue_set = "g_value_set_uint64" elif self.signature == "d": self.ctype_in_g = "gdouble " self.ctype_in = "gdouble " @@ -188,6 +197,7 @@ class Arg: self.format_out = "d" self.gvariant_get = "g_variant_get_double" self.gvalue_get = "g_value_get_double" + self.gvalue_set = "g_value_set_double" elif self.signature == "s": self.ctype_in_g = "const gchar *" self.ctype_in = "const gchar *" @@ -200,6 +210,7 @@ class Arg: self.format_out = "s" self.gvariant_get = "g_variant_get_string" self.gvalue_get = "g_value_get_string" + self.gvalue_set = "g_value_set_string" elif self.signature == "o": self.ctype_in_g = "const gchar *" self.ctype_in = "const gchar *" @@ -212,6 +223,7 @@ class Arg: self.format_out = "o" self.gvariant_get = "g_variant_get_string" self.gvalue_get = "g_value_get_string" + self.gvalue_set = "g_value_set_string" elif self.signature == "g": self.ctype_in_g = "const gchar *" self.ctype_in = "const gchar *" @@ -224,6 +236,7 @@ class Arg: self.format_out = "g" self.gvariant_get = "g_variant_get_string" self.gvalue_get = "g_value_get_string" + self.gvalue_set = "g_value_set_string" elif self.signature == "ay": self.ctype_in_g = "const gchar *" self.ctype_in = "const gchar *" @@ -236,6 +249,7 @@ class Arg: self.format_out = "^ay" self.gvariant_get = "g_variant_get_bytestring" self.gvalue_get = "g_value_get_string" + self.gvalue_set = "g_value_set_string" elif self.signature == "as": self.ctype_in_g = "const gchar *const *" self.ctype_in = "const gchar *const *" @@ -248,6 +262,7 @@ class Arg: self.format_out = "^as" self.gvariant_get = "g_variant_get_strv" self.gvalue_get = "g_value_get_boxed" + self.gvalue_set = "g_value_take_boxed" self.array_annotation = "(array zero-terminated=1)" elif self.signature == "ao": self.ctype_in_g = "const gchar *const *" @@ -261,6 +276,7 @@ class Arg: self.format_out = "^ao" self.gvariant_get = "g_variant_get_objv" self.gvalue_get = "g_value_get_boxed" + self.gvalue_set = "g_value_take_boxed" self.array_annotation = "(array zero-terminated=1)" elif self.signature == "aay": self.ctype_in_g = "const gchar *const *" @@ -274,6 +290,7 @@ class Arg: self.format_out = "^aay" self.gvariant_get = "g_variant_get_bytestring_array" self.gvalue_get = "g_value_get_boxed" + self.gvalue_set = "g_value_take_boxed" self.array_annotation = "(array zero-terminated=1)" for a in self.annotations: @@ -336,6 +353,20 @@ 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_get = "g_value_get_object" + 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_get = "g_value_get_object" + 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 +417,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) diff --git a/gio/tests/codegen.py b/gio/tests/codegen.py index d591df192..746517204 100644 --- a/gio/tests/codegen.py +++ b/gio/tests/codegen.py @@ -903,6 +903,247 @@ G_END_DECLS ) 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 = """ + + + + + + + + """ + + 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_value_get_object (param_values + 1)"), 2) + self.assertIs( + stripped_out.count("g_value_set_boolean (return_value, v_return);"), 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""" + + + + + + + """ + + 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_value_get_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_value_get_{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""" + + + + + + + """ + + 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_value_get_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"\n" + for t, props in self.ARGUMENTS_TYPES.items() + ] + + interface_xml = f""" + + + + {''.join(generated_args)} + + + """ + + 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_value_get_object (param_values + {index})"), 1 + ) + index += 1 + + for props in self.ARGUMENTS_TYPES.values(): + self.assertIs( + stripped_out.count( + f"g_value_get_{props['value_type']} (param_values + {index})" + ), + 1, + ) + index += 1 + + self.assertIs( + stripped_out.count("g_value_set_boolean (return_value, v_return);"), 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"\n" + for t, props in self.ARGUMENTS_TYPES.items() + ] + + interface_xml = f""" + + + + {''.join(generated_args)} + + + """ + + 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_value_get_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 + ) + + @unittest.skipIf(on_win32(), "requires /dev/stdout") + def test_generate_methods_marshallers_with_unix_fds(self): + """Test an interface with `h` arguments""" + interface_xml = """ + + + + + + + + + """ + + 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_value_get_object (param_values + {index})"), 1 + ) + index += 1 + + self.assertIs( + stripped_out.count(f"g_value_get_object (param_values + {index})"), 1 + ) + + index += 1 + self.assertIs( + stripped_out.count(f"g_value_get_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 = """