Merge branch 'issue-699' into 'master'
[glib.git] / gobject / glib-genmarshal.in
blobdc4c7eaccff7d00e15f38795e32c40a2f4016b99
1 #!/usr/bin/env @PYTHON@
3 # pylint: disable=too-many-lines, missing-docstring, invalid-name
5 # This file is part of GLib
7 # This library is free software; you can redistribute it and/or
8 # modify it under the terms of the GNU Lesser General Public
9 # License as published by the Free Software Foundation; either
10 # version 2.1 of the License, or (at your option) any later version.
12 # This library is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 # Lesser General Public License for more details.
17 # You should have received a copy of the GNU Lesser General Public
18 # License along with this library; if not, see <http://www.gnu.org/licenses/>.
20 import argparse
21 import os
22 import re
23 import sys
25 VERSION_STR = '''glib-genmarshal version @VERSION@
26 glib-genmarshal comes with ABSOLUTELY NO WARRANTY.
27 You may redistribute copies of glib-genmarshal under the terms of
28 the GNU General Public License which can be found in the
29 GLib source package. Sources, examples and contact
30 information are available at http://www.gtk.org'''
32 GETTERS_STR = '''#ifdef G_ENABLE_DEBUG
33 #define g_marshal_value_peek_boolean(v)  g_value_get_boolean (v)
34 #define g_marshal_value_peek_char(v)     g_value_get_schar (v)
35 #define g_marshal_value_peek_uchar(v)    g_value_get_uchar (v)
36 #define g_marshal_value_peek_int(v)      g_value_get_int (v)
37 #define g_marshal_value_peek_uint(v)     g_value_get_uint (v)
38 #define g_marshal_value_peek_long(v)     g_value_get_long (v)
39 #define g_marshal_value_peek_ulong(v)    g_value_get_ulong (v)
40 #define g_marshal_value_peek_int64(v)    g_value_get_int64 (v)
41 #define g_marshal_value_peek_uint64(v)   g_value_get_uint64 (v)
42 #define g_marshal_value_peek_enum(v)     g_value_get_enum (v)
43 #define g_marshal_value_peek_flags(v)    g_value_get_flags (v)
44 #define g_marshal_value_peek_float(v)    g_value_get_float (v)
45 #define g_marshal_value_peek_double(v)   g_value_get_double (v)
46 #define g_marshal_value_peek_string(v)   (char*) g_value_get_string (v)
47 #define g_marshal_value_peek_param(v)    g_value_get_param (v)
48 #define g_marshal_value_peek_boxed(v)    g_value_get_boxed (v)
49 #define g_marshal_value_peek_pointer(v)  g_value_get_pointer (v)
50 #define g_marshal_value_peek_object(v)   g_value_get_object (v)
51 #define g_marshal_value_peek_variant(v)  g_value_get_variant (v)
52 #else /* !G_ENABLE_DEBUG */
53 /* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
54  *          Do not access GValues directly in your code. Instead, use the
55  *          g_value_get_*() functions
56  */
57 #define g_marshal_value_peek_boolean(v)  (v)->data[0].v_int
58 #define g_marshal_value_peek_char(v)     (v)->data[0].v_int
59 #define g_marshal_value_peek_uchar(v)    (v)->data[0].v_uint
60 #define g_marshal_value_peek_int(v)      (v)->data[0].v_int
61 #define g_marshal_value_peek_uint(v)     (v)->data[0].v_uint
62 #define g_marshal_value_peek_long(v)     (v)->data[0].v_long
63 #define g_marshal_value_peek_ulong(v)    (v)->data[0].v_ulong
64 #define g_marshal_value_peek_int64(v)    (v)->data[0].v_int64
65 #define g_marshal_value_peek_uint64(v)   (v)->data[0].v_uint64
66 #define g_marshal_value_peek_enum(v)     (v)->data[0].v_long
67 #define g_marshal_value_peek_flags(v)    (v)->data[0].v_ulong
68 #define g_marshal_value_peek_float(v)    (v)->data[0].v_float
69 #define g_marshal_value_peek_double(v)   (v)->data[0].v_double
70 #define g_marshal_value_peek_string(v)   (v)->data[0].v_pointer
71 #define g_marshal_value_peek_param(v)    (v)->data[0].v_pointer
72 #define g_marshal_value_peek_boxed(v)    (v)->data[0].v_pointer
73 #define g_marshal_value_peek_pointer(v)  (v)->data[0].v_pointer
74 #define g_marshal_value_peek_object(v)   (v)->data[0].v_pointer
75 #define g_marshal_value_peek_variant(v)  (v)->data[0].v_pointer
76 #endif /* !G_ENABLE_DEBUG */'''
78 DEPRECATED_MSG_STR = 'The token "{}" is deprecated; use "{}" instead'
80 VA_ARG_STR = \
81     '  arg{:d} = ({:s}) va_arg (args_copy, {:s});'
82 STATIC_CHECK_STR = \
83     '(param_types[{:d}] & G_SIGNAL_TYPE_STATIC_SCOPE) == 0 && '
84 BOX_TYPED_STR = \
85     '    arg{idx:d} = {box_func} (param_types[{idx:d}] & ~G_SIGNAL_TYPE_STATIC_SCOPE, arg{idx:d});'
86 BOX_UNTYPED_STR = \
87     '    arg{idx:d} = {box_func} (arg{idx:d});'
88 UNBOX_TYPED_STR = \
89     '    {unbox_func} (param_types[{idx:d}] & ~G_SIGNAL_TYPE_STATIC_SCOPE, arg{idx:d});'
90 UNBOX_UNTYPED_STR = \
91     '    {unbox_func} (arg{idx:d});'
93 STD_PREFIX = 'g_cclosure_marshal'
95 # These are part of our ABI; keep this in sync with gmarshal.h
96 GOBJECT_MARSHALLERS = {
97     'g_cclosure_marshal_VOID__VOID',
98     'g_cclosure_marshal_VOID__BOOLEAN',
99     'g_cclosure_marshal_VOID__CHAR',
100     'g_cclosure_marshal_VOID__UCHAR',
101     'g_cclosure_marshal_VOID__INT',
102     'g_cclosure_marshal_VOID__UINT',
103     'g_cclosure_marshal_VOID__LONG',
104     'g_cclosure_marshal_VOID__ULONG',
105     'g_cclosure_marshal_VOID__ENUM',
106     'g_cclosure_marshal_VOID__FLAGS',
107     'g_cclosure_marshal_VOID__FLOAT',
108     'g_cclosure_marshal_VOID__DOUBLE',
109     'g_cclosure_marshal_VOID__STRING',
110     'g_cclosure_marshal_VOID__PARAM',
111     'g_cclosure_marshal_VOID__BOXED',
112     'g_cclosure_marshal_VOID__POINTER',
113     'g_cclosure_marshal_VOID__OBJECT',
114     'g_cclosure_marshal_VOID__VARIANT',
115     'g_cclosure_marshal_VOID__UINT_POINTER',
116     'g_cclosure_marshal_BOOLEAN__FLAGS',
117     'g_cclosure_marshal_STRING__OBJECT_POINTER',
118     'g_cclosure_marshal_BOOLEAN__BOXED_BOXED',
122 # pylint: disable=too-few-public-methods
123 class Color:
124     '''ANSI Terminal colors'''
125     GREEN = '\033[1;32m'
126     BLUE = '\033[1;34m'
127     YELLOW = '\033[1;33m'
128     RED = '\033[1;31m'
129     END = '\033[0m'
132 def print_color(msg, color=Color.END, prefix='MESSAGE'):
133     '''Print a string with a color prefix'''
134     if os.isatty(sys.stderr.fileno()):
135         real_prefix = '{start}{prefix}{end}'.format(start=color, prefix=prefix, end=Color.END)
136     else:
137         real_prefix = prefix
138     sys.stderr.write('{prefix}: {msg}\n'.format(prefix=real_prefix, msg=msg))
141 def print_error(msg):
142     '''Print an error, and terminate'''
143     print_color(msg, color=Color.RED, prefix='ERROR')
144     sys.exit(1)
147 def print_warning(msg, fatal=False):
148     '''Print a warning, and optionally terminate'''
149     if fatal:
150         color = Color.RED
151         prefix = 'ERROR'
152     else:
153         color = Color.YELLOW
154         prefix = 'WARNING'
155     print_color(msg, color, prefix)
156     if fatal:
157         sys.exit(1)
160 def print_info(msg):
161     '''Print a message'''
162     print_color(msg, color=Color.GREEN, prefix='INFO')
165 def generate_licensing_comment(outfile):
166     outfile.write('/* This file is generated by glib-genmarshal, do not '
167                   'modify it. This code is licensed under the same license as '
168                   'the containing project. Note that it links to GLib, so '
169                   'must comply with the LGPL linking clauses. */\n')
172 def generate_header_preamble(outfile, prefix='', std_includes=True, use_pragma=False):
173     '''Generate the preamble for the marshallers header file'''
174     generate_licensing_comment(outfile)
176     if use_pragma:
177         outfile.write('#pragma once\n')
178         outfile.write('\n')
179     else:
180         outfile.write('#ifndef __{}_MARSHAL_H__\n'.format(prefix.upper()))
181         outfile.write('#define __{}_MARSHAL_H__\n'.format(prefix.upper()))
182         outfile.write('\n')
183     # Maintain compatibility with the old C-based tool
184     if std_includes:
185         outfile.write('#include <glib-object.h>\n')
186         outfile.write('\n')
188     outfile.write('G_BEGIN_DECLS\n')
189     outfile.write('\n')
192 def generate_header_postamble(outfile, prefix='', use_pragma=False):
193     '''Generate the postamble for the marshallers header file'''
194     outfile.write('\n')
195     outfile.write('G_END_DECLS\n')
197     if not use_pragma:
198         outfile.write('\n')
199         outfile.write('#endif /* __{}_MARSHAL_H__ */\n'.format(prefix.upper()))
202 def generate_body_preamble(outfile, std_includes=True, include_headers=None, cpp_defines=None, cpp_undefines=None):
203     '''Generate the preamble for the marshallers source file'''
204     generate_licensing_comment(outfile)
206     for header in (include_headers or []):
207         outfile.write('#include "{}"\n'.format(header))
208     if include_headers:
209         outfile.write('\n')
211     for define in (cpp_defines or []):
212         s = define.split('=')
213         symbol = s[0]
214         value = s[1] if len(s) > 1 else '1'
215         outfile.write('#define {} {}\n'.format(symbol, value))
216     if cpp_defines:
217         outfile.write('\n')
219     for undefine in (cpp_undefines or []):
220         outfile.write('#undef {}\n'.format(undefine))
221     if cpp_undefines:
222         outfile.write('\n')
224     if std_includes:
225         outfile.write('#include <glib-object.h>\n')
226         outfile.write('\n')
228     outfile.write(GETTERS_STR)
229     outfile.write('\n\n')
232 # Marshaller arguments, as a dictionary where the key is the token used in
233 # the source file, and the value is another dictionary with the following
234 # keys:
236 #   - signal: the token used in the marshaller prototype (mandatory)
237 #   - ctype: the C type for the marshaller argument (mandatory)
238 #   - getter: the function used to retrieve the argument from the GValue
239 #       array when invoking the callback (optional)
240 #   - promoted: the C type used by va_arg() to retrieve the argument from
241 #       the va_list when invoking the callback (optional, only used when
242 #       generating va_list marshallers)
243 #   - box: an array of two elements, containing the boxing and unboxing
244 #       functions for the given type (optional, only used when generating
245 #       va_list marshallers)
246 #   - static-check: a boolean value, if the given type should perform
247 #       a static type check before boxing or unboxing the argument (optional,
248 #       only used when generating va_list marshallers)
249 #   - takes-type: a boolean value, if the boxing and unboxing functions
250 #       for the given type require the type (optional, only used when
251 #       generating va_list marshallers)
252 #   - deprecated: whether the token has been deprecated (optional)
253 #   - replaced-by: the token used to replace a deprecated token (optional,
254 #       only used if deprecated is True)
255 IN_ARGS = {
256     'VOID': {
257         'signal': 'VOID',
258         'ctype': 'void',
259     },
260     'BOOLEAN': {
261         'signal': 'BOOLEAN',
262         'ctype': 'gboolean',
263         'getter': 'g_marshal_value_peek_boolean',
264     },
265     'CHAR': {
266         'signal': 'CHAR',
267         'ctype': 'gchar',
268         'promoted': 'gint',
269         'getter': 'g_marshal_value_peek_char',
270     },
271     'UCHAR': {
272         'signal': 'UCHAR',
273         'ctype': 'guchar',
274         'promoted': 'guint',
275         'getter': 'g_marshal_value_peek_uchar',
276     },
277     'INT': {
278         'signal': 'INT',
279         'ctype': 'gint',
280         'getter': 'g_marshal_value_peek_int',
281     },
282     'UINT': {
283         'signal': 'UINT',
284         'ctype': 'guint',
285         'getter': 'g_marshal_value_peek_uint',
286     },
287     'LONG': {
288         'signal': 'LONG',
289         'ctype': 'glong',
290         'getter': 'g_marshal_value_peek_long',
291     },
292     'ULONG': {
293         'signal': 'ULONG',
294         'ctype': 'gulong',
295         'getter': 'g_marshal_value_peek_ulong',
296     },
297     'INT64': {
298         'signal': 'INT64',
299         'ctype': 'gint64',
300         'getter': 'g_marshal_value_peek_int64',
301     },
302     'UINT64': {
303         'signal': 'UINT64',
304         'ctype': 'guint64',
305         'getter': 'g_marshal_value_peek_uint64',
306     },
307     'ENUM': {
308         'signal': 'ENUM',
309         'ctype': 'gint',
310         'getter': 'g_marshal_value_peek_enum',
311     },
312     'FLAGS': {
313         'signal': 'FLAGS',
314         'ctype': 'guint',
315         'getter': 'g_marshal_value_peek_flags',
316     },
317     'FLOAT': {
318         'signal': 'FLOAT',
319         'ctype': 'gfloat',
320         'promoted': 'gdouble',
321         'getter': 'g_marshal_value_peek_float',
322     },
323     'DOUBLE': {
324         'signal': 'DOUBLE',
325         'ctype': 'gdouble',
326         'getter': 'g_marshal_value_peek_double',
327     },
328     'STRING': {
329         'signal': 'STRING',
330         'ctype': 'gpointer',
331         'getter': 'g_marshal_value_peek_string',
332         'box': ['g_strdup', 'g_free'],
333     },
334     'PARAM': {
335         'signal': 'PARAM',
336         'ctype': 'gpointer',
337         'getter': 'g_marshal_value_peek_param',
338         'box': ['g_param_spec_ref', 'g_param_spec_unref'],
339     },
340     'BOXED': {
341         'signal': 'BOXED',
342         'ctype': 'gpointer',
343         'getter': 'g_marshal_value_peek_boxed',
344         'box': ['g_boxed_copy', 'g_boxed_free'],
345         'static-check': True,
346         'takes-type': True,
347     },
348     'POINTER': {
349         'signal': 'POINTER',
350         'ctype': 'gpointer',
351         'getter': 'g_marshal_value_peek_pointer',
352     },
353     'OBJECT': {
354         'signal': 'OBJECT',
355         'ctype': 'gpointer',
356         'getter': 'g_marshal_value_peek_object',
357         'box': ['g_object_ref', 'g_object_unref'],
358     },
359     'VARIANT': {
360         'signal': 'VARIANT',
361         'ctype': 'gpointer',
362         'getter': 'g_marshal_value_peek_variant',
363         'box': ['g_variant_ref', 'g_variant_unref'],
364         'static-check': True,
365         'takes-type': False,
366     },
368     # Deprecated tokens
369     'NONE': {
370         'signal': 'VOID',
371         'ctype': 'void',
372         'deprecated': True,
373         'replaced_by': 'VOID'
374     },
375     'BOOL': {
376         'signal': 'BOOLEAN',
377         'ctype': 'gboolean',
378         'getter': 'g_marshal_value_peek_boolean',
379         'deprecated': True,
380         'replaced_by': 'BOOLEAN'
381     }
385 # Marshaller return values, as a dictionary where the key is the token used
386 # in the source file, and the value is another dictionary with the following
387 # keys:
389 #   - signal: the token used in the marshaller prototype (mandatory)
390 #   - ctype: the C type for the marshaller argument (mandatory)
391 #   - setter: the function used to set the return value of the callback
392 #       into a GValue (optional)
393 #   - deprecated: whether the token has been deprecated (optional)
394 #   - replaced-by: the token used to replace a deprecated token (optional,
395 #       only used if deprecated is True)
396 OUT_ARGS = {
397     'VOID': {
398         'signal': 'VOID',
399         'ctype': 'void',
400     },
401     'BOOLEAN': {
402         'signal': 'BOOLEAN',
403         'ctype': 'gboolean',
404         'setter': 'g_value_set_boolean',
405     },
406     'CHAR': {
407         'signal': 'CHAR',
408         'ctype': 'gchar',
409         'setter': 'g_value_set_char',
410     },
411     'UCHAR': {
412         'signal': 'UCHAR',
413         'ctype': 'guchar',
414         'setter': 'g_value_set_uchar',
415     },
416     'INT': {
417         'signal': 'INT',
418         'ctype': 'gint',
419         'setter': 'g_value_set_int',
420     },
421     'UINT': {
422         'signal': 'UINT',
423         'ctype': 'guint',
424         'setter': 'g_value_set_uint',
425     },
426     'LONG': {
427         'signal': 'LONG',
428         'ctype': 'glong',
429         'setter': 'g_value_set_long',
430     },
431     'ULONG': {
432         'signal': 'ULONG',
433         'ctype': 'gulong',
434         'setter': 'g_value_set_ulong',
435     },
436     'INT64': {
437         'signal': 'INT64',
438         'ctype': 'gint64',
439         'setter': 'g_value_set_int64',
440     },
441     'UINT64': {
442         'signal': 'UINT64',
443         'ctype': 'guint64',
444         'setter': 'g_value_set_uint64',
445     },
446     'ENUM': {
447         'signal': 'ENUM',
448         'ctype': 'gint',
449         'setter': 'g_value_set_enum',
450     },
451     'FLAGS': {
452         'signal': 'FLAGS',
453         'ctype': 'guint',
454         'setter': 'g_value_set_flags',
455     },
456     'FLOAT': {
457         'signal': 'FLOAT',
458         'ctype': 'gfloat',
459         'setter': 'g_value_set_float',
460     },
461     'DOUBLE': {
462         'signal': 'DOUBLE',
463         'ctype': 'gdouble',
464         'setter': 'g_value_set_double',
465     },
466     'STRING': {
467         'signal': 'STRING',
468         'ctype': 'gchar*',
469         'setter': 'g_value_take_string',
470     },
471     'PARAM': {
472         'signal': 'PARAM',
473         'ctype': 'GParamSpec*',
474         'setter': 'g_value_take_param',
475     },
476     'BOXED': {
477         'signal': 'BOXED',
478         'ctype': 'gpointer',
479         'setter': 'g_value_take_boxed',
480     },
481     'POINTER': {
482         'signal': 'POINTER',
483         'ctype': 'gpointer',
484         'setter': 'g_value_set_pointer',
485     },
486     'OBJECT': {
487         'signal': 'OBJECT',
488         'ctype': 'GObject*',
489         'setter': 'g_value_take_object',
490     },
491     'VARIANT': {
492         'signal': 'VARIANT',
493         'ctype': 'GVariant*',
494         'setter': 'g_value_take_variant',
495     },
497     # Deprecated tokens
498     'NONE': {
499         'signal': 'VOID',
500         'ctype': 'void',
501         'setter': None,
502         'deprecated': True,
503         'replaced_by': 'VOID',
504     },
505     'BOOL': {
506         'signal': 'BOOLEAN',
507         'ctype': 'gboolean',
508         'setter': 'g_value_set_boolean',
509         'deprecated': True,
510         'replaced_by': 'BOOLEAN',
511     },
515 def check_args(retval, params, fatal_warnings=False):
516     '''Check the @retval and @params tokens for invalid and deprecated symbols.'''
517     if retval not in OUT_ARGS:
518         print_error('Unknown return value type "{}"'.format(retval))
520     if OUT_ARGS[retval].get('deprecated', False):
521         replaced_by = OUT_ARGS[retval]['replaced_by']
522         print_warning(DEPRECATED_MSG_STR.format(retval, replaced_by), fatal_warnings)
524     for param in params:
525         if param not in IN_ARGS:
526             print_error('Unknown parameter type "{}"'.format(param))
527         else:
528             if IN_ARGS[param].get('deprecated', False):
529                 replaced_by = IN_ARGS[param]['replaced_by']
530                 print_warning(DEPRECATED_MSG_STR.format(param, replaced_by), fatal_warnings)
533 def indent(text, level=0, fill=' '):
534     '''Indent @text by @level columns, using the @fill character'''
535     return ''.join([fill for x in range(level)]) + text
538 # pylint: disable=too-few-public-methods
539 class Visibility:
540     '''Symbol visibility options'''
541     NONE = 0
542     INTERNAL = 1
543     EXTERN = 2
546 def generate_marshaller_name(prefix, retval, params, replace_deprecated=True):
547     '''Generate a marshaller name for the given @prefix, @retval, and @params.
548     If @replace_deprecated is True, the generated name will replace deprecated
549     tokens.'''
550     if replace_deprecated:
551         real_retval = OUT_ARGS[retval]['signal']
552         real_params = []
553         for param in params:
554             real_params.append(IN_ARGS[param]['signal'])
555     else:
556         real_retval = retval
557         real_params = params
558     return '{prefix}_{retval}__{args}'.format(prefix=prefix,
559                                               retval=real_retval,
560                                               args='_'.join(real_params))
563 def generate_prototype(retval, params,
564                        prefix='g_cclosure_user_marshal',
565                        visibility=Visibility.NONE,
566                        va_marshal=False):
567     '''Generate a marshaller declaration with the given @visibility. If @va_marshal
568     is True, the marshaller will use variadic arguments in place of a GValue array.'''
569     signature = []
571     if visibility == Visibility.INTERNAL:
572         signature += ['G_GNUC_INTERNAL']
573     elif visibility == Visibility.EXTERN:
574         signature += ['extern']
576     function_name = generate_marshaller_name(prefix, retval, params)
578     if not va_marshal:
579         signature += ['void ' + function_name + ' (GClosure     *closure,']
580         width = len('void ') + len(function_name) + 2
582         signature += [indent('GValue       *return_value,', level=width, fill=' ')]
583         signature += [indent('guint         n_param_values,', level=width, fill=' ')]
584         signature += [indent('const GValue *param_values,', level=width, fill=' ')]
585         signature += [indent('gpointer      invocation_hint,', level=width, fill=' ')]
586         signature += [indent('gpointer      marshal_data);', level=width, fill=' ')]
587     else:
588         signature += ['void ' + function_name + 'v (GClosure *closure,']
589         width = len('void ') + len(function_name) + 3
591         signature += [indent('GValue   *return_value,', level=width, fill=' ')]
592         signature += [indent('gpointer  instance,', level=width, fill=' ')]
593         signature += [indent('va_list   args,', level=width, fill=' ')]
594         signature += [indent('gpointer  marshal_data,', level=width, fill=' ')]
595         signature += [indent('int       n_params,', level=width, fill=' ')]
596         signature += [indent('GType    *param_types);', level=width, fill=' ')]
598     return signature
601 # pylint: disable=too-many-statements, too-many-locals, too-many-branches
602 def generate_body(retval, params, prefix, va_marshal=False):
603     '''Generate a marshaller definition. If @va_marshal is True, the marshaller
604     will use va_list and variadic arguments in place of a GValue array.'''
605     retval_setter = OUT_ARGS[retval].get('setter', None)
606     # If there's no return value then we can mark the retval argument as unused
607     # and get a minor optimisation, as well as avoid a compiler warning
608     if not retval_setter:
609         unused = ' G_GNUC_UNUSED'
610     else:
611         unused = ''
613     body = ['void']
615     function_name = generate_marshaller_name(prefix, retval, params)
617     if not va_marshal:
618         body += [function_name + ' (GClosure     *closure,']
619         width = len(function_name) + 2
621         body += [indent('GValue       *return_value{},'.format(unused), level=width, fill=' ')]
622         body += [indent('guint         n_param_values,', level=width, fill=' ')]
623         body += [indent('const GValue *param_values,', level=width, fill=' ')]
624         body += [indent('gpointer      invocation_hint G_GNUC_UNUSED,', level=width, fill=' ')]
625         body += [indent('gpointer      marshal_data)', level=width, fill=' ')]
626     else:
627         body += [function_name + 'v (GClosure *closure,']
628         width = len(function_name) + 3
630         body += [indent('GValue   *return_value{},'.format(unused), level=width, fill=' ')]
631         body += [indent('gpointer  instance,', level=width, fill=' ')]
632         body += [indent('va_list   args,', level=width, fill=' ')]
633         body += [indent('gpointer  marshal_data,', level=width, fill=' ')]
634         body += [indent('int       n_params,', level=width, fill=' ')]
635         body += [indent('GType    *param_types)', level=width, fill=' ')]
637     # Filter the arguments that have a getter
638     get_args = [x for x in params if IN_ARGS[x].get('getter', None) is not None]
640     body += ['{']
642     # Generate the type of the marshaller function
643     typedef_marshal = generate_marshaller_name('GMarshalFunc', retval, params)
645     typedef = '  typedef {ctype} (*{func_name}) ('.format(ctype=OUT_ARGS[retval]['ctype'],
646                                                           func_name=typedef_marshal)
647     pad = len(typedef)
648     typedef += 'gpointer data1,'
649     body += [typedef]
651     for idx, in_arg in enumerate(get_args):
652         body += [indent('{} arg{:d},'.format(IN_ARGS[in_arg]['ctype'], idx + 1), level=pad)]
654     body += [indent('gpointer data2);', level=pad)]
656     # Variable declarations
657     body += ['  GCClosure *cc = (GCClosure *) closure;']
658     body += ['  gpointer data1, data2;']
659     body += ['  {} callback;'.format(typedef_marshal)]
661     if retval_setter:
662         body += ['  {} v_return;'.format(OUT_ARGS[retval]['ctype'])]
664     if va_marshal:
665         for idx, arg in enumerate(get_args):
666             body += ['  {} arg{:d};'.format(IN_ARGS[arg]['ctype'], idx)]
668         if get_args:
669             body += ['  va_list args_copy;']
670             body += ['']
672             body += ['  G_VA_COPY (args_copy, args);']
674             for idx, arg in enumerate(get_args):
675                 ctype = IN_ARGS[arg]['ctype']
676                 promoted_ctype = IN_ARGS[arg].get('promoted', ctype)
677                 body += [VA_ARG_STR.format(idx, ctype, promoted_ctype)]
678                 if IN_ARGS[arg].get('box', None):
679                     box_func = IN_ARGS[arg]['box'][0]
680                     if IN_ARGS[arg].get('static-check', False):
681                         static_check = STATIC_CHECK_STR.format(idx)
682                     else:
683                         static_check = ''
684                     arg_check = 'arg{:d} != NULL'.format(idx)
685                     body += ['  if ({}{})'.format(static_check, arg_check)]
686                     if IN_ARGS[arg].get('takes-type', False):
687                         body += [BOX_TYPED_STR.format(idx=idx, box_func=box_func)]
688                     else:
689                         body += [BOX_UNTYPED_STR.format(idx=idx, box_func=box_func)]
691             body += ['  va_end (args_copy);']
693     body += ['']
695     # Preconditions check
696     if retval_setter:
697         body += ['  g_return_if_fail (return_value != NULL);']
699     if not va_marshal:
700         body += ['  g_return_if_fail (n_param_values == {:d});'.format(len(get_args) + 1)]
702     body += ['']
704     # Marshal instance, data, and callback set up
705     body += ['  if (G_CCLOSURE_SWAP_DATA (closure))']
706     body += ['    {']
707     body += ['      data1 = closure->data;']
708     if va_marshal:
709         body += ['      data2 = instance;']
710     else:
711         body += ['      data2 = g_value_peek_pointer (param_values + 0);']
712     body += ['    }']
713     body += ['  else']
714     body += ['    {']
715     if va_marshal:
716         body += ['      data1 = instance;']
717     else:
718         body += ['      data1 = g_value_peek_pointer (param_values + 0);']
719     body += ['      data2 = closure->data;']
720     body += ['    }']
721     # pylint: disable=line-too-long
722     body += ['  callback = ({}) (marshal_data ? marshal_data : cc->callback);'.format(typedef_marshal)]
723     body += ['']
725     # Marshal callback action
726     if retval_setter:
727         callback = ' {} callback ('.format(' v_return =')
728     else:
729         callback = '  callback ('
731     pad = len(callback)
732     body += [callback + 'data1,']
734     if va_marshal:
735         for idx, arg in enumerate(get_args):
736             body += [indent('arg{:d},'.format(idx), level=pad)]
737     else:
738         for idx, arg in enumerate(get_args):
739             arg_getter = IN_ARGS[arg]['getter']
740             body += [indent('{} (param_values + {:d}),'.format(arg_getter, idx + 1), level=pad)]
742     body += [indent('data2);', level=pad)]
744     if va_marshal:
745         boxed_args = [x for x in get_args if IN_ARGS[x].get('box', None) is not None]
746         if not boxed_args:
747             body += ['']
748         else:
749             for idx, arg in enumerate(get_args):
750                 if not IN_ARGS[arg].get('box', None):
751                     continue
752                 unbox_func = IN_ARGS[arg]['box'][1]
753                 if IN_ARGS[arg].get('static-check', False):
754                     static_check = STATIC_CHECK_STR.format(idx)
755                 else:
756                     static_check = ''
757                 arg_check = 'arg{:d} != NULL'.format(idx)
758                 body += ['  if ({}{})'.format(static_check, arg_check)]
759                 if IN_ARGS[arg].get('takes-type', False):
760                     body += [UNBOX_TYPED_STR.format(idx=idx, unbox_func=unbox_func)]
761                 else:
762                     body += [UNBOX_UNTYPED_STR.format(idx=idx, unbox_func=unbox_func)]
764     if retval_setter:
765         body += ['']
766         body += ['  {} (return_value, v_return);'.format(retval_setter)]
768     body += ['}']
770     return body
773 def generate_marshaller_alias(outfile, marshaller, real_marshaller,
774                               include_va=False,
775                               source_location=None):
776     '''Generate an alias between @marshaller and @real_marshaller, including
777     an optional alias for va_list marshallers'''
778     if source_location:
779         outfile.write('/* {} */\n'.format(source_location))
781     outfile.write('#define {}\t{}\n'.format(marshaller, real_marshaller))
783     if include_va:
784         outfile.write('#define {}v\t{}v\n'.format(marshaller, real_marshaller))
786     outfile.write('\n')
789 def generate_marshallers_header(outfile, retval, params,
790                                 prefix='g_cclosure_user_marshal',
791                                 internal=False,
792                                 include_va=False, source_location=None):
793     '''Generate a declaration for a marshaller function, to be used in the header,
794     with the given @retval, @params, and @prefix. An optional va_list marshaller
795     for the same arguments is also generated. The generated buffer is written to
796     the @outfile stream object.'''
797     if source_location:
798         outfile.write('/* {} */\n'.format(source_location))
800     if internal:
801         visibility = Visibility.INTERNAL
802     else:
803         visibility = Visibility.EXTERN
805     signature = generate_prototype(retval, params, prefix, visibility, False)
806     if include_va:
807         signature += generate_prototype(retval, params, prefix, visibility, True)
808     signature += ['']
810     outfile.write('\n'.join(signature))
811     outfile.write('\n')
814 def generate_marshallers_body(outfile, retval, params,
815                               prefix='g_cclosure_user_marshal',
816                               include_prototype=True,
817                               internal=False,
818                               include_va=False, source_location=None):
819     '''Generate a definition for a marshaller function, to be used in the source,
820     with the given @retval, @params, and @prefix. An optional va_list marshaller
821     for the same arguments is also generated. The generated buffer is written to
822     the @outfile stream object.'''
823     if source_location:
824         outfile.write('/* {} */\n'.format(source_location))
826     if include_prototype:
827         # Declaration visibility
828         if internal:
829             decl_visibility = Visibility.INTERNAL
830         else:
831             decl_visibility = Visibility.EXTERN
832         proto = ['/* Prototype for -Wmissing-prototypes */']
833         # Add C++ guards in case somebody compiles the generated code
834         # with a C++ compiler
835         proto += ['G_BEGIN_DECLS']
836         proto += generate_prototype(retval, params, prefix, decl_visibility, False)
837         proto += ['G_END_DECLS']
838         outfile.write('\n'.join(proto))
839         outfile.write('\n')
841     body = generate_body(retval, params, prefix, False)
842     outfile.write('\n'.join(body))
843     outfile.write('\n\n')
845     if include_va:
846         if include_prototype:
847             # Declaration visibility
848             if internal:
849                 decl_visibility = Visibility.INTERNAL
850             else:
851                 decl_visibility = Visibility.EXTERN
852             proto = ['/* Prototype for -Wmissing-prototypes */']
853             # Add C++ guards here as well
854             proto += ['G_BEGIN_DECLS']
855             proto += generate_prototype(retval, params, prefix, decl_visibility, True)
856             proto += ['G_END_DECLS']
857             outfile.write('\n'.join(proto))
858             outfile.write('\n')
860         body = generate_body(retval, params, prefix, True)
861         outfile.write('\n'.join(body))
862         outfile.write('\n\n')
865 if __name__ == '__main__':
866     arg_parser = argparse.ArgumentParser(description='Generate signal marshallers for GObject')
867     arg_parser.add_argument('--prefix', metavar='STRING',
868                             default='g_cclosure_user_marshal',
869                             help='Specify marshaller prefix')
870     arg_parser.add_argument('--output', metavar='FILE',
871                             type=argparse.FileType('w'),
872                             default=sys.stdout,
873                             help='Write output into the specified file')
874     arg_parser.add_argument('--skip-source',
875                             action='store_true',
876                             help='Skip source location comments')
877     arg_parser.add_argument('--internal',
878                             action='store_true',
879                             help='Mark generated functions as internal')
880     arg_parser.add_argument('--valist-marshallers',
881                             action='store_true',
882                             help='Generate va_list marshallers')
883     arg_parser.add_argument('-v', '--version',
884                             action='store_true',
885                             dest='show_version',
886                             help='Print version information, and exit')
887     arg_parser.add_argument('--g-fatal-warnings',
888                             action='store_true',
889                             dest='fatal_warnings',
890                             help='Make warnings fatal')
891     arg_parser.add_argument('--include-header', metavar='HEADER', nargs='?',
892                             action='append',
893                             dest='include_headers',
894                             help='Include the specified header in the body')
895     arg_parser.add_argument('--pragma-once',
896                             action='store_true',
897                             help='Use "pragma once" as the inclusion guard')
898     arg_parser.add_argument('-D',
899                             action='append',
900                             dest='cpp_defines',
901                             default=[],
902                             help='Pre-processor define')
903     arg_parser.add_argument('-U',
904                             action='append',
905                             dest='cpp_undefines',
906                             default=[],
907                             help='Pre-processor undefine')
908     arg_parser.add_argument('files', metavar='FILE', nargs='*',
909                             type=argparse.FileType('r'),
910                             help='Files with lists of marshallers to generate, ' +
911                             'or "-" for standard input')
912     arg_parser.add_argument('--prototypes',
913                             action='store_true',
914                             help='Generate the marshallers prototype in the C code')
915     arg_parser.add_argument('--header',
916                             action='store_true',
917                             help='Generate C headers')
918     arg_parser.add_argument('--body',
919                             action='store_true',
920                             help='Generate C code')
922     group = arg_parser.add_mutually_exclusive_group()
923     group.add_argument('--stdinc',
924                        action='store_true',
925                        dest='stdinc', default=True,
926                        help='Include standard marshallers')
927     group.add_argument('--nostdinc',
928                        action='store_false',
929                        dest='stdinc', default=True,
930                        help='Use standard marshallers')
932     group = arg_parser.add_mutually_exclusive_group()
933     group.add_argument('--quiet',
934                        action='store_true',
935                        help='Only print warnings and errors')
936     group.add_argument('--verbose',
937                        action='store_true',
938                        help='Be verbose, and include debugging information')
940     args = arg_parser.parse_args()
942     if args.show_version:
943         print(VERSION_STR)
944         sys.exit(0)
946     # Backward compatibility hack; some projects use both arguments to
947     # generate the marshallers prototype in the C source, even though
948     # it's not really a supported use case. We keep this behaviour by
949     # forcing the --prototypes and --body arguments instead. We make this
950     # warning non-fatal even with --g-fatal-warnings, as it's a deprecation
951     compatibility_mode = False
952     if args.header and args.body:
953         print_warning('Using --header and --body at the same time is deprecated; ' +
954                       'use --body --prototypes instead', False)
955         args.prototypes = True
956         args.header = False
957         compatibility_mode = True
959     if args.header:
960         generate_header_preamble(args.output,
961                                  prefix=args.prefix,
962                                  std_includes=args.stdinc,
963                                  use_pragma=args.pragma_once)
964     elif args.body:
965         generate_body_preamble(args.output,
966                                std_includes=args.stdinc,
967                                include_headers=args.include_headers,
968                                cpp_defines=args.cpp_defines,
969                                cpp_undefines=args.cpp_undefines)
971     seen_marshallers = set()
973     for infile in args.files:
974         if not args.quiet:
975             print_info('Reading {}...'.format(infile.name))
977         line_count = 0
978         for line in infile:
979             line_count += 1
981             if line == '\n' or line.startswith('#'):
982                 continue
984             matches = re.match(r'^([A-Z0-9]+)\s?:\s?([A-Z0-9,\s]+)$', line.strip())
985             if not matches or len(matches.groups()) != 2:
986                 print_warning('Invalid entry: "{}"'.format(line.strip()), args.fatal_warnings)
987                 continue
989             if not args.skip_source:
990                 location = '{} ({}:{:d})'.format(line.strip(), infile.name, line_count)
991             else:
992                 location = None
994             retval = matches.group(1).strip()
995             params = [x.strip() for x in matches.group(2).split(',')]
996             check_args(retval, params, args.fatal_warnings)
998             raw_marshaller = generate_marshaller_name(args.prefix, retval, params, False)
999             if raw_marshaller in seen_marshallers:
1000                 if args.verbose:
1001                     print_info('Skipping repeated marshaller {}'.format(line.strip()))
1002                 continue
1004             if args.header:
1005                 if args.verbose:
1006                     print_info('Generating declaration for {}'.format(line.strip()))
1007                 generate_std_alias = False
1008                 if args.stdinc:
1009                     std_marshaller = generate_marshaller_name(STD_PREFIX, retval, params)
1010                     if std_marshaller in GOBJECT_MARSHALLERS:
1011                         if args.verbose:
1012                             print_info('Skipping default marshaller {}'.format(line.strip()))
1013                         generate_std_alias = True
1015                 marshaller = generate_marshaller_name(args.prefix, retval, params)
1016                 if generate_std_alias:
1017                     generate_marshaller_alias(args.output, marshaller, std_marshaller,
1018                                               source_location=location,
1019                                               include_va=args.valist_marshallers)
1020                 else:
1021                     generate_marshallers_header(args.output, retval, params,
1022                                                 prefix=args.prefix,
1023                                                 internal=args.internal,
1024                                                 include_va=args.valist_marshallers,
1025                                                 source_location=location)
1026                 # If the marshaller is defined using a deprecated token, we want to maintain
1027                 # compatibility and generate an alias for the old name pointing to the new
1028                 # one
1029                 if marshaller != raw_marshaller:
1030                     if args.verbose:
1031                         print_info('Generating alias for deprecated tokens')
1032                     generate_marshaller_alias(args.output, raw_marshaller, marshaller,
1033                                               include_va=args.valist_marshallers)
1034             elif args.body:
1035                 if args.verbose:
1036                     print_info('Generating definition for {}'.format(line.strip()))
1037                 generate_std_alias = False
1038                 if args.stdinc:
1039                     std_marshaller = generate_marshaller_name(STD_PREFIX, retval, params)
1040                     if std_marshaller in GOBJECT_MARSHALLERS:
1041                         if args.verbose:
1042                             print_info('Skipping default marshaller {}'.format(line.strip()))
1043                         generate_std_alias = True
1044                 marshaller = generate_marshaller_name(args.prefix, retval, params)
1045                 if generate_std_alias:
1046                     # We need to generate the alias if we are in compatibility mode
1047                     if compatibility_mode:
1048                         generate_marshaller_alias(args.output, marshaller, std_marshaller,
1049                                                   source_location=location,
1050                                                   include_va=args.valist_marshallers)
1051                 else:
1052                     generate_marshallers_body(args.output, retval, params,
1053                                               prefix=args.prefix,
1054                                               internal=args.internal,
1055                                               include_prototype=args.prototypes,
1056                                               include_va=args.valist_marshallers,
1057                                               source_location=location)
1058                 if compatibility_mode and marshaller != raw_marshaller:
1059                     if args.verbose:
1060                         print_info('Generating alias for deprecated tokens')
1061                     generate_marshaller_alias(args.output, raw_marshaller, marshaller,
1062                                               include_va=args.valist_marshallers)
1064             seen_marshallers.add(raw_marshaller)
1066         if args.header:
1067             generate_header_postamble(args.output, prefix=args.prefix, use_pragma=args.pragma_once)