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