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