Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 #!/usr/bin/python | |
| 2 # | |
| 3 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
| 4 # Use of this source code is governed by a BSD-style license that can be | |
| 5 # found in the LICENSE file. | |
| 6 | |
| 7 """Generator for Pnacl Shim functions that bridges the calling conventions | |
| 8 between GCC and PNaCl. """ | |
| 9 | |
| 10 from datetime import datetime | |
| 11 import difflib | |
| 12 import glob | |
| 13 import os | |
| 14 import sys | |
| 15 | |
|
robertm
2011/11/28 19:44:04
are these all needed?
jvoung - send to chromium...
2011/11/28 20:14:25
Removed a few more.
| |
| 16 from idl_ast import IDLAst | |
| 17 from idl_c_proto import CGen | |
| 18 from idl_generator import Generator | |
| 19 from idl_gen_wrapper import Interface, WrapperGen | |
| 20 from idl_log import ErrOut, InfoOut, WarnOut | |
| 21 from idl_option import GetOption, Option, ParseOptions | |
| 22 from idl_outfile import IDLOutFile | |
| 23 from idl_parser import ParseFiles | |
| 24 | |
| 25 Option('pnaclshim', 'Name of the pnacl shim file.', | |
| 26 default='temp_pnacl_shim.c') | |
| 27 | |
| 28 | |
| 29 # TODO(jvoung): this doesn't do anything yet (except change some dbg messages). | |
| 30 Option('disable_pnacl_opt', 'Turn off optimization of pnacl shim.') | |
| 31 | |
| 32 | |
| 33 class PnaclGen(WrapperGen): | |
| 34 """PnaclGen generates shim code to bridge the Gcc ABI with PNaCl. | |
| 35 | |
| 36 This subclass of WrapperGenerator takes the IDL sources and | |
| 37 generates shim methods for bridging the calling conventions between GCC | |
| 38 and PNaCl (LLVM). Some of the PPAPI methods do not need shimming, so | |
| 39 this will also detect those situations and provide direct access to the | |
| 40 original PPAPI methods (rather than the shim methods). | |
| 41 """ | |
| 42 | |
| 43 def __init__(self): | |
| 44 WrapperGen.__init__(self, | |
| 45 'Pnacl', | |
| 46 'Pnacl Shim Gen', | |
| 47 'pnacl', | |
| 48 'Generate the PNaCl shim.') | |
| 49 self.cgen = CGen() | |
| 50 self._skip_opt = False | |
| 51 self._pnacl_attribute = '__attribute__((pnaclcall))' | |
| 52 | |
| 53 | |
| 54 ############################################################ | |
| 55 | |
| 56 def InterfaceNeedsWrapper(self, iface, releases): | |
| 57 """Return true if the interface has ANY methods that need wrapping. | |
| 58 """ | |
| 59 if self._skip_opt: | |
| 60 return True | |
| 61 for release in iface.GetUniqueReleases(releases): | |
| 62 version = iface.GetVersion(release) | |
| 63 if self.InterfaceVersionNeedsWrapping(iface, version): | |
| 64 return True | |
| 65 return False | |
| 66 | |
| 67 | |
| 68 def InterfaceVersionNeedsWrapping(self, iface, version): | |
| 69 """Return true if the interface+version has ANY methods that | |
| 70 need wrapping. | |
| 71 """ | |
| 72 if self._skip_opt: | |
| 73 return True | |
| 74 for member in iface.GetListOf('Member'): | |
| 75 release = member.GetRelease(version) | |
| 76 if self.MemberNeedsWrapping(member, release): | |
| 77 return True | |
| 78 return False | |
| 79 | |
| 80 | |
| 81 def MemberNeedsWrapping(self, member, release): | |
| 82 """Return true if a particular member function at a particular | |
| 83 release needs wrapping. | |
| 84 """ | |
| 85 if self._skip_opt: | |
| 86 return True | |
| 87 if not member.InReleases([release]): | |
| 88 return False | |
| 89 ret, name, array, args_spec = self.cgen.GetComponents(member, | |
| 90 release, | |
| 91 'store') | |
| 92 return self.TypeNeedsWrapping(ret, []) or self.ArgsNeedWrapping(args_spec) | |
| 93 | |
| 94 | |
| 95 def ArgsNeedWrapping(self, args): | |
| 96 """Return true if any parameter in the list needs wrapping. | |
| 97 """ | |
| 98 for arg in args: | |
| 99 (type_str, name, array_dims, more_args) = arg | |
| 100 if self.TypeNeedsWrapping(type_str, array_dims): | |
| 101 return True | |
| 102 return False | |
| 103 | |
| 104 | |
| 105 def TypeNeedsWrapping(self, type_node, array_dims): | |
| 106 """Return true if a parameter type needs wrapping. | |
| 107 Currently, this is true for byval aggregates. | |
| 108 """ | |
| 109 is_aggregate = type_node.startswith('struct') or \ | |
| 110 type_node.startswith('union') | |
| 111 is_reference = (type_node.find('*') != -1 or array_dims != []) | |
| 112 return is_aggregate and not is_reference | |
| 113 | |
| 114 ############################################################ | |
| 115 | |
| 116 | |
| 117 def GenerateWrapperForPPBMethod(self, iface, member): | |
| 118 result = [] | |
| 119 func_prefix = self.WrapperMethodPrefix(iface.node, iface.release) | |
| 120 sig = self.cgen.GetSignature(member, iface.release, 'store', | |
| 121 func_prefix, False) | |
| 122 result.append('static %s\n%s {\n' % (self._pnacl_attribute, sig)) | |
| 123 result.append(' const struct %s *iface = %s.real_iface;\n' % | |
| 124 (iface.struct_name, self.GetWrapperInfoName(iface))) | |
| 125 ret, name, array, cspec = self.cgen.GetComponents(member, | |
| 126 iface.release, | |
| 127 'store') | |
| 128 ret_str, args_str = self.GetReturnArgs(ret, cspec) | |
| 129 result.append(' %siface->%s(%s);\n}\n\n' % (ret_str, | |
| 130 member.GetName(), args_str)) | |
| 131 return result | |
| 132 | |
| 133 | |
| 134 def GenerateWrapperForPPPMethod(self, iface, member): | |
| 135 result = [] | |
| 136 func_prefix = self.WrapperMethodPrefix(iface.node, iface.release) | |
| 137 sig = self.cgen.GetSignature(member, iface.release, 'store', | |
| 138 func_prefix, False) | |
| 139 result.append('static %s {\n' % sig) | |
| 140 result.append(' const struct %s *iface = %s.real_iface;\n' % | |
| 141 (iface.struct_name, self.GetWrapperInfoName(iface))) | |
| 142 temp_fp = self.cgen.GetSignature(member, iface.release, 'return', | |
| 143 'temp_fp', | |
| 144 func_as_ptr=True, | |
| 145 ptr_prefix=self._pnacl_attribute + ' ', | |
| 146 include_name=False) | |
| 147 cast = self.cgen.GetSignature(member, iface.release, 'return', | |
| 148 prefix='', | |
| 149 func_as_ptr=True, | |
| 150 ptr_prefix=self._pnacl_attribute + ' ', | |
| 151 include_name=False) | |
| 152 result.append(' %s = ((%s)iface->%s);\n' % (temp_fp, | |
| 153 cast, | |
| 154 member.GetName())) | |
| 155 ret, name, array, cspec = self.cgen.GetComponents(member, | |
| 156 iface.release, | |
| 157 'store') | |
| 158 ret_str, args_str = self.GetReturnArgs(ret, cspec) | |
| 159 result.append(' %stemp_fp(%s);\n}\n\n' % (ret_str, args_str)) | |
| 160 return result | |
| 161 | |
| 162 | |
| 163 def GenerateRange(self, ast, releases, options): | |
| 164 """Generate shim code for a range of releases. | |
| 165 """ | |
| 166 self._skip_opt = GetOption('disable_pnacl_opt') | |
| 167 self.SetOutputFile(GetOption('pnaclshim')) | |
| 168 return WrapperGen.GenerateRange(self, ast, releases, options) | |
| 169 | |
| 170 pnaclgen = PnaclGen() | |
| 171 | |
| 172 ###################################################################### | |
| 173 # Tests. | |
| 174 | |
| 175 # Clean a string representing an object definition and return then string | |
| 176 # as a single space delimited set of tokens. | |
| 177 def CleanString(instr): | |
| 178 instr = instr.strip() | |
| 179 instr = instr.split() | |
| 180 return ' '.join(instr) | |
| 181 | |
| 182 | |
| 183 def PrintErrorDiff(old, new): | |
| 184 oldlines = old.split(';') | |
| 185 newlines = new.split(';') | |
| 186 d = difflib.Differ() | |
| 187 diff = d.compare(oldlines, newlines) | |
| 188 ErrOut.Log('Diff is:\n%s' % '\n'.join(diff)) | |
| 189 | |
| 190 | |
| 191 def GetOldTestOutput(ast): | |
| 192 # Scan the top-level comments in the IDL file for comparison. | |
| 193 old = [] | |
| 194 for filenode in ast.GetListOf('File'): | |
| 195 for node in filenode.GetChildren(): | |
| 196 instr = node.GetOneOf('Comment') | |
| 197 if not instr: continue | |
| 198 instr.Dump() | |
| 199 old.append(instr.GetName()) | |
| 200 return CleanString(''.join(old)) | |
| 201 | |
| 202 | |
| 203 def TestFiles(filenames, test_releases): | |
| 204 ast = ParseFiles(filenames) | |
| 205 iface_releases = pnaclgen.DetermineInterfaces(ast, test_releases) | |
| 206 new_output = CleanString(pnaclgen.GenerateWrapperForMethods( | |
| 207 iface_releases, comments=False)) | |
| 208 old_output = GetOldTestOutput(ast) | |
| 209 if new_output != old_output: | |
| 210 PrintErrorDiff(old_output, new_output) | |
| 211 ErrOut.Log('Failed pnacl generator test.') | |
| 212 return 1 | |
| 213 else: | |
| 214 InfoOut.Log('Passed pnacl generator test.') | |
| 215 return 0 | |
| 216 | |
| 217 | |
| 218 def Main(args): | |
| 219 filenames = ParseOptions(args) | |
| 220 test_releases = ['M13', 'M14', 'M15'] | |
| 221 if not filenames: | |
| 222 idldir = os.path.split(sys.argv[0])[0] | |
| 223 idldir = os.path.join(idldir, 'test_gen_pnacl', '*.idl') | |
| 224 filenames = glob.glob(idldir) | |
| 225 filenames = sorted(filenames) | |
| 226 if GetOption('test'): | |
| 227 # Run the tests. | |
| 228 return TestFiles(filenames, test_releases) | |
| 229 | |
| 230 # Otherwise, generate the output file (for potential use as golden file). | |
| 231 ast = ParseFiles(filenames) | |
| 232 return pnaclgen.GenerateRange(ast, test_releases, filenames) | |
| 233 | |
| 234 | |
| 235 if __name__ == '__main__': | |
| 236 retval = Main(sys.argv[1:]) | |
| 237 sys.exit(retval) | |
| OLD | NEW |