Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3)

Side by Side Diff: ppapi/generators/idl_gen_pnacl.py

Issue 8568025: Pnacl ppapi shim generator (from IDL), based on Noel's first cut. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Add code to skip wrapping Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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('pnaclshim', 'Name of the pnacl shim file.',
22 default=os.path.join('..',
23 'native_client',
24 'src',
25 'shared',
26 'ppapi_proxy',
27 'pnacl_shim.c'))
28
29
30 # TODO(jvoung): this doesn't do anything yet (except change some dbg messages).
31 Option('disable_pnacl_opt', 'Turn off optimization of pnacl shim.')
32
33
34 class PPKind(object):
35 @staticmethod
36 def ChoosePPFunc(iface, ppb_func, ppp_func):
37 name = iface.GetName()
38 if name.startswith("PPP"):
39 return ppp_func
40 elif name.startswith("PPB"):
41 return ppb_func
42 else:
43 raise Exception('Unknown PPKind for ' + name)
44
45
46 class Interface(object):
47 """ Tracks information about a particular interface version
48 - pnacl_id: the ID assigned to it in the PNaCl shim namespace
49 - struct_name: the struct type used by the ppapi headers to hold the
50 method pointers (the vtable).
51 - needs_wrapping: True if there is a method in the interface that needs
52 shimming
53 - header_file: the name of the header file that defined this interface
54 """
55 def __init__(self, interface_node, release, version,
56 pnacl_id, struct_name, needs_wrapping, header_file):
57 self.node = interface_node
58 self.release = release
59 self.version = version
60 self.pnacl_id = pnacl_id
61 self.struct_name = struct_name
62 # We may want finer grained filtering (method level), but it is not
63 # yet clear how to actually do that.
64 self.needs_wrapping = needs_wrapping
65 self.header_file = header_file
66
67
68 class PnaclGen(Generator):
69 """PnaclGen - A subclass of Generator which takes the IDL sources and
70 generates shim code for bridging the calling conventions between GCC
71 and PNaCl (LLVM).
72 """
73
74 def __init__(self):
75 Generator.__init__(self,
76 'Pnacl Shim Gen',
77 'pnacl',
78 'Generate the PNaCl shim.')
79 self.cgen = CGen()
80 self._skip_opt = False
81 self._pnacl_attribute = ' __attribute__((pnaclcall)) '
82
83
84 def GenerateRelease(self, ast, release, options):
85 return self.GenerateRange(ast, [release], options)
86
87
88 @staticmethod
89 def GetHeaderName(name):
90 """ Get the corresponding ppapi .h file from each IDL filename. """
91 name = os.path.splitext(name)[0] + '.h'
92 return 'ppapi/c/' + name
93
94 def WriteCopyrightGeneratedTime(self, out):
95 now = datetime.now()
96 c = """/* Copyright (c) %s The Chromium Authors. All rights reserved.
97 * Use of this source code is governed by a BSD-style license that can be
98 * found in the LICENSE file.
99 */
100
101 /* Last generated from IDL: %s. */
102 """ % (now.year, datetime.ctime(now))
103 out.Write(c)
104
105 def GenerateHeaderFile(self, out):
106 """ Writes out an interface header file for the exported symbols. """
107 self.WriteCopyrightGeneratedTime(out)
108 out.Write("""
109 #ifndef %(include_guard)s
110 #define %(include_guard)s
111
112 #include "ppapi/c/ppb.h"
113
114 PPB_GetInterface real_PPBGetInterface;
115 /* There is not a typedef for PPP_GetInterface, but it is currently the same as
116 PPB_GetInterface. */
117 PPB_GetInterface real_PPPGetInterface;
118
119 void set_real_PPBGetInterface(PPB_GetInterface real);
120 void set_real_PPPGetInterface(PPB_GetInterface real);
121
122 const void *Pnacl_PPBGetInterface(const char *name);
123 const void *Pnacl_PPPGetInterface(const char *name);
124
125 #endif /* %(include_guard)s */
126 """ % {
127 "include_guard" : "PNACL_SHIM_H_",
128 "timestamp" : datetime.ctime(datetime.now()),
129 })
130
131
132 def GenerateFixedFunctions(self, out):
133 """ Write out the set of constant functions (do not depend on the
134 current Pepper IDL. """
135 out.Write("""
136
137 void set_real_PPBGetInterface(PPB_GetInterface real) {
138 real_PPBGetInterface = real;
139 }
140
141 void set_real_PPPGetInterface(PPB_GetInterface real) {
142 real_PPPGetInterface = real;
143 }
144
145 const void *Pnacl_PPBGetInterface(const char *name) {
146 int wrapped;
147 int id = PnaclShimIfaceID(name, &wrapped);
148 if (id < 0) return NULL;
149
150 if (s_realPtrs[id] == NULL) {
151 const void *iface = (*real_PPBGetInterface)(name);
152 if (NULL == iface) return NULL;
153 s_realPtrs[id] = iface;
154 }
155
156 if (wrapped) {
157 return s_wrapPtrs[id];
158 } else {
159 return s_realPtrs[id];
160 }
161 }
162
163 const void *Pnacl_PPPGetInterface(const char *name) {
164 int wrapped;
165 int id = PnaclShimIfaceID(name, &wrapped);
166 if (id < 0) return NULL;
167
168 if (s_realPtrs[id] == NULL) {
169 const void *iface = (*real_PPPGetInterface)(name);
170 if (NULL == iface) return NULL;
171 s_realPtrs[id] = iface;
172 }
173
174 if (wrapped) {
175 return s_wrapPtrs[id];
176 } else {
177 return s_realPtrs[id];
178 }
179 }
180 """)
181
182 def PnaclIdForInterfaceVersion(self, iface, version):
183 return 'PNACL_%s_%s' % (iface.GetName(), str(version).replace('.', '_'))
184
185
186 def InterfaceNeedsWrapper(self, iface, releases):
187 """ Return true if the interface has ANY methods that need wrapping. """
188 if self._skip_opt:
189 return True
190 for release in iface.GetUniqueReleases(releases):
191 version = iface.GetVersion(release)
192 if self.InterfaceVersionNeedsWrapping(iface, version):
193 return True
194 return False
195
196
197 def InterfaceVersionNeedsWrapping(self, iface, version):
198 """ Return true if the interface+version has ANY methods that
199 need wrapping. """
200 if self._skip_opt:
201 return True
202 for member in iface.GetListOf('Member'):
203 release = member.GetRelease(version)
204 if self.MemberNeedsWrapping(member, release):
205 return True
206 return False
207
208
209 def MemberNeedsWrapping(self, member, release):
210 """ Return true if a particular member function at a particular
211 release needs wrapping. """
212 if self._skip_opt:
213 return True
214 if not member.InReleases([release]):
215 return False
216 ret, name, array, args_spec = self.cgen.GetComponents(member,
217 release,
218 'store')
219 return self.TypeNeedsWrapping(ret) or self.ArgsNeedWrapping(args_spec)
220
221
222 def ArgsNeedWrapping(self, args):
223 """ Return true if any parameter in the list needs wrapping. """
224 for arg in args:
225 (type_str, name, array_dims, more_args) = arg
226 if self.TypeNeedsWrapping(type_str):
227 return True
228 return False
229
230
231 def TypeNeedsWrapping(self, type_node):
232 """ Return true if a parameter type needs wrapping.
233 Currently, this is true for byval aggregates. """
234 is_aggregate = type_node.startswith('struct') or \
235 type_node.startswith('union')
236 is_reference = type_node.find('*') != -1
237 return is_aggregate and not is_reference
238
239
240 def DetermineInterfaces(self, ast, releases):
241 """ Get a list of interfaces along with whatever metadata we need. """
242 iface_releases = []
243 for filenode in ast.GetListOf('File'):
244 # If this file has errors, skip it
245 if filenode in self.skip_list:
246 print 'PnaclGen: Skipping %s due to errors\n' % filenode.GetName()
247 continue
248
249 file_name = self.GetHeaderName(filenode.GetName())
250 ifaces = filenode.GetListOf('Interface')
251 for iface in ifaces:
252 releases_for_iface = iface.GetUniqueReleases(releases)
253 for release in releases_for_iface:
254 version = iface.GetVersion(release)
255 pnacl_id = self.PnaclIdForInterfaceVersion(iface, version)
256 not_latest = release != releases_for_iface[-1]
257 struct_name = self.cgen.GetStructName(iface, release,
258 include_version=not_latest)
259 needs_wrap = self.InterfaceVersionNeedsWrapping(iface, version)
260 if not needs_wrap:
261 print 'Interface %s does not need wrapping' % pnacl_id
262 iface_releases.append(
263 Interface(iface, release, version, pnacl_id,
264 struct_name, needs_wrap, file_name))
265 return iface_releases
266
267
268 def GenerateIncludes(self, iface_releases, out_header_filename, out):
269 """ Write out the includes and other headers and return a list
270 of the interface nodes that were ultimately included. """
271 self.WriteCopyrightGeneratedTime(out)
272 # First include own header.
273 out.Write('#include "%s"\n\n' % out_header_filename)
274
275 # Include libc headers for strcmp.
276 out.Write('#include <string.h>\n\n')
277
278 # Get typedefs for PPB_GetInterface.
279 out.Write('#include "%s"\n' % self.GetHeaderName('ppb.h'))
280
281 # Get a conservative list of all #includes that are needed,
282 # whether it requires wrapping or not.
283 header_files = set()
284 for iface in iface_releases:
285 header_files.add(iface.header_file)
286 for header in sorted(header_files):
287 out.Write('#include "%s"\n' % header)
288
289
290 def EnumerateInterfaces(self, iface_releases, out):
291 # Get a conservative list of all IDs needed (even it not wrapped).
292 # We will need the IDs anyway to write the "real" version out.
293 out.Write('\nenum PnaclId {\n')
294 for iface in iface_releases:
295 out.Write(' %s,\n' % iface.pnacl_id)
296 out.Write(' PNACL_IFACE_COUNT\n')
297 out.Write('};\n\n')
298 out.Write('static const void *s_realPtrs[PNACL_IFACE_COUNT];\n\n');
299
300 # Also create a function that maps interface name to ID. The ID will be
301 # used to look up the wrapped interface in a table
302 # (so really it's string -> interface, but there is an ID in the middle).
303 out.Write('/* Map interface string -> pnacl ID */\n')
304 out.Write('static enum PnaclId PnaclShimIfaceID(const char *name, ' +
305 'int *wrapped) {\n')
306 for iface in iface_releases:
307 if iface.needs_wrapping:
308 wrap_str = '*wrapped = 1'
309 else:
310 wrap_str = '*wrapped = 0'
311 iface_macro = self.cgen.GetInterfaceMacro(iface.node, iface.version)
312 out.Write(""" if (!strcmp(name, %s)) {
313 %s;
314 return %s;
315 }\n""" % (iface_macro, wrap_str, iface.pnacl_id))
316
317 out.Write(' return -1;\n}\n\n')
318
319
320 def WrapperMethodPrefix(self, iface, release):
321 return 'PNACL_%s_%s_' % (release, iface.GetName())
322
323
324 def GetReturnArgs(self, ret_type, args_spec):
325 if ret_type != 'void':
326 ret = 'return '
327 else:
328 ret = ''
329 if args_spec:
330 args = []
331 for arg in args_spec:
332 args.append(arg[1])
333 args = ', '.join(args)
334 else:
335 args = ''
336 return (ret, args)
337
338
339 def GenerateWrapperForPPBMethods(self, iface, out):
340 for member in iface.node.GetListOf('Member'):
341 # Skip the method if it's not actually in the release.
342 if not member.InReleases([iface.release]):
343 continue
344 func_prefix = self.WrapperMethodPrefix(iface.node, iface.release)
345 sig = self.cgen.GetSignature(member, iface.release, 'store',
346 func_prefix, False)
347 out.Write('static %s %s {\n' % (self._pnacl_attribute, sig))
348 out.Write(' const struct %s *iface = s_realPtrs[%s];\n' % (
349 iface.struct_name, iface.pnacl_id))
350 ret, name, array, cspec = self.cgen.GetComponents(member,
351 iface.release,
352 'store')
353 ret_str, args_str = self.GetReturnArgs(ret, cspec)
354 out.Write(' %siface->%s(%s);\n}\n\n' % (ret_str,
355 member.GetName(), args_str))
356
357
358 def GenerateWrapperForPPPMethods(self, iface, out):
359 for member in iface.node.GetListOf('Member'):
360 # Skip the method if it's not actually in the release.
361 if not member.InReleases([iface.release]):
362 continue
363 func_prefix = self.WrapperMethodPrefix(iface.node, iface.release)
364 sig = self.cgen.GetSignature(member, iface.release, 'store',
365 func_prefix, False)
366 out.Write('static %s {\n' % sig)
367 out.Write(' const struct %s *iface = s_realPtrs[%s];\n' % (
368 iface.struct_name, iface.pnacl_id))
369 temp_fp = self.cgen.GetSignature(member, iface.release, 'return',
370 'temp_fp',
371 func_as_ptr=True,
372 ptr_prefix=self._pnacl_attribute,
373 include_name=False)
374 cast = self.cgen.GetSignature(member, iface.release, 'return',
375 prefix='',
376 func_as_ptr=True,
377 ptr_prefix=self._pnacl_attribute,
378 include_name=False)
379 out.Write(' %s = ((%s)iface->%s);\n' % (temp_fp,
380 cast,
381 member.GetName()))
382 ret, name, array, cspec = self.cgen.GetComponents(member,
383 iface.release,
384 'store')
385 ret_str, args_str = self.GetReturnArgs(ret, cspec)
386 out.Write(' %stemp_fp(%s);\n}\n\n' % (ret_str,
387 args_str))
388
389
390 def GenerateWrapperForMethods(self, iface_releases, releases, out):
391 for iface in iface_releases:
392 if not iface.needs_wrapping:
393 out.Write('/* Not generating wrappers for %s */\n' % iface.struct_name)
394 continue
395 generator = PPKind.ChoosePPFunc(iface.node,
396 self.GenerateWrapperForPPBMethods,
397 self.GenerateWrapperForPPPMethods)
398 generator(iface, out)
399
400
401 def GenerateWrapperInterfaces(self, iface_releases, releases, out):
402 for iface in iface_releases:
403 if not iface.needs_wrapping:
404 out.Write('/* Not generating wrappers for %s */\n' % iface.struct_name)
405 continue
406
407 out.Write('struct %s PNACL_Wrappers_%s = {\n' % (iface.struct_name,
408 iface.struct_name))
409 methods = []
410 for member in iface.node.GetListOf('Member'):
411 # Skip the method if it's not actually in the release.
412 if not member.InReleases([iface.release]):
413 continue
414 prefix = self.WrapperMethodPrefix(iface.node, iface.release)
415 cast = self.cgen.GetSignature(member, iface.release, 'return',
416 prefix='',
417 func_as_ptr=True,
418 ptr_prefix='',
419 include_name=False)
420 methods.append(' .%s = (%s)&%s%s' % (member.GetName(),
421 cast,
422 prefix,
423 member.GetName()))
424 out.Write(' ' + ',\n '.join(methods) + '\n')
425 out.Write('};\n\n')
426
427 # Write out a collection of the pnacl wrapper functions.
428 out.Write('static const void *s_wrapPtrs[PNACL_IFACE_COUNT + 1] = {\n')
429 for iface in iface_releases:
430 if not iface.needs_wrapping:
431 out.Write('/* Not generating wrappers for %s */\n' % iface.struct_name)
432 continue
433 out.Write(' (void *) &PNACL_Wrappers_%s,\n' % iface.struct_name)
434 out.Write(' NULL\n};\n\n')
435
436
437 def GenerateRange(self, ast, releases, options):
438 """ Generate shim code for a range of releases. """
439
440 self._skip_opt = GetOption('disable_pnacl_opt')
441
442 out_filename = GetOption('pnaclshim')
443 out_header_filename = os.path.splitext(out_filename)[0] + '.h'
444 print "Generating %s and %s" % (out_filename, out_header_filename)
445
446 out_header = IDLOutFile(out_header_filename)
447 self.GenerateHeaderFile(out_header)
448 out_header.Close()
449
450 out = IDLOutFile(out_filename)
451
452 # Get a list of all the interfaces along with metadata.
453 iface_releases = self.DetermineInterfaces(ast, releases)
454
455 # Generate the includes.
456 self.GenerateIncludes(iface_releases, out_header_filename, out)
457
458 # Assign each interface an ID number which is listed in an ENUM.
459 self.EnumerateInterfaces(iface_releases, out)
460
461 # Generate wrapper functions for each wrapped method in the interfaces.
462 self.GenerateWrapperForMethods(iface_releases, releases, out)
463
464 # Collect all the wrapper functions into interface structs. Then generate
465 # a table of the wrapped interface structs that can be looked up.
466 self.GenerateWrapperInterfaces(iface_releases, releases, out)
467
468 # Write out the IDL-invariant functions.
469 self.GenerateFixedFunctions(out)
470 out.Close()
471 return 0
472
473
474 pnaclgen = PnaclGen()
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698