• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env @PYTHON@
2
3# pylint: disable=too-many-lines, missing-docstring, invalid-name
4
5# This file is part of GLib
6#
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.
11#
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.
16#
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/>.
19
20import argparse
21import os
22import re
23import sys
24
25VERSION_STR = '''glib-genmarshal version @VERSION@
26glib-genmarshal comes with ABSOLUTELY NO WARRANTY.
27You may redistribute copies of glib-genmarshal under the terms of
28the GNU General Public License which can be found in the
29GLib source package. Sources, examples and contact
30information are available at http://www.gtk.org'''
31
32GETTERS_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 */'''
77
78DEPRECATED_MSG_STR = 'The token "{}" is deprecated; use "{}" instead'
79
80VA_ARG_STR = \
81    '  arg{:d} = ({:s}) va_arg (args_copy, {:s});'
82STATIC_CHECK_STR = \
83    '(param_types[{:d}] & G_SIGNAL_TYPE_STATIC_SCOPE) == 0 && '
84BOX_TYPED_STR = \
85    '    arg{idx:d} = {box_func} (param_types[{idx:d}] & ~G_SIGNAL_TYPE_STATIC_SCOPE, arg{idx:d});'
86BOX_UNTYPED_STR = \
87    '    arg{idx:d} = {box_func} (arg{idx:d});'
88UNBOX_TYPED_STR = \
89    '    {unbox_func} (param_types[{idx:d}] & ~G_SIGNAL_TYPE_STATIC_SCOPE, arg{idx:d});'
90UNBOX_UNTYPED_STR = \
91    '    {unbox_func} (arg{idx:d});'
92
93STD_PREFIX = 'g_cclosure_marshal'
94
95# These are part of our ABI; keep this in sync with gmarshal.h
96GOBJECT_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',
119}
120
121
122# pylint: disable=too-few-public-methods
123class 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'
130
131
132def 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))
139
140
141def print_error(msg):
142    '''Print an error, and terminate'''
143    print_color(msg, color=Color.RED, prefix='ERROR')
144    sys.exit(1)
145
146
147def 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)
158
159
160def print_info(msg):
161    '''Print a message'''
162    print_color(msg, color=Color.GREEN, prefix='INFO')
163
164
165def 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')
170
171
172def 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)
175
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')
187
188    outfile.write('G_BEGIN_DECLS\n')
189    outfile.write('\n')
190
191
192def 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')
196
197    if not use_pragma:
198        outfile.write('\n')
199        outfile.write('#endif /* __{}_MARSHAL_H__ */\n'.format(prefix.upper()))
200
201
202def 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)
205
206    for header in (include_headers or []):
207        outfile.write('#include "{}"\n'.format(header))
208    if include_headers:
209        outfile.write('\n')
210
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')
218
219    for undefine in (cpp_undefines or []):
220        outfile.write('#undef {}\n'.format(undefine))
221    if cpp_undefines:
222        outfile.write('\n')
223
224    if std_includes:
225        outfile.write('#include <glib-object.h>\n')
226        outfile.write('\n')
227
228    outfile.write(GETTERS_STR)
229    outfile.write('\n\n')
230
231
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:
235#
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)
255IN_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        'static-check': True,
334    },
335    'PARAM': {
336        'signal': 'PARAM',
337        'ctype': 'gpointer',
338        'getter': 'g_marshal_value_peek_param',
339        'box': ['g_param_spec_ref', 'g_param_spec_unref'],
340        'static-check': True,
341    },
342    'BOXED': {
343        'signal': 'BOXED',
344        'ctype': 'gpointer',
345        'getter': 'g_marshal_value_peek_boxed',
346        'box': ['g_boxed_copy', 'g_boxed_free'],
347        'static-check': True,
348        'takes-type': True,
349    },
350    'POINTER': {
351        'signal': 'POINTER',
352        'ctype': 'gpointer',
353        'getter': 'g_marshal_value_peek_pointer',
354    },
355    'OBJECT': {
356        'signal': 'OBJECT',
357        'ctype': 'gpointer',
358        'getter': 'g_marshal_value_peek_object',
359        'box': ['g_object_ref', 'g_object_unref'],
360    },
361    'VARIANT': {
362        'signal': 'VARIANT',
363        'ctype': 'gpointer',
364        'getter': 'g_marshal_value_peek_variant',
365        'box': ['g_variant_ref_sink', 'g_variant_unref'],
366        'static-check': True,
367        'takes-type': False,
368    },
369
370    # Deprecated tokens
371    'NONE': {
372        'signal': 'VOID',
373        'ctype': 'void',
374        'deprecated': True,
375        'replaced_by': 'VOID'
376    },
377    'BOOL': {
378        'signal': 'BOOLEAN',
379        'ctype': 'gboolean',
380        'getter': 'g_marshal_value_peek_boolean',
381        'deprecated': True,
382        'replaced_by': 'BOOLEAN'
383    }
384}
385
386
387# Marshaller return values, as a dictionary where the key is the token used
388# in the source file, and the value is another dictionary with the following
389# keys:
390#
391#   - signal: the token used in the marshaller prototype (mandatory)
392#   - ctype: the C type for the marshaller argument (mandatory)
393#   - setter: the function used to set the return value of the callback
394#       into a GValue (optional)
395#   - deprecated: whether the token has been deprecated (optional)
396#   - replaced-by: the token used to replace a deprecated token (optional,
397#       only used if deprecated is True)
398OUT_ARGS = {
399    'VOID': {
400        'signal': 'VOID',
401        'ctype': 'void',
402    },
403    'BOOLEAN': {
404        'signal': 'BOOLEAN',
405        'ctype': 'gboolean',
406        'setter': 'g_value_set_boolean',
407    },
408    'CHAR': {
409        'signal': 'CHAR',
410        'ctype': 'gchar',
411        'setter': 'g_value_set_char',
412    },
413    'UCHAR': {
414        'signal': 'UCHAR',
415        'ctype': 'guchar',
416        'setter': 'g_value_set_uchar',
417    },
418    'INT': {
419        'signal': 'INT',
420        'ctype': 'gint',
421        'setter': 'g_value_set_int',
422    },
423    'UINT': {
424        'signal': 'UINT',
425        'ctype': 'guint',
426        'setter': 'g_value_set_uint',
427    },
428    'LONG': {
429        'signal': 'LONG',
430        'ctype': 'glong',
431        'setter': 'g_value_set_long',
432    },
433    'ULONG': {
434        'signal': 'ULONG',
435        'ctype': 'gulong',
436        'setter': 'g_value_set_ulong',
437    },
438    'INT64': {
439        'signal': 'INT64',
440        'ctype': 'gint64',
441        'setter': 'g_value_set_int64',
442    },
443    'UINT64': {
444        'signal': 'UINT64',
445        'ctype': 'guint64',
446        'setter': 'g_value_set_uint64',
447    },
448    'ENUM': {
449        'signal': 'ENUM',
450        'ctype': 'gint',
451        'setter': 'g_value_set_enum',
452    },
453    'FLAGS': {
454        'signal': 'FLAGS',
455        'ctype': 'guint',
456        'setter': 'g_value_set_flags',
457    },
458    'FLOAT': {
459        'signal': 'FLOAT',
460        'ctype': 'gfloat',
461        'setter': 'g_value_set_float',
462    },
463    'DOUBLE': {
464        'signal': 'DOUBLE',
465        'ctype': 'gdouble',
466        'setter': 'g_value_set_double',
467    },
468    'STRING': {
469        'signal': 'STRING',
470        'ctype': 'gchar*',
471        'setter': 'g_value_take_string',
472    },
473    'PARAM': {
474        'signal': 'PARAM',
475        'ctype': 'GParamSpec*',
476        'setter': 'g_value_take_param',
477    },
478    'BOXED': {
479        'signal': 'BOXED',
480        'ctype': 'gpointer',
481        'setter': 'g_value_take_boxed',
482    },
483    'POINTER': {
484        'signal': 'POINTER',
485        'ctype': 'gpointer',
486        'setter': 'g_value_set_pointer',
487    },
488    'OBJECT': {
489        'signal': 'OBJECT',
490        'ctype': 'GObject*',
491        'setter': 'g_value_take_object',
492    },
493    'VARIANT': {
494        'signal': 'VARIANT',
495        'ctype': 'GVariant*',
496        'setter': 'g_value_take_variant',
497    },
498
499    # Deprecated tokens
500    'NONE': {
501        'signal': 'VOID',
502        'ctype': 'void',
503        'setter': None,
504        'deprecated': True,
505        'replaced_by': 'VOID',
506    },
507    'BOOL': {
508        'signal': 'BOOLEAN',
509        'ctype': 'gboolean',
510        'setter': 'g_value_set_boolean',
511        'deprecated': True,
512        'replaced_by': 'BOOLEAN',
513    },
514}
515
516
517def check_args(retval, params, fatal_warnings=False):
518    '''Check the @retval and @params tokens for invalid and deprecated symbols.'''
519    if retval not in OUT_ARGS:
520        print_error('Unknown return value type "{}"'.format(retval))
521
522    if OUT_ARGS[retval].get('deprecated', False):
523        replaced_by = OUT_ARGS[retval]['replaced_by']
524        print_warning(DEPRECATED_MSG_STR.format(retval, replaced_by), fatal_warnings)
525
526    for param in params:
527        if param not in IN_ARGS:
528            print_error('Unknown parameter type "{}"'.format(param))
529        else:
530            if IN_ARGS[param].get('deprecated', False):
531                replaced_by = IN_ARGS[param]['replaced_by']
532                print_warning(DEPRECATED_MSG_STR.format(param, replaced_by), fatal_warnings)
533
534
535def indent(text, level=0, fill=' '):
536    '''Indent @text by @level columns, using the @fill character'''
537    return ''.join([fill for x in range(level)]) + text
538
539
540# pylint: disable=too-few-public-methods
541class Visibility:
542    '''Symbol visibility options'''
543    NONE = 0
544    INTERNAL = 1
545    EXTERN = 2
546
547
548def generate_marshaller_name(prefix, retval, params, replace_deprecated=True):
549    '''Generate a marshaller name for the given @prefix, @retval, and @params.
550    If @replace_deprecated is True, the generated name will replace deprecated
551    tokens.'''
552    if replace_deprecated:
553        real_retval = OUT_ARGS[retval]['signal']
554        real_params = []
555        for param in params:
556            real_params.append(IN_ARGS[param]['signal'])
557    else:
558        real_retval = retval
559        real_params = params
560    return '{prefix}_{retval}__{args}'.format(prefix=prefix,
561                                              retval=real_retval,
562                                              args='_'.join(real_params))
563
564
565def generate_prototype(retval, params,
566                       prefix='g_cclosure_user_marshal',
567                       visibility=Visibility.NONE,
568                       va_marshal=False):
569    '''Generate a marshaller declaration with the given @visibility. If @va_marshal
570    is True, the marshaller will use variadic arguments in place of a GValue array.'''
571    signature = []
572
573    if visibility == Visibility.INTERNAL:
574        signature += ['G_GNUC_INTERNAL']
575    elif visibility == Visibility.EXTERN:
576        signature += ['extern']
577
578    function_name = generate_marshaller_name(prefix, retval, params)
579
580    if not va_marshal:
581        signature += ['void ' + function_name + ' (GClosure     *closure,']
582        width = len('void ') + len(function_name) + 2
583
584        signature += [indent('GValue       *return_value,', level=width, fill=' ')]
585        signature += [indent('guint         n_param_values,', level=width, fill=' ')]
586        signature += [indent('const GValue *param_values,', level=width, fill=' ')]
587        signature += [indent('gpointer      invocation_hint,', level=width, fill=' ')]
588        signature += [indent('gpointer      marshal_data);', level=width, fill=' ')]
589    else:
590        signature += ['void ' + function_name + 'v (GClosure *closure,']
591        width = len('void ') + len(function_name) + 3
592
593        signature += [indent('GValue   *return_value,', level=width, fill=' ')]
594        signature += [indent('gpointer  instance,', level=width, fill=' ')]
595        signature += [indent('va_list   args,', level=width, fill=' ')]
596        signature += [indent('gpointer  marshal_data,', level=width, fill=' ')]
597        signature += [indent('int       n_params,', level=width, fill=' ')]
598        signature += [indent('GType    *param_types);', level=width, fill=' ')]
599
600    return signature
601
602
603# pylint: disable=too-many-statements, too-many-locals, too-many-branches
604def generate_body(retval, params, prefix, va_marshal=False):
605    '''Generate a marshaller definition. If @va_marshal is True, the marshaller
606    will use va_list and variadic arguments in place of a GValue array.'''
607    retval_setter = OUT_ARGS[retval].get('setter', None)
608    # If there's no return value then we can mark the retval argument as unused
609    # and get a minor optimisation, as well as avoid a compiler warning
610    if not retval_setter:
611        unused = ' G_GNUC_UNUSED'
612    else:
613        unused = ''
614
615    body = ['void']
616
617    function_name = generate_marshaller_name(prefix, retval, params)
618
619    if not va_marshal:
620        body += [function_name + ' (GClosure     *closure,']
621        width = len(function_name) + 2
622
623        body += [indent('GValue       *return_value{},'.format(unused), level=width, fill=' ')]
624        body += [indent('guint         n_param_values,', level=width, fill=' ')]
625        body += [indent('const GValue *param_values,', level=width, fill=' ')]
626        body += [indent('gpointer      invocation_hint G_GNUC_UNUSED,', level=width, fill=' ')]
627        body += [indent('gpointer      marshal_data)', level=width, fill=' ')]
628    else:
629        body += [function_name + 'v (GClosure *closure,']
630        width = len(function_name) + 3
631
632        body += [indent('GValue   *return_value{},'.format(unused), level=width, fill=' ')]
633        body += [indent('gpointer  instance,', level=width, fill=' ')]
634        body += [indent('va_list   args,', level=width, fill=' ')]
635        body += [indent('gpointer  marshal_data,', level=width, fill=' ')]
636        body += [indent('int       n_params,', level=width, fill=' ')]
637        body += [indent('GType    *param_types)', level=width, fill=' ')]
638
639    # Filter the arguments that have a getter
640    get_args = [x for x in params if IN_ARGS[x].get('getter', None) is not None]
641
642    body += ['{']
643
644    # Generate the type of the marshaller function
645    typedef_marshal = generate_marshaller_name('GMarshalFunc', retval, params)
646
647    typedef = '  typedef {ctype} (*{func_name}) ('.format(ctype=OUT_ARGS[retval]['ctype'],
648                                                          func_name=typedef_marshal)
649    pad = len(typedef)
650    typedef += 'gpointer data1,'
651    body += [typedef]
652
653    for idx, in_arg in enumerate(get_args):
654        body += [indent('{} arg{:d},'.format(IN_ARGS[in_arg]['ctype'], idx + 1), level=pad)]
655
656    body += [indent('gpointer data2);', level=pad)]
657
658    # Variable declarations
659    body += ['  GCClosure *cc = (GCClosure *) closure;']
660    body += ['  gpointer data1, data2;']
661    body += ['  {} callback;'.format(typedef_marshal)]
662
663    if retval_setter:
664        body += ['  {} v_return;'.format(OUT_ARGS[retval]['ctype'])]
665
666    if va_marshal:
667        for idx, arg in enumerate(get_args):
668            body += ['  {} arg{:d};'.format(IN_ARGS[arg]['ctype'], idx)]
669
670        if get_args:
671            body += ['  va_list args_copy;']
672            body += ['']
673
674            body += ['  G_VA_COPY (args_copy, args);']
675
676            for idx, arg in enumerate(get_args):
677                ctype = IN_ARGS[arg]['ctype']
678                promoted_ctype = IN_ARGS[arg].get('promoted', ctype)
679                body += [VA_ARG_STR.format(idx, ctype, promoted_ctype)]
680                if IN_ARGS[arg].get('box', None):
681                    box_func = IN_ARGS[arg]['box'][0]
682                    if IN_ARGS[arg].get('static-check', False):
683                        static_check = STATIC_CHECK_STR.format(idx)
684                    else:
685                        static_check = ''
686                    arg_check = 'arg{:d} != NULL'.format(idx)
687                    body += ['  if ({}{})'.format(static_check, arg_check)]
688                    if IN_ARGS[arg].get('takes-type', False):
689                        body += [BOX_TYPED_STR.format(idx=idx, box_func=box_func)]
690                    else:
691                        body += [BOX_UNTYPED_STR.format(idx=idx, box_func=box_func)]
692
693            body += ['  va_end (args_copy);']
694
695    body += ['']
696
697    # Preconditions check
698    if retval_setter:
699        body += ['  g_return_if_fail (return_value != NULL);']
700
701    if not va_marshal:
702        body += ['  g_return_if_fail (n_param_values == {:d});'.format(len(get_args) + 1)]
703
704    body += ['']
705
706    # Marshal instance, data, and callback set up
707    body += ['  if (G_CCLOSURE_SWAP_DATA (closure))']
708    body += ['    {']
709    body += ['      data1 = closure->data;']
710    if va_marshal:
711        body += ['      data2 = instance;']
712    else:
713        body += ['      data2 = g_value_peek_pointer (param_values + 0);']
714    body += ['    }']
715    body += ['  else']
716    body += ['    {']
717    if va_marshal:
718        body += ['      data1 = instance;']
719    else:
720        body += ['      data1 = g_value_peek_pointer (param_values + 0);']
721    body += ['      data2 = closure->data;']
722    body += ['    }']
723    # pylint: disable=line-too-long
724    body += ['  callback = ({}) (marshal_data ? marshal_data : cc->callback);'.format(typedef_marshal)]
725    body += ['']
726
727    # Marshal callback action
728    if retval_setter:
729        callback = ' {} callback ('.format(' v_return =')
730    else:
731        callback = '  callback ('
732
733    pad = len(callback)
734    body += [callback + 'data1,']
735
736    if va_marshal:
737        for idx, arg in enumerate(get_args):
738            body += [indent('arg{:d},'.format(idx), level=pad)]
739    else:
740        for idx, arg in enumerate(get_args):
741            arg_getter = IN_ARGS[arg]['getter']
742            body += [indent('{} (param_values + {:d}),'.format(arg_getter, idx + 1), level=pad)]
743
744    body += [indent('data2);', level=pad)]
745
746    if va_marshal:
747        boxed_args = [x for x in get_args if IN_ARGS[x].get('box', None) is not None]
748        if not boxed_args:
749            body += ['']
750        else:
751            for idx, arg in enumerate(get_args):
752                if not IN_ARGS[arg].get('box', None):
753                    continue
754                unbox_func = IN_ARGS[arg]['box'][1]
755                if IN_ARGS[arg].get('static-check', False):
756                    static_check = STATIC_CHECK_STR.format(idx)
757                else:
758                    static_check = ''
759                arg_check = 'arg{:d} != NULL'.format(idx)
760                body += ['  if ({}{})'.format(static_check, arg_check)]
761                if IN_ARGS[arg].get('takes-type', False):
762                    body += [UNBOX_TYPED_STR.format(idx=idx, unbox_func=unbox_func)]
763                else:
764                    body += [UNBOX_UNTYPED_STR.format(idx=idx, unbox_func=unbox_func)]
765
766    if retval_setter:
767        body += ['']
768        body += ['  {} (return_value, v_return);'.format(retval_setter)]
769
770    body += ['}']
771
772    return body
773
774
775def generate_marshaller_alias(outfile, marshaller, real_marshaller,
776                              include_va=False,
777                              source_location=None):
778    '''Generate an alias between @marshaller and @real_marshaller, including
779    an optional alias for va_list marshallers'''
780    if source_location:
781        outfile.write('/* {} */\n'.format(source_location))
782
783    outfile.write('#define {}\t{}\n'.format(marshaller, real_marshaller))
784
785    if include_va:
786        outfile.write('#define {}v\t{}v\n'.format(marshaller, real_marshaller))
787
788    outfile.write('\n')
789
790
791def generate_marshallers_header(outfile, retval, params,
792                                prefix='g_cclosure_user_marshal',
793                                internal=False,
794                                include_va=False, source_location=None):
795    '''Generate a declaration for a marshaller function, to be used in the header,
796    with the given @retval, @params, and @prefix. An optional va_list marshaller
797    for the same arguments is also generated. The generated buffer is written to
798    the @outfile stream object.'''
799    if source_location:
800        outfile.write('/* {} */\n'.format(source_location))
801
802    if internal:
803        visibility = Visibility.INTERNAL
804    else:
805        visibility = Visibility.EXTERN
806
807    signature = generate_prototype(retval, params, prefix, visibility, False)
808    if include_va:
809        signature += generate_prototype(retval, params, prefix, visibility, True)
810    signature += ['']
811
812    outfile.write('\n'.join(signature))
813    outfile.write('\n')
814
815
816def generate_marshallers_body(outfile, retval, params,
817                              prefix='g_cclosure_user_marshal',
818                              include_prototype=True,
819                              internal=False,
820                              include_va=False, source_location=None):
821    '''Generate a definition for a marshaller function, to be used in the source,
822    with the given @retval, @params, and @prefix. An optional va_list marshaller
823    for the same arguments is also generated. The generated buffer is written to
824    the @outfile stream object.'''
825    if source_location:
826        outfile.write('/* {} */\n'.format(source_location))
827
828    if include_prototype:
829        # Declaration visibility
830        if internal:
831            decl_visibility = Visibility.INTERNAL
832        else:
833            decl_visibility = Visibility.EXTERN
834        proto = ['/* Prototype for -Wmissing-prototypes */']
835        # Add C++ guards in case somebody compiles the generated code
836        # with a C++ compiler
837        proto += ['G_BEGIN_DECLS']
838        proto += generate_prototype(retval, params, prefix, decl_visibility, False)
839        proto += ['G_END_DECLS']
840        outfile.write('\n'.join(proto))
841        outfile.write('\n')
842
843    body = generate_body(retval, params, prefix, False)
844    outfile.write('\n'.join(body))
845    outfile.write('\n\n')
846
847    if include_va:
848        if include_prototype:
849            # Declaration visibility
850            if internal:
851                decl_visibility = Visibility.INTERNAL
852            else:
853                decl_visibility = Visibility.EXTERN
854            proto = ['/* Prototype for -Wmissing-prototypes */']
855            # Add C++ guards here as well
856            proto += ['G_BEGIN_DECLS']
857            proto += generate_prototype(retval, params, prefix, decl_visibility, True)
858            proto += ['G_END_DECLS']
859            outfile.write('\n'.join(proto))
860            outfile.write('\n')
861
862        body = generate_body(retval, params, prefix, True)
863        outfile.write('\n'.join(body))
864        outfile.write('\n\n')
865
866
867def parse_args():
868    arg_parser = argparse.ArgumentParser(description='Generate signal marshallers for GObject')
869    arg_parser.add_argument('--prefix', metavar='STRING',
870                            default='g_cclosure_user_marshal',
871                            help='Specify marshaller prefix')
872    arg_parser.add_argument('--output', metavar='FILE',
873                            type=argparse.FileType('w'),
874                            default=sys.stdout,
875                            help='Write output into the specified file')
876    arg_parser.add_argument('--skip-source',
877                            action='store_true',
878                            help='Skip source location comments')
879    arg_parser.add_argument('--internal',
880                            action='store_true',
881                            help='Mark generated functions as internal')
882    arg_parser.add_argument('--valist-marshallers',
883                            action='store_true',
884                            help='Generate va_list marshallers')
885    arg_parser.add_argument('-v', '--version',
886                            action='store_true',
887                            dest='show_version',
888                            help='Print version information, and exit')
889    arg_parser.add_argument('--g-fatal-warnings',
890                            action='store_true',
891                            dest='fatal_warnings',
892                            help='Make warnings fatal')
893    arg_parser.add_argument('--include-header', metavar='HEADER', nargs='?',
894                            action='append',
895                            dest='include_headers',
896                            help='Include the specified header in the body')
897    arg_parser.add_argument('--pragma-once',
898                            action='store_true',
899                            help='Use "pragma once" as the inclusion guard')
900    arg_parser.add_argument('-D',
901                            action='append',
902                            dest='cpp_defines',
903                            default=[],
904                            help='Pre-processor define')
905    arg_parser.add_argument('-U',
906                            action='append',
907                            dest='cpp_undefines',
908                            default=[],
909                            help='Pre-processor undefine')
910    arg_parser.add_argument('files', metavar='FILE', nargs='*',
911                            type=argparse.FileType('r'),
912                            help='Files with lists of marshallers to generate, ' +
913                            'or "-" for standard input')
914    arg_parser.add_argument('--prototypes',
915                            action='store_true',
916                            help='Generate the marshallers prototype in the C code')
917    arg_parser.add_argument('--header',
918                            action='store_true',
919                            help='Generate C headers')
920    arg_parser.add_argument('--body',
921                            action='store_true',
922                            help='Generate C code')
923
924    group = arg_parser.add_mutually_exclusive_group()
925    group.add_argument('--stdinc',
926                       action='store_true',
927                       dest='stdinc', default=True,
928                       help='Include standard marshallers')
929    group.add_argument('--nostdinc',
930                       action='store_false',
931                       dest='stdinc', default=True,
932                       help='Use standard marshallers')
933
934    group = arg_parser.add_mutually_exclusive_group()
935    group.add_argument('--quiet',
936                       action='store_true',
937                       help='Only print warnings and errors')
938    group.add_argument('--verbose',
939                       action='store_true',
940                       help='Be verbose, and include debugging information')
941
942    args = arg_parser.parse_args()
943
944    if args.show_version:
945        print(VERSION_STR)
946        sys.exit(0)
947
948    return args
949
950
951def generate(args):
952    # Backward compatibility hack; some projects use both arguments to
953    # generate the marshallers prototype in the C source, even though
954    # it's not really a supported use case. We keep this behaviour by
955    # forcing the --prototypes and --body arguments instead. We make this
956    # warning non-fatal even with --g-fatal-warnings, as it's a deprecation
957    compatibility_mode = False
958    if args.header and args.body:
959        print_warning('Using --header and --body at the same time is deprecated; ' +
960                      'use --body --prototypes instead', False)
961        args.prototypes = True
962        args.header = False
963        compatibility_mode = True
964
965    if args.header:
966        generate_header_preamble(args.output,
967                                 prefix=args.prefix,
968                                 std_includes=args.stdinc,
969                                 use_pragma=args.pragma_once)
970    elif args.body:
971        generate_body_preamble(args.output,
972                               std_includes=args.stdinc,
973                               include_headers=args.include_headers,
974                               cpp_defines=args.cpp_defines,
975                               cpp_undefines=args.cpp_undefines)
976
977    seen_marshallers = set()
978
979    for infile in args.files:
980        if not args.quiet:
981            print_info('Reading {}...'.format(infile.name))
982
983        line_count = 0
984        for line in infile:
985            line_count += 1
986
987            if line == '\n' or line.startswith('#'):
988                continue
989
990            matches = re.match(r'^([A-Z0-9]+)\s?:\s?([A-Z0-9,\s]+)$', line.strip())
991            if not matches or len(matches.groups()) != 2:
992                print_warning('Invalid entry: "{}"'.format(line.strip()), args.fatal_warnings)
993                continue
994
995            if not args.skip_source:
996                location = '{} ({}:{:d})'.format(line.strip(), infile.name, line_count)
997            else:
998                location = None
999
1000            retval = matches.group(1).strip()
1001            params = [x.strip() for x in matches.group(2).split(',')]
1002            check_args(retval, params, args.fatal_warnings)
1003
1004            raw_marshaller = generate_marshaller_name(args.prefix, retval, params, False)
1005            if raw_marshaller in seen_marshallers:
1006                if args.verbose:
1007                    print_info('Skipping repeated marshaller {}'.format(line.strip()))
1008                continue
1009
1010            if args.header:
1011                if args.verbose:
1012                    print_info('Generating declaration for {}'.format(line.strip()))
1013                generate_std_alias = False
1014                if args.stdinc:
1015                    std_marshaller = generate_marshaller_name(STD_PREFIX, retval, params)
1016                    if std_marshaller in GOBJECT_MARSHALLERS:
1017                        if args.verbose:
1018                            print_info('Skipping default marshaller {}'.format(line.strip()))
1019                        generate_std_alias = True
1020
1021                marshaller = generate_marshaller_name(args.prefix, retval, params)
1022                if generate_std_alias:
1023                    generate_marshaller_alias(args.output, marshaller, std_marshaller,
1024                                              source_location=location,
1025                                              include_va=args.valist_marshallers)
1026                else:
1027                    generate_marshallers_header(args.output, retval, params,
1028                                                prefix=args.prefix,
1029                                                internal=args.internal,
1030                                                include_va=args.valist_marshallers,
1031                                                source_location=location)
1032                # If the marshaller is defined using a deprecated token, we want to maintain
1033                # compatibility and generate an alias for the old name pointing to the new
1034                # one
1035                if marshaller != raw_marshaller:
1036                    if args.verbose:
1037                        print_info('Generating alias for deprecated tokens')
1038                    generate_marshaller_alias(args.output, raw_marshaller, marshaller,
1039                                              include_va=args.valist_marshallers)
1040            elif args.body:
1041                if args.verbose:
1042                    print_info('Generating definition for {}'.format(line.strip()))
1043                generate_std_alias = False
1044                if args.stdinc:
1045                    std_marshaller = generate_marshaller_name(STD_PREFIX, retval, params)
1046                    if std_marshaller in GOBJECT_MARSHALLERS:
1047                        if args.verbose:
1048                            print_info('Skipping default marshaller {}'.format(line.strip()))
1049                        generate_std_alias = True
1050                marshaller = generate_marshaller_name(args.prefix, retval, params)
1051                if generate_std_alias:
1052                    # We need to generate the alias if we are in compatibility mode
1053                    if compatibility_mode:
1054                        generate_marshaller_alias(args.output, marshaller, std_marshaller,
1055                                                  source_location=location,
1056                                                  include_va=args.valist_marshallers)
1057                else:
1058                    generate_marshallers_body(args.output, retval, params,
1059                                              prefix=args.prefix,
1060                                              internal=args.internal,
1061                                              include_prototype=args.prototypes,
1062                                              include_va=args.valist_marshallers,
1063                                              source_location=location)
1064                if compatibility_mode and marshaller != raw_marshaller:
1065                    if args.verbose:
1066                        print_info('Generating alias for deprecated tokens')
1067                    generate_marshaller_alias(args.output, raw_marshaller, marshaller,
1068                                              include_va=args.valist_marshallers)
1069
1070            seen_marshallers.add(raw_marshaller)
1071
1072        if args.header:
1073            generate_header_postamble(args.output, prefix=args.prefix, use_pragma=args.pragma_once)
1074
1075
1076if __name__ == '__main__':
1077    args = parse_args()
1078
1079    with args.output:
1080        generate(args)
1081