cef/tools/cef_parser.py

2154 lines
66 KiB
Python
Raw Normal View History

# Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights
# reserved. Use of this source code is governed by a BSD-style license that
# can be found in the LICENSE file.
from __future__ import absolute_import
from date_util import *
from file_util import *
import os
import re
import shutil
import string
import sys
import textwrap
import time
def notify(msg):
""" Display a message. """
sys.stdout.write(' NOTE: ' + msg + '\n')
def wrap_text(text, indent='', maxchars=80, listitem=False):
""" Wrap the text to the specified number of characters. If
necessary a line will be broken and wrapped after a word.
"""
if listitem:
initial_indent = indent + '- '
subsequent_indent = indent + ' '
else:
initial_indent = indent
subsequent_indent = indent
lines = textwrap.wrap(
text,
maxchars,
initial_indent=initial_indent,
subsequent_indent=subsequent_indent)
return '\n'.join(lines) + '\n'
def is_base_class(clsname):
""" Returns true if |clsname| is a known base (root) class in the object
hierarchy.
"""
return clsname == 'CefBaseRefCounted' or clsname == 'CefBaseScoped'
def get_capi_file_name(cppname):
""" Convert a C++ header file name to a C API header file name. """
return cppname[:-2] + '_capi.h'
def get_capi_name(cppname, isclassname, prefix=None):
""" Convert a C++ CamelCaps name to a C API underscore name. """
result = ''
lastchr = ''
for chr in cppname:
# add an underscore if the current character is an upper case letter
# and the last character was a lower case letter
if len(result) > 0 and not chr.isdigit() \
and chr.upper() == chr \
and not lastchr.upper() == lastchr:
result += '_'
result += chr.lower()
lastchr = chr
if isclassname:
result += '_t'
if not prefix is None:
if prefix[0:3] == 'cef':
# if the prefix name is duplicated in the function name
# remove that portion of the function name
subprefix = prefix[3:]
pos = result.find(subprefix)
if pos >= 0:
result = result[0:pos] + result[pos + len(subprefix):]
result = prefix + '_' + result
return result
def get_wrapper_type_enum(cppname):
""" Returns the wrapper type enumeration value for the specified C++ class
name. """
return 'WT_' + get_capi_name(cppname, False)[4:].upper()
def get_prev_line(body, pos):
""" Retrieve the start and end positions and value for the line immediately
before the line containing the specified position.
"""
end = body.rfind('\n', 0, pos)
start = body.rfind('\n', 0, end) + 1
line = body[start:end]
return {'start': start, 'end': end, 'line': line}
def get_comment(body, name):
""" Retrieve the comment for a class or function. """
result = []
pos = body.find(name)
in_block_comment = False
while pos > 0:
data = get_prev_line(body, pos)
line = data['line'].strip()
pos = data['start']
if len(line) == 0:
break
# single line /*--cef()--*/
elif line[0:2] == '/*' and line[-2:] == '*/':
continue
# start of multi line /*--cef()--*/
elif in_block_comment and line[0:2] == '/*':
in_block_comment = False
continue
# end of multi line /*--cef()--*/
elif not in_block_comment and line[-2:] == '*/':
in_block_comment = True
continue
elif in_block_comment:
continue
elif line[0:3] == '///':
# keep the comment line including any leading spaces
result.append(line[3:])
else:
break
result.reverse()
return result
2016-03-01 23:17:08 +01:00
def validate_comment(file, name, comment):
""" Validate the comment array returned by get_comment(). """
# Verify that the comment contains beginning and ending '///' as required by
# Doxygen (the leading '///' from each line will already have been removed by
# the get_comment() logic).
if len(comment) < 3 or len(comment[0]) != 0 or len(comment[-1]) != 0:
raise Exception('Missing or incorrect comment in %s for: %s' % \
(file, name))
def format_comment(comment, indent, translate_map=None, maxchars=80):
""" Return the comments array as a formatted string. """
if not translate_map is None:
# Replace longest keys first in translation.
translate_keys = sorted(
translate_map.keys(), key=lambda item: (-len(item), item))
result = ''
wrapme = ''
hasemptyline = False
listitem = False
for line in comment:
# if the line starts with a leading space, remove that space
if not line is None and len(line) > 0 and line[0] == ' ':
line = line[1:]
didremovespace = True
else:
didremovespace = False
if line is None or len(line) == 0 or (line[0] == ' ' and not listitem) \
or line[0] == '-':
# the previous paragraph or list item, if any, has ended
if len(wrapme) > 0:
if not translate_map is None:
# apply the translation
for key in translate_keys:
wrapme = wrapme.replace(key, translate_map[key])
# output the previous paragraph
result += wrap_text(wrapme, indent + '/// ', maxchars, listitem)
wrapme = ''
listitem = False
if not line is None:
if len(line) > 0 and line[0] == '-':
# list item
listitem = True
wrapme = line[1:].strip()
if len(line) > 0 and line[0] == ' ' and listitem:
# list item continues
wrapme += line[1:]
if len(line) == 0 or (line[0] == ' ' and not listitem):
# blank lines or anything that's further indented should be
# output as-is
result += indent + '///'
if len(line) > 0:
if didremovespace:
result += ' ' + line
else:
result += line
result += '\n'
listitem = False
else:
if not listitem:
# add to the current paragraph
wrapme += line + ' '
else:
# output an empty line
hasemptyline = True
result += '\n'
if len(wrapme) > 0:
if not translate_map is None:
# apply the translation
for key in translate_map.keys():
wrapme = wrapme.replace(key, translate_map[key])
# output the previous paragraph
result += wrap_text(wrapme, indent + '/// ', maxchars, listitem)
if hasemptyline:
# an empty line means a break between comments, so the comment is
# probably a section heading and should have an extra line before it
result = '\n' + result
return result
def format_translation_changes(old, new):
""" Return a comment stating what is different between the old and new
function prototype parts.
"""
changed = False
result = ''
# normalize C API attributes
oldargs = [x.replace('struct _', '') for x in old['args']]
oldretval = old['retval'].replace('struct _', '')
newargs = [x.replace('struct _', '') for x in new['args']]
newretval = new['retval'].replace('struct _', '')
# check if the prototype has changed
oldset = set(oldargs)
newset = set(newargs)
if len(oldset.symmetric_difference(newset)) > 0:
changed = True
result += '\n // WARNING - CHANGED ATTRIBUTES'
# in the implementation set only
oldonly = oldset.difference(newset)
for arg in oldonly:
result += '\n // REMOVED: ' + arg
# in the current set only
newonly = newset.difference(oldset)
for arg in newonly:
result += '\n // ADDED: ' + arg
# check if the return value has changed
if oldretval != newretval:
changed = True
result += '\n // WARNING - CHANGED RETURN VALUE'+ \
'\n // WAS: '+old['retval']+ \
'\n // NOW: '+new['retval']
if changed:
result += '\n #pragma message("Warning: " __FILE__ ": '+new['name']+ \
' prototype has changed")\n'
return result
def format_translation_includes(header, body):
""" Return the necessary list of includes based on the contents of the
body.
"""
result = ''
# <algorithm> required for VS2013.
if body.find('std::min') > 0 or body.find('std::max') > 0:
result += '#include <algorithm>\n'
if body.find('cef_api_hash(') > 0:
result += '#include "include/cef_api_hash.h"\n'
if body.find('template_util::has_valid_size(') > 0:
result += '#include "libcef_dll/template_util.h"\n'
# identify what CppToC classes are being used
p = re.compile(r'([A-Za-z0-9_]{1,})CppToC')
list = sorted(set(p.findall(body)))
for item in list:
directory = ''
if not is_base_class(item):
cls = header.get_class(item)
dir = cls.get_file_directory()
if not dir is None:
directory = dir + '/'
result += '#include "libcef_dll/cpptoc/'+directory+ \
get_capi_name(item[3:], False)+'_cpptoc.h"\n'
# identify what CToCpp classes are being used
p = re.compile(r'([A-Za-z0-9_]{1,})CToCpp')
list = sorted(set(p.findall(body)))
for item in list:
directory = ''
if not is_base_class(item):
cls = header.get_class(item)
dir = cls.get_file_directory()
if not dir is None:
directory = dir + '/'
result += '#include "libcef_dll/ctocpp/'+directory+ \
get_capi_name(item[3:], False)+'_ctocpp.h"\n'
if body.find('shutdown_checker') > 0:
result += '#include "libcef_dll/shutdown_checker.h"\n'
if body.find('transfer_') > 0:
result += '#include "libcef_dll/transfer_util.h"\n'
return result
def str_to_dict(str):
""" Convert a string to a dictionary. If the same key has multiple values
the values will be stored in a list. """
dict = {}
parts = str.split(',')
for part in parts:
part = part.strip()
if len(part) == 0:
continue
sparts = part.split('=')
if len(sparts) > 2:
raise Exception('Invalid dictionary pair format: ' + part)
name = sparts[0].strip()
if len(sparts) == 2:
val = sparts[1].strip()
else:
val = True
if name in dict:
# a value with this name already exists
curval = dict[name]
if not isinstance(curval, list):
# convert the string value to a list
dict[name] = [curval]
dict[name].append(val)
else:
dict[name] = val
return dict
def dict_to_str(dict):
""" Convert a dictionary to a string. """
str = []
for name in dict.keys():
if not isinstance(dict[name], list):
if dict[name] is True:
# currently a bool value
str.append(name)
else:
# currently a string value
str.append(name + '=' + dict[name])
else:
# currently a list value
for val in dict[name]:
str.append(name + '=' + val)
return ','.join(str)
# regex for matching comment-formatted attributes
_cre_attrib = r'/\*--cef\(([A-Za-z0-9_ ,=:\n]{0,})\)--\*/'
# regex for matching class and function names
_cre_cfname = r'([A-Za-z0-9_]{1,})'
Implement Views framework on Windows and Linux (issue #1749). - Add Views header files in a new include/views directory. - Add initial top-level window (CefWindow), control (CefBrowserView, CefLabelButton, CefMenuButton, CefPanel, CefScrollView, CefTextfield) and layout (CefBoxLayout, CefFlowLayout) support. See libcef/browser/views/view_impl.h comments for implementation details. - Add Views example usage in cefclient and cefsimple and Views unit tests in cef_unittests. Pass the `--use-views` command-line flag to cefclient, cefsimple and cef_unittests to run using the Views framework instead of platform APIs. For cefclient and cefsimple this will create the browser window and all related functionality using the Views framework. For cef_unittests this will run all tests (except OSR tests) in a Views-based browser window. Views- specific unit tests (`--gtest_filter=Views*`) will be run even if the the `--use-views` flag is not specified. - Pass the `--hide-frame` command-line flag to cefclient to demo a frameless Views-based browser window. - Pass the `--hide-controls` command-line flag to cefclient to demo a browser window without top controls. This also works in non-Views mode. - Pass the `--enable-high-dpi-support` command-line flag to cef_unittests on Windows to test high-DPI support on a display that supports it. - Add CefImage for reading/writing image file formats. - Add CefBrowser::DownloadImage() for downloading image URLs as a CefImage representation. This is primarily for loading favicons. - Add CefMenuModel::CreateMenuModel() and CefMenuModelDelegate for creating custom menus. This is primarily for use with CefMenuButton. - Add CefBrowser::TryCloseBrowser() helper for closing a browser. Also improve related documentation in cef_life_span_handler.h. - Rename cef_page_range_t to cef_range_t. It is now also used by CefTextfield. - Remove CefLifeSpanHandler::RunModal() which is never called. - Add draggable regions example to cefclient.
2016-01-19 21:09:01 +01:00
# regex for matching class and function names including path separators
_cre_cfnameorpath = r'([A-Za-z0-9_\/]{1,})'
# regex for matching typedef value and name combination
_cre_typedef = r'([A-Za-z0-9_<>:,\*\&\s]{1,})'
# regex for matching function return value and name combination
_cre_func = r'([A-Za-z][A-Za-z0-9_<>:,\*\&\s]{1,})'
# regex for matching virtual function modifiers + arbitrary whitespace
_cre_vfmod = r'([\sA-Za-z0-9_]{0,})'
# regex for matching arbitrary whitespace
_cre_space = r'[\s]{1,}'
# regex for matching optional virtual keyword
_cre_virtual = r'(?:[\s]{1,}virtual){0,1}'
# Simple translation types. Format is:
# 'cpp_type' : ['capi_type', 'capi_default_value']
_simpletypes = {
'void': ['void', ''],
'void*': ['void*', 'NULL'],
'int': ['int', '0'],
'int16_t': ['int16_t', '0'],
'uint16_t': ['uint16_t', '0'],
'int32_t': ['int32_t', '0'],
'uint32_t': ['uint32_t', '0'],
'int64_t': ['int64_t', '0'],
'uint64_t': ['uint64_t', '0'],
'double': ['double', '0'],
'float': ['float', '0'],
'float*': ['float*', 'NULL'],
'long': ['long', '0'],
'unsigned long': ['unsigned long', '0'],
'long long': ['long long', '0'],
'size_t': ['size_t', '0'],
'bool': ['int', '0'],
'char': ['char', '0'],
'char* const': ['char* const', 'NULL'],
'cef_color_t': ['cef_color_t', '0'],
'cef_json_parser_error_t': ['cef_json_parser_error_t', 'JSON_NO_ERROR'],
'CefAcceleratedPaintInfo': [
'cef_accelerated_paint_info_t', 'CefAcceleratedPaintInfo()'
],
'CefAudioParameters': ['cef_audio_parameters_t', 'CefAudioParameters()'],
'CefBaseTime': ['cef_basetime_t', 'CefBaseTime()'],
'CefBoxLayoutSettings': [
'cef_box_layout_settings_t', 'CefBoxLayoutSettings()'
],
'CefCompositionUnderline': [
'cef_composition_underline_t', 'CefCompositionUnderline()'
],
'CefCursorHandle': ['cef_cursor_handle_t', 'kNullCursorHandle'],
'CefCursorInfo': ['cef_cursor_info_t', 'CefCursorInfo()'],
'CefDraggableRegion': ['cef_draggable_region_t', 'CefDraggableRegion()'],
'CefEventHandle': ['cef_event_handle_t', 'kNullEventHandle'],
'CefInsets': ['cef_insets_t', 'CefInsets()'],
'CefKeyEvent': ['cef_key_event_t', 'CefKeyEvent()'],
'CefMainArgs': ['cef_main_args_t', 'CefMainArgs()'],
'CefMouseEvent': ['cef_mouse_event_t', 'CefMouseEvent()'],
'CefPoint': ['cef_point_t', 'CefPoint()'],
'CefPopupFeatures': ['cef_popup_features_t', 'CefPopupFeatures()'],
'CefRange': ['cef_range_t', 'CefRange()'],
'CefRect': ['cef_rect_t', 'CefRect()'],
'CefScreenInfo': ['cef_screen_info_t', 'CefScreenInfo()'],
'CefSize': ['cef_size_t', 'CefSize()'],
'CefTouchEvent': ['cef_touch_event_t', 'CefTouchEvent()'],
'CefTouchHandleState': [
'cef_touch_handle_state_t', 'CefTouchHandleState()'
],
'CefThreadId': ['cef_thread_id_t', 'TID_UI'],
'CefTime': ['cef_time_t', 'CefTime()'],
'CefWindowHandle': ['cef_window_handle_t', 'kNullWindowHandle'],
}
def get_function_impls(content, ident, has_impl=True):
""" Retrieve the function parts from the specified contents as a set of
return value, name, arguments and body. Ident must occur somewhere in
the value.
"""
# Remove prefix from methods in CToCpp files.
content = content.replace('NO_SANITIZE("cfi-icall") ', '')
content = content.replace('NO_SANITIZE("cfi-icall")\n', '')
# extract the functions
find_regex = r'\n' + _cre_func + r'\((.*?)\)([A-Za-z0-9_\s]{0,})'
if has_impl:
find_regex += r'\{(.*?)\n\}'
else:
find_regex += r'(;)'
p = re.compile(find_regex, re.MULTILINE | re.DOTALL)
list = p.findall(content)
# build the function map with the function name as the key
result = []
for retval, argval, vfmod, body in list:
if retval.find(ident) < 0:
# the identifier was not found
continue
# remove the identifier
retval = retval.replace(ident, '')
retval = retval.strip()
# Normalize the delimiter.
retval = retval.replace('\n', ' ')
# retrieve the function name
parts = retval.split(' ')
name = parts[-1]
del parts[-1]
retval = ' '.join(parts)
# parse the arguments
args = []
if argval != 'void':
for v in argval.split(','):
v = v.strip()
if len(v) > 0:
args.append(v)
result.append({
'retval': retval.strip(),
'name': name,
'args': args,
'vfmod': vfmod.strip(),
'body': body if has_impl else '',
})
return result
def get_next_function_impl(existing, name):
result = None
for item in existing:
if item['name'] == name:
result = item
existing.remove(item)
break
return result
def get_copyright(full=False, translator=True):
if full:
result = \
"""// Copyright (c) $YEAR$ Marshall A. Greenblatt. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the name Chromium Embedded
// Framework nor the names of its contributors may be used to endorse
// or promote products derived from this software without specific prior
// written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
else:
result = \
"""// Copyright (c) $YEAR$ The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
"""
if translator:
result += \
"""//
// ---------------------------------------------------------------------------
//
// This file was generated by the CEF translator tool. If making changes by
// hand only do so within the body of existing method and function
// implementations. See the translator.README.txt file in the tools directory
// for more information.
//
// $hash=$$HASH$$$
//
"""
# add the copyright year
return result.replace('$YEAR$', get_year())
class obj_header:
""" Class representing a C++ header file. """
def __init__(self):
self.filenames = []
self.typedefs = []
self.funcs = []
self.classes = []
self.root_directory = None
def set_root_directory(self, root_directory):
""" Set the root directory. """
self.root_directory = root_directory
def get_root_directory(self):
""" Get the root directory. """
return self.root_directory
def add_directory(self, directory, excluded_files=[]):
""" Add all header files from the specified directory. """
files = get_files(os.path.join(directory, '*.h'))
for file in files:
if len(excluded_files) == 0 or \
not os.path.split(file)[1] in excluded_files:
self.add_file(file)
def add_file(self, filepath):
""" Add a header file. """
if self.root_directory is None:
filename = os.path.split(filepath)[1]
else:
filename = os.path.relpath(filepath, self.root_directory)
filename = filename.replace('\\', '/')
try:
# read the input file into memory
self.add_data(filename, read_file(filepath))
except Exception:
print('Exception while parsing %s' % filepath)
raise
def add_data(self, filename, data):
""" Add header file contents. """
added = False
# remove space from between template definition end brackets
data = data.replace("> >", ">>")
# extract global typedefs
p = re.compile(r'\ntypedef' + _cre_space + _cre_typedef + r';',
re.MULTILINE | re.DOTALL)
list = p.findall(data)
if len(list) > 0:
# build the global typedef objects
for value in list:
pos = value.rfind(' ')
if pos < 0:
raise Exception('Invalid typedef: ' + value)
alias = value[pos + 1:].strip()
value = value[:pos].strip()
self.typedefs.append(obj_typedef(self, filename, value, alias))
# extract global functions
p = re.compile(r'\n' + _cre_attrib + r'\n' + _cre_func + r'\((.*?)\)',
re.MULTILINE | re.DOTALL)
list = p.findall(data)
if len(list) > 0:
added = True
# build the global function objects
for attrib, retval, argval in list:
comment = get_comment(data, retval + '(' + argval + ');')
validate_comment(filename, retval, comment)
self.funcs.append(
obj_function(self, filename, attrib, retval, argval, comment))
# extract includes
p = re.compile(r'\n#include \"include/' + _cre_cfnameorpath + r'.h')
includes = p.findall(data)
# extract forward declarations
p = re.compile(r'\nclass' + _cre_space + _cre_cfname + r';')
forward_declares = p.findall(data)
# extract empty classes
p = re.compile(r'\n' + _cre_attrib + r'\nclass' + _cre_space + _cre_cfname +
_cre_space + r':' + _cre_space + r'public' + _cre_virtual +
_cre_space + _cre_cfname + _cre_space + r'{};',
re.MULTILINE | re.DOTALL)
list = p.findall(data)
if len(list) > 0:
added = True
# build the class objects
for attrib, name, parent_name in list:
# Style may place the ':' on the next line.
comment = get_comment(data, name + ' :')
if len(comment) == 0:
comment = get_comment(data, name + "\n")
validate_comment(filename, name, comment)
self.classes.append(
obj_class(self, filename, attrib, name, parent_name, "", comment,
includes, forward_declares))
# Remove empty classes from |data| so we don't mess up the non-empty
# class search that follows.
data = p.sub('', data)
# extract classes
p = re.compile(r'\n' + _cre_attrib + r'\nclass' + _cre_space + _cre_cfname +
_cre_space + r':' + _cre_space + r'public' + _cre_virtual +
_cre_space + _cre_cfname + _cre_space + r'{(.*?)\n};',
re.MULTILINE | re.DOTALL)
list = p.findall(data)
if len(list) > 0:
added = True
# build the class objects
for attrib, name, parent_name, body in list:
# Style may place the ':' on the next line.
comment = get_comment(data, name + ' :')
if len(comment) == 0:
comment = get_comment(data, name + "\n")
validate_comment(filename, name, comment)
self.classes.append(
obj_class(self, filename, attrib, name, parent_name, body, comment,
includes, forward_declares))
if added:
# a global function or class was read from the header file
self.filenames.append(filename)
def __repr__(self):
result = ''
if len(self.typedefs) > 0:
strlist = []
for cls in self.typedefs:
strlist.append(str(cls))
result += "\n".join(strlist) + "\n\n"
if len(self.funcs) > 0:
strlist = []
for cls in self.funcs:
strlist.append(str(cls))
result += "\n".join(strlist) + "\n\n"
if len(self.classes) > 0:
strlist = []
for cls in self.classes:
strlist.append(str(cls))
result += "\n".join(strlist)
return result
def get_file_names(self):
""" Return the array of header file names. """
return self.filenames
def get_typedefs(self):
""" Return the array of typedef objects. """
return self.typedefs
def get_funcs(self, filename=None):
""" Return the array of function objects. """
if filename is None:
return self.funcs
else:
# only return the functions in the specified file
res = []
for func in self.funcs:
if func.get_file_name() == filename:
res.append(func)
return res
def get_classes(self, filename=None):
""" Return the array of class objects. """
if filename is None:
return self.classes
else:
# only return the classes in the specified file
res = []
for cls in self.classes:
if cls.get_file_name() == filename:
res.append(cls)
return res
def get_class(self, classname, defined_structs=None):
""" Return the specified class or None if not found. """
for cls in self.classes:
if cls.get_name() == classname:
return cls
elif not defined_structs is None:
defined_structs.append(cls.get_capi_name())
return None
def get_class_names(self):
""" Returns the names of all classes in this object. """
result = []
for cls in self.classes:
result.append(cls.get_name())
return result
def get_base_class_name(self, classname):
""" Returns the base (root) class name for |classname|. """
cur_cls = self.get_class(classname)
while True:
parent_name = cur_cls.get_parent_name()
if is_base_class(parent_name):
return parent_name
else:
parent_cls = self.get_class(parent_name)
if parent_cls is None:
break
cur_cls = self.get_class(parent_name)
return None
def get_types(self, list):
""" Return a dictionary mapping data types to analyzed values. """
for cls in self.typedefs:
cls.get_types(list)
for cls in self.classes:
cls.get_types(list)
def get_alias_translation(self, alias):
""" Return a translation of alias to value based on typedef
statements. """
for cls in self.typedefs:
if cls.alias == alias:
return cls.value
return None
def get_analysis(self, value, named=True):
""" Return an analysis of the value based the header file context. """
return obj_analysis([self], value, named)
def get_defined_structs(self):
""" Return a list of already defined structure names. """
return [
'cef_print_info_t', 'cef_window_info_t', 'cef_base_ref_counted_t',
'cef_base_scoped_t'
]
def get_capi_translations(self):
""" Return a dictionary that maps C++ terminology to C API terminology.
"""
# strings that will be changed in C++ comments
map = {
'class': 'structure',
'Class': 'Structure',
'interface': 'structure',
'Interface': 'Structure',
'true': 'true (1)',
'false': 'false (0)',
'empty': 'NULL',
'method': 'function'
}
# add mappings for all classes and functions
funcs = self.get_funcs()
for func in funcs:
map[func.get_name() + '()'] = func.get_capi_name() + '()'
classes = self.get_classes()
for cls in classes:
map[cls.get_name()] = cls.get_capi_name()
funcs = cls.get_virtual_funcs()
for func in funcs:
map[func.get_name() + '()'] = func.get_capi_name() + '()'
funcs = cls.get_static_funcs()
for func in funcs:
map[func.get_name() + '()'] = func.get_capi_name() + '()'
return map
class obj_class:
""" Class representing a C++ class. """
def __init__(self, parent, filename, attrib, name, parent_name, body, comment,
includes, forward_declares):
if not isinstance(parent, obj_header):
raise Exception('Invalid parent object type')
self.parent = parent
self.filename = filename
self.attribs = str_to_dict(attrib)
self.name = name
self.parent_name = parent_name
self.comment = comment
self.includes = includes
self.forward_declares = forward_declares
# extract typedefs
p = re.compile(
r'\n' + _cre_space + r'typedef' + _cre_space + _cre_typedef + r';',
re.MULTILINE | re.DOTALL)
list = p.findall(body)
# build the typedef objects
self.typedefs = []
for value in list:
pos = value.rfind(' ')
if pos < 0:
raise Exception('Invalid typedef: ' + value)
alias = value[pos + 1:].strip()
value = value[:pos].strip()
self.typedefs.append(obj_typedef(self, filename, value, alias))
# extract static functions
p = re.compile(r'\n' + _cre_space + _cre_attrib + r'\n' + _cre_space +
r'static' + _cre_space + _cre_func + r'\((.*?)\)',
re.MULTILINE | re.DOTALL)
list = p.findall(body)
# build the static function objects
self.staticfuncs = []
for attrib, retval, argval in list:
comment = get_comment(body, retval + '(' + argval + ')')
validate_comment(filename, retval, comment)
self.staticfuncs.append(
obj_function_static(self, attrib, retval, argval, comment))
# extract virtual functions
p = re.compile(
r'\n' + _cre_space + _cre_attrib + r'\n' + _cre_space + r'virtual' +
_cre_space + _cre_func + r'\((.*?)\)' + _cre_vfmod,
re.MULTILINE | re.DOTALL)
list = p.findall(body)
# build the virtual function objects
self.virtualfuncs = []
for attrib, retval, argval, vfmod in list:
comment = get_comment(body, retval + '(' + argval + ')')
validate_comment(filename, retval, comment)
self.virtualfuncs.append(
obj_function_virtual(self, attrib, retval, argval, comment,
vfmod.strip()))
def __repr__(self):
result = '/* ' + dict_to_str(
self.attribs) + ' */ class ' + self.name + "\n{"
if len(self.typedefs) > 0:
result += "\n\t"
strlist = []
for cls in self.typedefs:
strlist.append(str(cls))
result += "\n\t".join(strlist)
if len(self.staticfuncs) > 0:
result += "\n\t"
strlist = []
for cls in self.staticfuncs:
strlist.append(str(cls))
result += "\n\t".join(strlist)
if len(self.virtualfuncs) > 0:
result += "\n\t"
strlist = []
for cls in self.virtualfuncs:
strlist.append(str(cls))
result += "\n\t".join(strlist)
result += "\n};\n"
return result
def get_file_name(self):
""" Return the C++ header file name. Includes the directory component,
if any. """
return self.filename
def get_capi_file_name(self):
""" Return the CAPI header file name. Includes the directory component,
if any. """
return get_capi_file_name(self.filename)
def get_file_directory(self):
""" Return the file directory component, if any. """
pos = self.filename.rfind('/')
if pos >= 0:
return self.filename[:pos]
return None
def get_name(self):
""" Return the class name. """
return self.name
def get_capi_name(self):
""" Return the CAPI structure name for this class. """
return get_capi_name(self.name, True)
def get_parent_name(self):
""" Return the parent class name. """
return self.parent_name
def get_parent_capi_name(self):
""" Return the CAPI structure name for the parent class. """
return get_capi_name(self.parent_name, True)
def has_parent(self, parent_name):
""" Returns true if this class has the specified class anywhere in its
inheritance hierarchy. """
# Every class has a known base class as the top-most parent.
if is_base_class(parent_name) or parent_name == self.parent_name:
return True
if is_base_class(self.parent_name):
return False
cur_cls = self.parent.get_class(self.parent_name)
while True:
cur_parent_name = cur_cls.get_parent_name()
if is_base_class(cur_parent_name):
break
elif cur_parent_name == parent_name:
return True
cur_cls = self.parent.get_class(cur_parent_name)
return False
def get_comment(self):
""" Return the class comment as an array of lines. """
return self.comment
def get_includes(self):
""" Return the list of classes that are included from this class'
header file. """
return self.includes
def get_forward_declares(self):
""" Return the list of classes that are forward declared for this
class. """
return self.forward_declares
def get_attribs(self):
""" Return all attributes as a dictionary. """
return self.attribs
def has_attrib(self, name):
""" Return true if the specified attribute exists. """
return name in self.attribs
def get_attrib(self, name):
""" Return the first or only value for specified attribute. """
if name in self.attribs:
if isinstance(self.attribs[name], list):
# the value is a list
return self.attribs[name][0]
else:
# the value is a string
return self.attribs[name]
return None
def get_attrib_list(self, name):
""" Return all values for specified attribute as a list. """
if name in self.attribs:
if isinstance(self.attribs[name], list):
# the value is already a list
return self.attribs[name]
else:
# convert the value to a list
return [self.attribs[name]]
return None
def get_typedefs(self):
""" Return the array of typedef objects. """
return self.typedefs
def has_typedef_alias(self, alias):
""" Returns true if the specified typedef alias is defined in the scope
of this class declaration. """
for typedef in self.typedefs:
if typedef.get_alias() == alias:
return True
return False
def get_static_funcs(self):
""" Return the array of static function objects. """
return self.staticfuncs
def get_virtual_funcs(self):
""" Return the array of virtual function objects. """
return self.virtualfuncs
def get_types(self, list):
""" Return a dictionary mapping data types to analyzed values. """
for cls in self.typedefs:
cls.get_types(list)
for cls in self.staticfuncs:
cls.get_types(list)
for cls in self.virtualfuncs:
cls.get_types(list)
def get_alias_translation(self, alias):
for cls in self.typedefs:
if cls.alias == alias:
return cls.value
return None
def get_analysis(self, value, named=True):
""" Return an analysis of the value based on the class definition
context.
"""
return obj_analysis([self, self.parent], value, named)
def is_library_side(self):
""" Returns true if the class is implemented by the library. """
return self.attribs['source'] == 'library'
def is_client_side(self):
""" Returns true if the class is implemented by the client. """
return self.attribs['source'] == 'client'
class obj_typedef:
""" Class representing a typedef statement. """
def __init__(self, parent, filename, value, alias):
if not isinstance(parent, obj_header) \
and not isinstance(parent, obj_class):
raise Exception('Invalid parent object type')
self.parent = parent
self.filename = filename
self.alias = alias
self.value = self.parent.get_analysis(value, False)
def __repr__(self):
return 'typedef ' + self.value.get_type() + ' ' + self.alias + ';'
def get_file_name(self):
""" Return the C++ header file name. """
return self.filename
def get_capi_file_name(self):
""" Return the CAPI header file name. """
return get_capi_file_name(self.filename)
def get_alias(self):
""" Return the alias. """
return self.alias
def get_value(self):
""" Return an analysis of the value based on the class or header file
definition context.
"""
return self.value
def get_types(self, list):
""" Return a dictionary mapping data types to analyzed values. """
name = self.value.get_type()
if not name in list:
list[name] = self.value
class obj_function:
""" Class representing a function. """
def __init__(self, parent, filename, attrib, retval, argval, comment):
self.parent = parent
self.filename = filename
self.attribs = str_to_dict(attrib)
self.retval = obj_argument(self, retval)
self.name = self.retval.remove_name()
self.comment = comment
# build the argument objects
self.arguments = []
arglist = argval.split(',')
argindex = 0
while argindex < len(arglist):
arg = arglist[argindex]
if arg.find('<') >= 0 and arg.find('>') == -1:
# We've split inside of a template type declaration. Join the
# next argument with this argument.
argindex += 1
arg += ',' + arglist[argindex]
arg = arg.strip()
if len(arg) > 0:
argument = obj_argument(self, arg)
if argument.needs_attrib_count_func() and \
argument.get_attrib_count_func() is None:
raise Exception("A 'count_func' attribute is required "+ \
"for the '"+argument.get_name()+ \
"' parameter to "+self.get_qualified_name())
self.arguments.append(argument)
argindex += 1
if self.retval.needs_attrib_default_retval() and \
self.retval.get_attrib_default_retval() is None:
raise Exception("A 'default_retval' attribute is required for "+ \
self.get_qualified_name())
def __repr__(self):
return '/* ' + dict_to_str(self.attribs) + ' */ ' + self.get_cpp_proto()
def get_file_name(self):
""" Return the C++ header file name. """
return self.filename
def get_capi_file_name(self):
""" Return the CAPI header file name. """
return get_capi_file_name(self.filename)
def get_name(self):
""" Return the function name. """
return self.name
def get_qualified_name(self):
""" Return the fully qualified function name. """
if isinstance(self.parent, obj_header):
# global function
return self.name
else:
# member function
return self.parent.get_name() + '::' + self.name
def get_capi_name(self, prefix=None):
""" Return the CAPI function name. """
if 'capi_name' in self.attribs:
return self.attribs['capi_name']
return get_capi_name(self.name, False, prefix)
def get_comment(self):
""" Return the function comment as an array of lines. """
return self.comment
def get_attribs(self):
""" Return all attributes as a dictionary. """
return self.attribs
def has_attrib(self, name):
""" Return true if the specified attribute exists. """
return name in self.attribs
def get_attrib(self, name):
""" Return the first or only value for specified attribute. """
if name in self.attribs:
if isinstance(self.attribs[name], list):
# the value is a list
return self.attribs[name][0]
else:
# the value is a string
return self.attribs[name]
return None
def get_attrib_list(self, name):
""" Return all values for specified attribute as a list. """
if name in self.attribs:
if isinstance(self.attribs[name], list):
# the value is already a list
return self.attribs[name]
else:
# convert the value to a list
return [self.attribs[name]]
return None
def get_retval(self):
""" Return the return value object. """
return self.retval
def get_arguments(self):
""" Return the argument array. """
return self.arguments
def get_types(self, list):
""" Return a dictionary mapping data types to analyzed values. """
for cls in self.arguments:
cls.get_types(list)
def get_capi_parts(self, defined_structs=[], isimpl=False, prefix=None):
""" Return the parts of the C API function definition. """
retval = ''
dict = self.retval.get_type().get_capi(defined_structs)
if dict['format'] == 'single':
retval = dict['value']
name = self.get_capi_name(prefix)
args = []
if isinstance(self, obj_function_virtual):
# virtual functions get themselves as the first argument
str = 'struct _' + self.parent.get_capi_name() + '* self'
if isinstance(self, obj_function_virtual) and self.is_const():
# const virtual functions get const self pointers
str = 'const ' + str
args.append(str)
elif not isimpl and len(self.arguments) == 0:
args.append('void')
if len(self.arguments) > 0:
for cls in self.arguments:
type = cls.get_type()
dict = type.get_capi(defined_structs)
if dict['format'] == 'single':
args.append(dict['value'])
elif dict['format'] == 'multi-arg':
# add an additional argument for the size of the array
type_name = type.get_name()
if type.is_const():
# for const arrays pass the size argument by value
args.append('size_t ' + type_name + 'Count')
else:
# for non-const arrays pass the size argument by address
args.append('size_t* ' + type_name + 'Count')
args.append(dict['value'])
return {'retval': retval, 'name': name, 'args': args}
def get_capi_proto(self, defined_structs=[], isimpl=False, prefix=None):
""" Return the prototype of the C API function. """
parts = self.get_capi_parts(defined_structs, isimpl, prefix)
result = parts['retval']+' '+parts['name']+ \
'('+', '.join(parts['args'])+')'
return result
def get_cpp_parts(self, isimpl=False):
""" Return the parts of the C++ function definition. """
retval = str(self.retval)
name = self.name
args = []
if len(self.arguments) > 0:
for cls in self.arguments:
args.append(str(cls))
if isimpl and isinstance(self, obj_function_virtual):
# enumeration return values must be qualified with the class name
# if the type is defined in the class declaration scope.
type = self.get_retval().get_type()
if type.is_result_struct() and type.is_result_struct_enum() and \
self.parent.has_typedef_alias(retval):
retval = self.parent.get_name() + '::' + retval
return {'retval': retval, 'name': name, 'args': args}
def get_cpp_proto(self, classname=None):
""" Return the prototype of the C++ function. """
parts = self.get_cpp_parts()
result = parts['retval'] + ' '
if not classname is None:
result += classname + '::'
result += parts['name'] + '(' + ', '.join(parts['args']) + ')'
if isinstance(self, obj_function_virtual) and self.is_const():
result += ' const'
return result
def is_same_side(self, other_class_name):
""" Returns true if this function is on the same side (library or
client) and the specified class. """
if isinstance(self.parent, obj_class):
# this function is part of a class
this_is_library_side = self.parent.is_library_side()
header = self.parent.parent
else:
# this function is global
this_is_library_side = True
header = self.parent
if is_base_class(other_class_name):
other_is_library_side = False
else:
other_class = header.get_class(other_class_name)
if other_class is None:
raise Exception('Unknown class: ' + other_class_name)
other_is_library_side = other_class.is_library_side()
return other_is_library_side == this_is_library_side
class obj_function_static(obj_function):
""" Class representing a static function. """
def __init__(self, parent, attrib, retval, argval, comment):
if not isinstance(parent, obj_class):
raise Exception('Invalid parent object type')
obj_function.__init__(self, parent, parent.filename, attrib, retval, argval,
comment)
def __repr__(self):
return 'static ' + obj_function.__repr__(self) + ';'
def get_capi_name(self, prefix=None):
""" Return the CAPI function name. """
if prefix is None:
# by default static functions are prefixed with the class name
prefix = get_capi_name(self.parent.get_name(), False)
return obj_function.get_capi_name(self, prefix)
class obj_function_virtual(obj_function):
""" Class representing a virtual function. """
def __init__(self, parent, attrib, retval, argval, comment, vfmod):
if not isinstance(parent, obj_class):
raise Exception('Invalid parent object type')
obj_function.__init__(self, parent, parent.filename, attrib, retval, argval,
comment)
if vfmod == 'const':
self.isconst = True
else:
self.isconst = False
def __repr__(self):
return 'virtual ' + obj_function.__repr__(self) + ';'
def is_const(self):
""" Returns true if the method declaration is const. """
return self.isconst
class obj_argument:
""" Class representing a function argument. """
def __init__(self, parent, argval):
if not isinstance(parent, obj_function):
raise Exception('Invalid parent object type')
self.parent = parent
self.type = self.parent.parent.get_analysis(argval)
def __repr__(self):
result = ''
if self.type.is_const():
result += 'const '
result += self.type.get_type()
if self.type.is_byref():
result += '&'
elif self.type.is_byaddr():
result += '*'
if self.type.has_name():
result += ' ' + self.type.get_name()
return result
def get_name(self):
""" Return the name for this argument. """
return self.type.get_name()
def remove_name(self):
""" Remove and return the name value. """
name = self.type.get_name()
self.type.name = None
return name
def get_type(self):
""" Return an analysis of the argument type based on the class
definition context.
"""
return self.type
def get_types(self, list):
""" Return a dictionary mapping data types to analyzed values. """
name = self.type.get_type()
if not name in list:
list[name] = self.type
def needs_attrib_count_func(self):
""" Returns true if this argument requires a 'count_func' attribute. """
# A 'count_func' attribute is required for non-const non-string vector
# attribute types
return self.type.has_name() and \
self.type.is_result_vector() and \
not self.type.is_result_vector_string() and \
not self.type.is_const()
def get_attrib_count_func(self):
""" Returns the count function for this argument. """
# The 'count_func' attribute value format is name:function
if not self.parent.has_attrib('count_func'):
return None
name = self.type.get_name()
vals = self.parent.get_attrib_list('count_func')
for val in vals:
parts = val.split(':')
if len(parts) != 2:
raise Exception("Invalid 'count_func' attribute value for "+ \
self.parent.get_qualified_name()+': '+val)
if parts[0].strip() == name:
return parts[1].strip()
return None
def needs_attrib_default_retval(self):
""" Returns true if this argument requires a 'default_retval' attribute.
"""
# A 'default_retval' attribute is required for enumeration return value
# types.
return not self.type.has_name() and \
self.type.is_result_struct() and \
self.type.is_result_struct_enum()
def get_attrib_default_retval(self):
""" Returns the defualt return value for this argument. """
return self.parent.get_attrib('default_retval')
def get_arg_type(self):
""" Returns the argument type as defined in translator.README.txt. """
if not self.type.has_name():
raise Exception('Cannot be called for retval types')
# simple or enumeration type
if (self.type.is_result_simple() and \
self.type.get_type() != 'bool') or \
(self.type.is_result_struct() and \
self.type.is_result_struct_enum()):
if self.type.is_byref():
if self.type.is_const():
return 'simple_byref_const'
return 'simple_byref'
elif self.type.is_byaddr():
return 'simple_byaddr'
return 'simple_byval'
# boolean type
if self.type.get_type() == 'bool':
if self.type.is_byref():
return 'bool_byref'
elif self.type.is_byaddr():
return 'bool_byaddr'
return 'bool_byval'
# structure type
if self.type.is_result_struct() and self.type.is_byref():
if self.type.is_const():
return 'struct_byref_const'
return 'struct_byref'
# string type
if self.type.is_result_string() and self.type.is_byref():
if self.type.is_const():
return 'string_byref_const'
return 'string_byref'
# *ptr type
if self.type.is_result_ptr():
prefix = self.type.get_result_ptr_type_prefix()
same_side = self.parent.is_same_side(self.type.get_ptr_type())
if self.type.is_byref():
if same_side:
return prefix + 'ptr_same_byref'
return prefix + 'ptr_diff_byref'
if same_side:
return prefix + 'ptr_same'
return prefix + 'ptr_diff'
if self.type.is_result_vector():
# all vector types must be passed by reference
if not self.type.is_byref():
return 'invalid'
if self.type.is_result_vector_string():
# string vector type
if self.type.is_const():
return 'string_vec_byref_const'
return 'string_vec_byref'
if self.type.is_result_vector_simple():
if self.type.get_vector_type() != 'bool':
# simple/enumeration vector types
if self.type.is_const():
return 'simple_vec_byref_const'
return 'simple_vec_byref'
# boolean vector types
if self.type.is_const():
return 'bool_vec_byref_const'
return 'bool_vec_byref'
if self.type.is_result_vector_ptr():
# *ptr vector types
prefix = self.type.get_result_vector_ptr_type_prefix()
same_side = self.parent.is_same_side(self.type.get_ptr_type())
if self.type.is_const():
if same_side:
return prefix + 'ptr_vec_same_byref_const'
return prefix + 'ptr_vec_diff_byref_const'
if same_side:
return prefix + 'ptr_vec_same_byref'
return prefix + 'ptr_vec_diff_byref'
# string single map type
if self.type.is_result_map_single():
if not self.type.is_byref():
return 'invalid'
if self.type.is_const():
return 'string_map_single_byref_const'
return 'string_map_single_byref'
# string multi map type
if self.type.is_result_map_multi():
if not self.type.is_byref():
return 'invalid'
if self.type.is_const():
return 'string_map_multi_byref_const'
return 'string_map_multi_byref'
return 'invalid'
def get_retval_type(self):
""" Returns the retval type as defined in translator.README.txt. """
if self.type.has_name():
raise Exception('Cannot be called for argument types')
# unsupported modifiers
if self.type.is_const() or self.type.is_byref() or \
self.type.is_byaddr():
return 'invalid'
# void types don't have a return value
if self.type.get_type() == 'void':
return 'none'
if (self.type.is_result_simple() and \
self.type.get_type() != 'bool') or \
(self.type.is_result_struct() and self.type.is_result_struct_enum()):
return 'simple'
if self.type.get_type() == 'bool':
return 'bool'
if self.type.is_result_string():
return 'string'
if self.type.is_result_ptr():
prefix = self.type.get_result_ptr_type_prefix()
if self.parent.is_same_side(self.type.get_ptr_type()):
return prefix + 'ptr_same'
else:
return prefix + 'ptr_diff'
return 'invalid'
def get_retval_default(self, for_capi):
""" Returns the default return value based on the retval type. """
# start with the default retval attribute, if any.
retval = self.get_attrib_default_retval()
if not retval is None:
if for_capi:
# apply any appropriate C API translations.
if retval == 'true':
return '1'
if retval == 'false':
return '0'
return retval
# next look at the retval type value.
type = self.get_retval_type()
if type == 'simple':
return self.get_type().get_result_simple_default()
elif type == 'bool':
if for_capi:
return '0'
return 'false'
elif type == 'string':
if for_capi:
return 'NULL'
return 'CefString()'
elif type == 'refptr_same' or type == 'refptr_diff' or \
type == 'rawptr_same' or type == 'rawptr_diff':
if for_capi:
return 'NULL'
return 'nullptr'
elif type == 'ownptr_same' or type == 'ownptr_diff':
if for_capi:
return 'NULL'
return 'CefOwnPtr<' + self.type.get_ptr_type() + '>()'
return ''
class obj_analysis:
""" Class representing an analysis of a data type value. """
def __init__(self, scopelist, value, named):
self.value = value
self.result_type = 'unknown'
self.result_value = None
self.result_default = None
self.ptr_type = None
# parse the argument string
partlist = value.strip().split()
if named == True:
# extract the name value
self.name = partlist[-1]
del partlist[-1]
else:
self.name = None
if len(partlist) == 0:
raise Exception('Invalid argument value: ' + value)
# check const status
if partlist[0] == 'const':
self.isconst = True
del partlist[0]
else:
self.isconst = False
if len(partlist) == 0:
raise Exception('Invalid argument value: ' + value)
# combine the data type
self.type = ' '.join(partlist)
# extract the last character of the data type
endchar = self.type[-1]
# check if the value is passed by reference
if endchar == '&':
self.isbyref = True
self.type = self.type[:-1]
else:
self.isbyref = False
# check if the value is passed by address
if endchar == '*':
self.isbyaddr = True
self.type = self.type[:-1]
else:
self.isbyaddr = False
# see if the value is directly identifiable
if self._check_advanced(self.type) == True:
return
# not identifiable, so look it up
translation = None
for scope in scopelist:
if not isinstance(scope, obj_header) \
and not isinstance(scope, obj_class):
raise Exception('Invalid scope object type')
translation = scope.get_alias_translation(self.type)
if not translation is None:
break
if translation is None:
raise Exception('Failed to translate type: ' + self.type)
# the translation succeeded so keep the result
self.result_type = translation.result_type
self.result_value = translation.result_value
def _check_advanced(self, value):
# check for vectors
if value.find('std::vector') == 0:
self.result_type = 'vector'
val = value[12:-1].strip()
self.result_value = [self._get_basic(val)]
self.result_value[0]['vector_type'] = val
return True
# check for maps
if value.find('std::map') == 0:
self.result_type = 'map'
vals = value[9:-1].split(',')
if len(vals) == 2:
self.result_value = [
self._get_basic(vals[0].strip()),
self._get_basic(vals[1].strip())
]
return True
# check for multimaps
if value.find('std::multimap') == 0:
self.result_type = 'multimap'
vals = value[14:-1].split(',')
if len(vals) == 2:
self.result_value = [
self._get_basic(vals[0].strip()),
self._get_basic(vals[1].strip())
]
return True
# check for basic types
basic = self._get_basic(value)
if not basic is None:
self.result_type = basic['result_type']
self.result_value = basic['result_value']
if 'ptr_type' in basic:
self.ptr_type = basic['ptr_type']
if 'result_default' in basic:
self.result_default = basic['result_default']
return True
return False
def _get_basic(self, value):
# check for string values
if value == "CefString":
return {'result_type': 'string', 'result_value': None}
# check for simple direct translations
if value in _simpletypes.keys():
return {
'result_type': 'simple',
'result_value': _simpletypes[value][0],
'result_default': _simpletypes[value][1],
}
# check if already a C API structure
if value[-2:] == '_t':
return {'result_type': 'structure', 'result_value': value}
# check for CEF reference pointers
p = re.compile(r'^CefRefPtr<(.*?)>$', re.DOTALL)
list = p.findall(value)
if len(list) == 1:
return {
'result_type': 'refptr',
'result_value': get_capi_name(list[0], True) + '*',
'ptr_type': list[0]
}
# check for CEF owned pointers
p = re.compile(r'^CefOwnPtr<(.*?)>$', re.DOTALL)
list = p.findall(value)
if len(list) == 1:
return {
'result_type': 'ownptr',
'result_value': get_capi_name(list[0], True) + '*',
'ptr_type': list[0]
}
# check for CEF raw pointers
p = re.compile(r'^CefRawPtr<(.*?)>$', re.DOTALL)
list = p.findall(value)
if len(list) == 1:
return {
'result_type': 'rawptr',
'result_value': get_capi_name(list[0], True) + '*',
'ptr_type': list[0]
}
# check for CEF structure types
if value[0:3] == 'Cef' and value[-4:] != 'List':
return {
'result_type': 'structure',
'result_value': get_capi_name(value, True)
}
return None
def __repr__(self):
return '(' + self.result_type + ') ' + str(self.result_value)
def has_name(self):
""" Returns true if a name value exists. """
return (not self.name is None)
def get_name(self):
""" Return the name. """
return self.name
def get_value(self):
""" Return the C++ value (type + name). """
return self.value
def get_type(self):
""" Return the C++ type. """
return self.type
def get_ptr_type(self):
""" Return the C++ class type referenced by a CefRefPtr. """
if self.is_result_vector() and self.is_result_vector_ptr():
# return the vector RefPtr type
return self.result_value[0]['ptr_type']
# return the basic RefPtr type
return self.ptr_type
def get_vector_type(self):
""" Return the C++ class type referenced by a std::vector. """
if self.is_result_vector():
return self.result_value[0]['vector_type']
return None
def is_const(self):
""" Returns true if the argument value is constant. """
return self.isconst
def is_byref(self):
""" Returns true if the argument is passed by reference. """
return self.isbyref
def is_byaddr(self):
""" Returns true if the argument is passed by address. """
return self.isbyaddr
def is_result_simple(self):
""" Returns true if this is a simple argument type. """
return (self.result_type == 'simple')
def get_result_simple_type_root(self):
""" Return the simple structure or basic type name. """
return self.result_value
def get_result_simple_type(self):
""" Return the simple type. """
result = ''
if self.is_const():
result += 'const '
result += self.result_value
if self.is_byaddr() or self.is_byref():
result += '*'
return result
def get_result_simple_default(self):
""" Return the default value fo the basic type. """
return self.result_default
def is_result_ptr(self):
""" Returns true if this is a *Ptr type. """
return self.is_result_refptr() or self.is_result_ownptr() or \
self.is_result_rawptr()
def get_result_ptr_type_root(self):
""" Return the *Ptr type structure name. """
return self.result_value[:-1]
def get_result_ptr_type(self, defined_structs=[]):
""" Return the *Ptr type. """
result = ''
if not self.result_value[:-1] in defined_structs:
result += 'struct _'
result += self.result_value
if self.is_byref() or self.is_byaddr():
result += '*'
return result
def get_result_ptr_type_prefix(self):
""" Returns the *Ptr type prefix. """
if self.is_result_refptr():
return 'ref'
if self.is_result_ownptr():
return 'own'
if self.is_result_rawptr():
return 'raw'
raise Exception('Not a pointer type')
def is_result_refptr(self):
""" Returns true if this is a RefPtr type. """
return (self.result_type == 'refptr')
def is_result_ownptr(self):
""" Returns true if this is a OwnPtr type. """
return (self.result_type == 'ownptr')
def is_result_rawptr(self):
""" Returns true if this is a RawPtr type. """
return (self.result_type == 'rawptr')
def is_result_struct(self):
""" Returns true if this is a structure type. """
return (self.result_type == 'structure')
def is_result_struct_enum(self):
""" Returns true if this struct type is likely an enumeration. """
# structure values that are passed by reference or address must be
# structures and not enumerations
if not self.is_byref() and not self.is_byaddr():
return True
return False
def get_result_struct_type(self, defined_structs=[]):
""" Return the structure or enumeration type. """
result = ''
is_enum = self.is_result_struct_enum()
if not is_enum:
if self.is_const():
result += 'const '
if not self.result_value in defined_structs:
result += 'struct _'
result += self.result_value
if not is_enum:
result += '*'
return result
def is_result_string(self):
""" Returns true if this is a string type. """
return (self.result_type == 'string')
def get_result_string_type(self):
""" Return the string type. """
if not self.has_name():
# Return values are string structs that the user must free. Use
# the name of the structure as a hint.
return 'cef_string_userfree_t'
elif not self.is_const() and (self.is_byref() or self.is_byaddr()):
# Parameters passed by reference or address. Use the normal
# non-const string struct.
return 'cef_string_t*'
# Const parameters use the const string struct.
return 'const cef_string_t*'
def is_result_vector(self):
""" Returns true if this is a vector type. """
return (self.result_type == 'vector')
def is_result_vector_string(self):
""" Returns true if this is a string vector. """
return self.result_value[0]['result_type'] == 'string'
def is_result_vector_simple(self):
""" Returns true if this is a string vector. """
return self.result_value[0]['result_type'] == 'simple'
def is_result_vector_ptr(self):
""" Returns true if this is a *Ptr vector. """
return self.is_result_vector_refptr() or \
self.is_result_vector_ownptr() or \
self.is_result_vector_rawptr()
def get_result_vector_ptr_type_prefix(self):
""" Returns the *Ptr type prefix. """
if self.is_result_vector_refptr():
return 'ref'
if self.is_result_vector_ownptr():
return 'own'
if self.is_result_vector_rawptr():
return 'raw'
raise Exception('Not a pointer type')
def is_result_vector_refptr(self):
""" Returns true if this is a RefPtr vector. """
return self.result_value[0]['result_type'] == 'refptr'
def is_result_vector_ownptr(self):
""" Returns true if this is a OwnPtr vector. """
return self.result_value[0]['result_type'] == 'ownptr'
def is_result_vector_rawptr(self):
""" Returns true if this is a RawPtr vector. """
return self.result_value[0]['result_type'] == 'rawptr'
def get_result_vector_type_root(self):
""" Return the vector structure or basic type name. """
return self.result_value[0]['result_value']
def get_result_vector_type(self, defined_structs=[]):
""" Return the vector type. """
if not self.has_name():
raise Exception('Cannot use vector as a return type')
type = self.result_value[0]['result_type']
value = self.result_value[0]['result_value']
result = {}
if type == 'string':
result['value'] = 'cef_string_list_t'
result['format'] = 'single'
return result
if type == 'simple':
str = value
if self.is_const():
str += ' const'
str += '*'
result['value'] = str
elif type == 'refptr' or type == 'ownptr' or type == 'rawptr':
str = ''
if not value[:-1] in defined_structs:
str += 'struct _'
str += value
if self.is_const():
str += ' const'
str += '*'
result['value'] = str
else:
raise Exception('Unsupported vector type: ' + type)
# vector values must be passed as a value array parameter
# and a size parameter
result['format'] = 'multi-arg'
return result
def is_result_map(self):
""" Returns true if this is a map type. """
return (self.result_type == 'map' or self.result_type == 'multimap')
def is_result_map_single(self):
""" Returns true if this is a single map type. """
return (self.result_type == 'map')
def is_result_map_multi(self):
""" Returns true if this is a multi map type. """
return (self.result_type == 'multimap')
def get_result_map_type(self, defined_structs=[]):
""" Return the map type. """
if not self.has_name():
raise Exception('Cannot use map as a return type')
if self.result_value[0]['result_type'] == 'string' \
and self.result_value[1]['result_type'] == 'string':
if self.result_type == 'map':
return {'value': 'cef_string_map_t', 'format': 'single'}
elif self.result_type == 'multimap':
return {'value': 'cef_string_multimap_t', 'format': 'multi'}
raise Exception('Only mappings of strings to strings are supported')
def get_capi(self, defined_structs=[]):
""" Format the value for the C API. """
result = ''
format = 'single'
if self.is_result_simple():
result += self.get_result_simple_type()
elif self.is_result_ptr():
result += self.get_result_ptr_type(defined_structs)
elif self.is_result_struct():
result += self.get_result_struct_type(defined_structs)
elif self.is_result_string():
result += self.get_result_string_type()
elif self.is_result_map():
resdict = self.get_result_map_type(defined_structs)
if resdict['format'] == 'single' or resdict['format'] == 'multi':
result += resdict['value']
else:
raise Exception('Unsupported map type')
elif self.is_result_vector():
resdict = self.get_result_vector_type(defined_structs)
if resdict['format'] != 'single':
format = resdict['format']
result += resdict['value']
if self.has_name():
result += ' ' + self.get_name()
return {'format': format, 'value': result}
# test the module
if __name__ == "__main__":
import pprint
import sys
# verify that the correct number of command-line arguments are provided
if len(sys.argv) != 2:
sys.stderr.write('Usage: ' + sys.argv[0] + ' <directory>')
sys.exit()
pp = pprint.PrettyPrinter(indent=4)
# create the header object
header = obj_header()
header.add_directory(sys.argv[1])
# output the type mapping
types = {}
header.get_types(types)
pp.pprint(types)
sys.stdout.write('\n')
# output the parsed C++ data
sys.stdout.write(str(header))
# output the C API formatted data
defined_names = header.get_defined_structs()
result = ''
# global functions
funcs = header.get_funcs()
if len(funcs) > 0:
for func in funcs:
result += func.get_capi_proto(defined_names, True) + ';\n'
result += '\n'
classes = header.get_classes()
for cls in classes:
# virtual functions are inside a structure
result += 'struct ' + cls.get_capi_name() + '\n{\n'
funcs = cls.get_virtual_funcs()
if len(funcs) > 0:
for func in funcs:
result += '\t' + func.get_capi_proto(defined_names, True) + ';\n'
result += '}\n\n'
defined_names.append(cls.get_capi_name())
# static functions become global
funcs = cls.get_static_funcs()
if len(funcs) > 0:
for func in funcs:
result += func.get_capi_proto(defined_names, True) + ';\n'
result += '\n'
sys.stdout.write(result)