idl_gen_pnacl.py revision 5821806d5e7f356e8fa4b058a389a808ea183019
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 7between GCC and PNaCl. """ 8 9from datetime import datetime 10import difflib 11import glob 12import os 13import sys 14 15from idl_c_proto import CGen 16from idl_gen_wrapper import Interface, WrapperGen 17from idl_log import ErrOut, InfoOut, WarnOut 18from idl_option import GetOption, Option, ParseOptions 19from idl_parser import ParseFiles 20 21Option('pnaclshim', 'Name of the pnacl shim file.', 22 default='temp_pnacl_shim.c') 23 24Option('disable_pnacl_opt', 'Turn off optimization of pnacl shim.') 25 26 27class 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 self._pnacl_attribute = '__attribute__((pnaclcall))' 46 47 ############################################################ 48 49 def OwnHeaderFile(self): 50 """Return the header file that specifies the API of this wrapper. 51 We do not generate the header files. """ 52 return 'ppapi/generators/pnacl_shim.h' 53 54 def GetGuardStart(self): 55 return ('\n/* The PNaCl PPAPI shims are only needed on x86-64 and arm. */\n' 56 '#if defined(__x86_64__) || defined(__arm__)\n\n') 57 58 def GetGuardEnd(self): 59 return '\n#endif\n' 60 61 def InterfaceNeedsWrapper(self, iface, releases): 62 """Return true if the interface has ANY methods that need wrapping. 63 """ 64 if self._skip_opt: 65 return True 66 for release in iface.GetUniqueReleases(releases): 67 version = iface.GetVersion(release) 68 if self.InterfaceVersionNeedsWrapping(iface, version): 69 return True 70 return False 71 72 73 def InterfaceVersionNeedsWrapping(self, iface, version): 74 """Return true if the interface+version has ANY methods that 75 need wrapping. 76 """ 77 if self._skip_opt: 78 return True 79 for member in iface.GetListOf('Member'): 80 release = member.GetRelease(version) 81 if self.MemberNeedsWrapping(member, release): 82 return True 83 return False 84 85 86 def MemberNeedsWrapping(self, member, release): 87 """Return true if a particular member function at a particular 88 release needs wrapping. 89 """ 90 if self._skip_opt: 91 return True 92 if not member.InReleases([release]): 93 return False 94 ret, name, array, args_spec = self.cgen.GetComponents(member, 95 release, 96 'store') 97 return self.TypeNeedsWrapping(ret, []) or self.ArgsNeedWrapping(args_spec) 98 99 100 def ArgsNeedWrapping(self, args): 101 """Return true if any parameter in the list needs wrapping. 102 """ 103 for arg in args: 104 (type_str, name, array_dims, more_args) = arg 105 if self.TypeNeedsWrapping(type_str, array_dims): 106 return True 107 return False 108 109 110 def TypeNeedsWrapping(self, type_node, array_dims): 111 """Return true if a parameter type needs wrapping. 112 Currently, this is true for byval aggregates. 113 """ 114 is_aggregate = type_node.startswith('struct') or \ 115 type_node.startswith('union') 116 is_reference = (type_node.find('*') != -1 or array_dims != []) 117 return is_aggregate and not is_reference 118 119 ############################################################ 120 121 122 def GenerateWrapperForPPBMethod(self, iface, member): 123 result = [] 124 func_prefix = self.WrapperMethodPrefix(iface.node, iface.release) 125 sig = self.cgen.GetSignature(member, iface.release, 'store', 126 func_prefix, False) 127 result.append('static %s\n%s {\n' % (self._pnacl_attribute, sig)) 128 result.append(' const struct %s *iface = %s.real_iface;\n' % 129 (iface.struct_name, self.GetWrapperInfoName(iface))) 130 ret, name, array, cspec = self.cgen.GetComponents(member, 131 iface.release, 132 'store') 133 ret_str, args_str = self.GetReturnArgs(ret, cspec) 134 result.append(' %siface->%s(%s);\n}\n\n' % (ret_str, 135 member.GetName(), args_str)) 136 return result 137 138 139 def GenerateWrapperForPPPMethod(self, iface, member): 140 result = [] 141 func_prefix = self.WrapperMethodPrefix(iface.node, iface.release) 142 sig = self.cgen.GetSignature(member, iface.release, 'store', 143 func_prefix, False) 144 result.append('static %s {\n' % sig) 145 result.append(' const struct %s *iface = %s.real_iface;\n' % 146 (iface.struct_name, self.GetWrapperInfoName(iface))) 147 temp_fp = self.cgen.GetSignature(member, iface.release, 'return', 148 'temp_fp', 149 func_as_ptr=True, 150 ptr_prefix=self._pnacl_attribute + ' ', 151 include_name=False) 152 cast = self.cgen.GetSignature(member, iface.release, 'return', 153 prefix='', 154 func_as_ptr=True, 155 ptr_prefix=self._pnacl_attribute + ' ', 156 include_name=False) 157 result.append(' %s = ((%s)iface->%s);\n' % (temp_fp, 158 cast, 159 member.GetName())) 160 ret, name, array, cspec = self.cgen.GetComponents(member, 161 iface.release, 162 'store') 163 ret_str, args_str = self.GetReturnArgs(ret, cspec) 164 result.append(' %stemp_fp(%s);\n}\n\n' % (ret_str, args_str)) 165 return result 166 167 168 def GenerateRange(self, ast, releases, options): 169 """Generate shim code for a range of releases. 170 """ 171 self._skip_opt = GetOption('disable_pnacl_opt') 172 self.SetOutputFile(GetOption('pnaclshim')) 173 return WrapperGen.GenerateRange(self, ast, releases, options) 174 175pnaclgen = PnaclGen() 176 177###################################################################### 178# Tests. 179 180# Clean a string representing an object definition and return then string 181# as a single space delimited set of tokens. 182def CleanString(instr): 183 instr = instr.strip() 184 instr = instr.split() 185 return ' '.join(instr) 186 187 188def PrintErrorDiff(old, new): 189 oldlines = old.split(';') 190 newlines = new.split(';') 191 d = difflib.Differ() 192 diff = d.compare(oldlines, newlines) 193 ErrOut.Log('Diff is:\n%s' % '\n'.join(diff)) 194 195 196def GetOldTestOutput(ast): 197 # Scan the top-level comments in the IDL file for comparison. 198 old = [] 199 for filenode in ast.GetListOf('File'): 200 for node in filenode.GetChildren(): 201 instr = node.GetOneOf('Comment') 202 if not instr: continue 203 instr.Dump() 204 old.append(instr.GetName()) 205 return CleanString(''.join(old)) 206 207 208def TestFiles(filenames, test_releases): 209 ast = ParseFiles(filenames) 210 iface_releases = pnaclgen.DetermineInterfaces(ast, test_releases) 211 new_output = CleanString(pnaclgen.GenerateWrapperForMethods( 212 iface_releases, comments=False)) 213 old_output = GetOldTestOutput(ast) 214 if new_output != old_output: 215 PrintErrorDiff(old_output, new_output) 216 ErrOut.Log('Failed pnacl generator test.') 217 return 1 218 else: 219 InfoOut.Log('Passed pnacl generator test.') 220 return 0 221 222 223def Main(args): 224 filenames = ParseOptions(args) 225 test_releases = ['M13', 'M14', 'M15'] 226 if not filenames: 227 idldir = os.path.split(sys.argv[0])[0] 228 idldir = os.path.join(idldir, 'test_gen_pnacl', '*.idl') 229 filenames = glob.glob(idldir) 230 filenames = sorted(filenames) 231 if GetOption('test'): 232 # Run the tests. 233 return TestFiles(filenames, test_releases) 234 235 # Otherwise, generate the output file (for potential use as golden file). 236 ast = ParseFiles(filenames) 237 return pnaclgen.GenerateRange(ast, test_releases, filenames) 238 239 240if __name__ == '__main__': 241 retval = Main(sys.argv[1:]) 242 sys.exit(retval) 243