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   if (wrapper->wrapped_iface) {
    172     return wrapper->wrapped_iface;
    173   } else {
    174     return wrapper->real_iface;
    175   }
    176 }
    177 
    178 const void *__%(wrapper_prefix)s_PPPGetInterface(const char *name) {
    179   struct %(wrapper_struct)s *wrapper = %(wrapper_prefix)sPPPShimIface(name);
    180   if (wrapper == NULL) {
    181     /* We did not generate a wrapper for this, so return the real interface. */
    182     return (*__real_PPPGetInterface)(name);
    183   }
    184 
    185   /* Initialize the real_iface if it hasn't been. The wrapper depends on it. */
    186   if (wrapper->real_iface == NULL) {
    187     const void *iface = (*__real_PPPGetInterface)(name);
    188     if (NULL == iface) return NULL;
    189     wrapper->real_iface = iface;
    190   }
    191 
    192   if (wrapper->wrapped_iface) {
    193     return wrapper->wrapped_iface;
    194   } else {
    195     return wrapper->real_iface;
    196   }
    197 }
    198 """ % { 'wrapper_struct' : self.GetWrapperMetadataName(),
    199         'wrapper_prefix' : self.wrapper_prefix,
    200         } )
    201 
    202 
    203   ############################################################
    204 
    205   def OwnHeaderFile(self):
    206     """Return the header file that specifies the API of this wrapper.
    207     We do not generate the header files.  """
    208     raise Exception('Child class must implement this')
    209 
    210 
    211   ############################################################
    212 
    213   def DetermineInterfaces(self, ast, releases):
    214     """Get a list of interfaces along with whatever metadata we need.
    215     """
    216     iface_releases = []
    217     for filenode in ast.GetListOf('File'):
    218       # If this file has errors, skip it
    219       if filenode in self.skip_list:
    220         if GetOption('verbose'):
    221           InfoOut.Log('WrapperGen: Skipping %s due to errors\n' %
    222                       filenode.GetName())
    223         continue
    224 
    225       file_name = self.GetHeaderName(filenode.GetName())
    226       ifaces = filenode.GetListOf('Interface')
    227       for iface in ifaces:
    228         releases_for_iface = iface.GetUniqueReleases(releases)
    229         for release in releases_for_iface:
    230           version = iface.GetVersion(release)
    231           struct_name = self.cgen.GetStructName(iface, release,
    232                                                 include_version=True)
    233           needs_wrap = self.InterfaceVersionNeedsWrapping(iface, version)
    234           if not needs_wrap:
    235             if GetOption('verbose'):
    236               InfoOut.Log('Interface %s ver %s does not need wrapping' %
    237                           (struct_name, version))
    238           iface_releases.append(
    239               Interface(iface, release, version,
    240                         struct_name, needs_wrap, file_name))
    241     return iface_releases
    242 
    243 
    244   def GenerateIncludes(self, iface_releases, out):
    245     """Generate the list of #include that define the original interfaces.
    246     """
    247     self.WriteCopyright(out)
    248     # First include own header.
    249     out.Write('#include "%s"\n\n' % self.OwnHeaderFile())
    250 
    251     # Get typedefs for PPB_GetInterface.
    252     out.Write('#include "%s"\n' % self.GetHeaderName('ppb.h'))
    253 
    254     # Get a conservative list of all #includes that are needed,
    255     # whether it requires wrapping or not. We currently depend on the macro
    256     # string for comparison, even when it is not wrapped, to decide when
    257     # to use the original/real interface.
    258     header_files = set()
    259     for iface in iface_releases:
    260       header_files.add(iface.header_file)
    261     for header in sorted(header_files):
    262       out.Write('#include "%s"\n' % header)
    263     out.Write('\n')
    264 
    265 
    266   def WrapperMethodPrefix(self, iface, release):
    267     return '%s_%s_%s_' % (self.wrapper_prefix, release, iface.GetName())
    268 
    269 
    270   def GenerateWrapperForPPBMethod(self, iface, member):
    271     result = []
    272     func_prefix = self.WrapperMethodPrefix(iface.node, iface.release)
    273     sig = self.cgen.GetSignature(member, iface.release, 'store',
    274                                  func_prefix, False)
    275     result.append('static %s {\n' % sig)
    276     result.append(' while(1) { /* Not implemented */ } \n')
    277     result.append('}\n')
    278     return result
    279 
    280 
    281   def GenerateWrapperForPPPMethod(self, iface, member):
    282     result = []
    283     func_prefix = self.WrapperMethodPrefix(iface.node, iface.release)
    284     sig = self.cgen.GetSignature(member, iface.release, 'store',
    285                                  func_prefix, False)
    286     result.append('static %s {\n' % sig)
    287     result.append(' while(1) { /* Not implemented */ } \n')
    288     result.append('}\n')
    289     return result
    290 
    291 
    292   def GenerateWrapperForMethods(self, iface_releases, comments=True):
    293     """Return a string representing the code for each wrapper method
    294     (using a string rather than writing to the file directly for testing.)
    295     """
    296     result = []
    297     for iface in iface_releases:
    298       if not iface.needs_wrapping:
    299         if comments:
    300           result.append('/* Not generating wrapper methods for %s */\n\n' %
    301                         iface.struct_name)
    302         continue
    303       if comments:
    304         result.append('/* Begin wrapper methods for %s */\n\n' %
    305                       iface.struct_name)
    306       generator =  PPKind.ChoosePPFunc(iface,
    307                                        self.GenerateWrapperForPPBMethod,
    308                                        self.GenerateWrapperForPPPMethod)
    309       for member in iface.node.GetListOf('Member'):
    310         # Skip the method if it's not actually in the release.
    311         if not member.InReleases([iface.release]):
    312           continue
    313         result.extend(generator(iface, member))
    314       if comments:
    315         result.append('/* End wrapper methods for %s */\n\n' %
    316                       iface.struct_name)
    317     return ''.join(result)
    318 
    319 
    320   def GenerateWrapperInterfaces(self, iface_releases, out):
    321     for iface in iface_releases:
    322       if not iface.needs_wrapping:
    323         out.Write('/* Not generating wrapper interface for %s */\n\n' %
    324                   iface.struct_name)
    325         continue
    326 
    327       out.Write('struct %s %s_Wrappers_%s = {\n' % (iface.struct_name,
    328                                                     self.wrapper_prefix,
    329                                                     iface.struct_name))
    330       methods = []
    331       for member in iface.node.GetListOf('Member'):
    332         # Skip the method if it's not actually in the release.
    333         if not member.InReleases([iface.release]):
    334           continue
    335         prefix = self.WrapperMethodPrefix(iface.node, iface.release)
    336         # Casts are necessary for the PPB_* wrappers because we must
    337         # cast away "__attribute__((pnaclcall))".  The PPP_* wrappers
    338         # must match the default calling conventions and so don't have
    339         # the attribute, so omitting casts for them provides a little
    340         # extra type checking.
    341         if iface.node.GetName().startswith('PPB_'):
    342           cast = '(%s)' % self.cgen.GetSignature(
    343               member, iface.release, 'return',
    344               prefix='',
    345               func_as_ptr=True,
    346               include_name=False)
    347         else:
    348           cast = ''
    349         methods.append('  .%s = %s&%s%s' % (member.GetName(),
    350                                             cast,
    351                                             prefix,
    352                                             member.GetName()))
    353       out.Write('  ' + ',\n  '.join(methods) + '\n')
    354       out.Write('};\n\n')
    355 
    356 
    357   def GetWrapperInfoName(self, iface):
    358     return '%s_WrapperInfo_%s' % (self.wrapper_prefix, iface.struct_name)
    359 
    360 
    361   def GenerateWrapperInfoAndCollection(self, iface_releases, out):
    362     for iface in iface_releases:
    363       iface_macro = self.cgen.GetInterfaceMacro(iface.node, iface.version)
    364       if iface.needs_wrapping:
    365         wrap_iface = '(void *) &%s_Wrappers_%s' % (self.wrapper_prefix,
    366                                                    iface.struct_name)
    367         out.Write("""static struct %s %s = {
    368   .iface_macro = %s,
    369   .wrapped_iface = %s,
    370   .real_iface = NULL
    371 };\n\n""" % (self.GetWrapperMetadataName(),
    372              self.GetWrapperInfoName(iface),
    373              iface_macro,
    374              wrap_iface))
    375 
    376     # Now generate NULL terminated arrays of the above wrapper infos.
    377     ppb_wrapper_infos = []
    378     ppp_wrapper_infos = []
    379     for iface in iface_releases:
    380       if iface.needs_wrapping:
    381         appender = PPKind.ChoosePPFunc(iface,
    382                                        ppb_wrapper_infos.append,
    383                                        ppp_wrapper_infos.append)
    384         appender('  &%s' % self.GetWrapperInfoName(iface))
    385     ppb_wrapper_infos.append('  NULL')
    386     ppp_wrapper_infos.append('  NULL')
    387     out.Write(
    388         'static struct %s *s_ppb_wrappers[] = {\n%s\n};\n\n' %
    389         (self.GetWrapperMetadataName(), ',\n'.join(ppb_wrapper_infos)))
    390     out.Write(
    391         'static struct %s *s_ppp_wrappers[] = {\n%s\n};\n\n' %
    392         (self.GetWrapperMetadataName(), ',\n'.join(ppp_wrapper_infos)))
    393 
    394 
    395   def DeclareWrapperInfos(self, iface_releases, out):
    396     """The wrapper methods usually need access to the real_iface, so we must
    397     declare these wrapper infos ahead of time (there is a circular dependency).
    398     """
    399     out.Write('/* BEGIN Declarations for all Wrapper Infos */\n\n')
    400     for iface in iface_releases:
    401       if iface.needs_wrapping:
    402         out.Write('static struct %s %s;\n' %
    403                   (self.GetWrapperMetadataName(),
    404                    self.GetWrapperInfoName(iface)))
    405     out.Write('/* END Declarations for all Wrapper Infos. */\n\n')
    406 
    407 
    408   def GenerateRange(self, ast, releases, options):
    409     """Generate shim code for a range of releases.
    410     """
    411 
    412     # Remember to set the output filename before running this.
    413     out_filename = self.output_file
    414     if out_filename is None:
    415       ErrOut.Log('Did not set filename for writing out wrapper\n')
    416       return 1
    417 
    418     InfoOut.Log("Generating %s for %s" % (out_filename, self.wrapper_prefix))
    419 
    420     out = IDLOutFile(out_filename)
    421 
    422     # Get a list of all the interfaces along with metadata.
    423     iface_releases = self.DetermineInterfaces(ast, releases)
    424 
    425     # Generate the includes.
    426     self.GenerateIncludes(iface_releases, out)
    427 
    428     # Write out static helper functions (mystrcmp).
    429     self.GenerateHelperFunctions(out)
    430 
    431     # Declare list of WrapperInfo before actual wrapper methods, since
    432     # they reference each other.
    433     self.DeclareWrapperInfos(iface_releases, out)
    434 
    435     # Generate wrapper functions for each wrapped method in the interfaces.
    436     result = self.GenerateWrapperForMethods(iface_releases)
    437     out.Write(result)
    438 
    439     # Collect all the wrapper functions into interface structs.
    440     self.GenerateWrapperInterfaces(iface_releases, out)
    441 
    442     # Generate a table of the wrapped interface structs that can be looked up.
    443     self.GenerateWrapperInfoAndCollection(iface_releases, out)
    444 
    445     # Write out the IDL-invariant functions.
    446     self.GenerateFixedFunctions(out)
    447 
    448     out.Close()
    449     return 0
    450