[email protected] | 3e5eb10 | 2012-04-23 18:07:54 | [diff] [blame] | 1 | #!/usr/bin/env python |
| 2 | # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
[email protected] | 2978339a7 | 2011-11-30 17:59:14 | [diff] [blame] | 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 |
[email protected] | 2978339a7 | 2011-11-30 17:59:14 | [diff] [blame] | 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 | |
[email protected] | 2978339a7 | 2011-11-30 17:59:14 | [diff] [blame] | 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 |
[email protected] | 99a2d13 | 2013-06-20 18:15:14 | [diff] [blame] | 60 | if iface.GetName().endswith('Trusted'): |
| 61 | return False |
[email protected] | 2978339a7 | 2011-11-30 17:59:14 | [diff] [blame] | 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 | |
[email protected] | c3064ec | 2013-04-26 23:38:35 | [diff] [blame] | 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 | |
[email protected] | 2978339a7 | 2011-11-30 17:59:14 | [diff] [blame] | 135 | def GenerateWrapperForPPBMethod(self, iface, member): |
| 136 | result = [] |
| 137 | func_prefix = self.WrapperMethodPrefix(iface.node, iface.release) |
[email protected] | 2978339a7 | 2011-11-30 17:59:14 | [diff] [blame] | 138 | ret, name, array, cspec = self.cgen.GetComponents(member, |
| 139 | iface.release, |
| 140 | 'store') |
[email protected] | c3064ec | 2013-04-26 23:38:35 | [diff] [blame] | 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))) |
[email protected] | 2978339a7 | 2011-11-30 17:59:14 | [diff] [blame] | 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))) |
[email protected] | 2978339a7 | 2011-11-30 17:59:14 | [diff] [blame] | 172 | ret, name, array, cspec = self.cgen.GetComponents(member, |
| 173 | iface.release, |
| 174 | 'store') |
[email protected] | c3064ec | 2013-04-26 23:38:35 | [diff] [blame] | 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') |
[email protected] | 2978339a7 | 2011-11-30 17:59:14 | [diff] [blame] | 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) |