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