Home | History | Annotate | Download | only in generators
      1 #!/usr/bin/env python
      2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
      3 # Use of this source code is governed by a BSD-style license that can be
      4 # found in the LICENSE file.
      5 
      6 """Generator for Pnacl Shim functions that bridges the calling conventions
      7 between GCC and PNaCl.  """
      8 
      9 from datetime import datetime
     10 import difflib
     11 import glob
     12 import os
     13 import sys
     14 
     15 from idl_c_proto import CGen
     16 from idl_gen_wrapper import Interface, WrapperGen
     17 from idl_log import ErrOut, InfoOut, WarnOut
     18 from idl_option import GetOption, Option, ParseOptions
     19 from idl_parser import ParseFiles
     20 
     21 Option('pnaclshim', 'Name of the pnacl shim file.',
     22        default='temp_pnacl_shim.c')
     23 
     24 Option('disable_pnacl_opt', 'Turn off optimization of pnacl shim.')
     25 
     26 
     27 class PnaclGen(WrapperGen):
     28   """PnaclGen generates shim code to bridge the Gcc ABI with PNaCl.
     29 
     30   This subclass of WrapperGenerator takes the IDL sources and
     31   generates shim methods for bridging the calling conventions between GCC
     32   and PNaCl (LLVM). Some of the PPAPI methods do not need shimming, so
     33   this will also detect those situations and provide direct access to the
     34   original PPAPI methods (rather than the shim methods).
     35   """
     36 
     37   def __init__(self):
     38     WrapperGen.__init__(self,
     39                         'Pnacl',
     40                         'Pnacl Shim Gen',
     41                         'pnacl',
     42                         'Generate the PNaCl shim.')
     43     self.cgen = CGen()
     44     self._skip_opt = False
     45 
     46   ############################################################
     47 
     48   def OwnHeaderFile(self):
     49     """Return the header file that specifies the API of this wrapper.
     50     We do not generate the header files.  """
     51     return 'ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.h'
     52 
     53 
     54   def InterfaceVersionNeedsWrapping(self, iface, version):
     55     """Return true if the interface+version has ANY methods that
     56     need wrapping.
     57     """
     58     if self._skip_opt:
     59       return True
     60     if iface.GetName().endswith('Trusted'):
     61       return False
     62     # TODO(dmichael): We have no way to wrap PPP_ interfaces without an
     63     # interface string. If any ever need wrapping, we'll need to figure out a
     64     # way to get the plugin-side of the Pepper proxy (within the IRT) to access
     65     # and use the wrapper.
     66     if iface.GetProperty("no_interface_string"):
     67       return False
     68     for member in iface.GetListOf('Member'):
     69       release = member.GetRelease(version)
     70       if self.MemberNeedsWrapping(member, release):
     71         return True
     72     return False
     73 
     74 
     75   def MemberNeedsWrapping(self, member, release):
     76     """Return true if a particular member function at a particular
     77     release needs wrapping.
     78     """
     79     if self._skip_opt:
     80       return True
     81     if not member.InReleases([release]):
     82       return False
     83     ret, name, array, args_spec = self.cgen.GetComponents(member,
     84                                                           release,
     85                                                           'store')
     86     return self.TypeNeedsWrapping(ret, []) or self.ArgsNeedWrapping(args_spec)
     87 
     88 
     89   def ArgsNeedWrapping(self, args):
     90     """Return true if any parameter in the list needs wrapping.
     91     """
     92     for arg in args:
     93       (type_str, name, array_dims, more_args) = arg
     94       if self.TypeNeedsWrapping(type_str, array_dims):
     95         return True
     96     return False
     97 
     98 
     99   def TypeNeedsWrapping(self, type_node, array_dims):
    100     """Return true if a parameter type needs wrapping.
    101     Currently, this is true for byval aggregates.
    102     """
    103     is_aggregate = type_node.startswith('struct') or \
    104         type_node.startswith('union')
    105     is_reference = (type_node.find('*') != -1 or array_dims != [])
    106     return is_aggregate and not is_reference
    107 
    108   ############################################################
    109 
    110 
    111   def ConvertByValueReturnType(self, ret, args_spec):
    112     if self.TypeNeedsWrapping(ret, array_dims=[]):
    113       args_spec = [(ret, '_struct_result', [], None)] + args_spec
    114       ret2 = 'void'
    115       wrap_return = True
    116     else:
    117       ret2 = ret
    118       wrap_return = False
    119     return wrap_return, ret2, args_spec
    120 
    121 
    122   def ConvertByValueArguments(self, args_spec):
    123     args = []
    124     for type_str, name, array_dims, more_args in args_spec:
    125       if self.TypeNeedsWrapping(type_str, array_dims):
    126         type_str += '*'
    127       args.append((type_str, name, array_dims, more_args))
    128     return args
    129 
    130 
    131   def FormatArgs(self, c_operator, args_spec):
    132     args = []
    133     for type_str, name, array_dims, more_args in args_spec:
    134       if self.TypeNeedsWrapping(type_str, array_dims):
    135         args.append(c_operator + name)
    136       else:
    137         args.append(name)
    138     return ', '.join(args)
    139 
    140 
    141   def GenerateWrapperForPPBMethod(self, iface, member):
    142     result = []
    143     func_prefix = self.WrapperMethodPrefix(iface.node, iface.release)
    144     ret, name, array, cspec = self.cgen.GetComponents(member,
    145                                                       iface.release,
    146                                                       'store')
    147     wrap_return, ret2, cspec2 = self.ConvertByValueReturnType(ret, cspec)
    148     cspec2 = self.ConvertByValueArguments(cspec2)
    149     sig = self.cgen.Compose(ret2, name, array, cspec2,
    150                             prefix=func_prefix,
    151                             func_as_ptr=False,
    152                             include_name=True,
    153                             unsized_as_ptr=False)
    154     result.append('static %s {\n' % sig)
    155     result.append('  const struct %s *iface = %s.real_iface;\n' %
    156                   (iface.struct_name, self.GetWrapperInfoName(iface)))
    157 
    158     return_prefix = ''
    159     if wrap_return:
    160       return_prefix = '*_struct_result = '
    161     elif ret != 'void':
    162       return_prefix = 'return '
    163 
    164     result.append('  %siface->%s(%s);\n}\n\n' % (return_prefix,
    165                                                  member.GetName(),
    166                                                  self.FormatArgs('*', cspec)))
    167     return result
    168 
    169 
    170   def GenerateWrapperForPPPMethod(self, iface, member):
    171     result = []
    172     func_prefix = self.WrapperMethodPrefix(iface.node, iface.release)
    173     sig = self.cgen.GetSignature(member, iface.release, 'store',
    174                                  func_prefix, False)
    175     result.append('static %s {\n' % sig)
    176     result.append('  const struct %s *iface = %s.real_iface;\n' %
    177                   (iface.struct_name, self.GetWrapperInfoName(iface)))
    178     ret, name, array, cspec = self.cgen.GetComponents(member,
    179                                                       iface.release,
    180                                                       'store')
    181     wrap_return, ret2, cspec = self.ConvertByValueReturnType(ret, cspec)
    182     cspec2 = self.ConvertByValueArguments(cspec)
    183     temp_fp = self.cgen.Compose(ret2, name, array, cspec2,
    184                                 prefix='temp_fp',
    185                                 func_as_ptr=True,
    186                                 include_name=False,
    187                                 unsized_as_ptr=False)
    188     cast = self.cgen.Compose(ret2, name, array, cspec2,
    189                              prefix='',
    190                              func_as_ptr=True,
    191                              include_name=False,
    192                              unsized_as_ptr=False)
    193     result.append('  %s =\n    ((%s)iface->%s);\n' % (temp_fp,
    194                                                       cast,
    195                                                       member.GetName()))
    196     return_prefix = ''
    197     if wrap_return:
    198       result.append('  %s _struct_result;\n' % ret)
    199     elif ret != 'void':
    200       return_prefix = 'return '
    201 
    202     result.append('  %stemp_fp(%s);\n' % (return_prefix,
    203                                           self.FormatArgs('&', cspec)))
    204     if wrap_return:
    205       result.append('  return _struct_result;\n')
    206     result.append('}\n\n')
    207     return result
    208 
    209 
    210   def GenerateRange(self, ast, releases, options):
    211     """Generate shim code for a range of releases.
    212     """
    213     self._skip_opt = GetOption('disable_pnacl_opt')
    214     self.SetOutputFile(GetOption('pnaclshim'))
    215     return WrapperGen.GenerateRange(self, ast, releases, options)
    216 
    217 pnaclgen = PnaclGen()
    218 
    219 ######################################################################
    220 # Tests.
    221 
    222 # Clean a string representing an object definition and return then string
    223 # as a single space delimited set of tokens.
    224 def CleanString(instr):
    225   instr = instr.strip()
    226   instr = instr.split()
    227   return ' '.join(instr)
    228 
    229 
    230 def PrintErrorDiff(old, new):
    231   oldlines = old.split(';')
    232   newlines = new.split(';')
    233   d = difflib.Differ()
    234   diff = d.compare(oldlines, newlines)
    235   ErrOut.Log('Diff is:\n%s' % '\n'.join(diff))
    236 
    237 
    238 def GetOldTestOutput(ast):
    239   # Scan the top-level comments in the IDL file for comparison.
    240   old = []
    241   for filenode in ast.GetListOf('File'):
    242     for node in filenode.GetChildren():
    243       instr = node.GetOneOf('Comment')
    244       if not instr: continue
    245       instr.Dump()
    246       old.append(instr.GetName())
    247   return CleanString(''.join(old))
    248 
    249 
    250 def TestFiles(filenames, test_releases):
    251   ast = ParseFiles(filenames)
    252   iface_releases = pnaclgen.DetermineInterfaces(ast, test_releases)
    253   new_output = CleanString(pnaclgen.GenerateWrapperForMethods(
    254       iface_releases, comments=False))
    255   old_output = GetOldTestOutput(ast)
    256   if new_output != old_output:
    257     PrintErrorDiff(old_output, new_output)
    258     ErrOut.Log('Failed pnacl generator test.')
    259     return 1
    260   else:
    261     InfoOut.Log('Passed pnacl generator test.')
    262     return 0
    263 
    264 
    265 def Main(args):
    266   filenames = ParseOptions(args)
    267   test_releases = ['M13', 'M14', 'M15']
    268   if not filenames:
    269     idldir = os.path.split(sys.argv[0])[0]
    270     idldir = os.path.join(idldir, 'test_gen_pnacl', '*.idl')
    271     filenames = glob.glob(idldir)
    272   filenames = sorted(filenames)
    273   if GetOption('test'):
    274     # Run the tests.
    275     return TestFiles(filenames, test_releases)
    276 
    277   # Otherwise, generate the output file (for potential use as golden file).
    278   ast = ParseFiles(filenames)
    279   return pnaclgen.GenerateRange(ast, test_releases, filenames)
    280 
    281 
    282 if __name__ == '__main__':
    283   retval = Main(sys.argv[1:])
    284   sys.exit(retval)
    285