| OLD | NEW |
| (Empty) | |
| 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 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 |
| 175 pnaclgen = 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. |
| 182 def CleanString(instr): |
| 183 instr = instr.strip() |
| 184 instr = instr.split() |
| 185 return ' '.join(instr) |
| 186 |
| 187 |
| 188 def 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 |
| 196 def 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 |
| 208 def 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 |
| 223 def 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 |
| 240 if __name__ == '__main__': |
| 241 retval = Main(sys.argv[1:]) |
| 242 sys.exit(retval) |
| OLD | NEW |