• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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