|  | # Copyright (c) 2012 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_c_proto import CGen | 
|  | from idl_generator import Generator | 
|  | from idl_log import ErrOut, InfoOut, WarnOut | 
|  | from idl_option import  GetOption | 
|  | 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 a method in the interface needs wrapping. | 
|  | - 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 - An abstract class that generates wrappers for PPAPI methods. | 
|  |  | 
|  | This generates a wrapper PPB and PPP GetInterface, which directs users | 
|  | to wrapper PPAPI methods. Wrapper PPAPI methods may perform arbitrary | 
|  | work before invoking the real PPAPI method (supplied by the original | 
|  | GetInterface functions). | 
|  |  | 
|  | Subclasses must implement GenerateWrapperForPPBMethod (and PPP). | 
|  | """ | 
|  |  | 
|  | 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' | 
|  | name = name.replace(os.sep, '/') | 
|  | return 'ppapi/c/' + name | 
|  |  | 
|  |  | 
|  | def WriteCopyright(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. | 
|  | */ | 
|  |  | 
|  | /* NOTE: this is auto-generated from IDL */ | 
|  | """ % now.year | 
|  | out.Write(c) | 
|  |  | 
|  | def GetWrapperMetadataName(self): | 
|  | return '__%sWrapperInfo' % self.wrapper_prefix | 
|  |  | 
|  |  | 
|  | def GenerateHelperFunctions(self, out): | 
|  | """Generate 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 (1) { | 
|  | if (*s1 == 0) break; | 
|  | if (*s2 == 0) break; | 
|  | if (*s1 != *s2) break; | 
|  | ++s1; | 
|  | ++s2; | 
|  | } | 
|  | return (int)(*s1) - (int)(*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 did not generate a wrapper for this, so return the real interface. */ | 
|  | 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; | 
|  | } | 
|  |  | 
|  | return wrapper->wrapped_iface; | 
|  | } | 
|  |  | 
|  | const void *__%(wrapper_prefix)s_PPPGetInterface(const char *name) { | 
|  | struct %(wrapper_struct)s *wrapper = %(wrapper_prefix)sPPPShimIface(name); | 
|  | if (wrapper == NULL) { | 
|  | /* We did not generate a wrapper for this, so return the real interface. */ | 
|  | 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; | 
|  | } | 
|  |  | 
|  | return wrapper->wrapped_iface; | 
|  | } | 
|  | """ % { 'wrapper_struct' : self.GetWrapperMetadataName(), | 
|  | 'wrapper_prefix' : self.wrapper_prefix, | 
|  | } ) | 
|  |  | 
|  |  | 
|  | ############################################################ | 
|  |  | 
|  | def OwnHeaderFile(self): | 
|  | """Return the header file that specifies the API of this wrapper. | 
|  | We do not generate the header files.  """ | 
|  | raise Exception('Child class must implement this') | 
|  |  | 
|  |  | 
|  | ############################################################ | 
|  |  | 
|  | 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: | 
|  | if GetOption('verbose'): | 
|  | 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) | 
|  | struct_name = self.cgen.GetStructName(iface, release, | 
|  | include_version=True) | 
|  | needs_wrap = self.InterfaceVersionNeedsWrapping(iface, version) | 
|  | if not needs_wrap: | 
|  | if GetOption('verbose'): | 
|  | 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): | 
|  | """Generate the list of #include that define the original interfaces. | 
|  | """ | 
|  | self.WriteCopyright(out) | 
|  | # First include own header. | 
|  | out.Write('#include "%s"\n\n' % self.OwnHeaderFile()) | 
|  |  | 
|  | # Get typedefs for PPB_GetInterface. | 
|  | out.Write('#include "%s"\n' % self.GetHeaderName('ppb.h')) | 
|  |  | 
|  | # Only include headers where *some* interface needs wrapping. | 
|  | header_files = set() | 
|  | for iface in iface_releases: | 
|  | if iface.needs_wrapping: | 
|  | 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 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('static const 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) | 
|  | # Casts are necessary for the PPB_* wrappers because we must | 
|  | # cast away "__attribute__((pnaclcall))".  The PPP_* wrappers | 
|  | # must match the default calling conventions and so don't have | 
|  | # the attribute, so omitting casts for them provides a little | 
|  | # extra type checking. | 
|  | if iface.node.GetName().startswith('PPB_'): | 
|  | cast = '(%s)' % self.cgen.GetSignature( | 
|  | member, iface.release, 'return', | 
|  | prefix='', | 
|  | func_as_ptr=True, | 
|  | include_name=False) | 
|  | else: | 
|  | cast = '' | 
|  | 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 = '(const void *) &%s_Wrappers_%s' % (self.wrapper_prefix, | 
|  | iface.struct_name) | 
|  | 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: | 
|  | if iface.needs_wrapping: | 
|  | 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: | 
|  | if iface.needs_wrapping: | 
|  | 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 | 
|  |  | 
|  | InfoOut.Log("Generating %s for %s" % (out_filename, self.wrapper_prefix)) | 
|  |  | 
|  | 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) | 
|  |  | 
|  | # 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 |