1# Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights 2# reserved. Use of this source code is governed by a BSD-style license that 3# can be found in the LICENSE file. 4 5from __future__ import absolute_import 6from date_util import * 7from file_util import * 8import os 9import re 10import shutil 11import string 12import sys 13import textwrap 14import time 15 16 17def notify(msg): 18 """ Display a message. """ 19 sys.stdout.write(' NOTE: ' + msg + '\n') 20 21 22def wrap_text(text, indent='', maxchars=80): 23 """ Wrap the text to the specified number of characters. If 24 necessary a line will be broken and wrapped after a word. 25 """ 26 result = '' 27 lines = textwrap.wrap(text, maxchars - len(indent)) 28 for line in lines: 29 result += indent + line + '\n' 30 return result 31 32 33def is_base_class(clsname): 34 """ Returns true if |clsname| is a known base (root) class in the object 35 hierarchy. 36 """ 37 return clsname == 'CefBaseRefCounted' or clsname == 'CefBaseScoped' 38 39 40def get_capi_file_name(cppname): 41 """ Convert a C++ header file name to a C API header file name. """ 42 return cppname[:-2] + '_capi.h' 43 44 45def get_capi_name(cppname, isclassname, prefix=None): 46 """ Convert a C++ CamelCaps name to a C API underscore name. """ 47 result = '' 48 lastchr = '' 49 for chr in cppname: 50 # add an underscore if the current character is an upper case letter 51 # and the last character was a lower case letter 52 if len(result) > 0 and not chr.isdigit() \ 53 and chr.upper() == chr \ 54 and not lastchr.upper() == lastchr: 55 result += '_' 56 result += chr.lower() 57 lastchr = chr 58 59 if isclassname: 60 result += '_t' 61 62 if not prefix is None: 63 if prefix[0:3] == 'cef': 64 # if the prefix name is duplicated in the function name 65 # remove that portion of the function name 66 subprefix = prefix[3:] 67 pos = result.find(subprefix) 68 if pos >= 0: 69 result = result[0:pos] + result[pos + len(subprefix):] 70 result = prefix + '_' + result 71 72 return result 73 74 75def get_wrapper_type_enum(cppname): 76 """ Returns the wrapper type enumeration value for the specified C++ class 77 name. """ 78 return 'WT_' + get_capi_name(cppname, False)[4:].upper() 79 80 81def get_prev_line(body, pos): 82 """ Retrieve the start and end positions and value for the line immediately 83 before the line containing the specified position. 84 """ 85 end = body.rfind('\n', 0, pos) 86 start = body.rfind('\n', 0, end) + 1 87 line = body[start:end] 88 return {'start': start, 'end': end, 'line': line} 89 90 91def get_comment(body, name): 92 """ Retrieve the comment for a class or function. """ 93 result = [] 94 95 pos = body.find(name) 96 in_block_comment = False 97 while pos > 0: 98 data = get_prev_line(body, pos) 99 line = data['line'].strip() 100 pos = data['start'] 101 if len(line) == 0: 102 # check if the next previous line is a comment 103 prevdata = get_prev_line(body, pos) 104 prevline = prevdata['line'].strip() 105 if prevline[0:2] == '//' and prevline[0:3] != '///': 106 result.append(None) 107 else: 108 break 109 # single line /*--cef()--*/ 110 elif line[0:2] == '/*' and line[-2:] == '*/': 111 continue 112 # start of multi line /*--cef()--*/ 113 elif in_block_comment and line[0:2] == '/*': 114 in_block_comment = False 115 continue 116 # end of multi line /*--cef()--*/ 117 elif not in_block_comment and line[-2:] == '*/': 118 in_block_comment = True 119 continue 120 elif in_block_comment: 121 continue 122 elif line[0:2] == '//': 123 # keep the comment line including any leading spaces 124 result.append(line[2:]) 125 else: 126 break 127 128 result.reverse() 129 return result 130 131 132def validate_comment(file, name, comment): 133 """ Validate the comment array returned by get_comment(). """ 134 # Verify that the comment contains beginning and ending '///' as required by 135 # CppDoc (the leading '//' from each line will already have been removed by 136 # the get_comment() logic). There may be additional comments proceeding the 137 # CppDoc block so we look at the quantity of lines equaling '/' and expect 138 # the last line to be '/'. 139 docct = 0 140 for line in comment: 141 if not line is None and len(line) > 0 and line == '/': 142 docct = docct + 1 143 if docct != 2 or len(comment) < 3 or comment[len(comment) - 1] != '/': 144 raise Exception('Missing or incorrect comment in %s for: %s' % \ 145 (file, name)) 146 147 148def format_comment(comment, indent, translate_map=None, maxchars=80): 149 """ Return the comments array as a formatted string. """ 150 if not translate_map is None: 151 # Replace longest keys first in translation. 152 translate_keys = sorted( 153 translate_map.keys(), key=lambda item: (-len(item), item)) 154 155 result = '' 156 wrapme = '' 157 hasemptyline = False 158 for line in comment: 159 # if the line starts with a leading space, remove that space 160 if not line is None and len(line) > 0 and line[0:1] == ' ': 161 line = line[1:] 162 didremovespace = True 163 else: 164 didremovespace = False 165 166 if line is None or len(line) == 0 or line[0:1] == ' ' \ 167 or line[0:1] == '/': 168 # the previous paragraph, if any, has ended 169 if len(wrapme) > 0: 170 if not translate_map is None: 171 # apply the translation 172 for key in translate_keys: 173 wrapme = wrapme.replace(key, translate_map[key]) 174 # output the previous paragraph 175 result += wrap_text(wrapme, indent + '// ', maxchars) 176 wrapme = '' 177 178 if not line is None: 179 if len(line) == 0 or line[0:1] == ' ' or line[0:1] == '/': 180 # blank lines or anything that's further indented should be 181 # output as-is 182 result += indent + '//' 183 if len(line) > 0: 184 if didremovespace: 185 result += ' ' + line 186 else: 187 result += line 188 result += '\n' 189 else: 190 # add to the current paragraph 191 wrapme += line + ' ' 192 else: 193 # output an empty line 194 hasemptyline = True 195 result += '\n' 196 197 if len(wrapme) > 0: 198 if not translate_map is None: 199 # apply the translation 200 for key in translate_map.keys(): 201 wrapme = wrapme.replace(key, translate_map[key]) 202 # output the previous paragraph 203 result += wrap_text(wrapme, indent + '// ', maxchars) 204 205 if hasemptyline: 206 # an empty line means a break between comments, so the comment is 207 # probably a section heading and should have an extra line before it 208 result = '\n' + result 209 return result 210 211 212def format_translation_changes(old, new): 213 """ Return a comment stating what is different between the old and new 214 function prototype parts. 215 """ 216 changed = False 217 result = '' 218 219 # normalize C API attributes 220 oldargs = [x.replace('struct _', '') for x in old['args']] 221 oldretval = old['retval'].replace('struct _', '') 222 newargs = [x.replace('struct _', '') for x in new['args']] 223 newretval = new['retval'].replace('struct _', '') 224 225 # check if the prototype has changed 226 oldset = set(oldargs) 227 newset = set(newargs) 228 if len(oldset.symmetric_difference(newset)) > 0: 229 changed = True 230 result += '\n // WARNING - CHANGED ATTRIBUTES' 231 232 # in the implementation set only 233 oldonly = oldset.difference(newset) 234 for arg in oldonly: 235 result += '\n // REMOVED: ' + arg 236 237 # in the current set only 238 newonly = newset.difference(oldset) 239 for arg in newonly: 240 result += '\n // ADDED: ' + arg 241 242 # check if the return value has changed 243 if oldretval != newretval: 244 changed = True 245 result += '\n // WARNING - CHANGED RETURN VALUE'+ \ 246 '\n // WAS: '+old['retval']+ \ 247 '\n // NOW: '+new['retval'] 248 249 if changed: 250 result += '\n #pragma message("Warning: "__FILE__": '+new['name']+ \ 251 ' prototype has changed")\n' 252 253 return result 254 255 256def format_translation_includes(header, body): 257 """ Return the necessary list of includes based on the contents of the 258 body. 259 """ 260 result = '' 261 262 # <algorithm> required for VS2013. 263 if body.find('std::min') > 0 or body.find('std::max') > 0: 264 result += '#include <algorithm>\n' 265 266 if body.find('cef_api_hash(') > 0: 267 result += '#include "include/cef_api_hash.h"\n' 268 269 # identify what CppToC classes are being used 270 p = re.compile('([A-Za-z0-9_]{1,})CppToC') 271 list = sorted(set(p.findall(body))) 272 for item in list: 273 directory = '' 274 if not is_base_class(item): 275 cls = header.get_class(item) 276 dir = cls.get_file_directory() 277 if not dir is None: 278 directory = dir + '/' 279 result += '#include "libcef_dll/cpptoc/'+directory+ \ 280 get_capi_name(item[3:], False)+'_cpptoc.h"\n' 281 282 # identify what CToCpp classes are being used 283 p = re.compile('([A-Za-z0-9_]{1,})CToCpp') 284 list = sorted(set(p.findall(body))) 285 for item in list: 286 directory = '' 287 if not is_base_class(item): 288 cls = header.get_class(item) 289 dir = cls.get_file_directory() 290 if not dir is None: 291 directory = dir + '/' 292 result += '#include "libcef_dll/ctocpp/'+directory+ \ 293 get_capi_name(item[3:], False)+'_ctocpp.h"\n' 294 295 if body.find('shutdown_checker') > 0: 296 result += '#include "libcef_dll/shutdown_checker.h"\n' 297 298 if body.find('transfer_') > 0: 299 result += '#include "libcef_dll/transfer_util.h"\n' 300 301 return result 302 303 304def str_to_dict(str): 305 """ Convert a string to a dictionary. If the same key has multiple values 306 the values will be stored in a list. """ 307 dict = {} 308 parts = str.split(',') 309 for part in parts: 310 part = part.strip() 311 if len(part) == 0: 312 continue 313 sparts = part.split('=') 314 if len(sparts) > 2: 315 raise Exception('Invalid dictionary pair format: ' + part) 316 name = sparts[0].strip() 317 if len(sparts) == 2: 318 val = sparts[1].strip() 319 else: 320 val = True 321 if name in dict: 322 # a value with this name already exists 323 curval = dict[name] 324 if not isinstance(curval, list): 325 # convert the string value to a list 326 dict[name] = [curval] 327 dict[name].append(val) 328 else: 329 dict[name] = val 330 return dict 331 332 333def dict_to_str(dict): 334 """ Convert a dictionary to a string. """ 335 str = [] 336 for name in dict.keys(): 337 if not isinstance(dict[name], list): 338 if dict[name] is True: 339 # currently a bool value 340 str.append(name) 341 else: 342 # currently a string value 343 str.append(name + '=' + dict[name]) 344 else: 345 # currently a list value 346 for val in dict[name]: 347 str.append(name + '=' + val) 348 return ','.join(str) 349 350 351# regex for matching comment-formatted attributes 352_cre_attrib = '/\*--cef\(([A-Za-z0-9_ ,=:\n]{0,})\)--\*/' 353# regex for matching class and function names 354_cre_cfname = '([A-Za-z0-9_]{1,})' 355# regex for matching class and function names including path separators 356_cre_cfnameorpath = '([A-Za-z0-9_\/]{1,})' 357# regex for matching function return values 358_cre_retval = '([A-Za-z0-9_<>:,\*\&]{1,})' 359# regex for matching typedef value and name combination 360_cre_typedef = '([A-Za-z0-9_<>:,\*\&\s]{1,})' 361# regex for matching function return value and name combination 362_cre_func = '([A-Za-z][A-Za-z0-9_<>:,\*\&\s]{1,})' 363# regex for matching virtual function modifiers + arbitrary whitespace 364_cre_vfmod = '([\sA-Za-z0-9_]{0,})' 365# regex for matching arbitrary whitespace 366_cre_space = '[\s]{1,}' 367# regex for matching optional virtual keyword 368_cre_virtual = '(?:[\s]{1,}virtual){0,1}' 369 370# Simple translation types. Format is: 371# 'cpp_type' : ['capi_type', 'capi_default_value'] 372_simpletypes = { 373 'void': ['void', ''], 374 'void*': ['void*', 'NULL'], 375 'int': ['int', '0'], 376 'int16': ['int16', '0'], 377 'uint16': ['uint16', '0'], 378 'int32': ['int32', '0'], 379 'uint32': ['uint32', '0'], 380 'int64': ['int64', '0'], 381 'uint64': ['uint64', '0'], 382 'double': ['double', '0'], 383 'float': ['float', '0'], 384 'float*': ['float*', 'NULL'], 385 'long': ['long', '0'], 386 'unsigned long': ['unsigned long', '0'], 387 'long long': ['long long', '0'], 388 'size_t': ['size_t', '0'], 389 'bool': ['int', '0'], 390 'char': ['char', '0'], 391 'char* const': ['char* const', 'NULL'], 392 'cef_color_t': ['cef_color_t', '0'], 393 'cef_json_parser_error_t': ['cef_json_parser_error_t', 'JSON_NO_ERROR'], 394 'cef_plugin_policy_t': ['cef_plugin_policy_t', 'PLUGIN_POLICY_ALLOW'], 395 'CefCursorHandle': ['cef_cursor_handle_t', 'kNullCursorHandle'], 396 'CefCompositionUnderline': [ 397 'cef_composition_underline_t', 'CefCompositionUnderline()' 398 ], 399 'CefEventHandle': ['cef_event_handle_t', 'kNullEventHandle'], 400 'CefWindowHandle': ['cef_window_handle_t', 'kNullWindowHandle'], 401 'CefInsets': ['cef_insets_t', 'CefInsets()'], 402 'CefPoint': ['cef_point_t', 'CefPoint()'], 403 'CefRect': ['cef_rect_t', 'CefRect()'], 404 'CefSize': ['cef_size_t', 'CefSize()'], 405 'CefRange': ['cef_range_t', 'CefRange()'], 406 'CefDraggableRegion': ['cef_draggable_region_t', 'CefDraggableRegion()'], 407 'CefThreadId': ['cef_thread_id_t', 'TID_UI'], 408 'CefTime': ['cef_time_t', 'CefTime()'], 409 'CefAudioParameters': ['cef_audio_parameters_t', 'CefAudioParameters()'] 410} 411 412 413def get_function_impls(content, ident, has_impl=True): 414 """ Retrieve the function parts from the specified contents as a set of 415 return value, name, arguments and body. Ident must occur somewhere in 416 the value. 417 """ 418 # extract the functions 419 find_regex = '\n' + _cre_func + '\((.*?)\)([A-Za-z0-9_\s]{0,})' 420 if has_impl: 421 find_regex += '\{(.*?)\n\}' 422 else: 423 find_regex += '(;)' 424 p = re.compile(find_regex, re.MULTILINE | re.DOTALL) 425 list = p.findall(content) 426 427 # build the function map with the function name as the key 428 result = [] 429 for retval, argval, vfmod, body in list: 430 if retval.find(ident) < 0: 431 # the identifier was not found 432 continue 433 434 # remove the identifier 435 retval = retval.replace(ident, '') 436 retval = retval.strip() 437 438 # Normalize the delimiter. 439 retval = retval.replace('\n', ' ') 440 441 # retrieve the function name 442 parts = retval.split(' ') 443 name = parts[-1] 444 del parts[-1] 445 retval = ' '.join(parts) 446 447 # parse the arguments 448 args = [] 449 for v in argval.split(','): 450 v = v.strip() 451 if len(v) > 0: 452 args.append(v) 453 454 result.append({ 455 'retval': retval.strip(), 456 'name': name, 457 'args': args, 458 'vfmod': vfmod.strip(), 459 'body': body if has_impl else '', 460 }) 461 462 return result 463 464 465def get_next_function_impl(existing, name): 466 result = None 467 for item in existing: 468 if item['name'] == name: 469 result = item 470 existing.remove(item) 471 break 472 return result 473 474 475def get_copyright(full=False, translator=True): 476 if full: 477 result = \ 478"""// Copyright (c) $YEAR$ Marshall A. Greenblatt. All rights reserved. 479// 480// Redistribution and use in source and binary forms, with or without 481// modification, are permitted provided that the following conditions are 482// met: 483// 484// * Redistributions of source code must retain the above copyright 485// notice, this list of conditions and the following disclaimer. 486// * Redistributions in binary form must reproduce the above 487// copyright notice, this list of conditions and the following disclaimer 488// in the documentation and/or other materials provided with the 489// distribution. 490// * Neither the name of Google Inc. nor the name Chromium Embedded 491// Framework nor the names of its contributors may be used to endorse 492// or promote products derived from this software without specific prior 493// written permission. 494// 495// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 496// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 497// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 498// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 499// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 500// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 501// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 502// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 503// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 504// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 505// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 506""" 507 else: 508 result = \ 509"""// Copyright (c) $YEAR$ The Chromium Embedded Framework Authors. All rights 510// reserved. Use of this source code is governed by a BSD-style license that 511// can be found in the LICENSE file. 512""" 513 514 if translator: 515 result += \ 516"""// 517// --------------------------------------------------------------------------- 518// 519// This file was generated by the CEF translator tool. If making changes by 520// hand only do so within the body of existing method and function 521// implementations. See the translator.README.txt file in the tools directory 522// for more information. 523// 524// $hash=$$HASH$$$ 525// 526 527""" 528 529 # add the copyright year 530 return result.replace('$YEAR$', get_year()) 531 532 533class obj_header: 534 """ Class representing a C++ header file. """ 535 536 def __init__(self): 537 self.filenames = [] 538 self.typedefs = [] 539 self.funcs = [] 540 self.classes = [] 541 self.root_directory = None 542 543 def set_root_directory(self, root_directory): 544 """ Set the root directory. """ 545 self.root_directory = root_directory 546 547 def get_root_directory(self): 548 """ Get the root directory. """ 549 return self.root_directory 550 551 def add_directory(self, directory, excluded_files=[]): 552 """ Add all header files from the specified directory. """ 553 files = get_files(os.path.join(directory, '*.h')) 554 for file in files: 555 if len(excluded_files) == 0 or \ 556 not os.path.split(file)[1] in excluded_files: 557 self.add_file(file) 558 559 def add_file(self, filepath): 560 """ Add a header file. """ 561 562 if self.root_directory is None: 563 filename = os.path.split(filepath)[1] 564 else: 565 filename = os.path.relpath(filepath, self.root_directory) 566 filename = filename.replace('\\', '/') 567 568 # read the input file into memory 569 self.add_data(filename, read_file(filepath)) 570 571 def add_data(self, filename, data): 572 """ Add header file contents. """ 573 574 added = False 575 576 # remove space from between template definition end brackets 577 data = data.replace("> >", ">>") 578 579 # extract global typedefs 580 p = re.compile('\ntypedef' + _cre_space + _cre_typedef + ';', 581 re.MULTILINE | re.DOTALL) 582 list = p.findall(data) 583 if len(list) > 0: 584 # build the global typedef objects 585 for value in list: 586 pos = value.rfind(' ') 587 if pos < 0: 588 raise Exception('Invalid typedef: ' + value) 589 alias = value[pos + 1:].strip() 590 value = value[:pos].strip() 591 self.typedefs.append(obj_typedef(self, filename, value, alias)) 592 593 # extract global functions 594 p = re.compile('\n' + _cre_attrib + '\n' + _cre_func + '\((.*?)\)', 595 re.MULTILINE | re.DOTALL) 596 list = p.findall(data) 597 if len(list) > 0: 598 added = True 599 600 # build the global function objects 601 for attrib, retval, argval in list: 602 comment = get_comment(data, retval + '(' + argval + ');') 603 validate_comment(filename, retval, comment) 604 self.funcs.append( 605 obj_function(self, filename, attrib, retval, argval, comment)) 606 607 # extract includes 608 p = re.compile('\n#include \"include/' + _cre_cfnameorpath + '.h') 609 includes = p.findall(data) 610 611 # extract forward declarations 612 p = re.compile('\nclass' + _cre_space + _cre_cfname + ';') 613 forward_declares = p.findall(data) 614 615 # extract empty classes 616 p = re.compile('\n' + _cre_attrib + '\nclass' + _cre_space + _cre_cfname + 617 _cre_space + ':' + _cre_space + 'public' + _cre_virtual + 618 _cre_space + _cre_cfname + _cre_space + '{};', 619 re.MULTILINE | re.DOTALL) 620 list = p.findall(data) 621 if len(list) > 0: 622 added = True 623 624 # build the class objects 625 for attrib, name, parent_name in list: 626 # Style may place the ':' on the next line. 627 comment = get_comment(data, name + ' :') 628 if len(comment) == 0: 629 comment = get_comment(data, name + "\n") 630 validate_comment(filename, name, comment) 631 self.classes.append( 632 obj_class(self, filename, attrib, name, parent_name, "", comment, 633 includes, forward_declares)) 634 635 # Remove empty classes from |data| so we don't mess up the non-empty 636 # class search that follows. 637 data = p.sub('', data) 638 639 # extract classes 640 p = re.compile('\n' + _cre_attrib + '\nclass' + _cre_space + _cre_cfname + 641 _cre_space + ':' + _cre_space + 'public' + _cre_virtual + 642 _cre_space + _cre_cfname + _cre_space + '{(.*?)\n};', 643 re.MULTILINE | re.DOTALL) 644 list = p.findall(data) 645 if len(list) > 0: 646 added = True 647 648 # build the class objects 649 for attrib, name, parent_name, body in list: 650 # Style may place the ':' on the next line. 651 comment = get_comment(data, name + ' :') 652 if len(comment) == 0: 653 comment = get_comment(data, name + "\n") 654 validate_comment(filename, name, comment) 655 self.classes.append( 656 obj_class(self, filename, attrib, name, parent_name, body, comment, 657 includes, forward_declares)) 658 659 if added: 660 # a global function or class was read from the header file 661 self.filenames.append(filename) 662 663 def __repr__(self): 664 result = '' 665 666 if len(self.typedefs) > 0: 667 strlist = [] 668 for cls in self.typedefs: 669 strlist.append(str(cls)) 670 result += "\n".join(strlist) + "\n\n" 671 672 if len(self.funcs) > 0: 673 strlist = [] 674 for cls in self.funcs: 675 strlist.append(str(cls)) 676 result += "\n".join(strlist) + "\n\n" 677 678 if len(self.classes) > 0: 679 strlist = [] 680 for cls in self.classes: 681 strlist.append(str(cls)) 682 result += "\n".join(strlist) 683 684 return result 685 686 def get_file_names(self): 687 """ Return the array of header file names. """ 688 return self.filenames 689 690 def get_typedefs(self): 691 """ Return the array of typedef objects. """ 692 return self.typedefs 693 694 def get_funcs(self, filename=None): 695 """ Return the array of function objects. """ 696 if filename is None: 697 return self.funcs 698 else: 699 # only return the functions in the specified file 700 res = [] 701 for func in self.funcs: 702 if func.get_file_name() == filename: 703 res.append(func) 704 return res 705 706 def get_classes(self, filename=None): 707 """ Return the array of class objects. """ 708 if filename is None: 709 return self.classes 710 else: 711 # only return the classes in the specified file 712 res = [] 713 for cls in self.classes: 714 if cls.get_file_name() == filename: 715 res.append(cls) 716 return res 717 718 def get_class(self, classname, defined_structs=None): 719 """ Return the specified class or None if not found. """ 720 for cls in self.classes: 721 if cls.get_name() == classname: 722 return cls 723 elif not defined_structs is None: 724 defined_structs.append(cls.get_capi_name()) 725 return None 726 727 def get_class_names(self): 728 """ Returns the names of all classes in this object. """ 729 result = [] 730 for cls in self.classes: 731 result.append(cls.get_name()) 732 return result 733 734 def get_base_class_name(self, classname): 735 """ Returns the base (root) class name for |classname|. """ 736 cur_cls = self.get_class(classname) 737 while True: 738 parent_name = cur_cls.get_parent_name() 739 if is_base_class(parent_name): 740 return parent_name 741 else: 742 parent_cls = self.get_class(parent_name) 743 if parent_cls is None: 744 break 745 cur_cls = self.get_class(parent_name) 746 return None 747 748 def get_types(self, list): 749 """ Return a dictionary mapping data types to analyzed values. """ 750 for cls in self.typedefs: 751 cls.get_types(list) 752 753 for cls in self.classes: 754 cls.get_types(list) 755 756 def get_alias_translation(self, alias): 757 """ Return a translation of alias to value based on typedef 758 statements. """ 759 for cls in self.typedefs: 760 if cls.alias == alias: 761 return cls.value 762 return None 763 764 def get_analysis(self, value, named=True): 765 """ Return an analysis of the value based the header file context. """ 766 return obj_analysis([self], value, named) 767 768 def get_defined_structs(self): 769 """ Return a list of already defined structure names. """ 770 return [ 771 'cef_print_info_t', 'cef_window_info_t', 'cef_base_ref_counted_t', 772 'cef_base_scoped_t' 773 ] 774 775 def get_capi_translations(self): 776 """ Return a dictionary that maps C++ terminology to C API terminology. 777 """ 778 # strings that will be changed in C++ comments 779 map = { 780 'class': 'structure', 781 'Class': 'Structure', 782 'interface': 'structure', 783 'Interface': 'Structure', 784 'true': 'true (1)', 785 'false': 'false (0)', 786 'empty': 'NULL', 787 'method': 'function' 788 } 789 790 # add mappings for all classes and functions 791 funcs = self.get_funcs() 792 for func in funcs: 793 map[func.get_name() + '()'] = func.get_capi_name() + '()' 794 795 classes = self.get_classes() 796 for cls in classes: 797 map[cls.get_name()] = cls.get_capi_name() 798 799 funcs = cls.get_virtual_funcs() 800 for func in funcs: 801 map[func.get_name() + '()'] = func.get_capi_name() + '()' 802 803 funcs = cls.get_static_funcs() 804 for func in funcs: 805 map[func.get_name() + '()'] = func.get_capi_name() + '()' 806 807 return map 808 809 810class obj_class: 811 """ Class representing a C++ class. """ 812 813 def __init__(self, parent, filename, attrib, name, parent_name, body, comment, 814 includes, forward_declares): 815 if not isinstance(parent, obj_header): 816 raise Exception('Invalid parent object type') 817 818 self.parent = parent 819 self.filename = filename 820 self.attribs = str_to_dict(attrib) 821 self.name = name 822 self.parent_name = parent_name 823 self.comment = comment 824 self.includes = includes 825 self.forward_declares = forward_declares 826 827 # extract typedefs 828 p = re.compile( 829 '\n' + _cre_space + 'typedef' + _cre_space + _cre_typedef + ';', 830 re.MULTILINE | re.DOTALL) 831 list = p.findall(body) 832 833 # build the typedef objects 834 self.typedefs = [] 835 for value in list: 836 pos = value.rfind(' ') 837 if pos < 0: 838 raise Exception('Invalid typedef: ' + value) 839 alias = value[pos + 1:].strip() 840 value = value[:pos].strip() 841 self.typedefs.append(obj_typedef(self, filename, value, alias)) 842 843 # extract static functions 844 p = re.compile('\n' + _cre_space + _cre_attrib + '\n' + _cre_space + 845 'static' + _cre_space + _cre_func + '\((.*?)\)', 846 re.MULTILINE | re.DOTALL) 847 list = p.findall(body) 848 849 # build the static function objects 850 self.staticfuncs = [] 851 for attrib, retval, argval in list: 852 comment = get_comment(body, retval + '(' + argval + ')') 853 validate_comment(filename, retval, comment) 854 self.staticfuncs.append( 855 obj_function_static(self, attrib, retval, argval, comment)) 856 857 # extract virtual functions 858 p = re.compile( 859 '\n' + _cre_space + _cre_attrib + '\n' + _cre_space + 'virtual' + 860 _cre_space + _cre_func + '\((.*?)\)' + _cre_vfmod, 861 re.MULTILINE | re.DOTALL) 862 list = p.findall(body) 863 864 # build the virtual function objects 865 self.virtualfuncs = [] 866 for attrib, retval, argval, vfmod in list: 867 comment = get_comment(body, retval + '(' + argval + ')') 868 validate_comment(filename, retval, comment) 869 self.virtualfuncs.append( 870 obj_function_virtual(self, attrib, retval, argval, comment, 871 vfmod.strip())) 872 873 def __repr__(self): 874 result = '/* ' + dict_to_str( 875 self.attribs) + ' */ class ' + self.name + "\n{" 876 877 if len(self.typedefs) > 0: 878 result += "\n\t" 879 strlist = [] 880 for cls in self.typedefs: 881 strlist.append(str(cls)) 882 result += "\n\t".join(strlist) 883 884 if len(self.staticfuncs) > 0: 885 result += "\n\t" 886 strlist = [] 887 for cls in self.staticfuncs: 888 strlist.append(str(cls)) 889 result += "\n\t".join(strlist) 890 891 if len(self.virtualfuncs) > 0: 892 result += "\n\t" 893 strlist = [] 894 for cls in self.virtualfuncs: 895 strlist.append(str(cls)) 896 result += "\n\t".join(strlist) 897 898 result += "\n};\n" 899 return result 900 901 def get_file_name(self): 902 """ Return the C++ header file name. Includes the directory component, 903 if any. """ 904 return self.filename 905 906 def get_capi_file_name(self): 907 """ Return the CAPI header file name. Includes the directory component, 908 if any. """ 909 return get_capi_file_name(self.filename) 910 911 def get_file_directory(self): 912 """ Return the file directory component, if any. """ 913 pos = self.filename.rfind('/') 914 if pos >= 0: 915 return self.filename[:pos] 916 return None 917 918 def get_name(self): 919 """ Return the class name. """ 920 return self.name 921 922 def get_capi_name(self): 923 """ Return the CAPI structure name for this class. """ 924 return get_capi_name(self.name, True) 925 926 def get_parent_name(self): 927 """ Return the parent class name. """ 928 return self.parent_name 929 930 def get_parent_capi_name(self): 931 """ Return the CAPI structure name for the parent class. """ 932 return get_capi_name(self.parent_name, True) 933 934 def has_parent(self, parent_name): 935 """ Returns true if this class has the specified class anywhere in its 936 inheritance hierarchy. """ 937 # Every class has a known base class as the top-most parent. 938 if is_base_class(parent_name) or parent_name == self.parent_name: 939 return True 940 if is_base_class(self.parent_name): 941 return False 942 943 cur_cls = self.parent.get_class(self.parent_name) 944 while True: 945 cur_parent_name = cur_cls.get_parent_name() 946 if is_base_class(cur_parent_name): 947 break 948 elif cur_parent_name == parent_name: 949 return True 950 cur_cls = self.parent.get_class(cur_parent_name) 951 952 return False 953 954 def get_comment(self): 955 """ Return the class comment as an array of lines. """ 956 return self.comment 957 958 def get_includes(self): 959 """ Return the list of classes that are included from this class' 960 header file. """ 961 return self.includes 962 963 def get_forward_declares(self): 964 """ Return the list of classes that are forward declared for this 965 class. """ 966 return self.forward_declares 967 968 def get_attribs(self): 969 """ Return all attributes as a dictionary. """ 970 return self.attribs 971 972 def has_attrib(self, name): 973 """ Return true if the specified attribute exists. """ 974 return name in self.attribs 975 976 def get_attrib(self, name): 977 """ Return the first or only value for specified attribute. """ 978 if name in self.attribs: 979 if isinstance(self.attribs[name], list): 980 # the value is a list 981 return self.attribs[name][0] 982 else: 983 # the value is a string 984 return self.attribs[name] 985 return None 986 987 def get_attrib_list(self, name): 988 """ Return all values for specified attribute as a list. """ 989 if name in self.attribs: 990 if isinstance(self.attribs[name], list): 991 # the value is already a list 992 return self.attribs[name] 993 else: 994 # convert the value to a list 995 return [self.attribs[name]] 996 return None 997 998 def get_typedefs(self): 999 """ Return the array of typedef objects. """ 1000 return self.typedefs 1001 1002 def has_typedef_alias(self, alias): 1003 """ Returns true if the specified typedef alias is defined in the scope 1004 of this class declaration. """ 1005 for typedef in self.typedefs: 1006 if typedef.get_alias() == alias: 1007 return True 1008 return False 1009 1010 def get_static_funcs(self): 1011 """ Return the array of static function objects. """ 1012 return self.staticfuncs 1013 1014 def get_virtual_funcs(self): 1015 """ Return the array of virtual function objects. """ 1016 return self.virtualfuncs 1017 1018 def get_types(self, list): 1019 """ Return a dictionary mapping data types to analyzed values. """ 1020 for cls in self.typedefs: 1021 cls.get_types(list) 1022 1023 for cls in self.staticfuncs: 1024 cls.get_types(list) 1025 1026 for cls in self.virtualfuncs: 1027 cls.get_types(list) 1028 1029 def get_alias_translation(self, alias): 1030 for cls in self.typedefs: 1031 if cls.alias == alias: 1032 return cls.value 1033 return None 1034 1035 def get_analysis(self, value, named=True): 1036 """ Return an analysis of the value based on the class definition 1037 context. 1038 """ 1039 return obj_analysis([self, self.parent], value, named) 1040 1041 def is_library_side(self): 1042 """ Returns true if the class is implemented by the library. """ 1043 return self.attribs['source'] == 'library' 1044 1045 def is_client_side(self): 1046 """ Returns true if the class is implemented by the client. """ 1047 return self.attribs['source'] == 'client' 1048 1049 1050class obj_typedef: 1051 """ Class representing a typedef statement. """ 1052 1053 def __init__(self, parent, filename, value, alias): 1054 if not isinstance(parent, obj_header) \ 1055 and not isinstance(parent, obj_class): 1056 raise Exception('Invalid parent object type') 1057 1058 self.parent = parent 1059 self.filename = filename 1060 self.alias = alias 1061 self.value = self.parent.get_analysis(value, False) 1062 1063 def __repr__(self): 1064 return 'typedef ' + self.value.get_type() + ' ' + self.alias + ';' 1065 1066 def get_file_name(self): 1067 """ Return the C++ header file name. """ 1068 return self.filename 1069 1070 def get_capi_file_name(self): 1071 """ Return the CAPI header file name. """ 1072 return get_capi_file_name(self.filename) 1073 1074 def get_alias(self): 1075 """ Return the alias. """ 1076 return self.alias 1077 1078 def get_value(self): 1079 """ Return an analysis of the value based on the class or header file 1080 definition context. 1081 """ 1082 return self.value 1083 1084 def get_types(self, list): 1085 """ Return a dictionary mapping data types to analyzed values. """ 1086 name = self.value.get_type() 1087 if not name in list: 1088 list[name] = self.value 1089 1090 1091class obj_function: 1092 """ Class representing a function. """ 1093 1094 def __init__(self, parent, filename, attrib, retval, argval, comment): 1095 self.parent = parent 1096 self.filename = filename 1097 self.attribs = str_to_dict(attrib) 1098 self.retval = obj_argument(self, retval) 1099 self.name = self.retval.remove_name() 1100 self.comment = comment 1101 1102 # build the argument objects 1103 self.arguments = [] 1104 arglist = argval.split(',') 1105 argindex = 0 1106 while argindex < len(arglist): 1107 arg = arglist[argindex] 1108 if arg.find('<') >= 0 and arg.find('>') == -1: 1109 # We've split inside of a template type declaration. Join the 1110 # next argument with this argument. 1111 argindex += 1 1112 arg += ',' + arglist[argindex] 1113 1114 arg = arg.strip() 1115 if len(arg) > 0: 1116 argument = obj_argument(self, arg) 1117 if argument.needs_attrib_count_func() and \ 1118 argument.get_attrib_count_func() is None: 1119 raise Exception("A 'count_func' attribute is required "+ \ 1120 "for the '"+argument.get_name()+ \ 1121 "' parameter to "+self.get_qualified_name()) 1122 self.arguments.append(argument) 1123 1124 argindex += 1 1125 1126 if self.retval.needs_attrib_default_retval() and \ 1127 self.retval.get_attrib_default_retval() is None: 1128 raise Exception("A 'default_retval' attribute is required for "+ \ 1129 self.get_qualified_name()) 1130 1131 def __repr__(self): 1132 return '/* ' + dict_to_str(self.attribs) + ' */ ' + self.get_cpp_proto() 1133 1134 def get_file_name(self): 1135 """ Return the C++ header file name. """ 1136 return self.filename 1137 1138 def get_capi_file_name(self): 1139 """ Return the CAPI header file name. """ 1140 return get_capi_file_name(self.filename) 1141 1142 def get_name(self): 1143 """ Return the function name. """ 1144 return self.name 1145 1146 def get_qualified_name(self): 1147 """ Return the fully qualified function name. """ 1148 if isinstance(self.parent, obj_header): 1149 # global function 1150 return self.name 1151 else: 1152 # member function 1153 return self.parent.get_name() + '::' + self.name 1154 1155 def get_capi_name(self, prefix=None): 1156 """ Return the CAPI function name. """ 1157 if 'capi_name' in self.attribs: 1158 return self.attribs['capi_name'] 1159 return get_capi_name(self.name, False, prefix) 1160 1161 def get_comment(self): 1162 """ Return the function comment as an array of lines. """ 1163 return self.comment 1164 1165 def get_attribs(self): 1166 """ Return all attributes as a dictionary. """ 1167 return self.attribs 1168 1169 def has_attrib(self, name): 1170 """ Return true if the specified attribute exists. """ 1171 return name in self.attribs 1172 1173 def get_attrib(self, name): 1174 """ Return the first or only value for specified attribute. """ 1175 if name in self.attribs: 1176 if isinstance(self.attribs[name], list): 1177 # the value is a list 1178 return self.attribs[name][0] 1179 else: 1180 # the value is a string 1181 return self.attribs[name] 1182 return None 1183 1184 def get_attrib_list(self, name): 1185 """ Return all values for specified attribute as a list. """ 1186 if name in self.attribs: 1187 if isinstance(self.attribs[name], list): 1188 # the value is already a list 1189 return self.attribs[name] 1190 else: 1191 # convert the value to a list 1192 return [self.attribs[name]] 1193 return None 1194 1195 def get_retval(self): 1196 """ Return the return value object. """ 1197 return self.retval 1198 1199 def get_arguments(self): 1200 """ Return the argument array. """ 1201 return self.arguments 1202 1203 def get_types(self, list): 1204 """ Return a dictionary mapping data types to analyzed values. """ 1205 for cls in self.arguments: 1206 cls.get_types(list) 1207 1208 def get_capi_parts(self, defined_structs=[], prefix=None): 1209 """ Return the parts of the C API function definition. """ 1210 retval = '' 1211 dict = self.retval.get_type().get_capi(defined_structs) 1212 if dict['format'] == 'single': 1213 retval = dict['value'] 1214 1215 name = self.get_capi_name(prefix) 1216 args = [] 1217 1218 if isinstance(self, obj_function_virtual): 1219 # virtual functions get themselves as the first argument 1220 str = 'struct _' + self.parent.get_capi_name() + '* self' 1221 if isinstance(self, obj_function_virtual) and self.is_const(): 1222 # const virtual functions get const self pointers 1223 str = 'const ' + str 1224 args.append(str) 1225 1226 if len(self.arguments) > 0: 1227 for cls in self.arguments: 1228 type = cls.get_type() 1229 dict = type.get_capi(defined_structs) 1230 if dict['format'] == 'single': 1231 args.append(dict['value']) 1232 elif dict['format'] == 'multi-arg': 1233 # add an additional argument for the size of the array 1234 type_name = type.get_name() 1235 if type.is_const(): 1236 # for const arrays pass the size argument by value 1237 args.append('size_t ' + type_name + 'Count') 1238 else: 1239 # for non-const arrays pass the size argument by address 1240 args.append('size_t* ' + type_name + 'Count') 1241 args.append(dict['value']) 1242 1243 return {'retval': retval, 'name': name, 'args': args} 1244 1245 def get_capi_proto(self, defined_structs=[], prefix=None): 1246 """ Return the prototype of the C API function. """ 1247 parts = self.get_capi_parts(defined_structs, prefix) 1248 result = parts['retval']+' '+parts['name']+ \ 1249 '('+', '.join(parts['args'])+')' 1250 return result 1251 1252 def get_cpp_parts(self, isimpl=False): 1253 """ Return the parts of the C++ function definition. """ 1254 retval = str(self.retval) 1255 name = self.name 1256 1257 args = [] 1258 if len(self.arguments) > 0: 1259 for cls in self.arguments: 1260 args.append(str(cls)) 1261 1262 if isimpl and isinstance(self, obj_function_virtual): 1263 # enumeration return values must be qualified with the class name 1264 # if the type is defined in the class declaration scope. 1265 type = self.get_retval().get_type() 1266 if type.is_result_struct() and type.is_result_struct_enum() and \ 1267 self.parent.has_typedef_alias(retval): 1268 retval = self.parent.get_name() + '::' + retval 1269 1270 return {'retval': retval, 'name': name, 'args': args} 1271 1272 def get_cpp_proto(self, classname=None): 1273 """ Return the prototype of the C++ function. """ 1274 parts = self.get_cpp_parts() 1275 result = parts['retval'] + ' ' 1276 if not classname is None: 1277 result += classname + '::' 1278 result += parts['name'] + '(' + ', '.join(parts['args']) + ')' 1279 if isinstance(self, obj_function_virtual) and self.is_const(): 1280 result += ' const' 1281 return result 1282 1283 def is_same_side(self, other_class_name): 1284 """ Returns true if this function is on the same side (library or 1285 client) and the specified class. """ 1286 if isinstance(self.parent, obj_class): 1287 # this function is part of a class 1288 this_is_library_side = self.parent.is_library_side() 1289 header = self.parent.parent 1290 else: 1291 # this function is global 1292 this_is_library_side = True 1293 header = self.parent 1294 1295 if is_base_class(other_class_name): 1296 other_is_library_side = False 1297 else: 1298 other_class = header.get_class(other_class_name) 1299 if other_class is None: 1300 raise Exception('Unknown class: ' + other_class_name) 1301 other_is_library_side = other_class.is_library_side() 1302 1303 return other_is_library_side == this_is_library_side 1304 1305 1306class obj_function_static(obj_function): 1307 """ Class representing a static function. """ 1308 1309 def __init__(self, parent, attrib, retval, argval, comment): 1310 if not isinstance(parent, obj_class): 1311 raise Exception('Invalid parent object type') 1312 obj_function.__init__(self, parent, parent.filename, attrib, retval, argval, 1313 comment) 1314 1315 def __repr__(self): 1316 return 'static ' + obj_function.__repr__(self) + ';' 1317 1318 def get_capi_name(self, prefix=None): 1319 """ Return the CAPI function name. """ 1320 if prefix is None: 1321 # by default static functions are prefixed with the class name 1322 prefix = get_capi_name(self.parent.get_name(), False) 1323 return obj_function.get_capi_name(self, prefix) 1324 1325 1326class obj_function_virtual(obj_function): 1327 """ Class representing a virtual function. """ 1328 1329 def __init__(self, parent, attrib, retval, argval, comment, vfmod): 1330 if not isinstance(parent, obj_class): 1331 raise Exception('Invalid parent object type') 1332 obj_function.__init__(self, parent, parent.filename, attrib, retval, argval, 1333 comment) 1334 if vfmod == 'const': 1335 self.isconst = True 1336 else: 1337 self.isconst = False 1338 1339 def __repr__(self): 1340 return 'virtual ' + obj_function.__repr__(self) + ';' 1341 1342 def is_const(self): 1343 """ Returns true if the method declaration is const. """ 1344 return self.isconst 1345 1346 1347class obj_argument: 1348 """ Class representing a function argument. """ 1349 1350 def __init__(self, parent, argval): 1351 if not isinstance(parent, obj_function): 1352 raise Exception('Invalid parent object type') 1353 1354 self.parent = parent 1355 self.type = self.parent.parent.get_analysis(argval) 1356 1357 def __repr__(self): 1358 result = '' 1359 if self.type.is_const(): 1360 result += 'const ' 1361 result += self.type.get_type() 1362 if self.type.is_byref(): 1363 result += '&' 1364 elif self.type.is_byaddr(): 1365 result += '*' 1366 if self.type.has_name(): 1367 result += ' ' + self.type.get_name() 1368 return result 1369 1370 def get_name(self): 1371 """ Return the name for this argument. """ 1372 return self.type.get_name() 1373 1374 def remove_name(self): 1375 """ Remove and return the name value. """ 1376 name = self.type.get_name() 1377 self.type.name = None 1378 return name 1379 1380 def get_type(self): 1381 """ Return an analysis of the argument type based on the class 1382 definition context. 1383 """ 1384 return self.type 1385 1386 def get_types(self, list): 1387 """ Return a dictionary mapping data types to analyzed values. """ 1388 name = self.type.get_type() 1389 if not name in list: 1390 list[name] = self.type 1391 1392 def needs_attrib_count_func(self): 1393 """ Returns true if this argument requires a 'count_func' attribute. """ 1394 # A 'count_func' attribute is required for non-const non-string vector 1395 # attribute types 1396 return self.type.has_name() and \ 1397 self.type.is_result_vector() and \ 1398 not self.type.is_result_vector_string() and \ 1399 not self.type.is_const() 1400 1401 def get_attrib_count_func(self): 1402 """ Returns the count function for this argument. """ 1403 # The 'count_func' attribute value format is name:function 1404 if not self.parent.has_attrib('count_func'): 1405 return None 1406 name = self.type.get_name() 1407 vals = self.parent.get_attrib_list('count_func') 1408 for val in vals: 1409 parts = val.split(':') 1410 if len(parts) != 2: 1411 raise Exception("Invalid 'count_func' attribute value for "+ \ 1412 self.parent.get_qualified_name()+': '+val) 1413 if parts[0].strip() == name: 1414 return parts[1].strip() 1415 return None 1416 1417 def needs_attrib_default_retval(self): 1418 """ Returns true if this argument requires a 'default_retval' attribute. 1419 """ 1420 # A 'default_retval' attribute is required for enumeration return value 1421 # types. 1422 return not self.type.has_name() and \ 1423 self.type.is_result_struct() and \ 1424 self.type.is_result_struct_enum() 1425 1426 def get_attrib_default_retval(self): 1427 """ Returns the defualt return value for this argument. """ 1428 return self.parent.get_attrib('default_retval') 1429 1430 def get_arg_type(self): 1431 """ Returns the argument type as defined in translator.README.txt. """ 1432 if not self.type.has_name(): 1433 raise Exception('Cannot be called for retval types') 1434 1435 # simple or enumeration type 1436 if (self.type.is_result_simple() and \ 1437 self.type.get_type() != 'bool') or \ 1438 (self.type.is_result_struct() and \ 1439 self.type.is_result_struct_enum()): 1440 if self.type.is_byref(): 1441 if self.type.is_const(): 1442 return 'simple_byref_const' 1443 return 'simple_byref' 1444 elif self.type.is_byaddr(): 1445 return 'simple_byaddr' 1446 return 'simple_byval' 1447 1448 # boolean type 1449 if self.type.get_type() == 'bool': 1450 if self.type.is_byref(): 1451 return 'bool_byref' 1452 elif self.type.is_byaddr(): 1453 return 'bool_byaddr' 1454 return 'bool_byval' 1455 1456 # structure type 1457 if self.type.is_result_struct() and self.type.is_byref(): 1458 if self.type.is_const(): 1459 return 'struct_byref_const' 1460 return 'struct_byref' 1461 1462 # string type 1463 if self.type.is_result_string() and self.type.is_byref(): 1464 if self.type.is_const(): 1465 return 'string_byref_const' 1466 return 'string_byref' 1467 1468 # *ptr type 1469 if self.type.is_result_ptr(): 1470 prefix = self.type.get_result_ptr_type_prefix() 1471 same_side = self.parent.is_same_side(self.type.get_ptr_type()) 1472 if self.type.is_byref(): 1473 if same_side: 1474 return prefix + 'ptr_same_byref' 1475 return prefix + 'ptr_diff_byref' 1476 if same_side: 1477 return prefix + 'ptr_same' 1478 return prefix + 'ptr_diff' 1479 1480 if self.type.is_result_vector(): 1481 # all vector types must be passed by reference 1482 if not self.type.is_byref(): 1483 return 'invalid' 1484 1485 if self.type.is_result_vector_string(): 1486 # string vector type 1487 if self.type.is_const(): 1488 return 'string_vec_byref_const' 1489 return 'string_vec_byref' 1490 1491 if self.type.is_result_vector_simple(): 1492 if self.type.get_vector_type() != 'bool': 1493 # simple/enumeration vector types 1494 if self.type.is_const(): 1495 return 'simple_vec_byref_const' 1496 return 'simple_vec_byref' 1497 1498 # boolean vector types 1499 if self.type.is_const(): 1500 return 'bool_vec_byref_const' 1501 return 'bool_vec_byref' 1502 1503 if self.type.is_result_vector_ptr(): 1504 # *ptr vector types 1505 prefix = self.type.get_result_vector_ptr_type_prefix() 1506 same_side = self.parent.is_same_side(self.type.get_ptr_type()) 1507 if self.type.is_const(): 1508 if same_side: 1509 return prefix + 'ptr_vec_same_byref_const' 1510 return prefix + 'ptr_vec_diff_byref_const' 1511 if same_side: 1512 return prefix + 'ptr_vec_same_byref' 1513 return prefix + 'ptr_vec_diff_byref' 1514 1515 # string single map type 1516 if self.type.is_result_map_single(): 1517 if not self.type.is_byref(): 1518 return 'invalid' 1519 if self.type.is_const(): 1520 return 'string_map_single_byref_const' 1521 return 'string_map_single_byref' 1522 1523 # string multi map type 1524 if self.type.is_result_map_multi(): 1525 if not self.type.is_byref(): 1526 return 'invalid' 1527 if self.type.is_const(): 1528 return 'string_map_multi_byref_const' 1529 return 'string_map_multi_byref' 1530 1531 return 'invalid' 1532 1533 def get_retval_type(self): 1534 """ Returns the retval type as defined in translator.README.txt. """ 1535 if self.type.has_name(): 1536 raise Exception('Cannot be called for argument types') 1537 1538 # unsupported modifiers 1539 if self.type.is_const() or self.type.is_byref() or \ 1540 self.type.is_byaddr(): 1541 return 'invalid' 1542 1543 # void types don't have a return value 1544 if self.type.get_type() == 'void': 1545 return 'none' 1546 1547 if (self.type.is_result_simple() and \ 1548 self.type.get_type() != 'bool') or \ 1549 (self.type.is_result_struct() and self.type.is_result_struct_enum()): 1550 return 'simple' 1551 1552 if self.type.get_type() == 'bool': 1553 return 'bool' 1554 1555 if self.type.is_result_string(): 1556 return 'string' 1557 1558 if self.type.is_result_ptr(): 1559 prefix = self.type.get_result_ptr_type_prefix() 1560 if self.parent.is_same_side(self.type.get_ptr_type()): 1561 return prefix + 'ptr_same' 1562 else: 1563 return prefix + 'ptr_diff' 1564 1565 return 'invalid' 1566 1567 def get_retval_default(self, for_capi): 1568 """ Returns the default return value based on the retval type. """ 1569 # start with the default retval attribute, if any. 1570 retval = self.get_attrib_default_retval() 1571 if not retval is None: 1572 if for_capi: 1573 # apply any appropriate C API translations. 1574 if retval == 'true': 1575 return '1' 1576 if retval == 'false': 1577 return '0' 1578 return retval 1579 1580 # next look at the retval type value. 1581 type = self.get_retval_type() 1582 if type == 'simple': 1583 return self.get_type().get_result_simple_default() 1584 elif type == 'bool': 1585 if for_capi: 1586 return '0' 1587 return 'false' 1588 elif type == 'string': 1589 if for_capi: 1590 return 'NULL' 1591 return 'CefString()' 1592 elif type == 'refptr_same' or type == 'refptr_diff' or \ 1593 type == 'rawptr_same' or type == 'rawptr_diff': 1594 if for_capi: 1595 return 'NULL' 1596 return 'nullptr' 1597 elif type == 'ownptr_same' or type == 'ownptr_diff': 1598 if for_capi: 1599 return 'NULL' 1600 return 'CefOwnPtr<' + self.type.get_ptr_type() + '>()' 1601 1602 return '' 1603 1604 1605class obj_analysis: 1606 """ Class representing an analysis of a data type value. """ 1607 1608 def __init__(self, scopelist, value, named): 1609 self.value = value 1610 self.result_type = 'unknown' 1611 self.result_value = None 1612 self.result_default = None 1613 self.ptr_type = None 1614 1615 # parse the argument string 1616 partlist = value.strip().split() 1617 1618 if named == True: 1619 # extract the name value 1620 self.name = partlist[-1] 1621 del partlist[-1] 1622 else: 1623 self.name = None 1624 1625 if len(partlist) == 0: 1626 raise Exception('Invalid argument value: ' + value) 1627 1628 # check const status 1629 if partlist[0] == 'const': 1630 self.isconst = True 1631 del partlist[0] 1632 else: 1633 self.isconst = False 1634 1635 if len(partlist) == 0: 1636 raise Exception('Invalid argument value: ' + value) 1637 1638 # combine the data type 1639 self.type = ' '.join(partlist) 1640 1641 # extract the last character of the data type 1642 endchar = self.type[-1] 1643 1644 # check if the value is passed by reference 1645 if endchar == '&': 1646 self.isbyref = True 1647 self.type = self.type[:-1] 1648 else: 1649 self.isbyref = False 1650 1651 # check if the value is passed by address 1652 if endchar == '*': 1653 self.isbyaddr = True 1654 self.type = self.type[:-1] 1655 else: 1656 self.isbyaddr = False 1657 1658 # see if the value is directly identifiable 1659 if self._check_advanced(self.type) == True: 1660 return 1661 1662 # not identifiable, so look it up 1663 translation = None 1664 for scope in scopelist: 1665 if not isinstance(scope, obj_header) \ 1666 and not isinstance(scope, obj_class): 1667 raise Exception('Invalid scope object type') 1668 translation = scope.get_alias_translation(self.type) 1669 if not translation is None: 1670 break 1671 1672 if translation is None: 1673 raise Exception('Failed to translate type: ' + self.type) 1674 1675 # the translation succeeded so keep the result 1676 self.result_type = translation.result_type 1677 self.result_value = translation.result_value 1678 1679 def _check_advanced(self, value): 1680 # check for vectors 1681 if value.find('std::vector') == 0: 1682 self.result_type = 'vector' 1683 val = value[12:-1].strip() 1684 self.result_value = [self._get_basic(val)] 1685 self.result_value[0]['vector_type'] = val 1686 return True 1687 1688 # check for maps 1689 if value.find('std::map') == 0: 1690 self.result_type = 'map' 1691 vals = value[9:-1].split(',') 1692 if len(vals) == 2: 1693 self.result_value = [ 1694 self._get_basic(vals[0].strip()), 1695 self._get_basic(vals[1].strip()) 1696 ] 1697 return True 1698 1699 # check for multimaps 1700 if value.find('std::multimap') == 0: 1701 self.result_type = 'multimap' 1702 vals = value[14:-1].split(',') 1703 if len(vals) == 2: 1704 self.result_value = [ 1705 self._get_basic(vals[0].strip()), 1706 self._get_basic(vals[1].strip()) 1707 ] 1708 return True 1709 1710 # check for basic types 1711 basic = self._get_basic(value) 1712 if not basic is None: 1713 self.result_type = basic['result_type'] 1714 self.result_value = basic['result_value'] 1715 if 'ptr_type' in basic: 1716 self.ptr_type = basic['ptr_type'] 1717 if 'result_default' in basic: 1718 self.result_default = basic['result_default'] 1719 return True 1720 1721 return False 1722 1723 def _get_basic(self, value): 1724 # check for string values 1725 if value == "CefString": 1726 return {'result_type': 'string', 'result_value': None} 1727 1728 # check for simple direct translations 1729 if value in _simpletypes.keys(): 1730 return { 1731 'result_type': 'simple', 1732 'result_value': _simpletypes[value][0], 1733 'result_default': _simpletypes[value][1], 1734 } 1735 1736 # check if already a C API structure 1737 if value[-2:] == '_t': 1738 return {'result_type': 'structure', 'result_value': value} 1739 1740 # check for CEF reference pointers 1741 p = re.compile('^CefRefPtr<(.*?)>$', re.DOTALL) 1742 list = p.findall(value) 1743 if len(list) == 1: 1744 return { 1745 'result_type': 'refptr', 1746 'result_value': get_capi_name(list[0], True) + '*', 1747 'ptr_type': list[0] 1748 } 1749 1750 # check for CEF owned pointers 1751 p = re.compile('^CefOwnPtr<(.*?)>$', re.DOTALL) 1752 list = p.findall(value) 1753 if len(list) == 1: 1754 return { 1755 'result_type': 'ownptr', 1756 'result_value': get_capi_name(list[0], True) + '*', 1757 'ptr_type': list[0] 1758 } 1759 1760 # check for CEF raw pointers 1761 p = re.compile('^CefRawPtr<(.*?)>$', re.DOTALL) 1762 list = p.findall(value) 1763 if len(list) == 1: 1764 return { 1765 'result_type': 'rawptr', 1766 'result_value': get_capi_name(list[0], True) + '*', 1767 'ptr_type': list[0] 1768 } 1769 1770 # check for CEF structure types 1771 if value[0:3] == 'Cef' and value[-4:] != 'List': 1772 return { 1773 'result_type': 'structure', 1774 'result_value': get_capi_name(value, True) 1775 } 1776 1777 return None 1778 1779 def __repr__(self): 1780 return '(' + self.result_type + ') ' + str(self.result_value) 1781 1782 def has_name(self): 1783 """ Returns true if a name value exists. """ 1784 return (not self.name is None) 1785 1786 def get_name(self): 1787 """ Return the name. """ 1788 return self.name 1789 1790 def get_value(self): 1791 """ Return the C++ value (type + name). """ 1792 return self.value 1793 1794 def get_type(self): 1795 """ Return the C++ type. """ 1796 return self.type 1797 1798 def get_ptr_type(self): 1799 """ Return the C++ class type referenced by a CefRefPtr. """ 1800 if self.is_result_vector() and self.is_result_vector_ptr(): 1801 # return the vector RefPtr type 1802 return self.result_value[0]['ptr_type'] 1803 # return the basic RefPtr type 1804 return self.ptr_type 1805 1806 def get_vector_type(self): 1807 """ Return the C++ class type referenced by a std::vector. """ 1808 if self.is_result_vector(): 1809 return self.result_value[0]['vector_type'] 1810 return None 1811 1812 def is_const(self): 1813 """ Returns true if the argument value is constant. """ 1814 return self.isconst 1815 1816 def is_byref(self): 1817 """ Returns true if the argument is passed by reference. """ 1818 return self.isbyref 1819 1820 def is_byaddr(self): 1821 """ Returns true if the argument is passed by address. """ 1822 return self.isbyaddr 1823 1824 def is_result_simple(self): 1825 """ Returns true if this is a simple argument type. """ 1826 return (self.result_type == 'simple') 1827 1828 def get_result_simple_type_root(self): 1829 """ Return the simple structure or basic type name. """ 1830 return self.result_value 1831 1832 def get_result_simple_type(self): 1833 """ Return the simple type. """ 1834 result = '' 1835 if self.is_const(): 1836 result += 'const ' 1837 result += self.result_value 1838 if self.is_byaddr() or self.is_byref(): 1839 result += '*' 1840 return result 1841 1842 def get_result_simple_default(self): 1843 """ Return the default value fo the basic type. """ 1844 return self.result_default 1845 1846 def is_result_ptr(self): 1847 """ Returns true if this is a *Ptr type. """ 1848 return self.is_result_refptr() or self.is_result_ownptr() or \ 1849 self.is_result_rawptr() 1850 1851 def get_result_ptr_type_root(self): 1852 """ Return the *Ptr type structure name. """ 1853 return self.result_value[:-1] 1854 1855 def get_result_ptr_type(self, defined_structs=[]): 1856 """ Return the *Ptr type. """ 1857 result = '' 1858 if not self.result_value[:-1] in defined_structs: 1859 result += 'struct _' 1860 result += self.result_value 1861 if self.is_byref() or self.is_byaddr(): 1862 result += '*' 1863 return result 1864 1865 def get_result_ptr_type_prefix(self): 1866 """ Returns the *Ptr type prefix. """ 1867 if self.is_result_refptr(): 1868 return 'ref' 1869 if self.is_result_ownptr(): 1870 return 'own' 1871 if self.is_result_rawptr(): 1872 return 'raw' 1873 raise Exception('Not a pointer type') 1874 1875 def is_result_refptr(self): 1876 """ Returns true if this is a RefPtr type. """ 1877 return (self.result_type == 'refptr') 1878 1879 def is_result_ownptr(self): 1880 """ Returns true if this is a OwnPtr type. """ 1881 return (self.result_type == 'ownptr') 1882 1883 def is_result_rawptr(self): 1884 """ Returns true if this is a RawPtr type. """ 1885 return (self.result_type == 'rawptr') 1886 1887 def is_result_struct(self): 1888 """ Returns true if this is a structure type. """ 1889 return (self.result_type == 'structure') 1890 1891 def is_result_struct_enum(self): 1892 """ Returns true if this struct type is likely an enumeration. """ 1893 # structure values that are passed by reference or address must be 1894 # structures and not enumerations 1895 if not self.is_byref() and not self.is_byaddr(): 1896 return True 1897 return False 1898 1899 def get_result_struct_type(self, defined_structs=[]): 1900 """ Return the structure or enumeration type. """ 1901 result = '' 1902 is_enum = self.is_result_struct_enum() 1903 if not is_enum: 1904 if self.is_const(): 1905 result += 'const ' 1906 if not self.result_value in defined_structs: 1907 result += 'struct _' 1908 result += self.result_value 1909 if not is_enum: 1910 result += '*' 1911 return result 1912 1913 def is_result_string(self): 1914 """ Returns true if this is a string type. """ 1915 return (self.result_type == 'string') 1916 1917 def get_result_string_type(self): 1918 """ Return the string type. """ 1919 if not self.has_name(): 1920 # Return values are string structs that the user must free. Use 1921 # the name of the structure as a hint. 1922 return 'cef_string_userfree_t' 1923 elif not self.is_const() and (self.is_byref() or self.is_byaddr()): 1924 # Parameters passed by reference or address. Use the normal 1925 # non-const string struct. 1926 return 'cef_string_t*' 1927 # Const parameters use the const string struct. 1928 return 'const cef_string_t*' 1929 1930 def is_result_vector(self): 1931 """ Returns true if this is a vector type. """ 1932 return (self.result_type == 'vector') 1933 1934 def is_result_vector_string(self): 1935 """ Returns true if this is a string vector. """ 1936 return self.result_value[0]['result_type'] == 'string' 1937 1938 def is_result_vector_simple(self): 1939 """ Returns true if this is a string vector. """ 1940 return self.result_value[0]['result_type'] == 'simple' 1941 1942 def is_result_vector_ptr(self): 1943 """ Returns true if this is a *Ptr vector. """ 1944 return self.is_result_vector_refptr() or \ 1945 self.is_result_vector_ownptr() or \ 1946 self.is_result_vector_rawptr() 1947 1948 def get_result_vector_ptr_type_prefix(self): 1949 """ Returns the *Ptr type prefix. """ 1950 if self.is_result_vector_refptr(): 1951 return 'ref' 1952 if self.is_result_vector_ownptr(): 1953 return 'own' 1954 if self.is_result_vector_rawptr(): 1955 return 'raw' 1956 raise Exception('Not a pointer type') 1957 1958 def is_result_vector_refptr(self): 1959 """ Returns true if this is a RefPtr vector. """ 1960 return self.result_value[0]['result_type'] == 'refptr' 1961 1962 def is_result_vector_ownptr(self): 1963 """ Returns true if this is a OwnPtr vector. """ 1964 return self.result_value[0]['result_type'] == 'ownptr' 1965 1966 def is_result_vector_rawptr(self): 1967 """ Returns true if this is a RawPtr vector. """ 1968 return self.result_value[0]['result_type'] == 'rawptr' 1969 1970 def get_result_vector_type_root(self): 1971 """ Return the vector structure or basic type name. """ 1972 return self.result_value[0]['result_value'] 1973 1974 def get_result_vector_type(self, defined_structs=[]): 1975 """ Return the vector type. """ 1976 if not self.has_name(): 1977 raise Exception('Cannot use vector as a return type') 1978 1979 type = self.result_value[0]['result_type'] 1980 value = self.result_value[0]['result_value'] 1981 1982 result = {} 1983 if type == 'string': 1984 result['value'] = 'cef_string_list_t' 1985 result['format'] = 'single' 1986 return result 1987 1988 if type == 'simple': 1989 str = value 1990 if self.is_const(): 1991 str += ' const' 1992 str += '*' 1993 result['value'] = str 1994 elif type == 'refptr' or type == 'ownptr' or type == 'rawptr': 1995 str = '' 1996 if not value[:-1] in defined_structs: 1997 str += 'struct _' 1998 str += value 1999 if self.is_const(): 2000 str += ' const' 2001 str += '*' 2002 result['value'] = str 2003 else: 2004 raise Exception('Unsupported vector type: ' + type) 2005 2006 # vector values must be passed as a value array parameter 2007 # and a size parameter 2008 result['format'] = 'multi-arg' 2009 return result 2010 2011 def is_result_map(self): 2012 """ Returns true if this is a map type. """ 2013 return (self.result_type == 'map' or self.result_type == 'multimap') 2014 2015 def is_result_map_single(self): 2016 """ Returns true if this is a single map type. """ 2017 return (self.result_type == 'map') 2018 2019 def is_result_map_multi(self): 2020 """ Returns true if this is a multi map type. """ 2021 return (self.result_type == 'multimap') 2022 2023 def get_result_map_type(self, defined_structs=[]): 2024 """ Return the map type. """ 2025 if not self.has_name(): 2026 raise Exception('Cannot use map as a return type') 2027 if self.result_value[0]['result_type'] == 'string' \ 2028 and self.result_value[1]['result_type'] == 'string': 2029 if self.result_type == 'map': 2030 return {'value': 'cef_string_map_t', 'format': 'single'} 2031 elif self.result_type == 'multimap': 2032 return {'value': 'cef_string_multimap_t', 'format': 'multi'} 2033 raise Exception('Only mappings of strings to strings are supported') 2034 2035 def get_capi(self, defined_structs=[]): 2036 """ Format the value for the C API. """ 2037 result = '' 2038 format = 'single' 2039 if self.is_result_simple(): 2040 result += self.get_result_simple_type() 2041 elif self.is_result_ptr(): 2042 result += self.get_result_ptr_type(defined_structs) 2043 elif self.is_result_struct(): 2044 result += self.get_result_struct_type(defined_structs) 2045 elif self.is_result_string(): 2046 result += self.get_result_string_type() 2047 elif self.is_result_map(): 2048 resdict = self.get_result_map_type(defined_structs) 2049 if resdict['format'] == 'single' or resdict['format'] == 'multi': 2050 result += resdict['value'] 2051 else: 2052 raise Exception('Unsupported map type') 2053 elif self.is_result_vector(): 2054 resdict = self.get_result_vector_type(defined_structs) 2055 if resdict['format'] != 'single': 2056 format = resdict['format'] 2057 result += resdict['value'] 2058 2059 if self.has_name(): 2060 result += ' ' + self.get_name() 2061 2062 return {'format': format, 'value': result} 2063 2064 2065# test the module 2066if __name__ == "__main__": 2067 import pprint 2068 import sys 2069 2070 # verify that the correct number of command-line arguments are provided 2071 if len(sys.argv) != 2: 2072 sys.stderr.write('Usage: ' + sys.argv[0] + ' <directory>') 2073 sys.exit() 2074 2075 pp = pprint.PrettyPrinter(indent=4) 2076 2077 # create the header object 2078 header = obj_header() 2079 header.add_directory(sys.argv[1]) 2080 2081 # output the type mapping 2082 types = {} 2083 header.get_types(types) 2084 pp.pprint(types) 2085 sys.stdout.write('\n') 2086 2087 # output the parsed C++ data 2088 sys.stdout.write(str(header)) 2089 2090 # output the C API formatted data 2091 defined_names = header.get_defined_structs() 2092 result = '' 2093 2094 # global functions 2095 funcs = header.get_funcs() 2096 if len(funcs) > 0: 2097 for func in funcs: 2098 result += func.get_capi_proto(defined_names) + ';\n' 2099 result += '\n' 2100 2101 classes = header.get_classes() 2102 for cls in classes: 2103 # virtual functions are inside a structure 2104 result += 'struct ' + cls.get_capi_name() + '\n{\n' 2105 funcs = cls.get_virtual_funcs() 2106 if len(funcs) > 0: 2107 for func in funcs: 2108 result += '\t' + func.get_capi_proto(defined_names) + ';\n' 2109 result += '}\n\n' 2110 2111 defined_names.append(cls.get_capi_name()) 2112 2113 # static functions become global 2114 funcs = cls.get_static_funcs() 2115 if len(funcs) > 0: 2116 for func in funcs: 2117 result += func.get_capi_proto(defined_names) + ';\n' 2118 result += '\n' 2119 sys.stdout.write(result) 2120