Home | History | Annotate | Download | only in generators
      1 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 """Base class for generating wrapper functions for PPAPI methods.
      6 """
      7 
      8 from datetime import datetime
      9 import os
     10 import sys
     11 
     12 from idl_c_proto import CGen
     13 from idl_generator import Generator
     14 from idl_log import ErrOut, InfoOut, WarnOut
     15 from idl_option import  GetOption
     16 from idl_outfile import IDLOutFile
     17 
     18 
     19 class PPKind(object):
     20   @staticmethod
     21   def ChoosePPFunc(iface, ppb_func, ppp_func):
     22     name = iface.node.GetName()
     23     if name.startswith("PPP"):
     24       return ppp_func
     25     elif name.startswith("PPB"):
     26       return ppb_func
     27     else:
     28       raise Exception('Unknown PPKind for ' + name)
     29 
     30 
     31 class Interface(object):
     32   """Tracks information about a particular interface version.
     33 
     34   - struct_name: the struct type used by the ppapi headers to hold the
     35   method pointers (the vtable).
     36   - needs_wrapping: True if a method in the interface needs wrapping.
     37   - header_file: the name of the header file that defined this interface.
     38   """
     39   def __init__(self, interface_node, release, version,
     40                struct_name, needs_wrapping, header_file):
     41     self.node = interface_node
     42     self.release = release
     43     self.version = version
     44     self.struct_name = struct_name
     45     # We may want finer grained filtering (method level), but it is not
     46     # yet clear how to actually do that.
     47     self.needs_wrapping = needs_wrapping
     48     self.header_file = header_file
     49 
     50 
     51 class WrapperGen(Generator):
     52   """WrapperGen - An abstract class that generates wrappers for PPAPI methods.
     53 
     54   This generates a wrapper PPB and PPP GetInterface, which directs users
     55   to wrapper PPAPI methods. Wrapper PPAPI methods may perform arbitrary
     56   work before invoking the real PPAPI method (supplied by the original
     57   GetInterface functions).
     58 
     59   Subclasses must implement GenerateWrapperForPPBMethod (and PPP).
     60   """
     61 
     62   def __init__(self, wrapper_prefix, s1, s2, s3):
     63     Generator.__init__(self, s1, s2, s3)
     64     self.wrapper_prefix = wrapper_prefix
     65     self._skip_opt = False
     66     self.output_file = None
     67     self.cgen = CGen()
     68 
     69   def SetOutputFile(self, fname):
     70     self.output_file = fname
     71 
     72 
     73   def GenerateRelease(self, ast, release, options):
     74     return self.GenerateRange(ast, [release], options)
     75 
     76 
     77   @staticmethod
     78   def GetHeaderName(name):
     79     """Get the corresponding ppapi .h file from each IDL filename.
     80     """
     81     name = os.path.splitext(name)[0] + '.h'
     82     name = name.replace(os.sep, '/')
     83     return 'ppapi/c/' + name
     84 
     85 
     86   def WriteCopyright(self, out):
     87     now = datetime.now()
     88     c = """/* Copyright (c) %s The Chromium Authors. All rights reserved.
     89  * Use of this source code is governed by a BSD-style license that can be
     90  * found in the LICENSE file.
     91  */
     92 
     93 /* NOTE: this is auto-generated from IDL */
     94 """ % now.year
     95     out.Write(c)
     96 
     97   def GetWrapperMetadataName(self):
     98     return '__%sWrapperInfo' % self.wrapper_prefix
     99 
    100 
    101   def GenerateHelperFunctions(self, out):
    102     """Generate helper functions to avoid dependencies on libc.
    103     """
    104     out.Write("""/* Use local strcmp to avoid dependency on libc. */
    105 static int mystrcmp(const char* s1, const char *s2) {
    106   while (1) {
    107     if (*s1 == 0) break;
    108     if (*s2 == 0) break;
    109     if (*s1 != *s2) break;
    110     ++s1;
    111     ++s2;
    112   }
    113   return (int)(*s1) - (int)(*s2);
    114 }\n
    115 """)
    116 
    117 
    118   def GenerateFixedFunctions(self, out):
    119     """Write out the set of constant functions (those that do not depend on
    120     the current Pepper IDL).
    121     """
    122     out.Write("""
    123 
    124 static PPB_GetInterface __real_PPBGetInterface;
    125 static PPP_GetInterface_Type __real_PPPGetInterface;
    126 
    127 void __set_real_%(wrapper_prefix)s_PPBGetInterface(PPB_GetInterface real) {
    128   __real_PPBGetInterface = real;
    129 }
    130 
    131 void __set_real_%(wrapper_prefix)s_PPPGetInterface(PPP_GetInterface_Type real) {
    132   __real_PPPGetInterface = real;
    133 }
    134 
    135 /* Map interface string -> wrapper metadata */
    136 static struct %(wrapper_struct)s *%(wrapper_prefix)sPPBShimIface(
    137     const char *name) {
    138   struct %(wrapper_struct)s **next = s_ppb_wrappers;
    139   while (*next != NULL) {
    140     if (mystrcmp(name, (*next)->iface_macro) == 0) return *next;
    141     ++next;
    142   }
    143   return NULL;
    144 }
    145 
    146 /* Map interface string -> wrapper metadata */
    147 static struct %(wrapper_struct)s *%(wrapper_prefix)sPPPShimIface(
    148     const char *name) {
    149   struct %(wrapper_struct)s **next = s_ppp_wrappers;
    150   while (*next != NULL) {
    151     if (mystrcmp(name, (*next)->iface_macro) == 0) return *next;
    152     ++next;
    153   }
    154   return NULL;
    155 }
    156 
    157 const void *__%(wrapper_prefix)s_PPBGetInterface(const char *name) {
    158   struct %(wrapper_struct)s *wrapper = %(wrapper_prefix)sPPBShimIface(name);
    159   if (wrapper == NULL) {
    160     /* We did not generate a wrapper for this, so return the real interface. */
    161     return (*__real_PPBGetInterface)(name);
    162   }
    163 
    164   /* Initialize the real_iface if it hasn't been. The wrapper depends on it. */
    165   if (wrapper->real_iface == NULL) {
    166     const void *iface = (*__real_PPBGetInterface)(name);
    167     if (NULL == iface) return NULL;
    168     wrapper->real_iface = iface;
    169   }
    170 
    171   return wrapper->wrapped_iface;
    172 }
    173 
    174 const void *__%(wrapper_prefix)s_PPPGetInterface(const char *name) {
    175   struct %(wrapper_struct)s *wrapper = %(wrapper_prefix)sPPPShimIface(name);
    176   if (wrapper == NULL) {
    177     /* We did not generate a wrapper for this, so return the real interface. */
    178     return (*__real_PPPGetInterface)(name);
    179   }
    180 
    181   /* Initialize the real_iface if it hasn't been. The wrapper depends on it. */
    182   if (wrapper->real_iface == NULL) {
    183     const void *iface = (*__real_PPPGetInterface)(name);
    184     if (NULL == iface) return NULL;
    185     wrapper->real_iface = iface;
    186   }
    187 
    188   return wrapper->wrapped_iface;
    189 }
    190 """ % { 'wrapper_struct' : self.GetWrapperMetadataName(),
    191         'wrapper_prefix' : self.wrapper_prefix,
    192         } )
    193 
    194 
    195   ############################################################
    196 
    197   def OwnHeaderFile(self):
    198     """Return the header file that specifies the API of this wrapper.
    199     We do not generate the header files.  """
    200     raise Exception('Child class must implement this')
    201 
    202 
    203   ############################################################
    204 
    205   def DetermineInterfaces(self, ast, releases):
    206     """Get a list of interfaces along with whatever metadata we need.
    207     """
    208     iface_releases = []
    209     for filenode in ast.GetListOf('File'):
    210       # If this file has errors, skip it
    211       if filenode in self.skip_list:
    212         if GetOption('verbose'):
    213           InfoOut.Log('WrapperGen: Skipping %s due to errors\n' %
    214                       filenode.GetName())
    215         continue
    216 
    217       file_name = self.GetHeaderName(filenode.GetName())
    218       ifaces = filenode.GetListOf('Interface')
    219       for iface in ifaces:
    220         releases_for_iface = iface.GetUniqueReleases(releases)
    221         for release in releases_for_iface:
    222           version = iface.GetVersion(release)
    223           struct_name = self.cgen.GetStructName(iface, release,
    224                                                 include_version=True)
    225           needs_wrap = self.InterfaceVersionNeedsWrapping(iface, version)
    226           if not needs_wrap:
    227             if GetOption('verbose'):
    228               InfoOut.Log('Interface %s ver %s does not need wrapping' %
    229                           (struct_name, version))
    230           iface_releases.append(
    231               Interface(iface, release, version,
    232                         struct_name, needs_wrap, file_name))
    233     return iface_releases
    234 
    235 
    236   def GenerateIncludes(self, iface_releases, out):
    237     """Generate the list of #include that define the original interfaces.
    238     """
    239     self.WriteCopyright(out)
    240     # First include own header.
    241     out.Write('#include "%s"\n\n' % self.OwnHeaderFile())
    242 
    243     # Get typedefs for PPB_GetInterface.
    244     out.Write('#include "%s"\n' % self.GetHeaderName('ppb.h'))
    245 
    246     # Only include headers where *some* interface needs wrapping.
    247     header_files = set()
    248     for iface in iface_releases:
    249       if iface.needs_wrapping:
    250         header_files.add(iface.header_file)
    251     for header in sorted(header_files):
    252       out.Write('#include "%s"\n' % header)
    253     out.Write('\n')
    254 
    255 
    256   def WrapperMethodPrefix(self, iface, release):
    257     return '%s_%s_%s_' % (self.wrapper_prefix, release, iface.GetName())
    258 
    259 
    260   def GenerateWrapperForPPBMethod(self, iface, member):
    261     result = []
    262     func_prefix = self.WrapperMethodPrefix(iface.node, iface.release)
    263     sig = self.cgen.GetSignature(member, iface.release, 'store',
    264                                  func_prefix, False)
    265     result.append('static %s {\n' % sig)
    266     result.append(' while(1) { /* Not implemented */ } \n')
    267     result.append('}\n')
    268     return result
    269 
    270 
    271   def GenerateWrapperForPPPMethod(self, iface, member):
    272     result = []
    273     func_prefix = self.WrapperMethodPrefix(iface.node, iface.release)
    274     sig = self.cgen.GetSignature(member, iface.release, 'store',
    275                                  func_prefix, False)
    276     result.append('static %s {\n' % sig)
    277     result.append(' while(1) { /* Not implemented */ } \n')
    278     result.append('}\n')
    279     return result
    280 
    281 
    282   def GenerateWrapperForMethods(self, iface_releases, comments=True):
    283     """Return a string representing the code for each wrapper method
    284     (using a string rather than writing to the file directly for testing.)
    285     """
    286     result = []
    287     for iface in iface_releases:
    288       if not iface.needs_wrapping:
    289         if comments:
    290           result.append('/* Not generating wrapper methods for %s */\n\n' %
    291                         iface.struct_name)
    292         continue
    293       if comments:
    294         result.append('/* Begin wrapper methods for %s */\n\n' %
    295                       iface.struct_name)
    296       generator =  PPKind.ChoosePPFunc(iface,
    297                                        self.GenerateWrapperForPPBMethod,
    298                                        self.GenerateWrapperForPPPMethod)
    299       for member in iface.node.GetListOf('Member'):
    300         # Skip the method if it's not actually in the release.
    301         if not member.InReleases([iface.release]):
    302           continue
    303         result.extend(generator(iface, member))
    304       if comments:
    305         result.append('/* End wrapper methods for %s */\n\n' %
    306                       iface.struct_name)
    307     return ''.join(result)
    308 
    309 
    310   def GenerateWrapperInterfaces(self, iface_releases, out):
    311     for iface in iface_releases:
    312       if not iface.needs_wrapping:
    313         out.Write('/* Not generating wrapper interface for %s */\n\n' %
    314                   iface.struct_name)
    315         continue
    316 
    317       out.Write('static const struct %s %s_Wrappers_%s = {\n' % (
    318           iface.struct_name, self.wrapper_prefix, iface.struct_name))
    319       methods = []
    320       for member in iface.node.GetListOf('Member'):
    321         # Skip the method if it's not actually in the release.
    322         if not member.InReleases([iface.release]):
    323           continue
    324         prefix = self.WrapperMethodPrefix(iface.node, iface.release)
    325         # Casts are necessary for the PPB_* wrappers because we must
    326         # cast away "__attribute__((pnaclcall))".  The PPP_* wrappers
    327         # must match the default calling conventions and so don't have
    328         # the attribute, so omitting casts for them provides a little
    329         # extra type checking.
    330         if iface.node.GetName().startswith('PPB_'):
    331           cast = '(%s)' % self.cgen.GetSignature(
    332               member, iface.release, 'return',
    333               prefix='',
    334               func_as_ptr=True,
    335               include_name=False)
    336         else:
    337           cast = ''
    338         methods.append('  .%s = %s&%s%s' % (member.GetName(),
    339                                             cast,
    340                                             prefix,
    341                                             member.GetName()))
    342       out.Write('  ' + ',\n  '.join(methods) + '\n')
    343       out.Write('};\n\n')
    344 
    345 
    346   def GetWrapperInfoName(self, iface):
    347     return '%s_WrapperInfo_%s' % (self.wrapper_prefix, iface.struct_name)
    348 
    349 
    350   def GenerateWrapperInfoAndCollection(self, iface_releases, out):
    351     for iface in iface_releases:
    352       iface_macro = self.cgen.GetInterfaceMacro(iface.node, iface.version)
    353       if iface.needs_wrapping:
    354         wrap_iface = '(const void *) &%s_Wrappers_%s' % (self.wrapper_prefix,
    355                                                          iface.struct_name)
    356         out.Write("""static struct %s %s = {
    357   .iface_macro = %s,
    358   .wrapped_iface = %s,
    359   .real_iface = NULL
    360 };\n\n""" % (self.GetWrapperMetadataName(),
    361              self.GetWrapperInfoName(iface),
    362              iface_macro,
    363              wrap_iface))
    364 
    365     # Now generate NULL terminated arrays of the above wrapper infos.
    366     ppb_wrapper_infos = []
    367     ppp_wrapper_infos = []
    368     for iface in iface_releases:
    369       if iface.needs_wrapping:
    370         appender = PPKind.ChoosePPFunc(iface,
    371                                        ppb_wrapper_infos.append,
    372                                        ppp_wrapper_infos.append)
    373         appender('  &%s' % self.GetWrapperInfoName(iface))
    374     ppb_wrapper_infos.append('  NULL')
    375     ppp_wrapper_infos.append('  NULL')
    376     out.Write(
    377         'static struct %s *s_ppb_wrappers[] = {\n%s\n};\n\n' %
    378         (self.GetWrapperMetadataName(), ',\n'.join(ppb_wrapper_infos)))
    379     out.Write(
    380         'static struct %s *s_ppp_wrappers[] = {\n%s\n};\n\n' %
    381         (self.GetWrapperMetadataName(), ',\n'.join(ppp_wrapper_infos)))
    382 
    383 
    384   def DeclareWrapperInfos(self, iface_releases, out):
    385     """The wrapper methods usually need access to the real_iface, so we must
    386     declare these wrapper infos ahead of time (there is a circular dependency).
    387     """
    388     out.Write('/* BEGIN Declarations for all Wrapper Infos */\n\n')
    389     for iface in iface_releases:
    390       if iface.needs_wrapping:
    391         out.Write('static struct %s %s;\n' %
    392                   (self.GetWrapperMetadataName(),
    393                    self.GetWrapperInfoName(iface)))
    394     out.Write('/* END Declarations for all Wrapper Infos. */\n\n')
    395 
    396 
    397   def GenerateRange(self, ast, releases, options):
    398     """Generate shim code for a range of releases.
    399     """
    400 
    401     # Remember to set the output filename before running this.
    402     out_filename = self.output_file
    403     if out_filename is None:
    404       ErrOut.Log('Did not set filename for writing out wrapper\n')
    405       return 1
    406 
    407     InfoOut.Log("Generating %s for %s" % (out_filename, self.wrapper_prefix))
    408 
    409     out = IDLOutFile(out_filename)
    410 
    411     # Get a list of all the interfaces along with metadata.
    412     iface_releases = self.DetermineInterfaces(ast, releases)
    413 
    414     # Generate the includes.
    415     self.GenerateIncludes(iface_releases, out)
    416 
    417     # Write out static helper functions (mystrcmp).
    418     self.GenerateHelperFunctions(out)
    419 
    420     # Declare list of WrapperInfo before actual wrapper methods, since
    421     # they reference each other.
    422     self.DeclareWrapperInfos(iface_releases, out)
    423 
    424     # Generate wrapper functions for each wrapped method in the interfaces.
    425     result = self.GenerateWrapperForMethods(iface_releases)
    426     out.Write(result)
    427 
    428     # Collect all the wrapper functions into interface structs.
    429     self.GenerateWrapperInterfaces(iface_releases, out)
    430 
    431     # Generate a table of the wrapped interface structs that can be looked up.
    432     self.GenerateWrapperInfoAndCollection(iface_releases, out)
    433 
    434     # Write out the IDL-invariant functions.
    435     self.GenerateFixedFunctions(out)
    436 
    437     out.Close()
    438     return 0
    439