mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-25 15:06:14 +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"
|
" G_STRUCT_OFFSET (%sIface, %s),\n"
|
||||||
" NULL,\n" # accumulator
|
" NULL,\n" # accumulator
|
||||||
" NULL,\n" # accu_data
|
" NULL,\n" # accu_data
|
||||||
" g_cclosure_marshal_generic,\n"
|
f" {i.name_lower}_signal_marshal_{s.name_lower},\n"
|
||||||
" G_TYPE_NONE,\n"
|
" G_TYPE_NONE,\n"
|
||||||
" %d"
|
" %d"
|
||||||
% (s.name_hyphen, i.camel_name, s.name_lower, len(s.args))
|
% (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):
|
def generate_method_calls(self, i):
|
||||||
for m in i.methods:
|
for m in i.methods:
|
||||||
# async begin
|
# async begin
|
||||||
@ -5249,6 +5320,7 @@ class CodeGenerator:
|
|||||||
self.generate_interface_intro(i)
|
self.generate_interface_intro(i)
|
||||||
self.generate_signals_enum_for_interface(i)
|
self.generate_signals_enum_for_interface(i)
|
||||||
self.generate_introspection_for_interface(i)
|
self.generate_introspection_for_interface(i)
|
||||||
|
self.generate_signal_marshallers(i)
|
||||||
self.generate_interface(i)
|
self.generate_interface(i)
|
||||||
self.generate_property_accessors(i)
|
self.generate_property_accessors(i)
|
||||||
self.generate_signal_emitters(i)
|
self.generate_signal_emitters(i)
|
||||||
|
@ -114,6 +114,10 @@ def camel_case_to_uscore(s):
|
|||||||
return ret
|
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):
|
def is_ugly_case(s):
|
||||||
if s and s.find("_") > 0:
|
if s and s.find("_") > 0:
|
||||||
return True
|
return True
|
||||||
|
@ -61,24 +61,24 @@ class TestCodegen(unittest.TestCase):
|
|||||||
cwd = ""
|
cwd = ""
|
||||||
|
|
||||||
ARGUMENTS_TYPES = {
|
ARGUMENTS_TYPES = {
|
||||||
"b": {},
|
"b": {"value_type": "boolean"},
|
||||||
"y": {},
|
"y": {"value_type": "uchar"},
|
||||||
"n": {},
|
"n": {"value_type": "int"},
|
||||||
"q": {},
|
"q": {"value_type": "uint"},
|
||||||
"i": {},
|
"i": {"value_type": "int"},
|
||||||
"u": {},
|
"u": {"value_type": "uint"},
|
||||||
"x": {},
|
"x": {"value_type": "int64"},
|
||||||
"t": {},
|
"t": {"value_type": "uint64"},
|
||||||
"d": {},
|
"d": {"value_type": "double"},
|
||||||
"s": {},
|
"s": {"value_type": "string"},
|
||||||
"o": {},
|
"o": {"value_type": "string"},
|
||||||
"g": {},
|
"g": {"value_type": "string"},
|
||||||
"h": {},
|
"h": {"value_type": "variant"},
|
||||||
"ay": {},
|
"ay": {"value_type": "string"},
|
||||||
"as": {},
|
"as": {"value_type": "boxed"},
|
||||||
"ao": {},
|
"ao": {"value_type": "boxed"},
|
||||||
"aay": {},
|
"aay": {"value_type": "boxed"},
|
||||||
"asv": {"variant_type": "a{sv}"},
|
"asv": {"value_type": "variant", "variant_type": "a{sv}"},
|
||||||
}
|
}
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@ -797,6 +797,112 @@ G_END_DECLS
|
|||||||
1,
|
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):
|
def test_generate_valid_docbook(self):
|
||||||
"""Test the basic functionality of the docbook generator."""
|
"""Test the basic functionality of the docbook generator."""
|
||||||
xml_contents = """
|
xml_contents = """
|
||||||
|
Loading…
Reference in New Issue
Block a user