blob: 82092bf5435f7eead4c8b381fa543cc1602e1198 [file] [log] [blame]
#!/usr/bin/python2.4
# Copyright 2009, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""O3D interface class generator.
This module generates the O3D interface classes.
"""
import os
import syntax_tree
import cpp_utils
import naming
class CircularDefinition(Exception):
"""Raised when a circular type definition is found."""
def __init__(self, type_defn):
Exception.__init__(self)
self.type = type_defn
class BadForwardDeclaration(Exception):
"""Raised when an impossible forward declaration is required."""
def ForwardDecl(section, type_defn):
"""Emits the forward declaration of a type, if possible.
Inner types (declared inside a class) cannot be forward-declared.
Only classes can be forward-declared.
Args:
section: the section to emit to.
type_defn: the Definition for the type to forward-declare.
Raises:
BadForwardDeclaration: an inner type or a non-class was passed as an
argument.
"""
# inner types cannot be forward-declared
if type_defn.parent.defn_type != 'Namespace':
raise BadForwardDeclaration
stack = type_defn.GetParentScopeStack()
if type_defn.defn_type == 'Class':
for scope in stack:
if scope.name:
section.PushNamespace(scope.name)
section.EmitCode('class %s;' % type_defn.name)
for scope in stack[::-1]:
if scope.name:
section.PopNamespace()
else:
raise BadForwardDeclaration
class O3DInterfaceGenerator(object):
"""Header generator class.
This class takes care of the details of generating a C++ header file
containing all the definitions from a syntax tree.
It contains a list of functions named after each of the Definition classes in
syntax_tree, with a common signature. The appropriate function will be called
for each definition, to generate the code for that definition.
"""
class GenerationContext(object):
def __init__(self, header_scope, cpp_scope, header_section, cpp_section):
self.header_scope = header_scope
self.header_section = header_section
self.cpp_scope = cpp_scope
self.cpp_section = cpp_section
self.needed_decl = set()
self.needed_defn = set()
self.emitted_defn = set()
def Fork(self, header_scope, cpp_scope, header_section, cpp_section):
new_context = type(self)(header_scope, cpp_scope, header_section,
cpp_section)
new_context.needed_decl = self.needed_decl
new_context.needed_defn = self.needed_defn
new_context.emitted_defn = self.emitted_defn
return new_context
def CheckType(self, need_defn, type_defn):
"""Checks for the definition or declaration of a type.
This function helps keeping track of which types are needed to be defined
or declared in the C++ file before other definitions can happen. If the
definition is needed (and is not in this C++ header file), the proper
include will be generated. If the type only needs to be forward-declared,
the forward declaration will be output (if the type is not otherwise
defined).
Args:
need_defn: a boolean, True if the C++ definition of the type is needed,
False if only the declaration is needed.
type_defn: the Definition of the type to check.
"""
while type_defn.defn_type == 'Array':
# arrays are implicitly defined with their data type
type_defn = type_defn.data_type
if need_defn:
if type_defn not in self.emitted_defn:
self.needed_defn.add(type_defn)
else:
if type_defn in self.emitted_defn:
return
if type_defn.parent and type_defn.parent.defn_type != 'Namespace':
# inner type: need the definition of the parent.
self.CheckType(True, type_defn.parent)
else:
# only forward-declare classes.
# typedefs could be forward-declared by emitting the definition again,
# but this necessitates the source type to be forward-declared before.
# TODO: see if it is possible to find a proper ordering that let
# us forward-declare typedefs instead of needing to include the
# definition.
if type_defn.defn_type == 'Class':
self.needed_decl.add(type_defn)
else:
self.needed_defn.add(type_defn)
def __init__(self, output_dir, namespace):
self._output_dir = output_dir
self._void_type = namespace.LookupTypeRecursive('void')
def GetHeaderFile(self, idl_file):
return idl_file.source.split('.')[0] + '.h'
def GetCppFile(self, idl_file):
return idl_file.source.split('.')[0] + '.cc'
def GetInterfaceInclude(self, type_defn):
if self.NeedsGlue(type_defn):
return self.GetHeaderFile(type_defn.source.file)
else:
return type_defn.GetDefinitionInclude()
def GetImplementationInclude(self, type_defn):
return type_defn.GetDefinitionInclude()
def IsVoid(self, type_defn):
return type_defn.GetFinalType() == self._void_type
def NeedsGlue(self, obj):
return obj.LookupBindingModel() == 'o3d' or 'glue_iface' in obj.attributes
def GetSectionFromAttributes(self, parent_section, defn):
"""Gets the code section appropriate for a given definition.
Classes have 3 definition sections: private, protected and public. This
function will pick one of the sections, based on the attributes of the
definition, if its parent is a class. For other scopes (namespaces) it will
return the parent scope main section.
Args:
parent_section: the main section for the parent scope.
defn: the definition.
Returns:
the appropriate section.
"""
if defn.parent and defn.parent.defn_type == 'Class':
if 'private' in defn.attributes:
return parent_section.GetSection('private:') or parent_section
elif 'protected' in defn.attributes:
return parent_section.GetSection('protected:') or parent_section
else:
return parent_section.GetSection('public:') or parent_section
else:
return parent_section
def Verbatim(self, context, obj):
"""Generates the code for a Verbatim definition.
Verbatim definitions being written for a particular type of output file,
this function will check the 'verbatim' attribute, and only emit the
verbatim code if it is 'cpp_header'.
Args:
parent_section: the main section of the parent scope.
obj: the Verbatim definition.
"""
try:
verbatim_attr = obj.attributes['verbatim']
except KeyError:
source = obj.source
print ('%s:%d ignoring verbatim with no verbatim attribute' %
(source.file.source, source.line))
return
if verbatim_attr == 'o3d_iface_header':
section = self.GetSectionFromAttributes(context.header_section, obj)
section.EmitCode(obj.text)
elif verbatim_attr == 'o3d_iface_cpp':
context.cpp_section.EmitCode(obj.text)
def Typedef(self, context, obj):
"""Generates the code for a Typedef definition.
Args:
parent_section: the main section of the parent scope.
obj: the Typedef definition.
Returns:
a list of (boolean, Definition) pairs, of all the types that need
to be declared (boolean is False) or defined (boolean is True) before
this definition.
"""
section = self.GetSectionFromAttributes(context.header_section, obj)
bm = obj.type.binding_model
type_string, unused_need_defn = bm.CppTypedefString(context.header_scope,
obj.type)
context.CheckType(True, obj.type)
section.EmitCode('typedef %s %s;' % (type_string, obj.name))
def Variable(self, context, obj):
"""Generates the code for a Variable definition.
This function will generate the member/global variable declaration, as well
as the setter/getter functions if specified in the attributes.
Args:
parent_section: the main section of the parent scope.
obj: the Variable definition.
"""
bm = obj.type.binding_model
type_string, need_defn = bm.CppMemberString(context.header_scope, obj.type)
context.CheckType(need_defn, obj.type)
need_glue = self.NeedsGlue(obj) or (obj.parent.is_type and
self.NeedsGlue(obj.parent));
getter_attributes = {}
if 'static' in obj.attributes:
getter_attributes['static'] = obj.attributes['static']
static = 'static '
else:
static = ''
for attr in ['public', 'protected', 'private']:
if attr in obj.attributes:
getter_attributes[attr] = obj.attributes[attr]
if not need_glue:
if obj.parent.defn_type == 'Class':
if 'field_access' in obj.attributes:
member_section = context.header_section.GetSection(
obj.attributes['field_access'] + ':')
else:
member_section = context.header_section.GetSection('private:')
else:
member_section = context.header_section
field_name = naming.Normalize(obj.name, naming.LowerTrailing)
member_section.EmitCode('%s%s %s;' % (static, type_string, field_name))
if 'getter' in obj.attributes:
func = obj.MakeGetter(getter_attributes, cpp_utils.GetGetterName(obj))
if need_glue:
self.FunctionGlue(context, func)
impl = None
else:
impl = ' { return %s; }' % field_name
self.FunctionDecl(context, func, impl)
if 'setter' in obj.attributes:
func = obj.MakeSetter(getter_attributes, cpp_utils.GetSetterName(obj))
if need_glue:
self.FunctionGlue(context, func)
impl = None
else:
impl = ' { %s = %s; }' % (field_name, obj.name)
self.FunctionDecl(context, func, impl)
def GetParamsDecls(self, scope, obj, context=None):
param_strings = []
for p in obj.params:
bm = p.type.binding_model
if p.mutable:
text, need_defn = bm.CppMutableParameterString(scope, p.type)
else:
text, need_defn = bm.CppParameterString(scope, p.type)
if context:
context.CheckType(need_defn, p.type)
param_strings += ['%s %s' % (text, p.name)]
return ', '.join(param_strings)
def FunctionDecl(self, context, obj, impl_string=None):
section = self.GetSectionFromAttributes(context.header_section, obj)
if not impl_string:
impl_string = ';'
params_string = self.GetParamsDecls(context.header_scope, obj, context)
prefix_strings = []
suffix_strings = []
for attrib in ['static', 'virtual', 'inline']:
if attrib in obj.attributes:
prefix_strings.append(attrib)
if prefix_strings:
prefix_strings.append('')
if 'const' in obj.attributes:
suffix_strings.append('const')
if 'pure' in obj.attributes:
suffix_strings.append('= 0')
if suffix_strings:
suffix_strings.insert(0, '')
prefix = ' '.join(prefix_strings)
suffix = ' '.join(suffix_strings)
if obj.type:
bm = obj.type.binding_model
return_type, need_defn = bm.CppReturnValueString(context.header_scope,
obj.type)
context.CheckType(need_defn, obj.type)
section.EmitCode('%s%s %s(%s)%s%s' % (prefix, return_type, obj.name,
params_string, suffix, impl_string))
else:
section.EmitCode('%s%s(%s)%s%s' % (prefix, obj.name, params_string,
suffix, impl_string))
def FunctionGlue(self, context, obj):
if not obj.type:
# TODO autogen a factory
return
if 'pure' in obj.attributes:
return
if obj.parent.is_type:
func_name = '%s::%s' % (obj.parent.name, obj.name)
if 'static' in obj.attributes:
call_prefix = 'impl::' + func_name
else:
# this_call
if self.NeedsGlue(obj.parent):
call_prefix = 'GetImpl()->'
else:
call_prefix = ''
else:
call_prefix = ''
func_name = obj.name
params_string = self.GetParamsDecls(context.cpp_scope, obj)
param_exprs = []
for p in obj.params:
if self.NeedsGlue(p.type):
param_exprs.append('%s->GetImpl()' % p.name)
else:
param_exprs.append(p.name)
if not self.IsVoid(obj.type):
return_prefix = 'return '
if self.NeedsGlue(obj.type):
return_suffix = '->GetIface()'
else:
return_suffix = ''
else:
return_prefix = ''
return_suffix = ''
bm = obj.type.binding_model
return_type, unused = bm.CppReturnValueString(context.header_scope,
obj.type)
if 'const' in obj.attributes:
func_suffix = ' const'
else:
func_suffix = ''
section = context.cpp_section
section.EmitCode('%s %s(%s)%s {' % (return_type, func_name, params_string,
func_suffix))
section.EmitCode('%s%s%s(%s)%s;' % (return_prefix, call_prefix, obj.name,
', '.join(param_exprs), return_suffix))
section.EmitCode('}')
def Function(self, context, obj):
"""Generates the code for a Function definition.
Args:
parent_section: the main section of the parent scope.
obj: the Function definition.
"""
self.FunctionDecl(context, obj)
if self.NeedsGlue(obj) or (obj.parent.is_type and
self.NeedsGlue(obj.parent)):
self.FunctionGlue(context, obj)
def Class(self, context, obj):
"""Generates the code for a Class definition.
This function will recursively generate the code for all the definitions
inside the class. These definitions will be output into one of 3 sections
(private, protected, public), depending on their attributes. These
individual sections will only be output if they are not empty.
Args:
parent_section: the main section of the parent scope.
obj: the Class definition.
"""
h_section = self.GetSectionFromAttributes(context.header_section,
obj).CreateSection(obj.name)
c_section = context.cpp_section
need_glue = self.NeedsGlue(obj)
if need_glue:
h_section.PushNamespace('impl')
h_section.EmitCode('class %s;' % obj.name)
h_section.PopNamespace()
h_section.EmitCode('')
if obj.base_type:
bm = obj.base_type.binding_model
h_section.EmitCode('class %s : public %s {' %
(obj.name, bm.CppBaseClassString(context.header_scope,
obj.base_type)))
context.CheckType(True, obj.base_type)
else:
h_section.EmitCode('class %s {' % obj.name)
public_section = h_section.CreateSection('public:')
protected_section = h_section.CreateSection('protected:')
private_section = h_section.CreateSection('private:')
new_context = context.Fork(obj, context.cpp_scope, h_section, c_section)
self.DefinitionList(new_context, obj.defn_list)
if need_glue:
public_section.EmitCode('impl::%s *GetImpl();' % obj.name)
c_section.EmitCode('impl::%s *%s::GetImpl() {' % (obj.name, obj.name))
c_section.EmitCode('return static_cast<impl::%s *>(impl_);' % obj.name)
c_section.EmitCode('}')
if not public_section.IsEmpty():
public_section.AddPrefix('public:')
if not protected_section.IsEmpty():
protected_section.AddPrefix('protected:')
if not private_section.IsEmpty():
private_section.AddPrefix('private:')
h_section.EmitCode('};')
def Namespace(self, context, obj):
"""Generates the code for a Namespace definition.
This function will recursively generate the code for all the definitions
inside the namespace.
Args:
parent_section: the main section of the parent scope.
obj: the Namespace definition.
"""
context.header_section.PushNamespace(obj.name)
context.cpp_section.PushNamespace(obj.name)
new_context = context.Fork(obj, obj, context.header_section,
context.cpp_section)
self.DefinitionList(new_context, obj.defn_list)
context.header_section.PopNamespace()
context.cpp_section.PopNamespace()
def Typename(self, context, obj):
"""Generates the code for a Typename definition.
Since typenames (undefined types) cannot be expressed in C++, this function
will not output code. The definition may be output with a verbatim section.
Args:
parent_section: the main section of the parent scope.
scope: the parent scope.
obj: the Typename definition.
"""
def Enum(self, context, obj):
"""Generates the code for an Enum definition.
Args:
parent_section: the main section of the parent scope.
scope: the parent scope.
obj: the Enum definition.
"""
section = self.GetSectionFromAttributes(context.header_section, obj)
section.EmitCode('enum %s {' % obj.name)
for value in obj.values:
if value.value is None:
section.EmitCode('%s,' % value.name)
else:
section.EmitCode('%s = %s,' % (value.name, value.value))
section.EmitCode('};')
def DefinitionList(self, context, defn_list):
"""Generates the code for all the definitions in a list.
Args:
parent_section: the main section of the parent scope.
scope: the parent scope.
defn_list: the list of definitions.
"""
for obj in defn_list:
context.emitted_defn.add(obj)
# array types are implicitly defined
for k in obj.array_defns:
context.emitted_defn.add(obj.array_defns[k])
getattr(self, obj.defn_type)(context, obj)
def Generate(self, idl_file, namespace, defn_list):
"""Generates the header file.
Args:
idl_file: the source IDL file containing the definitions, as a
idl_parser.File instance.
namespace: a Definition for the global namespace.
defn_list: the list of top-level definitions.
Returns:
a cpp_utils.CppFileWriter that contains the C++ header file code.
Raises:
CircularDefinition: circular definitions were found in the file.
"""
all_defn = syntax_tree.GetObjectsRecursive(defn_list)
need_glue = False
for defn in all_defn:
if self.NeedsGlue(defn):
need_glue = True
break
if not need_glue:
return []
header_writer = cpp_utils.CppFileWriter(
'%s/%s' % (self._output_dir, self.GetHeaderFile(idl_file)), True)
cpp_writer = cpp_utils.CppFileWriter(
'%s/%s' % (self._output_dir, self.GetCppFile(idl_file)), True)
h_decl_section = header_writer.CreateSection('decls')
h_code_section = header_writer.CreateSection('defns')
c_code_section = cpp_writer.CreateSection('glue')
context = self.GenerationContext(namespace, namespace, h_code_section,
c_code_section)
self.DefinitionList(context, defn_list)
context.needed_decl -= context.needed_defn
if context.needed_decl:
for type_defn in context.needed_decl:
# TODO: sort by namespace so that we don't open and close them
# more than necessary.
ForwardDecl(h_decl_section, type_defn)
h_decl_section.EmitCode('')
for type_defn in context.needed_defn:
if type_defn.source.file == idl_file:
raise CircularDefinition(type_defn)
h_includes = set(self.GetInterfaceInclude(type_defn)
for type_defn in context.needed_defn)
c_includes = set(self.GetImplementationInclude(type_defn)
for type_defn in context.emitted_defn
if self.NeedsGlue(type_defn))
c_includes.add(self.GetHeaderFile(idl_file))
for include_file in h_includes:
if include_file is not None:
header_writer.AddInclude(include_file)
for include_file in c_includes:
if include_file is not None:
cpp_writer.AddInclude(include_file)
return [header_writer, cpp_writer]
def ProcessFiles(output_dir, pairs, namespace):
"""Generates the headers for all input files.
Args:
output_dir: the output directory.
pairs: a list of (idl_parser.File, syntax_tree.Definition list) describing
the list of top-level definitions in each source file.
namespace: a syntax_tree.Namespace for the global namespace.
Returns:
a list of cpp_utils.CppFileWriter, one for each output file.
"""
output_dir = output_dir + '/iface'
if not os.access(output_dir + '/', os.F_OK):
os.makedirs(output_dir)
generator = O3DInterfaceGenerator(output_dir, namespace)
writer_list = []
for (f, defn) in pairs:
writer_list += generator.Generate(f, namespace, defn)
return writer_list
def main():
pass
if __name__ == '__main__':
main()