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