mirror of
https://bitbucket.org/chromiumembedded/cef
synced 2025-06-05 21:39:12 +02:00
Improve inheritance support in the CEF API (issue #1623).
- Support single parent inheritance in CEF API classes. - Support non-virtual inheritance in CEF API classes. - Support translation of CEF API sub-directories. - Add test sub-directories for testing-only functionality that will be available to unit tests but not exposed via the binary distribution. - Add unit tests for the translator tool. - Fix parsing of template parameter types that include commas.
This commit is contained in:
@ -9,39 +9,39 @@ def make_cpptoc_impl_proto(name, func, parts):
|
||||
proto = parts['retval']+' CEF_CALLBACK'
|
||||
else:
|
||||
proto = 'CEF_EXPORT '+parts['retval']
|
||||
|
||||
|
||||
proto += ' '+name+'('+string.join(parts['args'], ', ')+')'
|
||||
return proto
|
||||
|
||||
def make_cpptoc_function_impl_existing(name, func, impl, defined_names):
|
||||
def make_cpptoc_function_impl_existing(cls, name, func, impl, defined_names):
|
||||
notify(name+' has manual edits')
|
||||
|
||||
|
||||
# retrieve the C API prototype parts
|
||||
parts = func.get_capi_parts(defined_names)
|
||||
|
||||
|
||||
changes = format_translation_changes(impl, parts)
|
||||
if len(changes) > 0:
|
||||
notify(name+' prototype changed')
|
||||
|
||||
|
||||
return wrap_code(make_cpptoc_impl_proto(name, func, parts))+'{'+ \
|
||||
changes+impl['body']+'\n}\n'
|
||||
return result
|
||||
|
||||
def make_cpptoc_function_impl_new(name, func, defined_names):
|
||||
def make_cpptoc_function_impl_new(cls, name, func, defined_names):
|
||||
# retrieve the C API prototype parts
|
||||
parts = func.get_capi_parts(defined_names)
|
||||
result = make_cpptoc_impl_proto(name, func, parts)+' {'
|
||||
|
||||
|
||||
invalid = []
|
||||
|
||||
|
||||
# retrieve the function arguments
|
||||
args = func.get_arguments()
|
||||
|
||||
|
||||
# determine the argument types
|
||||
for arg in args:
|
||||
if arg.get_arg_type() == 'invalid':
|
||||
invalid.append(arg.get_name())
|
||||
|
||||
|
||||
# retrieve the function return value
|
||||
retval = func.get_retval()
|
||||
retval_type = retval.get_retval_type()
|
||||
@ -52,7 +52,7 @@ def make_cpptoc_function_impl_new(name, func, defined_names):
|
||||
retval_default = retval.get_retval_default(True)
|
||||
if len(retval_default) > 0:
|
||||
retval_default = ' '+retval_default;
|
||||
|
||||
|
||||
if len(invalid) > 0:
|
||||
notify(name+' could not be autogenerated')
|
||||
# code could not be auto-generated
|
||||
@ -63,29 +63,29 @@ def make_cpptoc_function_impl_new(name, func, defined_names):
|
||||
result += '\n // END DELETE BEFORE MODIFYING'
|
||||
result += '\n}\n\n'
|
||||
return wrap_code(result)
|
||||
|
||||
|
||||
result += '\n // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING\n'
|
||||
|
||||
|
||||
result_len = len(result)
|
||||
|
||||
|
||||
optional = []
|
||||
|
||||
|
||||
# parameter verification
|
||||
if isinstance(func, obj_function_virtual):
|
||||
result += '\n DCHECK(self);'\
|
||||
'\n if (!self)'\
|
||||
'\n return'+retval_default+';'
|
||||
|
||||
|
||||
for arg in args:
|
||||
arg_type = arg.get_arg_type()
|
||||
arg_name = arg.get_type().get_name()
|
||||
|
||||
|
||||
# skip optional params
|
||||
optional_params = arg.parent.get_attrib_list('optional_param')
|
||||
if not optional_params is None and arg_name in optional_params:
|
||||
optional.append(arg_name)
|
||||
continue
|
||||
|
||||
|
||||
comment = '\n // Verify param: '+arg_name+'; type: '+arg_type
|
||||
|
||||
if arg_type == 'simple_byref' or arg_type == 'simple_byref_const' or \
|
||||
@ -136,16 +136,16 @@ def make_cpptoc_function_impl_new(name, func, defined_names):
|
||||
if len(result) != result_len:
|
||||
result += '\n'
|
||||
result_len = len(result)
|
||||
|
||||
|
||||
# parameter translation
|
||||
params = []
|
||||
|
||||
|
||||
for arg in args:
|
||||
arg_type = arg.get_arg_type()
|
||||
arg_name = arg.get_type().get_name()
|
||||
|
||||
|
||||
comment = '\n // Translate param: '+arg_name+'; type: '+arg_type
|
||||
|
||||
|
||||
if arg_type == 'simple_byval' or arg_type == 'simple_byaddr':
|
||||
params.append(arg_name)
|
||||
elif arg_type == 'simple_byref' or arg_type == 'simple_byref_const':
|
||||
@ -258,11 +258,11 @@ def make_cpptoc_function_impl_new(name, func, defined_names):
|
||||
'\n }'\
|
||||
'\n }'
|
||||
params.append(arg_name+'List')
|
||||
|
||||
|
||||
if len(result) != result_len:
|
||||
result += '\n'
|
||||
result_len = len(result)
|
||||
|
||||
|
||||
# execution
|
||||
result += '\n // Execute\n '
|
||||
|
||||
@ -273,29 +273,34 @@ def make_cpptoc_function_impl_new(name, func, defined_names):
|
||||
else:
|
||||
result += retval.get_type().get_type()
|
||||
result += ' _retval = '
|
||||
|
||||
|
||||
if isinstance(func.parent, obj_class):
|
||||
# virtual and static class methods
|
||||
if isinstance(func, obj_function_virtual):
|
||||
result += func.parent.get_name()+'CppToC::Get(self)->'
|
||||
if cls.get_name() == func.parent.get_name():
|
||||
# virtual method for the current class
|
||||
result += func.parent.get_name()+'CppToC::Get(self)->'
|
||||
else:
|
||||
# virtual method for a parent class
|
||||
result += cls.get_name()+'CppToC::Get(reinterpret_cast<'+cls.get_capi_name()+'*>(self))->'
|
||||
else:
|
||||
result += func.parent.get_name()+'::'
|
||||
result += func.get_name()+'('
|
||||
|
||||
|
||||
if len(params) > 0:
|
||||
result += '\n '+string.join(params,',\n ')
|
||||
|
||||
|
||||
result += ');\n'
|
||||
|
||||
|
||||
result_len = len(result)
|
||||
|
||||
|
||||
# parameter restoration
|
||||
for arg in args:
|
||||
arg_type = arg.get_arg_type()
|
||||
arg_name = arg.get_type().get_name()
|
||||
|
||||
|
||||
comment = '\n // Restore param: '+arg_name+'; type: '+arg_type
|
||||
|
||||
|
||||
if arg_type == 'simple_byref':
|
||||
result += comment+\
|
||||
'\n if ('+arg_name+')'\
|
||||
@ -355,25 +360,25 @@ def make_cpptoc_function_impl_new(name, func, defined_names):
|
||||
'\n }'\
|
||||
'\n }'\
|
||||
'\n }'
|
||||
|
||||
|
||||
if len(result) != result_len:
|
||||
result += '\n'
|
||||
result_len = len(result)
|
||||
|
||||
|
||||
# special handling for the global cef_shutdown function
|
||||
if name == 'cef_shutdown' and isinstance(func.parent, obj_header):
|
||||
classes = func.parent.get_classes()
|
||||
|
||||
|
||||
names = []
|
||||
for cls in classes:
|
||||
if cls.has_attrib('no_debugct_check'):
|
||||
for tmpcls in classes:
|
||||
if tmpcls.has_attrib('no_debugct_check'):
|
||||
continue;
|
||||
|
||||
if cls.is_library_side():
|
||||
names.append(cls.get_name()+'CppToC')
|
||||
if tmpcls.is_library_side():
|
||||
names.append(tmpcls.get_name()+'CppToC')
|
||||
else:
|
||||
names.append(cls.get_name()+'CToCpp')
|
||||
|
||||
names.append(tmpcls.get_name()+'CToCpp')
|
||||
|
||||
if len(names) > 0:
|
||||
names = sorted(names)
|
||||
result += '\n#ifndef NDEBUG'\
|
||||
@ -381,11 +386,11 @@ def make_cpptoc_function_impl_new(name, func, defined_names):
|
||||
for name in names:
|
||||
result += '\n DCHECK(base::AtomicRefCountIsZero(&'+name+'::DebugObjCt));';
|
||||
result += '\n#endif // !NDEBUG'
|
||||
|
||||
|
||||
if len(result) != result_len:
|
||||
result += '\n'
|
||||
result_len = len(result)
|
||||
|
||||
|
||||
# return translation
|
||||
if retval_type != 'none':
|
||||
# has a return value
|
||||
@ -400,16 +405,16 @@ def make_cpptoc_function_impl_new(name, func, defined_names):
|
||||
elif retval_type == 'refptr_diff':
|
||||
refptr_class = retval.get_type().get_refptr_type()
|
||||
result += '\n return '+refptr_class+'CToCpp::Unwrap(_retval);'
|
||||
|
||||
|
||||
if len(result) != result_len:
|
||||
result += '\n'
|
||||
|
||||
|
||||
result += '}\n'
|
||||
return wrap_code(result)
|
||||
|
||||
def make_cpptoc_function_impl(funcs, existing, prefixname, defined_names):
|
||||
def make_cpptoc_function_impl(cls, funcs, existing, prefixname, defined_names):
|
||||
impl = ''
|
||||
|
||||
|
||||
for func in funcs:
|
||||
if not prefixname is None:
|
||||
name = prefixname+'_'+func.get_capi_name()
|
||||
@ -419,84 +424,155 @@ def make_cpptoc_function_impl(funcs, existing, prefixname, defined_names):
|
||||
if not value is None \
|
||||
and value['body'].find('// AUTO-GENERATED CONTENT') < 0:
|
||||
# an implementation exists that was not auto-generated
|
||||
impl += make_cpptoc_function_impl_existing(name, func, value, defined_names)
|
||||
impl += make_cpptoc_function_impl_existing(cls, name, func, value, defined_names)
|
||||
else:
|
||||
impl += make_cpptoc_function_impl_new(name, func, defined_names)
|
||||
|
||||
impl += make_cpptoc_function_impl_new(cls, name, func, defined_names)
|
||||
|
||||
return impl
|
||||
|
||||
|
||||
def make_cpptoc_virtual_function_impl(header, cls, existing, prefixname, defined_names):
|
||||
funcs = []
|
||||
funcs.extend(cls.get_virtual_funcs())
|
||||
cur_cls = cls
|
||||
while True:
|
||||
parent_name = cur_cls.get_parent_name()
|
||||
if parent_name == 'CefBase':
|
||||
break
|
||||
else:
|
||||
parent_cls = header.get_class(parent_name, defined_names)
|
||||
if parent_cls is None:
|
||||
raise Exception('Class does not exist: '+parent_name)
|
||||
funcs.extend(parent_cls.get_virtual_funcs())
|
||||
cur_cls = header.get_class(parent_name, defined_names)
|
||||
|
||||
return make_cpptoc_function_impl(cls, funcs, existing, prefixname, defined_names)
|
||||
|
||||
def make_cpptoc_virtual_function_assignment_block(funcs, offset, prefixname):
|
||||
impl = ''
|
||||
for func in funcs:
|
||||
name = func.get_capi_name()
|
||||
impl += ' GetStruct()->'+offset+name+' = '+prefixname+'_'+name+';\n'
|
||||
return impl
|
||||
|
||||
def make_cpptoc_virtual_function_assignment(header, cls, prefixname, defined_names):
|
||||
impl = make_cpptoc_virtual_function_assignment_block(cls.get_virtual_funcs(), '', prefixname)
|
||||
|
||||
cur_cls = cls
|
||||
offset = ''
|
||||
while True:
|
||||
parent_name = cur_cls.get_parent_name()
|
||||
offset += 'base.'
|
||||
if parent_name == 'CefBase':
|
||||
break
|
||||
else:
|
||||
parent_cls = header.get_class(parent_name, defined_names)
|
||||
if parent_cls is None:
|
||||
raise Exception('Class does not exist: '+parent_name)
|
||||
impl += make_cpptoc_virtual_function_assignment_block(parent_cls.get_virtual_funcs(), offset, prefixname)
|
||||
cur_cls = header.get_class(parent_name, defined_names)
|
||||
|
||||
return impl
|
||||
|
||||
def make_cpptoc_unwrap_derived(header, cls):
|
||||
# identify all classes that derive from cls
|
||||
derived_classes = []
|
||||
clsname = cls.get_name()
|
||||
allclasses = header.get_classes()
|
||||
for cur_cls in allclasses:
|
||||
if cur_cls.get_name() == clsname:
|
||||
continue
|
||||
if cur_cls.has_parent(clsname):
|
||||
derived_classes.append(cur_cls.get_name())
|
||||
|
||||
derived_classes = sorted(derived_classes)
|
||||
|
||||
impl = ''
|
||||
for clsname in derived_classes:
|
||||
impl += ' if (type == '+get_wrapper_type_enum(clsname)+') {\n'+\
|
||||
' return '+clsname+'CppToC::Unwrap(reinterpret_cast<'+\
|
||||
get_capi_name(clsname, True)+'*>(s));\n'+\
|
||||
' }\n'
|
||||
return impl
|
||||
|
||||
def make_cpptoc_class_impl(header, clsname, impl):
|
||||
# structure names that have already been defined
|
||||
defined_names = header.get_defined_structs()
|
||||
|
||||
|
||||
# retrieve the class and populate the defined names
|
||||
cls = header.get_class(clsname, defined_names)
|
||||
if cls is None:
|
||||
raise Exception('Class does not exist: '+clsname)
|
||||
|
||||
|
||||
capiname = cls.get_capi_name()
|
||||
prefixname = get_capi_name(clsname[3:], False)
|
||||
|
||||
|
||||
# retrieve the existing virtual function implementations
|
||||
existing = get_function_impls(impl, 'CEF_CALLBACK')
|
||||
|
||||
|
||||
# generate virtual functions
|
||||
virtualimpl = make_cpptoc_function_impl(cls.get_virtual_funcs(), existing, prefixname, defined_names)
|
||||
virtualimpl = make_cpptoc_virtual_function_impl(header, cls, existing, prefixname, defined_names)
|
||||
if len(virtualimpl) > 0:
|
||||
virtualimpl = '\n// MEMBER FUNCTIONS - Body may be edited by hand.\n\n'+virtualimpl
|
||||
|
||||
# the current class is already defined for static functions
|
||||
virtualimpl = '\nnamespace {\n\n// MEMBER FUNCTIONS - Body may be edited by hand.\n\n'+virtualimpl+'} // namespace'
|
||||
|
||||
# the current class is already defined for static functions
|
||||
defined_names.append(cls.get_capi_name())
|
||||
|
||||
|
||||
# retrieve the existing static function implementations
|
||||
existing = get_function_impls(impl, 'CEF_EXPORT')
|
||||
|
||||
|
||||
# generate static functions
|
||||
staticimpl = make_cpptoc_function_impl(cls.get_static_funcs(), existing, None, defined_names)
|
||||
staticimpl = make_cpptoc_function_impl(cls, cls.get_static_funcs(), existing, None, defined_names)
|
||||
if len(staticimpl) > 0:
|
||||
staticimpl = '\n// GLOBAL FUNCTIONS - Body may be edited by hand.\n\n'+staticimpl
|
||||
|
||||
|
||||
resultingimpl = staticimpl + virtualimpl
|
||||
|
||||
|
||||
# any derived classes can be unwrapped
|
||||
unwrapderived = make_cpptoc_unwrap_derived(header, cls)
|
||||
|
||||
# determine what includes are required by identifying what translation
|
||||
# classes are being used
|
||||
includes = format_translation_includes(resultingimpl)
|
||||
|
||||
includes = format_translation_includes(header, resultingimpl + unwrapderived)
|
||||
|
||||
# build the final output
|
||||
result = get_copyright()
|
||||
|
||||
result += includes+'\n'+resultingimpl+'\n'
|
||||
|
||||
|
||||
parent_sig = 'CefCppToC<'+clsname+'CppToC, '+clsname+', '+capiname+'>'
|
||||
|
||||
const = '// CONSTRUCTOR - Do not edit by hand.\n\n'+ \
|
||||
clsname+'CppToC::'+clsname+'CppToC('+clsname+'* cls)\n'+ \
|
||||
' : CefCppToC<'+clsname+'CppToC, '+clsname+', '+capiname+'>(cls) '+ \
|
||||
'{\n';
|
||||
|
||||
funcs = cls.get_virtual_funcs()
|
||||
for func in funcs:
|
||||
name = func.get_capi_name()
|
||||
const += ' struct_.struct_.'+name+' = '+prefixname+'_'+name+';\n'
|
||||
|
||||
clsname+'CppToC::'+clsname+'CppToC() {\n'
|
||||
|
||||
const += make_cpptoc_virtual_function_assignment(header, cls, prefixname, defined_names)
|
||||
|
||||
const += '}\n\n'+ \
|
||||
'template<> CefRefPtr<'+clsname+'> '+parent_sig+'::UnwrapDerived(CefWrapperType type, '+capiname+'* s) {\n' + \
|
||||
unwrapderived + \
|
||||
' NOTREACHED() << "Unexpected class type: " << type;\n'+ \
|
||||
' return NULL;\n'+ \
|
||||
'}\n\n'+ \
|
||||
'#ifndef NDEBUG\n'+ \
|
||||
'template<> base::AtomicRefCount CefCppToC<'+clsname+'CppToC, '+clsname+', '+capiname+'>::DebugObjCt = 0;\n'+ \
|
||||
'#endif\n'
|
||||
result += wrap_code(const)
|
||||
'template<> base::AtomicRefCount '+parent_sig+'::DebugObjCt = 0;\n'+ \
|
||||
'#endif\n\n'+ \
|
||||
'template<> CefWrapperType '+parent_sig+'::kWrapperType = '+get_wrapper_type_enum(clsname)+';'
|
||||
|
||||
result += '\n\n'+wrap_code(const)
|
||||
|
||||
return result
|
||||
|
||||
def make_cpptoc_global_impl(header, impl):
|
||||
# structure names that have already been defined
|
||||
defined_names = header.get_defined_structs()
|
||||
|
||||
|
||||
# retrieve the existing global function implementations
|
||||
existing = get_function_impls(impl, 'CEF_EXPORT')
|
||||
|
||||
|
||||
# generate global functions
|
||||
impl = make_cpptoc_function_impl(header.get_funcs(), existing, None, defined_names)
|
||||
impl = make_cpptoc_function_impl(None, header.get_funcs(), existing, None, defined_names)
|
||||
if len(impl) > 0:
|
||||
impl = '\n// GLOBAL FUNCTIONS - Body may be edited by hand.\n\n'+impl
|
||||
|
||||
|
||||
includes = ''
|
||||
|
||||
# include required headers for global functions
|
||||
@ -507,10 +583,10 @@ def make_cpptoc_global_impl(header, impl):
|
||||
includes += '#include "include/'+func.get_file_name()+'"\n' \
|
||||
'#include "include/capi/'+func.get_capi_file_name()+'"\n'
|
||||
filenames.append(filename)
|
||||
|
||||
|
||||
# determine what includes are required by identifying what translation
|
||||
# classes are being used
|
||||
includes += format_translation_includes(impl)
|
||||
includes += format_translation_includes(header, impl)
|
||||
|
||||
# build the final output
|
||||
result = get_copyright()
|
||||
@ -525,13 +601,16 @@ def write_cpptoc_impl(header, clsname, dir, backup):
|
||||
file = dir
|
||||
else:
|
||||
# class file
|
||||
file = dir+os.sep+get_capi_name(clsname[3:], False)+'_cpptoc.cc'
|
||||
|
||||
# give the output file the same directory offset as the input file
|
||||
cls = header.get_class(clsname)
|
||||
dir = os.path.dirname(os.path.join(dir, cls.get_file_name()))
|
||||
file = os.path.join(dir, get_capi_name(clsname[3:], False)+'_cpptoc.cc')
|
||||
|
||||
if path_exists(file):
|
||||
oldcontents = read_file(file)
|
||||
else:
|
||||
oldcontents = ''
|
||||
|
||||
|
||||
if clsname is None:
|
||||
newcontents = make_cpptoc_global_impl(header, oldcontents)
|
||||
else:
|
||||
@ -541,23 +620,23 @@ def write_cpptoc_impl(header, clsname, dir, backup):
|
||||
backup_file(file)
|
||||
write_file(file, newcontents)
|
||||
return True
|
||||
|
||||
|
||||
return False
|
||||
|
||||
|
||||
# test the module
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
|
||||
|
||||
# verify that the correct number of command-line arguments are provided
|
||||
if len(sys.argv) < 4:
|
||||
sys.stderr.write('Usage: '+sys.argv[0]+' <infile> <classname> <existing_impl>')
|
||||
sys.exit()
|
||||
|
||||
|
||||
# create the header object
|
||||
header = obj_header()
|
||||
header.add_file(sys.argv[1])
|
||||
|
||||
|
||||
# read the existing implementation file into memory
|
||||
try:
|
||||
f = open(sys.argv[3], 'r')
|
||||
@ -566,6 +645,6 @@ if __name__ == "__main__":
|
||||
raise Exception('Failed to read file '+sys.argv[3]+': '+strerror)
|
||||
else:
|
||||
f.close()
|
||||
|
||||
|
||||
# dump the result to stdout
|
||||
sys.stdout.write(make_cpptoc_class_impl(header, sys.argv[2], data))
|
||||
|
Reference in New Issue
Block a user