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/generators/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     for member in iface.GetListOf('Member'):
     63       release = member.GetRelease(version)
     64       if self.MemberNeedsWrapping(member, release):
     65         return True
     66     return False
     67 
     68 
     69   def MemberNeedsWrapping(self, member, release):
     70     """Return true if a particular member function at a particular
     71     release needs wrapping.
     72     """
     73     if self._skip_opt:
     74       return True
     75     if not member.InReleases([release]):
     76       return False
     77     ret, name, array, args_spec = self.cgen.GetComponents(member,
     78                                                           release,
     79                                                           'store')
     80     return self.TypeNeedsWrapping(ret, []) or self.ArgsNeedWrapping(args_spec)
     81 
     82 
     83   def ArgsNeedWrapping(self, args):
     84     """Return true if any parameter in the list needs wrapping.
     85     """
     86     for arg in args:
     87       (type_str, name, array_dims, more_args) = arg
     88       if self.TypeNeedsWrapping(type_str, array_dims):
     89         return True
     90     return False
     91 
     92 
     93   def TypeNeedsWrapping(self, type_node, array_dims):
     94     """Return true if a parameter type needs wrapping.
     95     Currently, this is true for byval aggregates.
     96     """
     97     is_aggregate = type_node.startswith('struct') or \
     98         type_node.startswith('union')
     99     is_reference = (type_node.find('*') != -1 or array_dims != [])
    100     return is_aggregate and not is_reference
    101 
    102   ############################################################
    103 
    104 
    105   def ConvertByValueReturnType(self, ret, args_spec):
    106     if self.TypeNeedsWrapping(ret, array_dims=[]):
    107       args_spec = [(ret, '_struct_result', [], None)] + args_spec
    108       ret2 = 'void'
    109       wrap_return = True
    110     else:
    111       ret2 = ret
    112       wrap_return = False
    113     return wrap_return, ret2, args_spec
    114 
    115 
    116   def ConvertByValueArguments(self, args_spec):
    117     args = []
    118     for type_str, name, array_dims, more_args in args_spec:
    119       if self.TypeNeedsWrapping(type_str, array_dims):
    120         type_str += '*'
    121       args.append((type_str, name, array_dims, more_args))
    122     return args
    123 
    124 
    125   def FormatArgs(self, c_operator, args_spec):
    126     args = []
    127     for type_str, name, array_dims, more_args in args_spec:
    128       if self.TypeNeedsWrapping(type_str, array_dims):
    129         args.append(c_operator + name)
    130       else:
    131         args.append(name)
    132     return ', '.join(args)
    133 
    134 
    135   def GenerateWrapperForPPBMethod(self, iface, member):
    136     result = []
    137     func_prefix = self.WrapperMethodPrefix(iface.node, iface.release)
    138     ret, name, array, cspec = self.cgen.GetComponents(member,
    139                                                       iface.release,
    140                                                       'store')
    141     wrap_return, ret2, cspec2 = self.ConvertByValueReturnType(ret, cspec)
    142     cspec2 = self.ConvertByValueArguments(cspec2)
    143     sig = self.cgen.Compose(ret2, name, array, cspec2,
    144                             prefix=func_prefix,
    145                             func_as_ptr=False,
    146                             include_name=True,
    147                             unsized_as_ptr=False)
    148     result.append('static %s {\n' % sig)
    149     result.append('  const struct %s *iface = %s.real_iface;\n' %
    150                   (iface.struct_name, self.GetWrapperInfoName(iface)))
    151 
    152     return_prefix = ''
    153     if wrap_return:
    154       return_prefix = '*_struct_result = '
    155     elif ret != 'void':
    156       return_prefix = 'return '
    157 
    158     result.append('  %siface->%s(%s);\n}\n\n' % (return_prefix,
    159                                                  member.GetName(),
    160                                                  self.FormatArgs('*', cspec)))
    161     return result
    162 
    163 
    164   def GenerateWrapperForPPPMethod(self, iface, member):
    165     result = []
    166     func_prefix = self.WrapperMethodPrefix(iface.node, iface.release)
    167     sig = self.cgen.GetSignature(member, iface.release, 'store',
    168                                  func_prefix, False)
    169     result.append('static %s {\n' % sig)
    170     result.append('  const struct %s *iface = %s.real_iface;\n' %
    171                   (iface.struct_name, self.GetWrapperInfoName(iface)))
    172     ret, name, array, cspec = self.cgen.GetComponents(member,
    173                                                       iface.release,
    174                                                       'store')
    175     wrap_return, ret2, cspec = self.ConvertByValueReturnType(ret, cspec)
    176     cspec2 = self.ConvertByValueArguments(cspec)
    177     temp_fp = self.cgen.Compose(ret2, name, array, cspec2,
    178                                 prefix='temp_fp',
    179                                 func_as_ptr=True,
    180                                 include_name=False,
    181                                 unsized_as_ptr=False)
    182     cast = self.cgen.Compose(ret2, name, array, cspec2,
    183                              prefix='',
    184                              func_as_ptr=True,
    185                              include_name=False,
    186                              unsized_as_ptr=False)
    187     result.append('  %s =\n    ((%s)iface->%s);\n' % (temp_fp,
    188                                                       cast,
    189                                                       member.GetName()))
    190     return_prefix = ''
    191     if wrap_return:
    192       result.append('  %s _struct_result;\n' % ret)
    193     elif ret != 'void':
    194       return_prefix = 'return '
    195 
    196     result.append('  %stemp_fp(%s);\n' % (return_prefix,
    197                                           self.FormatArgs('&', cspec)))
    198     if wrap_return:
    199       result.append('  return _struct_result;\n')
    200     result.append('}\n\n')
    201     return result
    202 
    203 
    204   def GenerateRange(self, ast, releases, options):
    205     """Generate shim code for a range of releases.
    206     """
    207     self._skip_opt = GetOption('disable_pnacl_opt')
    208     self.SetOutputFile(GetOption('pnaclshim'))
    209     return WrapperGen.GenerateRange(self, ast, releases, options)
    210 
    211 pnaclgen = PnaclGen()
    212 
    213 ######################################################################
    214 # Tests.
    215 
    216 # Clean a string representing an object definition and return then string
    217 # as a single space delimited set of tokens.
    218 def CleanString(instr):
    219   instr = instr.strip()
    220   instr = instr.split()
    221   return ' '.join(instr)
    222 
    223 
    224 def PrintErrorDiff(old, new):
    225   oldlines = old.split(';')
    226   newlines = new.split(';')
    227   d = difflib.Differ()
    228   diff = d.compare(oldlines, newlines)
    229   ErrOut.Log('Diff is:\n%s' % '\n'.join(diff))
    230 
    231 
    232 def GetOldTestOutput(ast):
    233   # Scan the top-level comments in the IDL file for comparison.
    234   old = []
    235   for filenode in ast.GetListOf('File'):
    236     for node in filenode.GetChildren():
    237       instr = node.GetOneOf('Comment')
    238       if not instr: continue
    239       instr.Dump()
    240       old.append(instr.GetName())
    241   return CleanString(''.join(old))
    242 
    243 
    244 def TestFiles(filenames, test_releases):
    245   ast = ParseFiles(filenames)
    246   iface_releases = pnaclgen.DetermineInterfaces(ast, test_releases)
    247   new_output = CleanString(pnaclgen.GenerateWrapperForMethods(
    248       iface_releases, comments=False))
    249   old_output = GetOldTestOutput(ast)
    250   if new_output != old_output:
    251     PrintErrorDiff(old_output, new_output)
    252     ErrOut.Log('Failed pnacl generator test.')
    253     return 1
    254   else:
    255     InfoOut.Log('Passed pnacl generator test.')
    256     return 0
    257 
    258 
    259 def Main(args):
    260   filenames = ParseOptions(args)
    261   test_releases = ['M13', 'M14', 'M15']
    262   if not filenames:
    263     idldir = os.path.split(sys.argv[0])[0]
    264     idldir = os.path.join(idldir, 'test_gen_pnacl', '*.idl')
    265     filenames = glob.glob(idldir)
    266   filenames = sorted(filenames)
    267   if GetOption('test'):
    268     # Run the tests.
    269     return TestFiles(filenames, test_releases)
    270 
    271   # Otherwise, generate the output file (for potential use as golden file).
    272   ast = ParseFiles(filenames)
    273   return pnaclgen.GenerateRange(ast, test_releases, filenames)
    274 
    275 
    276 if __name__ == '__main__':
    277   retval = Main(sys.argv[1:])
    278   sys.exit(retval)
    279