mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-11-10 03:16:17 +01:00
gdbus-codegen: Generate signal marshallers for each interface signal
We relied on g_cclosure_marshal_generic() to easily generate signal marshallers, but this relies on inspecting each parameter type with ffi and this implies a performance hit, other than breaking the stack-frame unwinder used by Linux perf and so by sysprof. Given that we know the types we work on, it's easy enough to generate the marshallers ourself. Helps with: https://gitlab.gnome.org/GNOME/glib/-/issues/3028
This commit is contained in:
parent
51c023f189
commit
1cd5c5678a
@ -2187,7 +2187,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))
|
||||
@ -2525,6 +2525,77 @@ class CodeGenerator:
|
||||
|
||||
# ---------------------------------------------------------------------------------------------------
|
||||
|
||||
def generate_marshaller(self, func_name, in_args):
|
||||
self.generate_marshaller_declaration(func_name)
|
||||
self.outfile.write("{\n")
|
||||
self.generate_marshaller_body(func_name, in_args)
|
||||
self.outfile.write("}\n" "\n")
|
||||
|
||||
def generate_marshaller_declaration(self, func_name):
|
||||
self.outfile.write(
|
||||
"static void\n"
|
||||
f"{func_name} (\n"
|
||||
" GClosure *closure,\n"
|
||||
" GValue *return_value G_GNUC_UNUSED,\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=[]):
|
||||
marshal_func_type = f"{utils.uscore_to_camel_case(func_name)}Func"
|
||||
self.outfile.write(
|
||||
f" typedef 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"
|
||||
"\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"
|
||||
" callback (data1,\n"
|
||||
+ "".join(
|
||||
[
|
||||
f" {in_args[i].gvalue_get} (param_values + {i+1}),\n"
|
||||
for i in range(len(in_args))
|
||||
]
|
||||
)
|
||||
+ " data2);\n"
|
||||
)
|
||||
|
||||
def generate_marshaller_for_type(self, i, t):
|
||||
assert isinstance(t, dbustypes.Signal)
|
||||
|
||||
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,
|
||||
)
|
||||
|
||||
def generate_signal_marshallers(self, i):
|
||||
for s in i.signals:
|
||||
self.generate_marshaller_for_type(i, s)
|
||||
|
||||
# ---------------------------------------------------------------------------------------------------
|
||||
|
||||
def generate_method_calls(self, i):
|
||||
for m in i.methods:
|
||||
# async begin
|
||||
@ -5249,6 +5320,7 @@ class CodeGenerator:
|
||||
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_interface(i)
|
||||
self.generate_property_accessors(i)
|
||||
self.generate_signal_emitters(i)
|
||||
|
@ -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
|
||||
|
@ -61,24 +61,24 @@ class TestCodegen(unittest.TestCase):
|
||||
cwd = ""
|
||||
|
||||
ARGUMENTS_TYPES = {
|
||||
"b": {},
|
||||
"y": {},
|
||||
"n": {},
|
||||
"q": {},
|
||||
"i": {},
|
||||
"u": {},
|
||||
"x": {},
|
||||
"t": {},
|
||||
"d": {},
|
||||
"s": {},
|
||||
"o": {},
|
||||
"g": {},
|
||||
"h": {},
|
||||
"ay": {},
|
||||
"as": {},
|
||||
"ao": {},
|
||||
"aay": {},
|
||||
"asv": {"variant_type": "a{sv}"},
|
||||
"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"},
|
||||
"t": {"value_type": "uint64"},
|
||||
"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):
|
||||
@ -797,6 +797,112 @@ G_END_DECLS
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
@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)
|
||||
|
||||
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)
|
||||
self.assertIs(
|
||||
stripped_out.count(
|
||||
f"g_value_get_{props['value_type']} (param_values + 1)"
|
||||
),
|
||||
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_value_get_{props['value_type']} (param_values + {index})"
|
||||
),
|
||||
1,
|
||||
)
|
||||
index += 1
|
||||
|
||||
def test_generate_valid_docbook(self):
|
||||
"""Test the basic functionality of the docbook generator."""
|
||||
xml_contents = """
|
||||
|
Loading…
Reference in New Issue
Block a user