2041 lines
62 KiB
Python
2041 lines
62 KiB
Python
# 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 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):
|
|
""" Wrap the text to the specified number of characters. If
|
|
necessary a line will be broken and wrapped after a word.
|
|
"""
|
|
result = ''
|
|
lines = textwrap.wrap(text, maxchars - len(indent))
|
|
for line in lines:
|
|
result += indent + line + '\n'
|
|
return result
|
|
|
|
|
|
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 string.upper(chr) == chr \
|
|
and not string.upper(lastchr) == lastchr:
|
|
result += '_'
|
|
result += string.lower(chr)
|
|
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 = string.rfind(body, '\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)
|
|
while pos > 0:
|
|
data = get_prev_line(body, pos)
|
|
line = string.strip(data['line'])
|
|
pos = data['start']
|
|
if len(line) == 0:
|
|
# check if the next previous line is a comment
|
|
prevdata = get_prev_line(body, pos)
|
|
if string.strip(prevdata['line'])[0:2] == '//':
|
|
result.append(None)
|
|
else:
|
|
break
|
|
elif line[0:2] == '/*' or line[-2:] == '*/':
|
|
continue
|
|
elif line[0:2] == '//':
|
|
# keep the comment line including any leading spaces
|
|
result.append(line[2:])
|
|
else:
|
|
break
|
|
|
|
result.reverse()
|
|
return result
|
|
|
|
|
|
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
|
|
# CppDoc (the leading '//' from each line will already have been removed by
|
|
# the get_comment() logic). There may be additional comments proceeding the
|
|
# CppDoc block so we look at the quantity of lines equaling '/' and expect
|
|
# the last line to be '/'.
|
|
docct = 0
|
|
for line in comment:
|
|
if not line is None and len(line) > 0 and line == '/':
|
|
docct = docct + 1
|
|
if docct != 2 or len(comment) < 3 or comment[len(comment) - 1] != '/':
|
|
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. """
|
|
result = ''
|
|
wrapme = ''
|
|
hasemptyline = 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:1] == ' ':
|
|
line = line[1:]
|
|
didremovespace = True
|
|
else:
|
|
didremovespace = False
|
|
|
|
if line is None or len(line) == 0 or line[0:1] == ' ' \
|
|
or line[0:1] == '/':
|
|
# the previous paragraph, if any, has ended
|
|
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)
|
|
wrapme = ''
|
|
|
|
if not line is None:
|
|
if len(line) == 0 or line[0:1] == ' ' or line[0:1] == '/':
|
|
# 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'
|
|
else:
|
|
# 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)
|
|
|
|
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_version.h"\n'
|
|
|
|
# identify what CppToC classes are being used
|
|
p = re.compile('([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('([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('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 = string.split(str, ',')
|
|
for part in parts:
|
|
part = string.strip(part)
|
|
if len(part) == 0:
|
|
continue
|
|
sparts = string.split(part, '=')
|
|
if len(sparts) > 2:
|
|
raise Exception('Invalid dictionary pair format: ' + part)
|
|
name = string.strip(sparts[0])
|
|
if len(sparts) == 2:
|
|
val = string.strip(sparts[1])
|
|
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 string.join(str, ',')
|
|
|
|
|
|
# regex for matching comment-formatted attributes
|
|
_cre_attrib = '/\*--cef\(([A-Za-z0-9_ ,=:\n]{0,})\)--\*/'
|
|
# regex for matching class and function names
|
|
_cre_cfname = '([A-Za-z0-9_]{1,})'
|
|
# regex for matching class and function names including path separators
|
|
_cre_cfnameorpath = '([A-Za-z0-9_\/]{1,})'
|
|
# regex for matching function return values
|
|
_cre_retval = '([A-Za-z0-9_<>:,\*\&]{1,})'
|
|
# regex for matching typedef value and name combination
|
|
_cre_typedef = '([A-Za-z0-9_<>:,\*\&\s]{1,})'
|
|
# regex for matching function return value and name combination
|
|
_cre_func = '([A-Za-z][A-Za-z0-9_<>:,\*\&\s]{1,})'
|
|
# regex for matching virtual function modifiers + arbitrary whitespace
|
|
_cre_vfmod = '([\sA-Za-z0-9_]{0,})'
|
|
# regex for matching arbitrary whitespace
|
|
_cre_space = '[\s]{1,}'
|
|
# regex for matching optional virtual keyword
|
|
_cre_virtual = '(?:[\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'],
|
|
'int32': ['int32', '0'],
|
|
'uint32': ['uint32', '0'],
|
|
'int64': ['int64', '0'],
|
|
'uint64': ['uint64', '0'],
|
|
'double': ['double', '0'],
|
|
'float': ['float', '0'],
|
|
'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'],
|
|
'cef_plugin_policy_t': ['cef_plugin_policy_t', 'PLUGIN_POLICY_ALLOW'],
|
|
'CefCursorHandle': ['cef_cursor_handle_t', 'kNullCursorHandle'],
|
|
'CefCompositionUnderline': [
|
|
'cef_composition_underline_t', 'CefCompositionUnderline()'
|
|
],
|
|
'CefEventHandle': ['cef_event_handle_t', 'kNullEventHandle'],
|
|
'CefWindowHandle': ['cef_window_handle_t', 'kNullWindowHandle'],
|
|
'CefPoint': ['cef_point_t', 'CefPoint()'],
|
|
'CefRect': ['cef_rect_t', 'CefRect()'],
|
|
'CefSize': ['cef_size_t', 'CefSize()'],
|
|
'CefRange': ['cef_range_t', 'CefRange()'],
|
|
'CefDraggableRegion': ['cef_draggable_region_t', 'CefDraggableRegion()'],
|
|
'CefThreadId': ['cef_thread_id_t', 'TID_UI'],
|
|
'CefTime': ['cef_time_t', 'CefTime()'],
|
|
}
|
|
|
|
|
|
def get_function_impls(content, ident):
|
|
""" 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.
|
|
"""
|
|
# extract the functions
|
|
p = re.compile(
|
|
'\n' + _cre_func + '\((.*?)\)([A-Za-z0-9_\s]{0,})' + '\{(.*?)\n\}',
|
|
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 = string.replace(retval, ident, '')
|
|
retval = string.strip(retval)
|
|
|
|
# retrieve the function name
|
|
parts = string.split(retval, ' ')
|
|
name = parts[-1]
|
|
del parts[-1]
|
|
retval = string.join(parts, ' ')
|
|
|
|
# parse the arguments
|
|
args = []
|
|
for v in string.split(argval, ','):
|
|
v = string.strip(v)
|
|
if len(v) > 0:
|
|
args.append(v)
|
|
|
|
result.append({
|
|
'retval': string.strip(retval),
|
|
'name': name,
|
|
'args': args,
|
|
'vfmod': string.strip(vfmod),
|
|
'body': body
|
|
})
|
|
|
|
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():
|
|
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.
|
|
//
|
|
// ---------------------------------------------------------------------------
|
|
//
|
|
// 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 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('\\', '/')
|
|
|
|
# read the input file into memory
|
|
self.add_data(filename, read_file(filepath))
|
|
|
|
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('\ntypedef' + _cre_space + _cre_typedef + ';',
|
|
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('\n' + _cre_attrib + '\n' + _cre_func + '\((.*?)\)',
|
|
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('\n#include \"include/' + _cre_cfnameorpath + '.h')
|
|
includes = p.findall(data)
|
|
|
|
# extract forward declarations
|
|
p = re.compile('\nclass' + _cre_space + _cre_cfname + ';')
|
|
forward_declares = p.findall(data)
|
|
|
|
# extract classes
|
|
p = re.compile('\n' + _cre_attrib + '\nclass' + _cre_space + _cre_cfname +
|
|
_cre_space + ':' + _cre_space + 'public' + _cre_virtual +
|
|
_cre_space + _cre_cfname + _cre_space + '{(.*?)\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))
|
|
|
|
# extract empty classes
|
|
p = re.compile('\n' + _cre_attrib + '\nclass' + _cre_space + _cre_cfname +
|
|
_cre_space + ':' + _cre_space + 'public' + _cre_virtual +
|
|
_cre_space + _cre_cfname + _cre_space + '{};',
|
|
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))
|
|
|
|
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 += string.join(strlist, "\n") + "\n\n"
|
|
|
|
if len(self.funcs) > 0:
|
|
strlist = []
|
|
for cls in self.funcs:
|
|
strlist.append(str(cls))
|
|
result += string.join(strlist, "\n") + "\n\n"
|
|
|
|
if len(self.classes) > 0:
|
|
strlist = []
|
|
for cls in self.classes:
|
|
strlist.append(str(cls))
|
|
result += string.join(strlist, "\n")
|
|
|
|
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(
|
|
'\n' + _cre_space + 'typedef' + _cre_space + _cre_typedef + ';',
|
|
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('\n' + _cre_space + _cre_attrib + '\n' + _cre_space +
|
|
'static' + _cre_space + _cre_func + '\((.*?)\)',
|
|
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(
|
|
'\n' + _cre_space + _cre_attrib + '\n' + _cre_space + 'virtual' +
|
|
_cre_space + _cre_func + '\((.*?)\)' + _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 += string.join(strlist, "\n\t")
|
|
|
|
if len(self.staticfuncs) > 0:
|
|
result += "\n\t"
|
|
strlist = []
|
|
for cls in self.staticfuncs:
|
|
strlist.append(str(cls))
|
|
result += string.join(strlist, "\n\t")
|
|
|
|
if len(self.virtualfuncs) > 0:
|
|
result += "\n\t"
|
|
strlist = []
|
|
for cls in self.virtualfuncs:
|
|
strlist.append(str(cls))
|
|
result += string.join(strlist, "\n\t")
|
|
|
|
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 = string.split(argval, ',')
|
|
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 = string.strip(arg)
|
|
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=[], 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)
|
|
|
|
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=[], prefix=None):
|
|
""" Return the prototype of the C API function. """
|
|
parts = self.get_capi_parts(defined_structs, prefix)
|
|
result = parts['retval']+' '+parts['name']+ \
|
|
'('+string.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'] + '(' + string.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 = string.split(val, ':')
|
|
if len(parts) != 2:
|
|
raise Exception("Invalid 'count_func' attribute value for "+ \
|
|
self.parent.get_qualified_name()+': '+val)
|
|
if string.strip(parts[0]) == name:
|
|
return string.strip(parts[1])
|
|
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':
|
|
return 'NULL'
|
|
elif type == 'ownptr_same' or type == 'ownptr_diff':
|
|
if for_capi:
|
|
return 'NULL'
|
|
else:
|
|
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 = string.split(string.strip(value))
|
|
|
|
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 = string.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 = string.strip(value[12:-1])
|
|
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 = string.split(value[9:-1], ',')
|
|
if len(vals) == 2:
|
|
self.result_value = [
|
|
self._get_basic(string.strip(vals[0])),
|
|
self._get_basic(string.strip(vals[1]))
|
|
]
|
|
return True
|
|
|
|
# check for multimaps
|
|
if value.find('std::multimap') == 0:
|
|
self.result_type = 'multimap'
|
|
vals = string.split(value[14:-1], ',')
|
|
if len(vals) == 2:
|
|
self.result_value = [
|
|
self._get_basic(string.strip(vals[0])),
|
|
self._get_basic(string.strip(vals[1]))
|
|
]
|
|
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('^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('^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('^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) + ';\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) + ';\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) + ';\n'
|
|
result += '\n'
|
|
sys.stdout.write(result)
|