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