1# Copyright (C) 2017 Belledonne Communications SARL 2# 3# This program is free software; you can redistribute it and/or 4# modify it under the terms of the GNU General Public License 5# as published by the Free Software Foundation; either version 2 6# of the License, or (at your option) any later version. 7# 8# This program is distributed in the hope that it will be useful, 9# but WITHOUT ANY WARRANTY; without even the implied warranty of 10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11# GNU General Public License for more details. 12# 13# You should have received a copy of the GNU General Public License 14# along with this program; if not, write to the Free Software 15# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 17 18import re 19import genapixml as CApi 20 21 22class Error(RuntimeError): 23 pass 24 25 26class BlacklistedException(Error): 27 pass 28 29 30class Name(object): 31 camelCaseParsingRegex = re.compile('[A-Z][a-z0-9]*') 32 lowerCamelCaseSplitingRegex = re.compile('([a-z][a-z0-9]*)([A-Z][a-z0-9]*)') 33 34 def __init__(self): 35 self.words = [] 36 self.prev = None 37 38 def copy(self): 39 nameType = type(self) 40 name = nameType() 41 name.words = list(self.words) 42 name.prev = None if self.prev is None else self.prev.copy() 43 return name 44 45 def delete_prefix(self, prefix): 46 it = self 47 _next = None 48 while it is not None and it.words != prefix.words: 49 _next = it 50 it = it.prev 51 52 if it is None or it != prefix: 53 raise Error('no common prefix') 54 elif _next is not None: 55 _next.prev = None 56 57 def _set_namespace(self, namespace): 58 self.prev = namespace 59 if self.prev is not None: 60 prefix = namespace.to_word_list() 61 i = 0 62 while i<len(self.words) and i<len(prefix) and self.words[i] == prefix[i]: 63 i += 1 64 if i == len(self.words): 65 raise Error('name equal to namespace \'{0}\'', self.words) 66 else: 67 self.words = self.words[i:] 68 69 def _lower_all_words(self): 70 i = 0 71 while i<len(self.words): 72 self.words[i] = self.words[i].lower() 73 i += 1 74 75 def from_snake_case(self, name, namespace=None): 76 self.words = name.split('_') 77 Name._set_namespace(self, namespace) 78 79 def from_camel_case(self, name, islowercased=False, namespace=None): 80 if not islowercased: 81 self.words = Name.camelCaseParsingRegex.findall(name) 82 else: 83 match = Name.lowerCamelCaseSplitingRegex.match(name) 84 self.words = [match.group(1)] 85 self.words += Name.camelCaseParsingRegex.findall(match.group(2)) 86 87 Name._lower_all_words(self) 88 Name._set_namespace(self, namespace) 89 90 def to_snake_case(self, fullName=False, upper=False): 91 if self.prev is None or not fullName: 92 res = '_'.join(self.words) 93 if upper: 94 res = res.upper() 95 return res 96 else: 97 return Name.to_snake_case(self.prev, fullName=True, upper=upper) + '_' + Name.to_snake_case(self, upper=upper) 98 99 def to_camel_case(self, lower=False, fullName=False): 100 if self.prev is None or not fullName: 101 res = '' 102 for elem in self.words: 103 if elem is self.words[0] and lower: 104 res += elem 105 else: 106 res += elem.title() 107 return res 108 else: 109 return Name.to_camel_case(self.prev, fullName=True, lower=lower) + Name.to_camel_case(self) 110 111 def concatenate(self, upper=False, fullName=False): 112 if self.prev is None or not fullName: 113 res = '' 114 for elem in self.words: 115 if upper: 116 res += elem.upper() 117 else: 118 res += elem 119 return res 120 else: 121 return Name.concatenate(self.prev, upper=upper, fullName=True) + Name.concatenate(self, upper=upper) 122 123 def to_word_list(self): 124 if self.prev is None: 125 return list(self.words) 126 else: 127 return Name.to_word_list(self.prev) + self.words 128 129 @staticmethod 130 def find_common_parent(name1, name2): 131 if name1.prev is None or name2.prev is None: 132 return None 133 elif name1.prev is name2.prev: 134 return name1.prev 135 else: 136 commonParent = Name.find_common_parent(name1.prev, name2) 137 if commonParent is not None: 138 return commonParent 139 else: 140 return Name.find_common_parent(name1, name2.prev) 141 142 143class ClassName(Name): 144 def to_c(self): 145 return Name.to_camel_case(self, fullName=True) 146 147 148class InterfaceName(ClassName): 149 def to_c(self): 150 return ClassName.to_c(self)[:-8] + 'Cbs' 151 152 153class EnumName(ClassName): 154 pass 155 156 157class EnumValueName(ClassName): 158 pass 159 160 161class MethodName(Name): 162 regex = re.compile('^\d+$') 163 164 def __init__(self): 165 self.overloadRef = 0 166 167 def from_snake_case(self, name, namespace=None): 168 Name.from_snake_case(self, name, namespace=namespace) 169 if len(self.words) > 0: 170 suffix = self.words[-1] 171 if MethodName.regex.match(suffix) is not None: 172 self.overloadRef = int(suffix) 173 del self.words[-1] 174 175 def to_c(self): 176 suffix = ('_' + str(self.overloadRef)) if self.overloadRef > 0 else '' 177 return self.to_snake_case(fullName=True) + suffix 178 179 180class ArgName(Name): 181 def to_c(self): 182 return self.to_snake_case() 183 184 185class PropertyName(ArgName): 186 pass 187 188 189class NamespaceName(Name): 190 def __init__(self, *params): 191 Name.__init__(self) 192 if len(params) > 0: 193 self.words = params[0] 194 195 196class Object(object): 197 def __init__(self, name): 198 self.name = name 199 self.parent = None 200 self.deprecated = False 201 202 def find_first_ancestor_by_type(self, _type): 203 ancestor = self.parent 204 while ancestor is not None and type(ancestor) is not _type: 205 ancestor = ancestor.parent 206 return ancestor 207 208 209class Type(Object): 210 def __init__(self, name, isconst=False, isref=False): 211 Object.__init__(self, name) 212 self.isconst = isconst 213 self.isref = isref 214 self.cname = None 215 216 217class BaseType(Type): 218 def __init__(self, name, isconst=False, isref=False, size=None, isUnsigned=False): 219 Type.__init__(self, name, isconst=isconst, isref=isref) 220 self.size = size 221 self.isUnsigned = isUnsigned 222 223 224class EnumType(Type): 225 def __init__(self, name, isconst=False, isref=False, enumDesc=None): 226 Type.__init__(self, name, isconst=isconst, isref=isref) 227 self.desc = enumDesc 228 229 230class ClassType(Type): 231 def __init__(self, name, isconst=False, isref=False, classDesc=None): 232 Type.__init__(self, name, isconst=isconst, isref=isref) 233 self.desc = classDesc 234 235 236class ListType(Type): 237 def __init__(self, containedTypeName, isconst=False, isref=False): 238 Type.__init__(self, 'list', isconst=isconst, isref=isref) 239 self.containedTypeName = containedTypeName 240 self._containedTypeDesc = None 241 242 def set_contained_type_desc(self, desc): 243 self._containedTypeDesc = desc 244 desc.parent = self 245 246 def get_contained_type_desc(self): 247 return self._containedTypeDesc 248 249 containedTypeDesc = property(fset=set_contained_type_desc, fget=get_contained_type_desc) 250 251 252class DocumentableObject(Object): 253 def __init__(self, name): 254 Object.__init__(self, name) 255 self.briefDescription = None 256 self.detailedDescription = None 257 self.deprecated = None 258 259 def set_from_c(self, cObject, namespace=None): 260 self.briefDescription = cObject.briefDescription 261 self.detailedDescription = cObject.detailedDescription 262 self.deprecated = cObject.deprecated 263 self.parent = namespace 264 265 def get_namespace_object(self): 266 if isinstance(self, (Namespace,Enum,Class)): 267 return self 268 elif self.parent is None: 269 raise Error('{0} is not attached to a namespace object'.format(self)) 270 else: 271 return self.parent.get_namespace_object() 272 273 274class Namespace(DocumentableObject): 275 def __init__(self, name): 276 DocumentableObject.__init__(self, name) 277 self.children = [] 278 279 def add_child(self, child): 280 self.children.append(child) 281 child.parent = self 282 283 284class Flag: 285 def __init__(self, position): 286 self.position = position 287 288 289class EnumValue(DocumentableObject): 290 def __init__(self, name): 291 DocumentableObject.__init__(self, name) 292 self.value = None 293 294 def value_from_string(self, stringValue): 295 m = re.match('^1\s*<<\s*([0-9]+)$', stringValue) 296 if m is not None: 297 self.value = Flag(int(m.group(1))) 298 else: 299 self.value = int(stringValue, base=0) 300 301 302class Enum(DocumentableObject): 303 def __init__(self, name): 304 DocumentableObject.__init__(self, name) 305 self.values = [] 306 307 def add_value(self, value): 308 self.values.append(value) 309 value.parent = self 310 311 def set_from_c(self, cEnum, namespace=None): 312 Object.set_from_c(self, cEnum, namespace=namespace) 313 314 if 'associatedTypedef' in dir(cEnum): 315 name = cEnum.associatedTypedef.name 316 else: 317 name = cEnum.name 318 319 self.name = EnumName() 320 self.name.prev = None if namespace is None else namespace.name 321 self.name.set_from_c(name) 322 323 for cEnumValue in cEnum.values: 324 aEnumValue = EnumValue() 325 aEnumValue.set_from_c(cEnumValue, namespace=self) 326 self.add_value(aEnumValue) 327 328 329class Argument(DocumentableObject): 330 def __init__(self, name, argType, optional=False, default=None): 331 DocumentableObject.__init__(self, name) 332 self._type = argType 333 argType.parent = self 334 self.optional = optional 335 self.default = default 336 337 def _set_type(self, _type): 338 self._type = _type 339 _type.parent = self 340 341 def _get_type(self): 342 return self._type 343 344 type = property(fset=_set_type, fget=_get_type) 345 346 347class Method(DocumentableObject): 348 class Type: 349 Instance = 0, 350 Class = 1 351 352 def __init__(self, name, type=Type.Instance): 353 DocumentableObject.__init__(self, name) 354 self.type = type 355 self.constMethod = False 356 self.args = [] 357 self._returnType = None 358 359 def _set_return_type(self, returnType): 360 self._returnType = returnType 361 returnType.parent = self 362 363 def _get_return_type(self): 364 return self._returnType 365 366 def add_arguments(self, arg): 367 self.args.append(arg) 368 arg.parent = self 369 370 returnType = property(fset=_set_return_type, fget=_get_return_type) 371 372 373class Property(DocumentableObject): 374 def __init__(self, name): 375 DocumentableObject.__init__(self, name) 376 self._setter = None 377 self._getter = None 378 self._type = None 379 380 def set_setter(self, setter): 381 self._setter = setter 382 setter.parent = self 383 384 def get_setter(self): 385 return self._setter 386 387 def set_getter(self, getter): 388 self._getter = getter 389 if self._type is None: 390 self._type = getter.returnType 391 getter.parent = self 392 393 def get_getter(self): 394 return self._getter 395 396 setter = property(fset=set_setter, fget=get_setter) 397 getter = property(fset=set_getter, fget=get_getter) 398 399 400class Class(DocumentableObject): 401 def __init__(self, name): 402 DocumentableObject.__init__(self, name) 403 self.properties = [] 404 self.instanceMethods = [] 405 self.classMethods = [] 406 self._listenerInterface = None 407 self.multilistener = False 408 self.refcountable = False 409 410 def add_property(self, property): 411 self.properties.append(property) 412 property.parent = self 413 414 def add_instance_method(self, method): 415 self.instanceMethods.append(method) 416 method.parent = self 417 418 def add_class_method(self, method): 419 self.classMethods.append(method) 420 method.parent = self 421 422 def set_listener_interface(self, interface): 423 self._listenerInterface = interface 424 interface._listenedClass = self 425 426 def get_listener_interface(self): 427 return self._listenerInterface 428 429 listenerInterface = property(fget=get_listener_interface, fset=set_listener_interface) 430 431 432class Interface(DocumentableObject): 433 def __init__(self, name): 434 DocumentableObject.__init__(self, name) 435 self.methods = [] 436 self._listenedClass = None 437 438 def add_method(self, method): 439 self.methods.append(method) 440 method.parent = self 441 442 def get_listened_class(self): 443 return self._listenedClass 444 445 listenedClass = property(fget=get_listened_class) 446 447 448class CParser(object): 449 def __init__(self, cProject): 450 self.cBaseType = ['void', 'bool_t', 'char', 'short', 'int', 'long', 'size_t', 'time_t', 'float', 'double', 'LinphoneStatus'] 451 self.cListType = 'bctbx_list_t' 452 self.regexFixedSizeInteger = '^(u?)int(\d?\d)_t$' 453 self.methodBl = ['ref', 'unref', 'new', 'destroy', 'getCurrentCallbacks', 'setUserData', 'getUserData'] 454 self.functionBl = [ 455 'linphone_factory_create_core', # manualy wrapped 456 'linphone_factory_create_core_with_config', # manualy wrapped 457 'linphone_vcard_get_belcard'] # manualy wrapped 458 459 self.classBl = ['LpConfig'] # temporarly blacklisted 460 461 # list of classes that must be concidered as refcountable even if 462 # they are no ref()/unref() methods 463 self.forcedRefcountableClasses = ['LinphoneFactory'] 464 465 self.cProject = cProject 466 467 self.enumsIndex = {} 468 for enum in self.cProject.enums: 469 if enum.associatedTypedef is None: 470 self.enumsIndex[enum.name] = None 471 else: 472 self.enumsIndex[enum.associatedTypedef.name] = None 473 474 self.classesIndex = {} 475 self.interfacesIndex = {} 476 for _class in self.cProject.classes: 477 if _class.name not in self.classBl: 478 if _class.name.endswith('Cbs'): 479 self.interfacesIndex[_class.name] = None 480 else: 481 self.classesIndex[_class.name] = None 482 483 self.methodsIndex = {} 484 for _class in self.cProject.classes: 485 for funcname in _class.classMethods: 486 self.methodsIndex[funcname] = None 487 for funcname in _class.instanceMethods: 488 self.methodsIndex[funcname] = None 489 for _property in _class.properties.values(): 490 if _property.setter is not None: 491 self.methodsIndex[_property.setter.name] = None 492 if _property.getter is not None: 493 self.methodsIndex[_property.getter.name] = None 494 495 name = NamespaceName() 496 name.from_snake_case('linphone') 497 498 self.namespace = Namespace(name) 499 500 def _is_blacklisted(self, name): 501 if type(name) is MethodName: 502 return name.to_camel_case(lower=True) in self.methodBl or name.to_c() in self.functionBl 503 elif type(name) is ClassName: 504 return name.to_c() in self.classBl 505 else: 506 return False 507 508 def parse_all(self): 509 for enum in self.cProject.enums: 510 try: 511 self.parse_enum(enum) 512 except Error as e: 513 print('Could not parse \'{0}\' enum: {1}'.format(enum.name, e.args[0])) 514 515 for _class in self.cProject.classes: 516 try: 517 self.parse_class(_class) 518 except BlacklistedException: 519 pass 520 except Error as e: 521 print('Could not parse \'{0}\' class: {1}'.format(_class.name, e.args[0])) 522 523 524 self._clean_all_indexes() 525 self._fix_all_types() 526 self._fix_all_docs() 527 528 def _clean_all_indexes(self): 529 for index in [self.classesIndex, self.interfacesIndex, self.methodsIndex]: 530 self._clean_index(index) 531 532 def _clean_index(self, index): 533 keysToRemove = [] 534 for key in index.keys(): 535 if index[key] is None: 536 keysToRemove.append(key) 537 538 for key in keysToRemove: 539 del index[key] 540 541 def _class_is_refcountable(self, _class): 542 if _class.name in self.forcedRefcountableClasses: 543 return True 544 545 for method in _class.instanceMethods: 546 if method.startswith(_class.cFunctionPrefix) and method[len(_class.cFunctionPrefix):] == 'ref': 547 return True 548 return False 549 550 def _fix_all_types_in_class_or_interface(self, _class): 551 if _class is not None: 552 if type(_class) is Class: 553 self._fix_all_types_in_class(_class) 554 else: 555 self._fix_all_types_in_interface(_class) 556 557 def _fix_all_types(self): 558 for _class in self.interfacesIndex.values(): 559 self._fix_all_types_in_class_or_interface(_class) 560 for _class in self.classesIndex.values(): 561 self._fix_all_types_in_class_or_interface(_class) 562 563 def _fix_all_types_in_class(self, _class): 564 for property in _class.properties: 565 if property.setter is not None: 566 self._fix_all_types_in_method(property.setter) 567 if property.getter is not None: 568 self._fix_all_types_in_method(property.getter) 569 570 for method in (_class.instanceMethods + _class.classMethods): 571 self._fix_all_types_in_method(method) 572 573 def _fix_all_types_in_interface(self, interface): 574 for method in interface.methods: 575 self._fix_all_types_in_method(method) 576 577 def _fix_all_types_in_method(self, method): 578 try: 579 self._fix_type(method.returnType) 580 for arg in method.args: 581 self._fix_type(arg.type) 582 except Error as e: 583 print('warning: some types could not be fixed in {0}() function: {1}'.format(method.name.to_snake_case(fullName=True), e.args[0])) 584 585 def _fix_type(self, _type): 586 if isinstance(_type, EnumType) and _type.desc is None: 587 _type.desc = self.enumsIndex[_type.name] 588 elif isinstance(_type, ClassType) and _type.desc is None: 589 if _type.name in self.classesIndex: 590 _type.desc = self.classesIndex[_type.name] 591 else: 592 _type.desc = self.interfacesIndex[_type.name] 593 elif isinstance(_type, ListType) and _type.containedTypeDesc is None: 594 if _type.containedTypeName in self.classesIndex: 595 _type.containedTypeDesc = ClassType(_type.containedTypeName, classDesc=self.classesIndex[_type.containedTypeName]) 596 elif _type.containedTypeName in self.interfacesIndex: 597 _type.containedTypeDesc = ClassType(_type.containedTypeName, classDesc=self.interfacesIndex[_type.containedTypeName]) 598 elif _type.containedTypeName in self.enumsIndex: 599 _type.containedTypeDesc = EnumType(_type.containedTypeName, enumDesc=self.enumsIndex[_type.containedTypeName]) 600 else: 601 if _type.containedTypeName is not None: 602 _type.containedTypeDesc = self.parse_c_base_type(_type.containedTypeName) 603 else: 604 raise Error('bctbx_list_t type without specified contained type') 605 606 def _fix_all_docs(self): 607 for _class in self.classesIndex.values(): 608 if _class.briefDescription is not None: 609 _class.briefDescription.resolve_all_references(self) 610 for method in self.methodsIndex.values(): 611 if method.briefDescription is not None: 612 method.briefDescription.resolve_all_references(self) 613 614 def parse_enum(self, cenum): 615 if 'associatedTypedef' in dir(cenum): 616 nameStr = cenum.associatedTypedef.name 617 else: 618 nameStr = cenum.name 619 620 name = EnumName() 621 name.from_camel_case(nameStr, namespace=self.namespace.name) 622 enum = Enum(name) 623 enum.briefDescription = cenum.briefDoc 624 self.namespace.add_child(enum) 625 626 for cEnumValue in cenum.values: 627 valueName = EnumValueName() 628 valueName.from_camel_case(cEnumValue.name, namespace=name) 629 aEnumValue = EnumValue(valueName) 630 aEnumValue.briefDescription = cEnumValue.briefDoc 631 if cEnumValue.value is not None: 632 try: 633 aEnumValue.value_from_string(cEnumValue.value) 634 except ValueError: 635 raise Error('{0} enum value has an invalid definition ({1})'.format(cEnumValue.name, cEnumValue.value)) 636 enum.add_value(aEnumValue) 637 638 self.enumsIndex[nameStr] = enum 639 return enum 640 641 def parse_class(self, cclass): 642 if cclass.name in self.classBl: 643 raise BlacklistedException('{0} is blacklisted'.format(cclass.name)); 644 645 if cclass.name.endswith('Cbs'): 646 _class = self._parse_listener(cclass) 647 self.interfacesIndex[cclass.name] = _class 648 else: 649 _class = self._parse_class(cclass) 650 self.classesIndex[cclass.name] = _class 651 self.namespace.add_child(_class) 652 return _class 653 654 def _parse_class(self, cclass): 655 name = ClassName() 656 name.from_camel_case(cclass.name, namespace=self.namespace.name) 657 _class = Class(name) 658 _class.briefDescription = cclass.briefDoc 659 _class.refcountable = self._class_is_refcountable(cclass) 660 661 for cproperty in cclass.properties.values(): 662 try: 663 if cproperty.name != 'callbacks': 664 absProperty = self._parse_property(cproperty, namespace=name) 665 _class.add_property(absProperty) 666 else: 667 _class.listenerInterface = self.interfacesIndex[cproperty.getter.returnArgument.ctype] 668 except Error as e: 669 print('Could not parse {0} property in {1}: {2}'.format(cproperty.name, cclass.name, e.args[0])) 670 671 for cMethod in cclass.instanceMethods.values(): 672 try: 673 method = self.parse_method(cMethod, namespace=name) 674 if method.name.to_snake_case() == 'add_callbacks' or method.name.to_snake_case() == 'remove_callbacks': 675 if _class.listenerInterface is None or not _class.multilistener: 676 _class.multilistener = True 677 _class.listenerInterface = self.interfacesIndex[_class.name.to_camel_case(fullName=True) + 'Cbs'] 678 elif isinstance(method.returnType, ClassType) and method.returnType.name.endswith('Cbs'): 679 pass 680 else: 681 _class.add_instance_method(method) 682 683 except BlacklistedException: 684 pass 685 except Error as e: 686 print('Could not parse {0} function: {1}'.format(cMethod.name, e.args[0])) 687 688 for cMethod in cclass.classMethods.values(): 689 try: 690 method = self.parse_method(cMethod, type=Method.Type.Class, namespace=name) 691 _class.add_class_method(method) 692 except BlacklistedException: 693 pass 694 except Error as e: 695 print('Could not parse {0} function: {1}'.format(cMethod.name, e.args[0])) 696 697 return _class 698 699 def _parse_property(self, cproperty, namespace=None): 700 name = PropertyName() 701 name.from_snake_case(cproperty.name) 702 if (cproperty.setter is not None and len(cproperty.setter.arguments) == 1) or (cproperty.getter is not None and len(cproperty.getter.arguments) == 0): 703 methodType = Method.Type.Class 704 else: 705 methodType = Method.Type.Instance 706 aproperty = Property(name) 707 if cproperty.setter is not None: 708 method = self.parse_method(cproperty.setter, namespace=namespace, type=methodType) 709 aproperty.setter = method 710 if cproperty.getter is not None: 711 method = self.parse_method(cproperty.getter, namespace=namespace, type=methodType) 712 aproperty.getter = method 713 return aproperty 714 715 716 def _parse_listener(self, cclass): 717 name = InterfaceName() 718 name.from_camel_case(cclass.name, namespace=self.namespace.name) 719 720 if name.words[len(name.words)-1] == 'cbs': 721 name.words[len(name.words)-1] = 'listener' 722 else: 723 raise Error('{0} is not a listener'.format(cclass.name)) 724 725 listener = Interface(name) 726 listener.briefDescription = cclass.briefDoc 727 728 for property in cclass.properties.values(): 729 if property.name != 'user_data': 730 try: 731 method = self._parse_listener_property(property, listener, cclass.events) 732 listener.add_method(method) 733 except Error as e: 734 print('Could not parse property \'{0}\' of listener \'{1}\': {2}'.format(property.name, cclass.name, e.args[0])) 735 736 return listener 737 738 def _parse_listener_property(self, property, listener, events): 739 methodName = MethodName() 740 methodName.from_snake_case(property.name) 741 methodName.words.insert(0, 'on') 742 methodName.prev = listener.name 743 744 if property.getter is not None: 745 eventName = property.getter.returnArgument.ctype 746 elif property.setter is not None and len(property.setter.arguments) == 2: 747 eventName = property.setter.arguments[1].ctype 748 else: 749 raise Error('event name for {0} property of {1} listener not found'.format(property.name, listener.name.to_snake_case(fullName=True))) 750 751 try: 752 event = events[eventName] 753 except KeyError: 754 raise Error('invalid event name \'{0}\''.format(eventName)) 755 756 method = Method(methodName) 757 method.returnType = self.parse_type(event.returnArgument) 758 for arg in event.arguments: 759 argName = ArgName() 760 argName.from_snake_case(arg.name) 761 argument = Argument(argName, self.parse_type(arg)) 762 method.add_arguments(argument) 763 764 return method 765 766 def parse_method(self, cfunction, namespace, type=Method.Type.Instance): 767 name = MethodName() 768 name.from_snake_case(cfunction.name, namespace=namespace) 769 770 if self._is_blacklisted(name): 771 raise BlacklistedException('{0} is blacklisted'.format(name.to_c())); 772 773 method = Method(name, type=type) 774 method.briefDescription = cfunction.briefDoc 775 method.deprecated = cfunction.deprecated 776 method.returnType = self.parse_type(cfunction.returnArgument) 777 778 for arg in cfunction.arguments: 779 if type == Method.Type.Instance and arg is cfunction.arguments[0]: 780 method.constMethod = ('const' in arg.completeType.split(' ')) 781 else: 782 aType = self.parse_type(arg) 783 argName = ArgName() 784 argName.from_snake_case(arg.name) 785 absArg = Argument(argName, aType) 786 method.add_arguments(absArg) 787 788 self.methodsIndex[cfunction.name] = method 789 return method 790 791 def parse_type(self, cType): 792 if cType.ctype in self.cBaseType or re.match(self.regexFixedSizeInteger, cType.ctype): 793 absType = self.parse_c_base_type(cType.completeType) 794 elif cType.ctype in self.enumsIndex: 795 absType = EnumType(cType.ctype, enumDesc=self.enumsIndex[cType.ctype]) 796 elif cType.ctype in self.classesIndex or cType.ctype in self.interfacesIndex: 797 absType = ClassType(cType.ctype) 798 absType.isconst = cType.completeType.startswith('const ') 799 absType.isref = cType.completeType.endswith('*') 800 elif cType.ctype == self.cListType: 801 absType = ListType(cType.containedType) 802 elif cType.ctype.endswith('Mask'): 803 absType = BaseType('integer', isUnsigned=True) 804 else: 805 raise Error('Unknown C type \'{0}\''.format(cType.ctype)) 806 807 absType.cname = cType.completeType 808 return absType 809 810 def parse_c_base_type(self, cDecl): 811 declElems = cDecl.split(' ') 812 param = {} 813 name = None 814 for elem in declElems: 815 if elem == 'const': 816 if name is None: 817 param['isconst'] = True 818 elif elem == 'unsigned': 819 param['isUnsigned'] = True 820 elif elem == 'char': 821 name = 'character' 822 elif elem == 'void': 823 name = 'void' 824 elif elem == 'bool_t': 825 name = 'boolean' 826 elif elem in ['short', 'long']: 827 param['size'] = elem 828 elif elem == 'int': 829 name = 'integer' 830 elif elem == 'float': 831 name = 'floatant' 832 param['size'] = 'float' 833 elif elem == 'size_t': 834 name = 'size' 835 elif elem == 'time_t': 836 name = 'time' 837 elif elem == 'double': 838 name = 'floatant' 839 if 'size' in param and param['size'] == 'long': 840 param['size'] = 'long double' 841 else: 842 param['size'] = 'double' 843 elif elem == 'LinphoneStatus': 844 name = 'status' 845 elif elem == '*': 846 if name is not None: 847 if name == 'character': 848 name = 'string' 849 elif name == 'string': 850 name = 'string_array' 851 elif 'isref' not in param or param['isref'] is False: 852 param['isref'] = True 853 else: 854 raise Error('Unhandled double-pointer') 855 else: 856 matchCtx = re.match(self.regexFixedSizeInteger, elem) 857 if matchCtx: 858 name = 'integer' 859 if matchCtx.group(1) == 'u': 860 param['isUnsigned'] = True 861 862 param['size'] = int(matchCtx.group(2)) 863 if param['size'] not in [8, 16, 32, 64]: 864 raise Error('{0} C basic type has an invalid size ({1})'.format(cDecl, param['size'])) 865 866 867 if name is not None: 868 return BaseType(name, **param) 869 else: 870 raise Error('could not find type in \'{0}\''.format(cDecl)) 871