Chromium Code Reviews| Index: ppapi/generators/idl_gen_wrapper.py |
| diff --git a/ppapi/generators/idl_gen_wrapper.py b/ppapi/generators/idl_gen_wrapper.py |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..fa5abc78bb33662c9dfd627adcc1bc96d792475b |
| --- /dev/null |
| +++ b/ppapi/generators/idl_gen_wrapper.py |
| @@ -0,0 +1,481 @@ |
| +#!/usr/bin/python |
| +# |
| +# Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| +# Use of this source code is governed by a BSD-style license that can be |
| +# found in the LICENSE file. |
| + |
| +""" Base class for generating wrapper functions for PPAPI methods. """ |
| + |
| +from datetime import datetime |
| +import os |
| +import sys |
| + |
| +from idl_ast import IDLAst |
| +from idl_c_proto import CGen |
| +from idl_generator import Generator |
| +from idl_log import ErrOut, InfoOut, WarnOut |
| +from idl_option import Option |
| +from idl_outfile import IDLOutFile |
| + |
| + |
| +class PPKind(object): |
| + @staticmethod |
| + def ChoosePPFunc(iface, ppb_func, ppp_func): |
| + name = iface.node.GetName() |
| + if name.startswith("PPP"): |
| + return ppp_func |
| + elif name.startswith("PPB"): |
| + return ppb_func |
| + else: |
| + raise Exception('Unknown PPKind for ' + name) |
| + |
| + |
| +class Interface(object): |
| + """ Tracks information about a particular interface version |
| + - struct_name: the struct type used by the ppapi headers to hold the |
| + method pointers (the vtable). |
| + - needs_wrapping: True if there is a method in the interface that needs |
| + shimming |
| + - header_file: the name of the header file that defined this interface |
| + """ |
| + def __init__(self, interface_node, release, version, |
| + struct_name, needs_wrapping, header_file): |
| + self.node = interface_node |
| + self.release = release |
| + self.version = version |
| + self.struct_name = struct_name |
| + # We may want finer grained filtering (method level), but it is not |
| + # yet clear how to actually do that. |
| + self.needs_wrapping = needs_wrapping |
| + self.header_file = header_file |
| + |
| + |
| +class WrapperGen(Generator): |
| + """WrapperGen - A subclass of Generator which takes the IDL sources and |
|
noelallen1
2011/11/23 18:06:11
"""One Line
Multi
Line
"""
|
| + generates wrapper code for each method and allows redirecting the |
| + the GetInterface functions to these new wrappers. |
| + """ |
| + |
| + def __init__(self, wrapper_prefix, s1, s2, s3): |
| + Generator.__init__(self, s1, s2, s3) |
| + self.wrapper_prefix = wrapper_prefix |
| + self._skip_opt = False |
| + self.output_file = None |
| + self.cgen = CGen() |
| + |
| + def SetOutputFile(self, fname): |
| + self.output_file = fname |
| + |
| + |
| + def GenerateRelease(self, ast, release, options): |
| + return self.GenerateRange(ast, [release], options) |
| + |
| + |
| + @staticmethod |
| + def GetHeaderName(name): |
| + """ Get the corresponding ppapi .h file from each IDL filename. """ |
| + name = os.path.splitext(name)[0] + '.h' |
| + return 'ppapi/c/' + name |
| + |
| + |
| + def WriteCopyrightGeneratedTime(self, out): |
| + now = datetime.now() |
| + c = """/* Copyright (c) %s The Chromium Authors. All rights reserved. |
| + * Use of this source code is governed by a BSD-style license that can be |
| + * found in the LICENSE file. |
| + */ |
| + |
| +/* Last generated from IDL: %s. */ |
| +""" % (now.year, datetime.ctime(now)) |
| + out.Write(c) |
| + |
| + def GetWrapperMetadataName(self): |
| + return '__%sWrapperInfo' % self.wrapper_prefix |
| + |
| + |
| + def GetHeaderGuard(self, header_filename): |
| + g = header_filename.upper().replace(os.path.sep, '_').replace('.','_') + '_' |
| + return g |
| + |
| + def GenerateHeaderFile(self, header_filename, out): |
| + """ Writes out an interface header file for the exported symbols. """ |
| + self.WriteCopyrightGeneratedTime(out) |
| + include_guard = self.GetHeaderGuard(header_filename) |
| + out.Write(""" |
| +#ifndef %(include_guard)s |
| +#define %(include_guard)s |
| + |
| +#include "ppapi/c/ppb.h" |
| + |
| +/* There is not a typedef for PPP_GetInterface_type, but it is currently |
| + * the same as PPB_GetInterface. */ |
| +typedef PPB_GetInterface PPP_GetInterface_type; |
| + |
| +void __set_real_%(wrapper_prefix)s_PPBGetInterface(PPB_GetInterface real); |
| +void __set_real_%(wrapper_prefix)s_PPPGetInterface(PPP_GetInterface_type real); |
| + |
| +const void *__%(wrapper_prefix)s_PPBGetInterface(const char *name); |
| +const void *__%(wrapper_prefix)s_PPPGetInterface(const char *name); |
| + |
| +struct %(wrapper_struct)s { |
| + const char* iface_macro; |
| + const void* wrapped_iface; /* If NULL then it was not wrapped! */ |
| + const void* real_iface; |
| +}; |
| + |
| +#endif /* %(include_guard)s */ |
| +""" % { |
| + 'include_guard' : include_guard, |
| + 'timestamp' : datetime.ctime(datetime.now()), |
| + 'wrapper_prefix' : self.wrapper_prefix, |
| + 'wrapper_struct' : self.GetWrapperMetadataName(), |
| + }) |
| + |
| + |
| + def GenerateHelperFunctions(self, out): |
| + """ Write helper functions to avoid dependencies on libc. """ |
| + out.Write("""/* Use local strcmp to avoid dependency on libc. */ |
| +static int mystrcmp(const char* s1, const char *s2) { |
| + while((*s1 && *s2) && (*s1++ == *s2++)); |
| + return *(--s1) - *(--s2); |
| +}\n |
| +""") |
| + |
| + |
| + def GenerateFixedFunctions(self, out): |
| + """ Write out the set of constant functions (those that do not depend on |
| + the current Pepper IDL). """ |
| + out.Write(""" |
| + |
| +static PPB_GetInterface __real_PPBGetInterface; |
| +static PPP_GetInterface_type __real_PPPGetInterface; |
| + |
| +void __set_real_%(wrapper_prefix)s_PPBGetInterface(PPB_GetInterface real) { |
| + __real_PPBGetInterface = real; |
| +} |
| + |
| +void __set_real_%(wrapper_prefix)s_PPPGetInterface(PPP_GetInterface_type real) { |
| + __real_PPPGetInterface = real; |
| +} |
| + |
| +/* Map interface string -> wrapper metadata */ |
| +static struct %(wrapper_struct)s *%(wrapper_prefix)sPPBShimIface( |
| + const char *name) { |
| + struct %(wrapper_struct)s **next = s_ppb_wrappers; |
| + while (*next != NULL) { |
| + if (mystrcmp(name, (*next)->iface_macro) == 0) return *next; |
| + ++next; |
| + } |
| + return NULL; |
| +} |
| + |
| +/* Map interface string -> wrapper metadata */ |
| +static struct %(wrapper_struct)s *%(wrapper_prefix)sPPPShimIface( |
| + const char *name) { |
| + struct %(wrapper_struct)s **next = s_ppp_wrappers; |
| + while (*next != NULL) { |
| + if (mystrcmp(name, (*next)->iface_macro) == 0) return *next; |
| + ++next; |
| + } |
| + return NULL; |
| +} |
| + |
| +const void *__%(wrapper_prefix)s_PPBGetInterface(const char *name) { |
| + struct %(wrapper_struct)s *wrapper = %(wrapper_prefix)sPPBShimIface(name); |
| + if (wrapper == NULL) { |
| + /* We don't have an IDL for this, for some reason. Take our chances. */ |
| + return (*__real_PPBGetInterface)(name); |
| + } |
| + |
| + /* Initialize the real_iface if it hasn't been. The wrapper depends on it. */ |
| + if (wrapper->real_iface == NULL) { |
| + const void *iface = (*__real_PPBGetInterface)(name); |
| + if (NULL == iface) return NULL; |
| + wrapper->real_iface = iface; |
| + } |
| + |
| + if (wrapper->wrapped_iface) { |
| + return wrapper->wrapped_iface; |
| + } else { |
| + return wrapper->real_iface; |
| + } |
| +} |
| + |
| +const void *__%(wrapper_prefix)s_PPPGetInterface(const char *name) { |
| + struct %(wrapper_struct)s *wrapper = %(wrapper_prefix)sPPPShimIface(name); |
| + if (wrapper == NULL) { |
| + /* We don't have an IDL for this, for some reason. Take our chances. */ |
| + return (*__real_PPPGetInterface)(name); |
| + } |
| + |
| + /* Initialize the real_iface if it hasn't been. The wrapper depends on it. */ |
| + if (wrapper->real_iface == NULL) { |
| + const void *iface = (*__real_PPPGetInterface)(name); |
| + if (NULL == iface) return NULL; |
| + wrapper->real_iface = iface; |
| + } |
| + |
| + if (wrapper->wrapped_iface) { |
| + return wrapper->wrapped_iface; |
| + } else { |
| + return wrapper->real_iface; |
| + } |
| +} |
| +""" % { 'wrapper_struct' : self.GetWrapperMetadataName(), |
| + 'wrapper_prefix' : self.wrapper_prefix, |
| + } ) |
| + |
| + |
| + ############################################################ |
| + |
| + def InterfaceNeedsWrapper(self, iface, releases): |
| + """ Return true if the interface has ANY methods that need wrapping. """ |
| + return True |
| + |
| + |
| + ############################################################ |
| + |
| + def DetermineInterfaces(self, ast, releases): |
| + """ Get a list of interfaces along with whatever metadata we need. """ |
| + iface_releases = [] |
| + for filenode in ast.GetListOf('File'): |
| + # If this file has errors, skip it |
| + if filenode in self.skip_list: |
| + InfoOut.Log('WrapperGen: Skipping %s due to errors\n' % |
| + filenode.GetName()) |
| + continue |
| + |
| + file_name = self.GetHeaderName(filenode.GetName()) |
| + ifaces = filenode.GetListOf('Interface') |
| + for iface in ifaces: |
| + releases_for_iface = iface.GetUniqueReleases(releases) |
| + for release in releases_for_iface: |
| + version = iface.GetVersion(release) |
| + not_latest = release != releases_for_iface[-1] |
| + struct_name = self.cgen.GetStructName(iface, release, |
| + include_version=not_latest) |
| + needs_wrap = self.InterfaceVersionNeedsWrapping(iface, version) |
| + if not needs_wrap: |
| + InfoOut.Log('Interface %s ver %s does not need wrapping' % |
| + (struct_name, version)) |
| + iface_releases.append( |
| + Interface(iface, release, version, |
| + struct_name, needs_wrap, file_name)) |
| + return iface_releases |
| + |
| + |
| + def GenerateIncludes(self, iface_releases, out_header_filename, out): |
| + """ Write out the includes and other headers and return a list |
| + of the interface nodes that were ultimately included. """ |
| + self.WriteCopyrightGeneratedTime(out) |
| + # First include own header. |
| + out.Write('#include "%s"\n\n' % out_header_filename) |
| + |
| + # Get typedefs for PPB_GetInterface. |
| + out.Write('#include "%s"\n' % self.GetHeaderName('ppb.h')) |
| + |
| + # Get a conservative list of all #includes that are needed, |
| + # whether it requires wrapping or not. We currently depend on the macro |
| + # string for comparison, even when it is not wrapped, to decide when |
| + # to use the original/real interface. |
| + header_files = set() |
| + for iface in iface_releases: |
| + header_files.add(iface.header_file) |
| + for header in sorted(header_files): |
| + out.Write('#include "%s"\n' % header) |
| + out.Write('\n') |
| + |
| + |
| + def WrapperMethodPrefix(self, iface, release): |
| + return '%s_%s_%s_' % (self.wrapper_prefix, release, iface.GetName()) |
| + |
| + |
| + def GetReturnArgs(self, ret_type, args_spec): |
| + if ret_type != 'void': |
| + ret = 'return ' |
| + else: |
| + ret = '' |
| + if args_spec: |
| + args = [] |
| + for arg in args_spec: |
| + args.append(arg[1]) |
| + args = ', '.join(args) |
| + else: |
| + args = '' |
| + return (ret, args) |
| + |
| + |
| + def GenerateWrapperForPPBMethod(self, iface, member): |
| + result = [] |
| + func_prefix = self.WrapperMethodPrefix(iface.node, iface.release) |
| + sig = self.cgen.GetSignature(member, iface.release, 'store', |
| + func_prefix, False) |
| + result.append('static %s {\n' % sig) |
| + result.append(' while(1) { /* Not implemented */ } \n') |
| + result.append('}\n') |
| + return result |
| + |
| + |
| + def GenerateWrapperForPPPMethod(self, iface, member): |
| + result = [] |
| + func_prefix = self.WrapperMethodPrefix(iface.node, iface.release) |
| + sig = self.cgen.GetSignature(member, iface.release, 'store', |
| + func_prefix, False) |
| + result.append('static %s {\n' % sig) |
| + result.append(' while(1) { /* Not implemented */ } \n') |
| + result.append('}\n') |
| + return result |
| + |
| + |
| + def GenerateWrapperForMethods(self, iface_releases, comments=True): |
| + """ Return a string representing the code for each wrapper method |
| + (using a string rather than writing to the file directly for testing.) """ |
| + result = [] |
| + for iface in iface_releases: |
| + if not iface.needs_wrapping: |
| + if comments: |
| + result.append('/* Not generating wrapper methods for %s */\n\n' % |
| + iface.struct_name) |
| + continue |
| + if comments: |
| + result.append('/* Begin wrapper methods for %s */\n\n' % |
| + iface.struct_name) |
| + generator = PPKind.ChoosePPFunc(iface, |
| + self.GenerateWrapperForPPBMethod, |
| + self.GenerateWrapperForPPPMethod) |
| + for member in iface.node.GetListOf('Member'): |
| + # Skip the method if it's not actually in the release. |
| + if not member.InReleases([iface.release]): |
| + continue |
| + result.extend(generator(iface, member)) |
| + if comments: |
| + result.append('/* End wrapper methods for %s */\n\n' % |
| + iface.struct_name) |
| + return ''.join(result) |
| + |
| + |
| + def GenerateWrapperInterfaces(self, iface_releases, out): |
| + for iface in iface_releases: |
| + if not iface.needs_wrapping: |
| + out.Write('/* Not generating wrapper interface for %s */\n\n' % |
| + iface.struct_name) |
| + continue |
| + |
| + out.Write('struct %s %s_Wrappers_%s = {\n' % (iface.struct_name, |
| + self.wrapper_prefix, |
| + iface.struct_name)) |
| + methods = [] |
| + for member in iface.node.GetListOf('Member'): |
| + # Skip the method if it's not actually in the release. |
| + if not member.InReleases([iface.release]): |
| + continue |
| + prefix = self.WrapperMethodPrefix(iface.node, iface.release) |
| + cast = self.cgen.GetSignature(member, iface.release, 'return', |
| + prefix='', |
| + func_as_ptr=True, |
| + ptr_prefix='', |
| + include_name=False) |
| + methods.append(' .%s = (%s)&%s%s' % (member.GetName(), |
| + cast, |
| + prefix, |
| + member.GetName())) |
| + out.Write(' ' + ',\n '.join(methods) + '\n') |
| + out.Write('};\n\n') |
| + |
| + |
| + def GetWrapperInfoName(self, iface): |
| + return '%s_WrapperInfo_%s' % (self.wrapper_prefix, iface.struct_name) |
| + |
| + |
| + def GenerateWrapperInfoAndCollection(self, iface_releases, out): |
| + for iface in iface_releases: |
| + iface_macro = self.cgen.GetInterfaceMacro(iface.node, iface.version) |
| + if iface.needs_wrapping: |
| + wrap_iface = '(void *) &%s_Wrappers_%s' % (self.wrapper_prefix, |
| + iface.struct_name) |
| + else: |
| + wrap_iface = 'NULL /* Still need slot for real_iface */' |
| + out.Write("""static struct %s %s = { |
| + .iface_macro = %s, |
| + .wrapped_iface = %s, |
| + .real_iface = NULL |
| +};\n\n""" % (self.GetWrapperMetadataName(), |
| + self.GetWrapperInfoName(iface), |
| + iface_macro, |
| + wrap_iface)) |
| + |
| + # Now generate NULL terminated arrays of the above wrapper infos. |
| + ppb_wrapper_infos = [] |
| + ppp_wrapper_infos = [] |
| + for iface in iface_releases: |
| + appender = PPKind.ChoosePPFunc(iface, |
| + ppb_wrapper_infos.append, |
| + ppp_wrapper_infos.append) |
| + appender(' &%s' % self.GetWrapperInfoName(iface)) |
| + ppb_wrapper_infos.append(' NULL') |
| + ppp_wrapper_infos.append(' NULL') |
| + out.Write( |
| + 'static struct %s *s_ppb_wrappers[] = {\n%s\n};\n\n' % |
| + (self.GetWrapperMetadataName(), ',\n'.join(ppb_wrapper_infos))) |
| + out.Write( |
| + 'static struct %s *s_ppp_wrappers[] = {\n%s\n};\n\n' % |
| + (self.GetWrapperMetadataName(), ',\n'.join(ppp_wrapper_infos))) |
| + |
| + |
| + def DeclareWrapperInfos(self, iface_releases, out): |
| + """ The wrapper methods usually need access to the real_iface, so we must |
| + declare these wrapper infos ahead of time (there is a circular dependency). |
| + """ |
| + out.Write('/* BEGIN Declarations for all Wrapper Infos */\n\n') |
| + for iface in iface_releases: |
| + out.Write('static struct %s %s;\n' % |
| + (self.GetWrapperMetadataName(), self.GetWrapperInfoName(iface))) |
| + out.Write('/* END Declarations for all Wrapper Infos. */\n\n') |
| + |
| + |
| + def GenerateRange(self, ast, releases, options): |
| + """ Generate shim code for a range of releases. """ |
| + |
| + # Remember to set the output filename before running this. |
| + out_filename = self.output_file |
| + if out_filename is None: |
| + ErrOut.Log('Did not set filename for writing out wrapper\n') |
| + return 1 |
| + |
| + out_header_filename = os.path.splitext(out_filename)[0] + '.h' |
| + InfoOut.Log("Generating %s and %s for %s" % |
| + (out_filename, out_header_filename, self.wrapper_prefix)) |
| + |
| + out_header = IDLOutFile(out_header_filename) |
| + self.GenerateHeaderFile(out_header_filename, out_header) |
| + out_header.Close() |
| + |
| + out = IDLOutFile(out_filename) |
| + |
| + # Get a list of all the interfaces along with metadata. |
| + iface_releases = self.DetermineInterfaces(ast, releases) |
| + |
| + # Generate the includes. |
| + self.GenerateIncludes(iface_releases, out_header_filename, out) |
| + |
| + # Write out static helper functions (mystrcmp). |
| + self.GenerateHelperFunctions(out) |
| + |
| + # Declare list of WrapperInfo before actual wrapper methods, since |
| + # they reference each other. |
| + self.DeclareWrapperInfos(iface_releases, out) |
| + |
| + # Generate wrapper functions for each wrapped method in the interfaces. |
| + result = self.GenerateWrapperForMethods(iface_releases) |
| + out.Write(result) |
| + |
| + # Collect all the wrapper functions into interface structs. |
| + self.GenerateWrapperInterfaces(iface_releases, out) |
| + |
| + # Generate a table of the wrapped interface structs that can be looked up. |
| + self.GenerateWrapperInfoAndCollection(iface_releases, out) |
| + |
| + # Write out the IDL-invariant functions. |
| + self.GenerateFixedFunctions(out) |
| + out.Close() |
| + return 0 |