| # -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
| # |
| # This program is free software; you can redistribute it and/or modify it under |
| # the terms of the GNU Lesser General Public License as published by the Free |
| # Software Foundation; either version 2 of the License, or (at your option) any |
| # later version. |
| # |
| # This program is distributed in the hope that it will be useful, but WITHOUT |
| # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more |
| # details. |
| # |
| # You should have received a copy of the GNU Lesser General Public License along |
| # with this program; if not, write to the Free Software Foundation, Inc., 51 |
| # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| # |
| # Copyright (C) 2012 Lanedo GmbH |
| # Copyright (C) 2012-2022 Aleksander Morgado <aleksander@aleksander.es> |
| # Copyright (c) 2022 Qualcomm Innovation Center, Inc. |
| # |
| |
| import string |
| import utils |
| from Variable import Variable |
| import VariableFactory |
| |
| """ |
| Variable type for Structs ('struct' format) |
| These variables are exclusively used as array elements, they can not be used as |
| independent TLVs. Use Sequence instead for that. |
| """ |
| class VariableStruct(Variable): |
| |
| def __init__(self, service, dictionary, struct_type_name, container_type): |
| |
| # Call the parent constructor |
| Variable.__init__(self, service, dictionary) |
| |
| self.container_type = container_type |
| |
| # The public format of the struct is built directly from the suggested |
| # struct type name |
| self.struct_type_name = struct_type_name |
| self.element_type = utils.build_camelcase_name(self.struct_type_name) |
| self.public_format = self.element_type |
| self.private_format = self.public_format |
| |
| self.since = dictionary['since'] if 'since' in dictionary else '' |
| |
| # Load members of this struct |
| self.members = [] |
| for member_dictionary in dictionary['contents']: |
| member = {} |
| member['name'] = utils.build_underscore_name(member_dictionary['name']) |
| member['object'] = VariableFactory.create_variable(self.service, member_dictionary, struct_type_name + ' ' + member['name'], self.container_type) |
| # Specify that the variable will be defined in the public header |
| member['object'].flag_public() |
| self.members.append(member) |
| |
| # We'll need to dispose if at least one of the members needs it |
| for member in self.members: |
| if member['object'].needs_dispose: |
| self.needs_dispose = True |
| break |
| |
| if self.needs_dispose: |
| self.clear_method = '__' + utils.build_underscore_name(self.struct_type_name) + '_clear' |
| |
| |
| # When using structs, we always require GIR compat methods because they're going |
| # to be stored via reference pointers in the arrays |
| self.needs_compat_gir = True |
| |
| # If any of the contents needs GIR compat methods, then we must also create a new |
| # GIR compat struct type; otherwise just use the original one |
| self.content_needs_compat_gir = False |
| for member in self.members: |
| if member['object'].needs_compat_gir: |
| self.content_needs_compat_gir = True |
| break |
| |
| if self.content_needs_compat_gir: |
| self.struct_type_name_gir = self.struct_type_name + ' Gir' |
| else: |
| self.struct_type_name_gir = self.struct_type_name |
| self.element_type_gir = utils.build_camelcase_name(self.struct_type_name_gir) |
| self.public_format_gir = self.element_type_gir |
| self.private_format_gir = self.public_format_gir |
| self.new_method_gir = '__' + utils.build_underscore_name(self.struct_type_name_gir) + '_new' |
| self.free_method_gir = '__' + utils.build_underscore_name(self.struct_type_name_gir) + '_free' |
| |
| # We'll contain personal info if at least one of the members contains personal info or we ourselves are personal info |
| if not self.contains_personal_info: |
| for member in self.members: |
| if member['object'].contains_personal_info: |
| self.contains_personal_info = True |
| break |
| |
| |
| def build_variable_declaration(self, line_prefix, variable_name): |
| raise RuntimeError('Variable of type "struct" can only be defined as array members') |
| |
| |
| def build_struct_field_declaration(self, line_prefix, variable_name): |
| raise RuntimeError('Variable of type "struct" cannot be defined as struct fields') |
| |
| |
| def emit_types(self, hfile, cfile, since, static): |
| for member in self.members: |
| member['object'].emit_types(hfile, cfile, since, static) |
| |
| translations = { 'format' : self.public_format, |
| 'since' : since if not self.since else self.since } |
| template = '\n' |
| hfile.write(string.Template(template).substitute(translations)) |
| |
| if not static and self.service != 'CTL': |
| template = ( |
| '\n' |
| '/**\n') |
| if self.content_needs_compat_gir: |
| template += ( |
| ' * ${format}: (skip)\n') |
| else: |
| template += ( |
| ' * ${format}:\n') |
| hfile.write(string.Template(template).substitute(translations)) |
| for member in self.members: |
| hfile.write(member['object'].build_struct_field_documentation(' * ', member['name'])) |
| template = ( |
| ' *\n' |
| ' * A ${format} struct.\n' |
| ' *\n' |
| ' * Since: ${since}\n' |
| ' */\n') |
| hfile.write(string.Template(template).substitute(translations)) |
| |
| template = ( |
| 'typedef struct _${format} {\n') |
| hfile.write(string.Template(template).substitute(translations)) |
| |
| for member in self.members: |
| hfile.write(member['object'].build_struct_field_declaration(' ', member['name'])) |
| |
| template = ('} ${format};\n') |
| hfile.write(string.Template(template).substitute(translations)) |
| |
| # No need for the clear func if no need to dispose the contents |
| if self.needs_dispose: |
| translations['clear_method'] = self.clear_method |
| template = ( |
| '\n' |
| 'static void\n' |
| '${clear_method} (${format} *value)\n' |
| '{\n') |
| for member in self.members: |
| template += member['object'].build_dispose(' ', 'value->' + member['name']) |
| template += ( |
| '}\n') |
| cfile.write(string.Template(template).substitute(translations)) |
| |
| |
| def emit_types_gir(self, hfile, cfile, since): |
| for member in self.members: |
| member['object'].emit_types_gir(hfile, cfile, since) |
| |
| translations = { 'element_type_no_gir' : self.public_format, |
| 'element_type' : self.element_type_gir, |
| 'underscore' : utils.build_underscore_name(self.struct_type_name_gir), |
| 'new_method' : self.new_method_gir, |
| 'free_method' : self.free_method_gir, |
| 'since' : since if utils.version_compare('1.32',since) > 0 else '1.32' } |
| |
| # The type emission should happen ONLY if the GIR required type is |
| # different to the original one |
| if self.content_needs_compat_gir: |
| template = ( |
| '\n' |
| '/**\n' |
| ' * ${element_type}: (rename-to ${element_type_no_gir})\n') |
| hfile.write(string.Template(template).substitute(translations)) |
| for member in self.members: |
| hfile.write(member['object'].build_struct_field_documentation_gir(' * ', member['name'])) |
| template = ( |
| ' *\n' |
| ' * A ${element_type} struct.\n' |
| ' *\n' |
| ' * This type is a version of #${element_type_no_gir}, using arrays of pointers to\n' |
| ' * structs instead of arrays of structs, for easier binding in other languages.\n' |
| ' *\n' |
| ' * Since: ${since}\n' |
| ' */\n') |
| hfile.write(string.Template(template).substitute(translations)) |
| |
| template = ( |
| 'typedef struct _${element_type} {\n') |
| hfile.write(string.Template(template).substitute(translations)) |
| for member in self.members: |
| hfile.write(member['object'].build_struct_field_declaration_gir(' ', member['name'])) |
| template = ( |
| '} ${element_type};\n') |
| hfile.write(string.Template(template).substitute(translations)) |
| |
| # Free and New methods are required always for the GIR required type, |
| # regardless of whether it's different to the original one or the same |
| template = ( |
| '\n' |
| 'static void\n' |
| '${free_method} (${element_type} *value)\n' |
| '{\n') |
| if not self.content_needs_compat_gir and self.needs_dispose: |
| translations['clear_method'] = self.clear_method |
| template += ' ${clear_method} (value);\n' |
| else: |
| for member in self.members: |
| template += member['object'].build_dispose_gir(' ', 'value->' + member['name']) |
| template += ( |
| ' g_slice_free (${element_type}, value);\n' |
| '}\n' |
| '\n' |
| 'static ${element_type} *\n' |
| '${new_method} (void)\n' |
| '{\n' |
| ' return g_slice_new0 (${element_type});\n' |
| '}\n') |
| cfile.write(string.Template(template).substitute(translations)) |
| |
| template = ( |
| '\n' |
| 'GType ${underscore}_get_type (void) G_GNUC_CONST;\n') |
| hfile.write(string.Template(template).substitute(translations)) |
| |
| template = ( |
| '\n' |
| 'static ${element_type} *\n' |
| '__${underscore}_copy (const ${element_type} *value)\n' |
| '{\n' |
| ' ${element_type} *copy;\n' |
| '\n' |
| ' copy = ${new_method} ();\n') |
| template += self.build_copy_gir(' ', 'value', 'copy') |
| template += ( |
| ' return copy;\n' |
| '}\n' |
| '\n' |
| 'G_DEFINE_BOXED_TYPE (${element_type}, ${underscore}, (GBoxedCopyFunc)__${underscore}_copy, (GBoxedFreeFunc)__${underscore}_free)\n') |
| cfile.write(string.Template(template).substitute(translations)) |
| |
| |
| def emit_buffer_read(self, f, line_prefix, tlv_out, error, variable_name): |
| for member in self.members: |
| member['object'].emit_buffer_read(f, line_prefix, tlv_out, error, variable_name + '.' + member['name']) |
| |
| |
| def emit_buffer_write(self, f, line_prefix, tlv_name, variable_name): |
| for member in self.members: |
| member['object'].emit_buffer_write(f, line_prefix, tlv_name, variable_name + '.' + member['name']) |
| |
| |
| def emit_get_printable(self, f, line_prefix, is_personal): |
| translations = { 'lp' : line_prefix } |
| |
| template = ( |
| '${lp}g_string_append (printable, "[");\n') |
| f.write(string.Template(template).substitute(translations)) |
| |
| for member in self.members: |
| translations['variable_name'] = member['name'] |
| template = ( |
| '${lp}g_string_append (printable, " ${variable_name} = \'");\n') |
| f.write(string.Template(template).substitute(translations)) |
| |
| member['object'].emit_get_printable(f, line_prefix, self.personal_info or is_personal) |
| |
| template = ( |
| '${lp}g_string_append (printable, "\'");\n') |
| f.write(string.Template(template).substitute(translations)) |
| |
| template = ( |
| '${lp}g_string_append (printable, " ]");\n') |
| f.write(string.Template(template).substitute(translations)) |
| |
| |
| def build_dispose(self, line_prefix, variable_name): |
| translations = { 'lp' : line_prefix, |
| 'underscore' : utils.build_underscore_name(self.struct_type_name), |
| 'variable_name' : variable_name } |
| |
| template = '${lp}${underscore}_clear (&${variable_name});\n' |
| return string.Template(template).substitute(translations) |
| |
| |
| def build_dispose_gir(self, line_prefix, variable_name): |
| translations = { 'lp' : line_prefix, |
| 'underscore' : utils.build_underscore_name(self.struct_type_name_gir), |
| 'variable_name' : variable_name } |
| |
| template = '${lp}${underscore}_free (${variable_name});\n' |
| return string.Template(template).substitute(translations) |
| |
| |
| def build_copy_to_gir(self, line_prefix, variable_name_from, variable_name_to): |
| translations = { 'lp' : line_prefix, |
| 'element_type_gir' : self.element_type_gir, |
| 'variable_name_to' : variable_name_to, |
| 'variable_name_from' : variable_name_from } |
| |
| template = '' |
| for member in self.members: |
| template += member['object'].build_copy_to_gir(line_prefix, |
| '${variable_name_from}.' + member['name'], |
| '${variable_name_to}->' + member['name']) |
| return string.Template(template).substitute(translations) |
| |
| |
| def build_copy_from_gir(self, line_prefix, variable_name_from, variable_name_to): |
| translations = { 'lp' : line_prefix, |
| 'element_type_gir' : self.element_type_gir, |
| 'variable_name_to' : variable_name_to, |
| 'variable_name_from' : variable_name_from } |
| |
| template = '' |
| for member in self.members: |
| template += member['object'].build_copy_from_gir(line_prefix, |
| '${variable_name_from}->' + member['name'], |
| '${variable_name_to}.' + member['name']) |
| return string.Template(template).substitute(translations) |
| |
| |
| def build_copy_gir(self, line_prefix, variable_name_from, variable_name_to): |
| translations = { 'lp' : line_prefix, |
| 'element_type_gir' : self.element_type_gir, |
| 'variable_name_to' : variable_name_to, |
| 'variable_name_from' : variable_name_from } |
| |
| template = '' |
| for member in self.members: |
| template += member['object'].build_copy_gir(line_prefix, |
| '${variable_name_from}->' + member['name'], |
| '${variable_name_to}->' + member['name']) |
| return string.Template(template).substitute(translations) |
| |
| |
| def add_sections(self, sections): |
| for member in self.members: |
| member['object'].add_sections(sections) |
| |
| sections['public-types'] += self.element_type + '\n' |
| sections['standard'] += utils.build_underscore_name(self.struct_type_name) + '_get_type\n' |
| if self.content_needs_compat_gir: |
| sections['public-types'] += self.element_type_gir + '\n' |
| sections['standard'] += utils.build_underscore_name(self.struct_type_name_gir) + '_get_type\n' |