mirror of
https://bitbucket.org/chromiumembedded/cef
synced 2025-06-05 21:39:12 +02:00
Add initial support for API versioning (see #3836)
- Generated files are now created when running cef_create_projects or the new version_manager.py tool. These files are still created in the cef/ source tree (same location as before) but Git ignores them due to the generated .gitignore file. - API hashes are committed to Git as a new cef_api_versions.json file. This file is used for both code generation and CEF version calculation (replacing the previous usage of cef_api_hash.h for this purpose). It will be updated by the CEF admin before merging breaking API changes upstream. - As an added benefit to the above, contributor PRs will no longer contain generated code that is susceptible to frequent merge conflicts. - From a code generation perspective, the main difference is that we now use versioned structs (e.g. cef_browser_0_t instead of cef_browser_t) on the libcef (dll/framework) side. Most of the make_*.py tool changes are related to supporting this. - From the client perspective, you can now define CEF_API_VERSION in the project configuration (or get CEF_EXPERIMENTAL by default). This define will change the API exposed in CEF’s include/ and include/capi header files. All client-side targets including libcef_dll_wrapper will need be recompiled when changing this define. - Examples of the new API-related define usage are provided in cef_api_version_test.h, api_version_test_impl.cc and api_version_unittest.cc. To test: - Run `ceftests --gtest_filter=ApiVersionTest.*` - Add `cef_api_version=13300` to GN_DEFINES. Re-run configure, build and ceftests steps. - Repeat with 13301, 13302, 13303 (all supported test versions).
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
# can be found in the LICENSE file.
|
||||
|
||||
from __future__ import absolute_import
|
||||
import bisect
|
||||
from date_util import *
|
||||
from file_util import *
|
||||
import os
|
||||
@@ -12,11 +13,26 @@ import string
|
||||
import sys
|
||||
import textwrap
|
||||
import time
|
||||
from version_util import version_as_numeric, version_as_variable
|
||||
|
||||
_NOTIFY_CONTEXT = None
|
||||
_NOTIFY_CONTEXT_LAST = None
|
||||
|
||||
|
||||
def set_notify_context(context):
|
||||
global _NOTIFY_CONTEXT
|
||||
_NOTIFY_CONTEXT = context
|
||||
|
||||
|
||||
def notify(msg):
|
||||
""" Display a message. """
|
||||
sys.stdout.write(' NOTE: ' + msg + '\n')
|
||||
global _NOTIFY_CONTEXT_LAST
|
||||
|
||||
if not _NOTIFY_CONTEXT is None and _NOTIFY_CONTEXT != _NOTIFY_CONTEXT_LAST:
|
||||
print('In %s:' % _NOTIFY_CONTEXT)
|
||||
_NOTIFY_CONTEXT_LAST = _NOTIFY_CONTEXT
|
||||
|
||||
print(' NOTE: ' + msg)
|
||||
|
||||
|
||||
def wrap_text(text, indent='', maxchars=80, listitem=False):
|
||||
@@ -44,25 +60,28 @@ def is_base_class(clsname):
|
||||
return clsname == 'CefBaseRefCounted' or clsname == 'CefBaseScoped'
|
||||
|
||||
|
||||
def get_capi_file_name(cppname):
|
||||
def get_capi_file_name(cppname, versions=False):
|
||||
""" Convert a C++ header file name to a C API header file name. """
|
||||
return cppname[:-2] + '_capi.h'
|
||||
return cppname[:-2] + ('_capi_versions.h' if versions else '_capi.h')
|
||||
|
||||
|
||||
def get_capi_name(cppname, isclassname, prefix=None):
|
||||
def get_capi_name(cppname, isclassname, prefix=None, version=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 the last character was a lower case letter or number.
|
||||
if len(result) > 0 and chr.isalpha() \
|
||||
and chr.upper() == chr \
|
||||
and not lastchr.upper() == lastchr:
|
||||
and lastchr.isalnum() and lastchr.lower() == lastchr:
|
||||
result += '_'
|
||||
result += chr.lower()
|
||||
lastchr = chr
|
||||
|
||||
if isclassname and not version is None:
|
||||
result += '_%d' % version
|
||||
|
||||
if isclassname:
|
||||
result += '_t'
|
||||
|
||||
@@ -259,7 +278,10 @@ def format_translation_changes(old, new):
|
||||
return result
|
||||
|
||||
|
||||
def format_translation_includes(header, body):
|
||||
def format_translation_includes(header,
|
||||
body,
|
||||
with_versions=False,
|
||||
other_includes=None):
|
||||
""" Return the necessary list of includes based on the contents of the
|
||||
body.
|
||||
"""
|
||||
@@ -269,47 +291,187 @@ def format_translation_includes(header, body):
|
||||
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'
|
||||
paths = set()
|
||||
|
||||
if body.find('cef_api_hash(') > 0 or body.find('cef_api_version(') > 0:
|
||||
paths.add('include/cef_api_hash.h')
|
||||
|
||||
if body.find('template_util::has_valid_size(') > 0:
|
||||
result += '#include "libcef_dll/template_util.h"\n'
|
||||
paths.add('libcef_dll/template_util.h')
|
||||
|
||||
# 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'
|
||||
search = ((True, True, r'([A-Za-z0-9_]{1,})_[0-9]{1,}_CppToC'),
|
||||
(True, False, r'([A-Za-z0-9_]{1,})CppToC'),
|
||||
(False, True, r'([A-Za-z0-9_]{1,})_[0-9]{1,}_CToCpp'),
|
||||
(False, False, r'([A-Za-z0-9_]{1,})CToCpp'))
|
||||
for cpptoc, versioned, regex in search:
|
||||
# identify what classes are being used
|
||||
p = re.compile(regex)
|
||||
items = set(p.findall(body))
|
||||
for item in items:
|
||||
if item == 'Cef':
|
||||
continue
|
||||
if not versioned and item[-1] == '_':
|
||||
# skip versioned names that are picked up by the unversioned regex
|
||||
continue
|
||||
directory = ''
|
||||
if not is_base_class(item):
|
||||
cls = header.get_class(item)
|
||||
if cls is None:
|
||||
raise Exception('Class does not exist: ' + item)
|
||||
dir = cls.get_file_directory()
|
||||
if not dir is None:
|
||||
directory = dir + '/'
|
||||
type = 'cpptoc' if cpptoc else 'ctocpp'
|
||||
paths.add('libcef_dll/' + type + '/'+directory+ \
|
||||
get_capi_name(item[3:], False)+'_' + type + '.h')
|
||||
|
||||
if body.find('shutdown_checker') > 0:
|
||||
result += '#include "libcef_dll/shutdown_checker.h"\n'
|
||||
paths.add('libcef_dll/shutdown_checker.h')
|
||||
|
||||
if body.find('transfer_') > 0:
|
||||
result += '#include "libcef_dll/transfer_util.h"\n'
|
||||
paths.add('libcef_dll/transfer_util.h')
|
||||
|
||||
if not other_includes is None:
|
||||
paths.update(other_includes)
|
||||
|
||||
if len(paths) > 0:
|
||||
if len(result) > 0:
|
||||
result += '\n'
|
||||
|
||||
paths = sorted(list(paths))
|
||||
result += '\n'.join(['#include "%s"' % p for p in paths]) + '\n'
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def format_notreached(library_side, msg, default_retval='', indent=' '):
|
||||
if library_side:
|
||||
return 'NOTREACHED() << __func__ << ' + msg + ';'
|
||||
return 'CHECK(false) << __func__ << ' + msg + ';\n' + \
|
||||
indent + 'return%s;' % ((' ' + default_retval) if len(default_retval) > 0 else '')
|
||||
|
||||
|
||||
def _has_version_added(attribs):
|
||||
return 'added' in attribs
|
||||
|
||||
|
||||
def _has_version_removed(attribs):
|
||||
return 'removed' in attribs
|
||||
|
||||
|
||||
def _has_version(attribs):
|
||||
return _has_version_added(attribs) or _has_version_removed(attribs)
|
||||
|
||||
|
||||
def get_version_check(attribs):
|
||||
assert _has_version(attribs)
|
||||
|
||||
added = attribs.get('added', None)
|
||||
if not added is None:
|
||||
added = version_as_variable(added)
|
||||
removed = attribs.get('removed', None)
|
||||
if not removed is None:
|
||||
removed = version_as_variable(removed)
|
||||
|
||||
if not added is None and not removed is None:
|
||||
return 'CEF_API_RANGE(%s, %s)' % (added, removed)
|
||||
elif not added is None:
|
||||
return 'CEF_API_ADDED(%s)' % added
|
||||
return 'CEF_API_REMOVED(%s)' % removed
|
||||
|
||||
|
||||
def _get_version_attrib(attribs, key):
|
||||
value = attribs.get(key, None)
|
||||
if not value is None:
|
||||
return version_as_numeric(value)
|
||||
|
||||
# Unversioned is always the first value.
|
||||
return 0
|
||||
|
||||
|
||||
def _get_version_added(attribs):
|
||||
""" Returns a numeric 'added' value used for sorting purposes. """
|
||||
return _get_version_attrib(attribs, 'added')
|
||||
|
||||
|
||||
def _get_version_removed(attribs):
|
||||
""" Returns a numeric 'removed' value used for sorting purposes. """
|
||||
return _get_version_attrib(attribs, 'removed')
|
||||
|
||||
|
||||
def get_version_surround(obj, long=False):
|
||||
""" Returns (pre,post) strings for a version check. """
|
||||
version_check = obj.get_version_check() if obj.has_version() else None
|
||||
|
||||
# Don't duplicate the surrounding class version check for a virtual method.
|
||||
if not version_check is None and \
|
||||
isinstance(obj, obj_function) and isinstance(obj.parent, obj_class) and \
|
||||
obj.parent.has_version() and obj.parent.get_version_check() == version_check:
|
||||
version_check = None
|
||||
|
||||
if not version_check is None:
|
||||
return ('#if %s\n' % version_check, ('#endif // %s\n' % version_check)
|
||||
if long else '#endif\n')
|
||||
return ('', '')
|
||||
|
||||
|
||||
def get_clsname(cls, version):
|
||||
name = cls.get_name()
|
||||
|
||||
if not version is None:
|
||||
# Select the appropriate version for this class.
|
||||
closest_version = cls.get_closest_version(version)
|
||||
if closest_version is None:
|
||||
raise Exception('Cannot find version <= %d for %s' % (version, name))
|
||||
return '%s_%d_' % (name, closest_version)
|
||||
|
||||
return name
|
||||
|
||||
|
||||
def _version_order_funcs(funcs, max_version=None):
|
||||
""" Applies version-based ordering to a list of funcs. """
|
||||
versions = {0: []}
|
||||
|
||||
for func in funcs:
|
||||
if func.has_version():
|
||||
added = func.get_version_added()
|
||||
if not added in versions:
|
||||
versions[added] = [func]
|
||||
else:
|
||||
versions[added].append(func)
|
||||
else:
|
||||
# Unversioned funcs.
|
||||
versions[0].append(func)
|
||||
|
||||
result = []
|
||||
for version in sorted(versions.keys()):
|
||||
if not max_version is None and version > max_version:
|
||||
break
|
||||
result.extend(versions[version])
|
||||
return result
|
||||
|
||||
|
||||
def _get_all_versions(funcs):
|
||||
# Using a set to ensure uniqueness.
|
||||
versions = set({0})
|
||||
|
||||
for func in funcs:
|
||||
if func.has_version():
|
||||
versions.add(func.get_version_added())
|
||||
versions.add(func.get_version_removed())
|
||||
|
||||
return versions
|
||||
|
||||
|
||||
def _find_closest_not_greater(lst, target):
|
||||
assert isinstance(lst, list), lst
|
||||
assert isinstance(target, int), target
|
||||
idx = bisect.bisect_right(lst, target) - 1
|
||||
if idx < 0:
|
||||
return None
|
||||
return lst[idx]
|
||||
|
||||
|
||||
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. """
|
||||
@@ -357,6 +519,13 @@ def dict_to_str(dict):
|
||||
return ','.join(str)
|
||||
|
||||
|
||||
# Attribute keys allowed in CEF metadata comments.
|
||||
COMMON_ATTRIB_KEYS = ('added', 'removed')
|
||||
CLASS_ATTRIB_KEYS = COMMON_ATTRIB_KEYS + ('no_debugct_check', 'source')
|
||||
FUNCTION_ATTRIB_KEYS = COMMON_ATTRIB_KEYS + ('api_hash_check', 'capi_name',
|
||||
'count_func', 'default_retval',
|
||||
'index_param', 'optional_param')
|
||||
|
||||
# regex for matching comment-formatted attributes
|
||||
_cre_attrib = r'/\*--cef\(([A-Za-z0-9_ ,=:\n]{0,})\)--\*/'
|
||||
# regex for matching class and function names
|
||||
@@ -747,13 +916,18 @@ class obj_header:
|
||||
res.append(cls)
|
||||
return res
|
||||
|
||||
def get_class(self, classname, defined_structs=None):
|
||||
def get_class(self, classname):
|
||||
""" 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_capi_class(self, classname):
|
||||
""" Return the specified class or None if not found. """
|
||||
for cls in self.classes:
|
||||
if cls.get_capi_name() == classname:
|
||||
return cls
|
||||
return None
|
||||
|
||||
def get_class_names(self):
|
||||
@@ -856,6 +1030,8 @@ class obj_class:
|
||||
self.includes = includes
|
||||
self.forward_declares = forward_declares
|
||||
|
||||
self._validate_attribs()
|
||||
|
||||
# extract typedefs
|
||||
p = re.compile(
|
||||
r'\n' + _cre_space + r'typedef' + _cre_space + _cre_typedef + r';',
|
||||
@@ -895,13 +1071,19 @@ class obj_class:
|
||||
|
||||
# build the virtual function objects
|
||||
self.virtualfuncs = []
|
||||
self.has_versioned_funcs = False
|
||||
for attrib, retval, argval, vfmod in list:
|
||||
comment = get_comment(body, retval + '(' + argval + ')')
|
||||
validate_comment(filename, retval, comment)
|
||||
if not self.has_versioned_funcs and _has_version(attrib):
|
||||
self.has_versioned_funcs = True
|
||||
self.virtualfuncs.append(
|
||||
obj_function_virtual(self, attrib, retval, argval, comment,
|
||||
vfmod.strip()))
|
||||
|
||||
self.virtualfuncs_ordered = None
|
||||
self.allversions = None
|
||||
|
||||
def __repr__(self):
|
||||
result = '/* ' + dict_to_str(
|
||||
self.attribs) + ' */ class ' + self.name + "\n{"
|
||||
@@ -930,15 +1112,21 @@ class obj_class:
|
||||
result += "\n};\n"
|
||||
return result
|
||||
|
||||
def _validate_attribs(self):
|
||||
for key in self.attribs.keys():
|
||||
if not key in CLASS_ATTRIB_KEYS:
|
||||
raise Exception('Invalid attribute key \"%s\" for class %s' %
|
||||
(key, self.get_name()))
|
||||
|
||||
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):
|
||||
def get_capi_file_name(self, versions=False):
|
||||
""" Return the CAPI header file name. Includes the directory component,
|
||||
if any. """
|
||||
return get_capi_file_name(self.filename)
|
||||
return get_capi_file_name(self.filename, versions)
|
||||
|
||||
def get_file_directory(self):
|
||||
""" Return the file directory component, if any. """
|
||||
@@ -947,21 +1135,44 @@ class obj_class:
|
||||
return self.filename[:pos]
|
||||
return None
|
||||
|
||||
def get_name(self):
|
||||
def get_name(self, version=None):
|
||||
""" Return the class name. """
|
||||
if not version is None:
|
||||
# Select the appropriate version for this class.
|
||||
closest_version = self.get_closest_version(version)
|
||||
if closest_version is None:
|
||||
raise Exception('Cannot find version <= %d for %s' % (version,
|
||||
self.name))
|
||||
return '%s_%d_' % (self.name, closest_version)
|
||||
return self.name
|
||||
|
||||
def get_capi_name(self):
|
||||
def get_capi_name(self, version=None, first_version=False):
|
||||
""" Return the CAPI structure name for this class. """
|
||||
return get_capi_name(self.name, True)
|
||||
# Select the appropriate version for this class.
|
||||
if first_version:
|
||||
version = self.get_first_version()
|
||||
elif not version is None:
|
||||
closest_version = self.get_closest_version(version)
|
||||
if closest_version is None:
|
||||
raise Exception('Cannot find version <= %d for %s' % (version,
|
||||
self.name))
|
||||
version = closest_version
|
||||
return get_capi_name(self.name, True, version=version)
|
||||
|
||||
def get_parent_name(self):
|
||||
""" Return the parent class name. """
|
||||
return self.parent_name
|
||||
|
||||
def get_parent_capi_name(self):
|
||||
def get_parent_capi_name(self, version=None):
|
||||
""" Return the CAPI structure name for the parent class. """
|
||||
return get_capi_name(self.parent_name, True)
|
||||
if not version is None:
|
||||
# Select the appropriate version for the parent class.
|
||||
if is_base_class(self.parent_name):
|
||||
version = None
|
||||
else:
|
||||
parent_cls = self.parent.get_class(self.parent_name)
|
||||
version = parent_cls.get_closest_version(version)
|
||||
return get_capi_name(self.parent_name, True, version=version)
|
||||
|
||||
def has_parent(self, parent_name):
|
||||
""" Returns true if this class has the specified class anywhere in its
|
||||
@@ -1043,8 +1254,17 @@ class obj_class:
|
||||
""" Return the array of static function objects. """
|
||||
return self.staticfuncs
|
||||
|
||||
def get_virtual_funcs(self):
|
||||
def get_virtual_funcs(self, version_order=False, version=None):
|
||||
""" Return the array of virtual function objects. """
|
||||
if version_order and self.has_versioned_funcs:
|
||||
if version is None:
|
||||
# Cache the ordering result for future use.
|
||||
if self.virtualfuncs_ordered is None:
|
||||
self.virtualfuncs_ordered = _version_order_funcs(self.virtualfuncs)
|
||||
return self.virtualfuncs_ordered
|
||||
|
||||
# Need to order each time to apply the max version.
|
||||
return _version_order_funcs(self.virtualfuncs, version)
|
||||
return self.virtualfuncs
|
||||
|
||||
def get_types(self, list):
|
||||
@@ -1078,6 +1298,75 @@ class obj_class:
|
||||
""" Returns true if the class is implemented by the client. """
|
||||
return self.attribs['source'] == 'client'
|
||||
|
||||
def has_version(self):
|
||||
""" Returns true if the class has an associated version. """
|
||||
return _has_version(self.attribs)
|
||||
|
||||
def has_version_added(self):
|
||||
""" Returns true if the class has an associated 'added' version. """
|
||||
return _has_version_added(self.attribs)
|
||||
|
||||
def get_version_added(self):
|
||||
""" Returns the associated 'added' version. """
|
||||
return _get_version_added(self.attribs)
|
||||
|
||||
def has_version_removed(self):
|
||||
""" Returns true if the class has an associated 'removed' version. """
|
||||
return _has_version_removed(self.attribs)
|
||||
|
||||
def get_version_removed(self):
|
||||
""" Returns the associated 'removed' version. """
|
||||
return _get_version_removed(self.attribs)
|
||||
|
||||
def removed_at_version(self, version):
|
||||
""" Returns true if this class is removed at the specified version. """
|
||||
return self.has_version_removed() and self.get_version_removed() <= version
|
||||
|
||||
def exists_at_version(self, version):
|
||||
""" Returns true if this class exists at the specified version. """
|
||||
if self.has_version_added() and self.get_version_added() > version:
|
||||
return False
|
||||
return not self.removed_at_version(version)
|
||||
|
||||
def get_version_check(self):
|
||||
""" Returns the #if check for the associated version. """
|
||||
return get_version_check(self.attribs)
|
||||
|
||||
def get_all_versions(self):
|
||||
""" Returns all distinct versions of this class. """
|
||||
if not self.allversions is None:
|
||||
return self.allversions
|
||||
|
||||
# Using a set to ensure uniqueness.
|
||||
versions = set()
|
||||
|
||||
# Versions from class inheritance.
|
||||
if not is_base_class(self.parent_name):
|
||||
versions.update(
|
||||
self.parent.get_class(self.parent_name).get_all_versions())
|
||||
|
||||
# Versions from virtual methods.
|
||||
versions.update(_get_all_versions(self.virtualfuncs))
|
||||
|
||||
versions = list(versions)
|
||||
|
||||
# Clamp to class versions, if specified.
|
||||
if self.has_version_added():
|
||||
versions = [x for x in versions if x >= self.get_version_added()]
|
||||
if self.has_version_removed():
|
||||
versions = [x for x in versions if x < self.get_version_removed()]
|
||||
|
||||
self.allversions = sorted(versions)
|
||||
return self.allversions
|
||||
|
||||
def get_first_version(self):
|
||||
""" Returns the first version. """
|
||||
return self.get_all_versions()[0]
|
||||
|
||||
def get_closest_version(self, version):
|
||||
""" Returns the closest version to |version| that is not greater, or None. """
|
||||
return _find_closest_not_greater(self.get_all_versions(), version)
|
||||
|
||||
|
||||
class obj_typedef:
|
||||
""" Class representing a typedef statement. """
|
||||
@@ -1099,9 +1388,9 @@ class obj_typedef:
|
||||
""" Return the C++ header file name. """
|
||||
return self.filename
|
||||
|
||||
def get_capi_file_name(self):
|
||||
def get_capi_file_name(self, versions=False):
|
||||
""" Return the CAPI header file name. """
|
||||
return get_capi_file_name(self.filename)
|
||||
return get_capi_file_name(self.filename, versions)
|
||||
|
||||
def get_alias(self):
|
||||
""" Return the alias. """
|
||||
@@ -1131,6 +1420,8 @@ class obj_function:
|
||||
self.name = self.retval.remove_name()
|
||||
self.comment = comment
|
||||
|
||||
self._validate_attribs()
|
||||
|
||||
# build the argument objects
|
||||
self.arguments = []
|
||||
arglist = argval.split(',')
|
||||
@@ -1163,13 +1454,19 @@ class obj_function:
|
||||
def __repr__(self):
|
||||
return '/* ' + dict_to_str(self.attribs) + ' */ ' + self.get_cpp_proto()
|
||||
|
||||
def _validate_attribs(self):
|
||||
for key in self.attribs.keys():
|
||||
if not key in FUNCTION_ATTRIB_KEYS:
|
||||
raise Exception('Invalid attribute key \"%s\" for %s' %
|
||||
(key, self.get_qualified_name()))
|
||||
|
||||
def get_file_name(self):
|
||||
""" Return the C++ header file name. """
|
||||
return self.filename
|
||||
|
||||
def get_capi_file_name(self):
|
||||
def get_capi_file_name(self, versions=False):
|
||||
""" Return the CAPI header file name. """
|
||||
return get_capi_file_name(self.filename)
|
||||
return get_capi_file_name(self.filename, versions)
|
||||
|
||||
def get_name(self):
|
||||
""" Return the function name. """
|
||||
@@ -1186,9 +1483,7 @@ class obj_function:
|
||||
|
||||
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)
|
||||
return get_capi_name(self.get_attrib('capi_name', self.name), False, prefix)
|
||||
|
||||
def get_comment(self):
|
||||
""" Return the function comment as an array of lines. """
|
||||
@@ -1202,7 +1497,7 @@ class obj_function:
|
||||
""" Return true if the specified attribute exists. """
|
||||
return name in self.attribs
|
||||
|
||||
def get_attrib(self, name):
|
||||
def get_attrib(self, name, default=None):
|
||||
""" Return the first or only value for specified attribute. """
|
||||
if name in self.attribs:
|
||||
if isinstance(self.attribs[name], list):
|
||||
@@ -1211,7 +1506,7 @@ class obj_function:
|
||||
else:
|
||||
# the value is a string
|
||||
return self.attribs[name]
|
||||
return None
|
||||
return default
|
||||
|
||||
def get_attrib_list(self, name):
|
||||
""" Return all values for specified attribute as a list. """
|
||||
@@ -1237,10 +1532,15 @@ class obj_function:
|
||||
for cls in self.arguments:
|
||||
cls.get_types(list)
|
||||
|
||||
def get_capi_parts(self, defined_structs=[], isimpl=False, prefix=None):
|
||||
def get_capi_parts(self,
|
||||
defined_structs=[],
|
||||
isimpl=False,
|
||||
prefix=None,
|
||||
version=None,
|
||||
version_finder=None):
|
||||
""" Return the parts of the C API function definition. """
|
||||
retval = ''
|
||||
dict = self.retval.get_type().get_capi(defined_structs)
|
||||
dict = self.retval.get_type().get_capi(defined_structs, version_finder)
|
||||
if dict['format'] == 'single':
|
||||
retval = dict['value']
|
||||
|
||||
@@ -1249,7 +1549,7 @@ class obj_function:
|
||||
|
||||
if isinstance(self, obj_function_virtual):
|
||||
# virtual functions get themselves as the first argument
|
||||
str = 'struct _' + self.parent.get_capi_name() + '* self'
|
||||
str = 'struct _' + self.parent.get_capi_name(version=version) + '* self'
|
||||
if isinstance(self, obj_function_virtual) and self.is_const():
|
||||
# const virtual functions get const self pointers
|
||||
str = 'const ' + str
|
||||
@@ -1260,7 +1560,7 @@ class obj_function:
|
||||
if len(self.arguments) > 0:
|
||||
for cls in self.arguments:
|
||||
type = cls.get_type()
|
||||
dict = type.get_capi(defined_structs)
|
||||
dict = type.get_capi(defined_structs, version_finder)
|
||||
if dict['format'] == 'single':
|
||||
args.append(dict['value'])
|
||||
elif dict['format'] == 'multi-arg':
|
||||
@@ -1276,9 +1576,15 @@ class obj_function:
|
||||
|
||||
return {'retval': retval, 'name': name, 'args': args}
|
||||
|
||||
def get_capi_proto(self, defined_structs=[], isimpl=False, prefix=None):
|
||||
def get_capi_proto(self,
|
||||
defined_structs=[],
|
||||
isimpl=False,
|
||||
prefix=None,
|
||||
version=None,
|
||||
version_finder=None):
|
||||
""" Return the prototype of the C API function. """
|
||||
parts = self.get_capi_parts(defined_structs, isimpl, prefix)
|
||||
parts = self.get_capi_parts(defined_structs, isimpl, prefix, version,
|
||||
version_finder)
|
||||
result = parts['retval']+' '+parts['name']+ \
|
||||
'('+', '.join(parts['args'])+')'
|
||||
return result
|
||||
@@ -1336,6 +1642,14 @@ class obj_function:
|
||||
|
||||
return other_is_library_side == this_is_library_side
|
||||
|
||||
def has_version(self):
|
||||
""" Returns true if the class has an associated version. """
|
||||
return _has_version(self.attribs)
|
||||
|
||||
def get_version_check(self):
|
||||
""" Returns the #if check for the associated version. """
|
||||
return get_version_check(self.attribs)
|
||||
|
||||
|
||||
class obj_function_static(obj_function):
|
||||
""" Class representing a static function. """
|
||||
@@ -1377,6 +1691,34 @@ class obj_function_virtual(obj_function):
|
||||
""" Returns true if the method declaration is const. """
|
||||
return self.isconst
|
||||
|
||||
def has_version_added(self):
|
||||
""" Returns true if a 'added' value was specified. """
|
||||
return _has_version_added(self.attribs)
|
||||
|
||||
def get_version_added(self):
|
||||
""" Returns the numeric 'added' value, or 0 if unspecified.
|
||||
Used for sorting purposes only. """
|
||||
return _get_version_added(self.attribs)
|
||||
|
||||
def has_version_removed(self):
|
||||
""" Returns true if a 'removed' value was specified. """
|
||||
return _has_version_removed(self.attribs)
|
||||
|
||||
def get_version_removed(self):
|
||||
""" Returns the numeric 'removed' value, or 0 if unspecified.
|
||||
Used for sorting purposes only. """
|
||||
return _get_version_removed(self.attribs)
|
||||
|
||||
def removed_at_version(self, version):
|
||||
""" Returns true if this function is removed at the specified version. """
|
||||
return self.has_version_removed() and self.get_version_removed() <= version
|
||||
|
||||
def exists_at_version(self, version):
|
||||
""" Returns true if this function exists at the specified version. """
|
||||
if self.has_version_added() and self.get_version_added() > version:
|
||||
return False
|
||||
return not self.removed_at_version(version)
|
||||
|
||||
|
||||
class obj_argument:
|
||||
""" Class representing a function argument. """
|
||||
@@ -1907,12 +2249,15 @@ class obj_analysis:
|
||||
""" Return the *Ptr type structure name. """
|
||||
return self.result_value[:-1]
|
||||
|
||||
def get_result_ptr_type(self, defined_structs=[]):
|
||||
def get_result_ptr_type(self, defined_structs=[], version_finder=None):
|
||||
""" Return the *Ptr type. """
|
||||
result = ''
|
||||
if not self.result_value[:-1] in defined_structs:
|
||||
name = self.result_value[:-1]
|
||||
if not version_finder is None:
|
||||
name = version_finder(name)
|
||||
if not name in defined_structs:
|
||||
result += 'struct _'
|
||||
result += self.result_value
|
||||
result += name + self.result_value[-1]
|
||||
if self.is_byref() or self.is_byaddr():
|
||||
result += '*'
|
||||
return result
|
||||
@@ -1951,16 +2296,22 @@ class obj_analysis:
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_result_struct_type(self, defined_structs=[]):
|
||||
def get_result_struct_type(self, defined_structs=[], version_finder=None):
|
||||
""" Return the structure or enumeration type. """
|
||||
result = ''
|
||||
|
||||
name = self.result_value
|
||||
|
||||
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 version_finder is None:
|
||||
name = version_finder(name)
|
||||
|
||||
result += name
|
||||
if not is_enum:
|
||||
result += '*'
|
||||
return result
|
||||
@@ -2026,7 +2377,7 @@ class obj_analysis:
|
||||
""" Return the vector structure or basic type name. """
|
||||
return self.result_value[0]['result_value']
|
||||
|
||||
def get_result_vector_type(self, defined_structs=[]):
|
||||
def get_result_vector_type(self, defined_structs=[], version_finder=None):
|
||||
""" Return the vector type. """
|
||||
if not self.has_name():
|
||||
raise Exception('Cannot use vector as a return type')
|
||||
@@ -2048,9 +2399,15 @@ class obj_analysis:
|
||||
result['value'] = str
|
||||
elif type == 'refptr' or type == 'ownptr' or type == 'rawptr':
|
||||
str = ''
|
||||
if not value[:-1] in defined_structs:
|
||||
|
||||
# remove the * suffix
|
||||
name = value[:-1]
|
||||
if not version_finder is None:
|
||||
name = version_finder(name)
|
||||
|
||||
if not name in defined_structs:
|
||||
str += 'struct _'
|
||||
str += value
|
||||
str += name + value[-1]
|
||||
if self.is_const():
|
||||
str += ' const'
|
||||
str += '*'
|
||||
@@ -2087,16 +2444,16 @@ class obj_analysis:
|
||||
return {'value': 'cef_string_multimap_t', 'format': 'multi'}
|
||||
raise Exception('Only mappings of strings to strings are supported')
|
||||
|
||||
def get_capi(self, defined_structs=[]):
|
||||
def get_capi(self, defined_structs=[], version_finder=None):
|
||||
""" 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)
|
||||
result += self.get_result_ptr_type(defined_structs, version_finder)
|
||||
elif self.is_result_struct():
|
||||
result += self.get_result_struct_type(defined_structs)
|
||||
result += self.get_result_struct_type(defined_structs, version_finder)
|
||||
elif self.is_result_string():
|
||||
result += self.get_result_string_type()
|
||||
elif self.is_result_map():
|
||||
@@ -2106,7 +2463,7 @@ class obj_analysis:
|
||||
else:
|
||||
raise Exception('Unsupported map type')
|
||||
elif self.is_result_vector():
|
||||
resdict = self.get_result_vector_type(defined_structs)
|
||||
resdict = self.get_result_vector_type(defined_structs, version_finder)
|
||||
if resdict['format'] != 'single':
|
||||
format = resdict['format']
|
||||
result += resdict['value']
|
||||
|
Reference in New Issue
Block a user