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 bridge the calling conventions | |
8 between GCC and PNaCl. """ | |
9 | |
10 from datetime import datetime | |
11 import os | |
12 import sys | |
13 | |
14 from idl_log import ErrOut, InfoOut, WarnOut | |
15 from idl_ast import IDLAst | |
16 from idl_option import GetOption, Option, ParseOptions | |
17 from idl_outfile import IDLOutFile | |
18 from idl_c_proto import CGen | |
19 from idl_generator import Generator | |
20 | |
21 Option('pnaclglue', 'Name of the pnacl glue file.', | |
sehr (please use chromium)
2011/11/16 00:44:01
We should use either 'glue' or 'shim' consistently
jvoung - send to chromium...
2011/11/16 01:45:22
Changed to shim.
| |
22 default=os.path.join('..', | |
23 'native_client', | |
24 'src', | |
25 'shared', | |
26 'ppapi_proxy', | |
27 'pnacl_glue.c')) | |
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 glue.') | |
31 | |
32 | |
33 class PPKind(object): | |
34 @staticmethod | |
35 def ChoosePPFunc(iface, ppb_func, ppp_func): | |
36 name = iface.GetName() | |
37 if name.startswith("PPP"): | |
38 return ppp_func | |
39 elif name.startswith("PPB"): | |
40 return ppb_func | |
41 else: | |
42 raise Exception('Unknown PPKind for ' + name) | |
43 | |
44 | |
45 class InterfaceVersion(object): | |
46 """ Tracks information about a particular interface version, including | |
47 the ID assigned to it in the PNaCl glue namespace. """ | |
48 def __init__(self, interface_node, release, version, pnacl_id): | |
49 self.node = interface_node | |
50 self.release = release | |
51 self.version = version | |
52 self.pnacl_id = pnacl_id | |
53 | |
54 | |
55 class PnaclGen(Generator): | |
56 """PnaclGen - A subclass of Generator which takes the IDL sources and | |
57 generates glue code for bridging the calling conventions between GCC | |
58 and PNaCl (LLVM). | |
59 """ | |
60 | |
61 def __init__(self): | |
62 Generator.__init__(self, | |
63 'Pnacl Glue Gen', | |
64 'pnacl', | |
65 'Generate the PNaCl glue.') | |
66 self.cgen = CGen() | |
67 self._skip_opt = False | |
68 self._pnacl_attribute = ' __attribute__((pnaclcall)) ' | |
69 | |
70 | |
71 def GenerateRelease(self, ast, release, options): | |
72 return self.GenerateRange(ast, [release], options) | |
73 | |
74 | |
75 @staticmethod | |
76 def GetHeaderName(name): | |
77 """ Get the corresponding ppapi .h file from each IDL filename. """ | |
78 name = os.path.splitext(name)[0] + '.h' | |
79 return 'ppapi/c/' + name | |
80 | |
81 | |
82 def GenerateHeaderFile(self, out): | |
83 """ Writes out an interface header file for the exported symbols. """ | |
84 out.Write(""" | |
85 /* Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
86 * Use of this source code is governed by a BSD-style license that can be | |
87 * found in the LICENSE file. | |
88 */ | |
89 | |
90 /* Last generated: %(timestamp)s. */ | |
91 | |
92 #ifndef %(include_guard)s | |
93 #define %(include_guard)s | |
94 | |
95 typedef void *(*get_interface_fp)(const char *interface_name); | |
96 | |
97 get_interface_fp real_PPBGetInterface; | |
98 get_interface_fp real_PPPGetInterface; | |
99 | |
100 void set_real_PPBGetInterface(get_interface_fp real); | |
101 void set_real_PPPGetInterface(get_interface_fp real); | |
102 | |
103 void *Pnacl_PPBGetInterface(const char *name); | |
104 void *Pnacl_PPPGetInterface(const char *name); | |
105 | |
106 #endif /* %(include_guard)s */ | |
107 """ % { | |
108 "include_guard" : "PNACL_GLUE_H_", | |
109 "timestamp" : datetime.ctime(datetime.now()), | |
110 }) | |
111 | |
112 | |
113 def GenerateFixedFunctions(self, out): | |
114 """ Write out the set of constant functions (do not depend on the | |
115 current Pepper IDL. """ | |
116 out.Write(""" | |
117 | |
118 void set_real_PPBGetInterface(get_interface_fp real) { | |
119 real_PPBGetInterface = real; | |
120 } | |
121 | |
122 void set_real_PPPGetInterface(get_interface_fp real) { | |
123 real_PPPGetInterface = real; | |
124 } | |
125 | |
126 void *Pnacl_PPBGetInterface(const char *name) { | |
127 int id = PnaclGlueIfaceID(name); | |
128 if (id < 0) return NULL; | |
129 | |
130 if (s_realPtrs[id] == NULL) { | |
131 void *iface = (*real_PPBGetInterface)(name); | |
132 if (NULL == iface) return NULL; | |
133 s_realPtrs[id] = iface; | |
134 } | |
135 return s_wrapPtrs[id]; | |
136 } | |
137 | |
138 void *Pnacl_PPPGetInterface(const char *name) { | |
139 int id = PnaclGlueIfaceID(name); | |
140 if (id < 0) return NULL; | |
141 | |
142 if (s_realPtrs[id] == NULL) { | |
143 void *iface = (*real_PPPGetInterface)(name); | |
144 if (NULL == iface) return NULL; | |
145 s_realPtrs[id] = iface; | |
146 } | |
147 return s_wrapPtrs[id]; | |
148 } | |
149 """) | |
150 | |
151 def InterfaceNeedsWrapper(self, iface, releases): | |
152 """ Return true if the interface has ANY methods that need wrapping. """ | |
153 if self._skip_opt: | |
154 return True | |
155 for release in iface.GetUniqueReleases(releases): | |
156 version = iface.GetVersion(release) | |
157 if self.InterfaceVersionNeedsWrapping(iface, version): | |
158 return True | |
159 return False | |
160 | |
161 | |
162 def InterfaceVersionNeedsWrapping(self, iface, version): | |
163 """ Return true if the interface+version has ANY methods that | |
164 need wrapping. """ | |
165 if self._skip_opt: | |
166 return True | |
167 for member in iface.GetListOf('Member'): | |
168 release = member.GetRelease(version) | |
169 if self.MemberNeedsWrapping(member, release): | |
170 return True | |
171 return False | |
172 | |
173 def MemberNeedsWrapping(self, member, release): | |
174 """ Return true if a particular member function at a particular | |
175 release needs wrapping. """ | |
176 if self._skip_opt: | |
177 return True | |
178 ret, name, array, args_spec = self.cgen.GetComponents(member, | |
179 release, | |
180 'store') | |
181 return self.TypeNeedsWrapping(ret) or self.ArgsNeedWrapping(args_spec) | |
182 | |
183 | |
184 def ArgsNeedWrapping(self, args): | |
185 """ Return true if any parameter in the list needs wrapping. """ | |
186 for arg in args: | |
187 if self.TypeNeedsWrapping(arg[0]): | |
188 return True | |
189 return False | |
190 | |
191 | |
192 def TypeNeedsWrapping(self, type_node): | |
193 """ Return true if a parameter type needs wrapping. | |
194 Currently, this is true for byval aggregates. """ | |
195 return ((type_node.startswith('struct') or type_node.startswith('union')) | |
196 and not (type_node.find('*') != -1)) | |
197 | |
198 | |
199 def GenerateIncludes(self, ast, releases, options, out): | |
200 """ Write out the includes and other headers and return a list | |
201 of the interface nodes that were ultimately included. """ | |
202 out.Write('#include "pnacl_glue.h"\n\n') | |
203 | |
204 # Get a conservative list of all the interfaces, as we are generating | |
205 # #includes for each. | |
206 # TODO(jvoung): Filter out interfaces that don't need wrapping. | |
207 # TODO(jvoung): Separate PPB and PPP interfaces to slightly shorten the | |
208 # big strcmp function. | |
209 wrapped_iface_list = [] | |
210 skipped_iface_list = [] | |
211 for filenode in ast.GetListOf('File'): | |
212 # If this file has errors, skip it | |
213 if filenode in self.skip_list: continue | |
214 | |
215 name = self.GetHeaderName(filenode.GetName()) | |
216 ifaces = filenode.GetListOf('Interface') | |
217 for iface in ifaces: | |
218 if not self.InterfaceNeedsWrapper(iface, releases): | |
219 print 'Interface %s does not need wrapping!' % iface.GetName() | |
220 skipped_iface_list.append(iface) | |
221 else: | |
222 print 'Interface %s does need wrapping!' % iface.GetName() | |
223 | |
224 # TODO(jvoung): Don't #include if we didn't wrap any of the | |
225 # ifaces in the list / header file | |
226 if ifaces: | |
227 out.Write('#include "%s"\n' % name) | |
228 wrapped_iface_list.extend(ifaces) | |
229 return wrapped_iface_list, skipped_iface_list | |
230 | |
231 | |
232 def PnaclIdForInterfaceVersion(self, iface, version): | |
233 return 'PNACL_%s_%s' % (iface.GetName(), str(version).replace('.', '_')) | |
234 | |
235 | |
236 def EnumerateInterfaces(self, wrapped_iface_list, releases, out): | |
237 iface_releases = [] | |
238 out.Write('\nenum PnaclId {\n') | |
239 for iface in wrapped_iface_list: | |
240 for release in iface.GetUniqueReleases(releases): | |
241 version = iface.GetVersion(release) | |
242 pnacl_id = self.PnaclIdForInterfaceVersion(iface, version) | |
243 iface_releases.append( | |
244 InterfaceVersion(iface, release, version, pnacl_id)) | |
245 out.Write(' %s,\n' % pnacl_id) | |
246 out.Write(' PNACL_IFACE_COUNT\n') | |
247 out.Write('};\n\n') | |
248 out.Write('static void *s_realPtrs[PNACL_IFACE_COUNT];\n\n'); | |
249 | |
250 # Also create a function that maps interface name to ID. The ID will be | |
251 # used to look up the wrapped interface in a table | |
252 # (so really it's string -> interface, but there is an ID in the middle). | |
253 out.Write('/* Map interface string -> pnacl ID */\n') | |
254 out.Write('static enum PnaclId PnaclGlueIfaceID(const char *name) {\n') | |
255 for iface in iface_releases: | |
256 out.Write(' if (!strcmp(name, %s)) return %s;\n' % | |
257 (self.cgen.GetInterfaceMacro( | |
258 iface.node, iface.version), iface.pnacl_id)) | |
259 out.Write(' return -1;\n}\n\n') | |
260 return iface_releases | |
261 | |
262 | |
263 def WrapperMethodPrefix(self, iface, release): | |
264 return 'PNACL_%s_%s_' % (release, iface.GetName()) | |
265 | |
266 def GetReturnArgs(self, ret_type, args_spec): | |
267 if ret_type != 'void': | |
268 ret = 'return ' | |
269 else: | |
270 ret = '' | |
271 if args_spec: | |
272 args = [] | |
273 for arg in args_spec: | |
274 args.append(arg[1]) | |
275 args = ', '.join(args) | |
276 else: | |
277 args = '' | |
278 return (ret, args) | |
279 | |
280 def GenerateWrapperForPPBMethods(self, iface, is_latest_release, out): | |
281 struct_name = self.cgen.GetStructName(iface.node, | |
282 iface.release, | |
283 is_latest_release=is_latest_release) | |
284 for member in iface.node.GetListOf('Member'): | |
285 # Skip the method if it's not actually in the release. | |
286 if not member.InReleases([iface.release]): | |
287 continue | |
288 func_prefix = self.WrapperMethodPrefix(iface.node, iface.release) | |
289 sig = self.cgen.GetSignature(member, iface.release, 'store', | |
290 func_prefix, False) | |
291 out.Write('static %s %s {\n' % (self._pnacl_attribute, sig)) | |
292 out.Write(' struct %s *iface = s_realPtrs[%s];\n' % ( | |
293 struct_name, iface.pnacl_id)) | |
294 ret, name, array, cspec = self.cgen.GetComponents(member, | |
295 iface.release, | |
296 'store') | |
297 ret_str, args_str = self.GetReturnArgs(ret, cspec) | |
298 out.Write(' %siface->%s(%s);\n}\n\n' % (ret_str, | |
299 member.GetName(), args_str)) | |
300 | |
301 | |
302 def GenerateWrapperForPPPMethods(self, iface, is_latest_release, out): | |
303 struct_name = self.cgen.GetStructName(iface.node, | |
304 iface.release, | |
305 is_latest_release=is_latest_release) | |
306 for member in iface.node.GetListOf('Member'): | |
307 # Skip the method if it's not actually in the release. | |
308 if not member.InReleases([iface.release]): | |
309 continue | |
310 func_prefix = self.WrapperMethodPrefix(iface.node, iface.release) | |
311 sig = self.cgen.GetSignature(member, iface.release, 'store', | |
312 func_prefix, False) | |
313 out.Write('static %s {\n' % sig) | |
314 out.Write(' struct %s *iface = s_realPtrs[%s];\n' % ( | |
315 struct_name, iface.pnacl_id)) | |
316 temp_fp = self.cgen.GetSignature(member, iface.release, 'return', | |
317 'temp_fp', | |
318 func_attributes=self._pnacl_attribute, | |
319 func_as_ptr=True) | |
320 cast = self.cgen.GetSignature(member, iface.release, 'return', | |
321 prefix='', | |
322 func_attributes=self._pnacl_attribute, | |
323 func_as_ptr=True, | |
324 is_cast=True) | |
325 out.Write(' %s = ((%s)iface->%s);\n' % (temp_fp, | |
326 cast, | |
327 member.GetName())) | |
328 ret, name, array, cspec = self.cgen.GetComponents(member, | |
329 iface.release, | |
330 'store') | |
331 ret_str, args_str = self.GetReturnArgs(ret, cspec) | |
332 out.Write(' %stemp_fp%s(%s);\n}\n\n' % (ret_str, | |
333 member.GetName(), | |
334 args_str)) | |
335 | |
336 | |
337 def GenerateWrapperForMethods(self, iface_releases, releases, out): | |
338 for iface in iface_releases: | |
339 is_latest_release = (iface.release == | |
340 iface.node.GetUniqueReleases(releases)[-1]) | |
341 generator = PPKind.ChoosePPFunc(iface.node, | |
342 self.GenerateWrapperForPPBMethods, | |
343 self.GenerateWrapperForPPPMethods) | |
344 generator(iface, is_latest_release, out) | |
345 | |
346 | |
347 def GenerateWrapperInterfaces(self, iface_releases, releases, out): | |
348 for iface in iface_releases: | |
349 is_latest_release = (iface.release == | |
350 iface.node.GetUniqueReleases(releases)[-1]) | |
351 struct_name = self.cgen.GetStructName(iface.node, | |
352 iface.release, | |
353 is_latest_release=is_latest_release) | |
354 out.Write('struct %s PNACL_Wrappers_%s = {\n' % (struct_name, | |
355 struct_name)) | |
356 methods = [] | |
357 for member in iface.node.GetListOf('Member'): | |
358 # Skip the method if it's not actually in the release. | |
359 if not member.InReleases([iface.release]): | |
360 continue | |
361 prefix = self.WrapperMethodPrefix(iface.node, iface.release) | |
362 cast = self.cgen.GetSignature(member, iface.release, 'return', | |
363 prefix='', | |
364 func_attributes='', | |
365 func_as_ptr=True, | |
366 is_cast=True) | |
367 methods.append(' .%s = (%s)&%s%s' % (member.GetName(), | |
368 cast, | |
369 prefix, | |
370 member.GetName())) | |
371 out.Write(' ' + ',\n '.join(methods) + '\n') | |
372 out.Write('};\n\n') | |
373 | |
374 # Write out a collection of the pnacl wrapper functions. | |
375 out.Write('static void *s_wrapPtrs[PNACL_IFACE_COUNT + 1] = {\n') | |
376 for iface in iface_releases: | |
377 is_latest_release = (iface.release == | |
378 iface.node.GetUniqueReleases(releases)[-1]) | |
379 struct_name = self.cgen.GetStructName(iface.node, | |
380 iface.release, | |
381 is_latest_release=is_latest_release) | |
382 out.Write(' (void *) &PNACL_Wrappers_%s,\n' % struct_name) | |
383 out.Write(' NULL\n};\n\n') | |
384 | |
385 | |
386 def GenerateRange(self, ast, releases, options): | |
387 """ Generate glue code for a range of releases. """ | |
388 | |
389 self._skip_opt = GetOption('disable_pnacl_opt') | |
390 | |
391 out_filename = GetOption('pnaclglue') | |
392 out_header_filename = os.path.splitext(out_filename)[0] + '.h' | |
393 print "Generating %s and %s" % (out_filename, out_header_filename) | |
394 | |
395 out_header = IDLOutFile(out_header_filename) | |
396 self.GenerateHeaderFile(out_header) | |
397 out_header.Close() | |
398 | |
399 out = IDLOutFile(out_filename) | |
400 | |
401 # Generate the includes. | |
402 wrapped_iface_list, skipped_iface_list = \ | |
403 self.GenerateIncludes(ast, releases, options, out) | |
404 | |
405 # Generate a list of interfaces and assign each an ID number which | |
406 # is listed in an ENUM. | |
407 iface_releases = self.EnumerateInterfaces(wrapped_iface_list, releases, out) | |
408 | |
409 # Generate wrapper functions for each wrapped method in the interfaces. | |
410 self.GenerateWrapperForMethods(iface_releases, releases, out) | |
411 | |
412 # Collect all the wrapper functions into interface structs. Then generate | |
413 # a table of the wrapped interface structs that can be looked up. | |
414 self.GenerateWrapperInterfaces(iface_releases, releases, out) | |
415 | |
416 # Write out the IDL-invariant functions. | |
417 self.GenerateFixedFunctions(out) | |
418 out.Close() | |
419 return 0 | |
420 | |
421 | |
422 pnaclgen = PnaclGen() | |
OLD | NEW |