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