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 """ Base class for generating wrapper functions for PPAPI methods. """ | |
8 | |
9 from datetime import datetime | |
10 import os | |
11 import sys | |
12 | |
13 from idl_ast import IDLAst | |
14 from idl_c_proto import CGen | |
15 from idl_generator import Generator | |
16 from idl_log import ErrOut, InfoOut, WarnOut | |
17 from idl_option import Option | |
18 from idl_outfile import IDLOutFile | |
19 | |
20 | |
21 class PPKind(object): | |
22 @staticmethod | |
23 def ChoosePPFunc(iface, ppb_func, ppp_func): | |
24 name = iface.node.GetName() | |
25 if name.startswith("PPP"): | |
26 return ppp_func | |
27 elif name.startswith("PPB"): | |
28 return ppb_func | |
29 else: | |
30 raise Exception('Unknown PPKind for ' + name) | |
31 | |
32 | |
33 class Interface(object): | |
34 """ Tracks information about a particular interface version | |
35 - struct_name: the struct type used by the ppapi headers to hold the | |
36 method pointers (the vtable). | |
37 - needs_wrapping: True if there is a method in the interface that needs | |
38 shimming | |
39 - header_file: the name of the header file that defined this interface | |
40 """ | |
41 def __init__(self, interface_node, release, version, | |
42 struct_name, needs_wrapping, header_file): | |
43 self.node = interface_node | |
44 self.release = release | |
45 self.version = version | |
46 self.struct_name = struct_name | |
47 # We may want finer grained filtering (method level), but it is not | |
48 # yet clear how to actually do that. | |
49 self.needs_wrapping = needs_wrapping | |
50 self.header_file = header_file | |
51 | |
52 | |
53 class WrapperGen(Generator): | |
54 """WrapperGen - A subclass of Generator which takes the IDL sources and | |
noelallen1
2011/11/23 18:06:11
"""One Line
Multi
Line
"""
| |
55 generates wrapper code for each method and allows redirecting the | |
56 the GetInterface functions to these new wrappers. | |
57 """ | |
58 | |
59 def __init__(self, wrapper_prefix, s1, s2, s3): | |
60 Generator.__init__(self, s1, s2, s3) | |
61 self.wrapper_prefix = wrapper_prefix | |
62 self._skip_opt = False | |
63 self.output_file = None | |
64 self.cgen = CGen() | |
65 | |
66 def SetOutputFile(self, fname): | |
67 self.output_file = fname | |
68 | |
69 | |
70 def GenerateRelease(self, ast, release, options): | |
71 return self.GenerateRange(ast, [release], options) | |
72 | |
73 | |
74 @staticmethod | |
75 def GetHeaderName(name): | |
76 """ Get the corresponding ppapi .h file from each IDL filename. """ | |
77 name = os.path.splitext(name)[0] + '.h' | |
78 return 'ppapi/c/' + name | |
79 | |
80 | |
81 def WriteCopyrightGeneratedTime(self, out): | |
82 now = datetime.now() | |
83 c = """/* Copyright (c) %s The Chromium Authors. All rights reserved. | |
84 * Use of this source code is governed by a BSD-style license that can be | |
85 * found in the LICENSE file. | |
86 */ | |
87 | |
88 /* Last generated from IDL: %s. */ | |
89 """ % (now.year, datetime.ctime(now)) | |
90 out.Write(c) | |
91 | |
92 def GetWrapperMetadataName(self): | |
93 return '__%sWrapperInfo' % self.wrapper_prefix | |
94 | |
95 | |
96 def GetHeaderGuard(self, header_filename): | |
97 g = header_filename.upper().replace(os.path.sep, '_').replace('.','_') + '_' | |
98 return g | |
99 | |
100 def GenerateHeaderFile(self, header_filename, out): | |
101 """ Writes out an interface header file for the exported symbols. """ | |
102 self.WriteCopyrightGeneratedTime(out) | |
103 include_guard = self.GetHeaderGuard(header_filename) | |
104 out.Write(""" | |
105 #ifndef %(include_guard)s | |
106 #define %(include_guard)s | |
107 | |
108 #include "ppapi/c/ppb.h" | |
109 | |
110 /* There is not a typedef for PPP_GetInterface_type, but it is currently | |
111 * the same as PPB_GetInterface. */ | |
112 typedef PPB_GetInterface PPP_GetInterface_type; | |
113 | |
114 void __set_real_%(wrapper_prefix)s_PPBGetInterface(PPB_GetInterface real); | |
115 void __set_real_%(wrapper_prefix)s_PPPGetInterface(PPP_GetInterface_type real); | |
116 | |
117 const void *__%(wrapper_prefix)s_PPBGetInterface(const char *name); | |
118 const void *__%(wrapper_prefix)s_PPPGetInterface(const char *name); | |
119 | |
120 struct %(wrapper_struct)s { | |
121 const char* iface_macro; | |
122 const void* wrapped_iface; /* If NULL then it was not wrapped! */ | |
123 const void* real_iface; | |
124 }; | |
125 | |
126 #endif /* %(include_guard)s */ | |
127 """ % { | |
128 'include_guard' : include_guard, | |
129 'timestamp' : datetime.ctime(datetime.now()), | |
130 'wrapper_prefix' : self.wrapper_prefix, | |
131 'wrapper_struct' : self.GetWrapperMetadataName(), | |
132 }) | |
133 | |
134 | |
135 def GenerateHelperFunctions(self, out): | |
136 """ Write helper functions to avoid dependencies on libc. """ | |
137 out.Write("""/* Use local strcmp to avoid dependency on libc. */ | |
138 static int mystrcmp(const char* s1, const char *s2) { | |
139 while((*s1 && *s2) && (*s1++ == *s2++)); | |
140 return *(--s1) - *(--s2); | |
141 }\n | |
142 """) | |
143 | |
144 | |
145 def GenerateFixedFunctions(self, out): | |
146 """ Write out the set of constant functions (those that do not depend on | |
147 the current Pepper IDL). """ | |
148 out.Write(""" | |
149 | |
150 static PPB_GetInterface __real_PPBGetInterface; | |
151 static PPP_GetInterface_type __real_PPPGetInterface; | |
152 | |
153 void __set_real_%(wrapper_prefix)s_PPBGetInterface(PPB_GetInterface real) { | |
154 __real_PPBGetInterface = real; | |
155 } | |
156 | |
157 void __set_real_%(wrapper_prefix)s_PPPGetInterface(PPP_GetInterface_type real) { | |
158 __real_PPPGetInterface = real; | |
159 } | |
160 | |
161 /* Map interface string -> wrapper metadata */ | |
162 static struct %(wrapper_struct)s *%(wrapper_prefix)sPPBShimIface( | |
163 const char *name) { | |
164 struct %(wrapper_struct)s **next = s_ppb_wrappers; | |
165 while (*next != NULL) { | |
166 if (mystrcmp(name, (*next)->iface_macro) == 0) return *next; | |
167 ++next; | |
168 } | |
169 return NULL; | |
170 } | |
171 | |
172 /* Map interface string -> wrapper metadata */ | |
173 static struct %(wrapper_struct)s *%(wrapper_prefix)sPPPShimIface( | |
174 const char *name) { | |
175 struct %(wrapper_struct)s **next = s_ppp_wrappers; | |
176 while (*next != NULL) { | |
177 if (mystrcmp(name, (*next)->iface_macro) == 0) return *next; | |
178 ++next; | |
179 } | |
180 return NULL; | |
181 } | |
182 | |
183 const void *__%(wrapper_prefix)s_PPBGetInterface(const char *name) { | |
184 struct %(wrapper_struct)s *wrapper = %(wrapper_prefix)sPPBShimIface(name); | |
185 if (wrapper == NULL) { | |
186 /* We don't have an IDL for this, for some reason. Take our chances. */ | |
187 return (*__real_PPBGetInterface)(name); | |
188 } | |
189 | |
190 /* Initialize the real_iface if it hasn't been. The wrapper depends on it. */ | |
191 if (wrapper->real_iface == NULL) { | |
192 const void *iface = (*__real_PPBGetInterface)(name); | |
193 if (NULL == iface) return NULL; | |
194 wrapper->real_iface = iface; | |
195 } | |
196 | |
197 if (wrapper->wrapped_iface) { | |
198 return wrapper->wrapped_iface; | |
199 } else { | |
200 return wrapper->real_iface; | |
201 } | |
202 } | |
203 | |
204 const void *__%(wrapper_prefix)s_PPPGetInterface(const char *name) { | |
205 struct %(wrapper_struct)s *wrapper = %(wrapper_prefix)sPPPShimIface(name); | |
206 if (wrapper == NULL) { | |
207 /* We don't have an IDL for this, for some reason. Take our chances. */ | |
208 return (*__real_PPPGetInterface)(name); | |
209 } | |
210 | |
211 /* Initialize the real_iface if it hasn't been. The wrapper depends on it. */ | |
212 if (wrapper->real_iface == NULL) { | |
213 const void *iface = (*__real_PPPGetInterface)(name); | |
214 if (NULL == iface) return NULL; | |
215 wrapper->real_iface = iface; | |
216 } | |
217 | |
218 if (wrapper->wrapped_iface) { | |
219 return wrapper->wrapped_iface; | |
220 } else { | |
221 return wrapper->real_iface; | |
222 } | |
223 } | |
224 """ % { 'wrapper_struct' : self.GetWrapperMetadataName(), | |
225 'wrapper_prefix' : self.wrapper_prefix, | |
226 } ) | |
227 | |
228 | |
229 ############################################################ | |
230 | |
231 def InterfaceNeedsWrapper(self, iface, releases): | |
232 """ Return true if the interface has ANY methods that need wrapping. """ | |
233 return True | |
234 | |
235 | |
236 ############################################################ | |
237 | |
238 def DetermineInterfaces(self, ast, releases): | |
239 """ Get a list of interfaces along with whatever metadata we need. """ | |
240 iface_releases = [] | |
241 for filenode in ast.GetListOf('File'): | |
242 # If this file has errors, skip it | |
243 if filenode in self.skip_list: | |
244 InfoOut.Log('WrapperGen: Skipping %s due to errors\n' % | |
245 filenode.GetName()) | |
246 continue | |
247 | |
248 file_name = self.GetHeaderName(filenode.GetName()) | |
249 ifaces = filenode.GetListOf('Interface') | |
250 for iface in ifaces: | |
251 releases_for_iface = iface.GetUniqueReleases(releases) | |
252 for release in releases_for_iface: | |
253 version = iface.GetVersion(release) | |
254 not_latest = release != releases_for_iface[-1] | |
255 struct_name = self.cgen.GetStructName(iface, release, | |
256 include_version=not_latest) | |
257 needs_wrap = self.InterfaceVersionNeedsWrapping(iface, version) | |
258 if not needs_wrap: | |
259 InfoOut.Log('Interface %s ver %s does not need wrapping' % | |
260 (struct_name, version)) | |
261 iface_releases.append( | |
262 Interface(iface, release, version, | |
263 struct_name, needs_wrap, file_name)) | |
264 return iface_releases | |
265 | |
266 | |
267 def GenerateIncludes(self, iface_releases, out_header_filename, out): | |
268 """ Write out the includes and other headers and return a list | |
269 of the interface nodes that were ultimately included. """ | |
270 self.WriteCopyrightGeneratedTime(out) | |
271 # First include own header. | |
272 out.Write('#include "%s"\n\n' % out_header_filename) | |
273 | |
274 # Get typedefs for PPB_GetInterface. | |
275 out.Write('#include "%s"\n' % self.GetHeaderName('ppb.h')) | |
276 | |
277 # Get a conservative list of all #includes that are needed, | |
278 # whether it requires wrapping or not. We currently depend on the macro | |
279 # string for comparison, even when it is not wrapped, to decide when | |
280 # to use the original/real interface. | |
281 header_files = set() | |
282 for iface in iface_releases: | |
283 header_files.add(iface.header_file) | |
284 for header in sorted(header_files): | |
285 out.Write('#include "%s"\n' % header) | |
286 out.Write('\n') | |
287 | |
288 | |
289 def WrapperMethodPrefix(self, iface, release): | |
290 return '%s_%s_%s_' % (self.wrapper_prefix, release, iface.GetName()) | |
291 | |
292 | |
293 def GetReturnArgs(self, ret_type, args_spec): | |
294 if ret_type != 'void': | |
295 ret = 'return ' | |
296 else: | |
297 ret = '' | |
298 if args_spec: | |
299 args = [] | |
300 for arg in args_spec: | |
301 args.append(arg[1]) | |
302 args = ', '.join(args) | |
303 else: | |
304 args = '' | |
305 return (ret, args) | |
306 | |
307 | |
308 def GenerateWrapperForPPBMethod(self, iface, member): | |
309 result = [] | |
310 func_prefix = self.WrapperMethodPrefix(iface.node, iface.release) | |
311 sig = self.cgen.GetSignature(member, iface.release, 'store', | |
312 func_prefix, False) | |
313 result.append('static %s {\n' % sig) | |
314 result.append(' while(1) { /* Not implemented */ } \n') | |
315 result.append('}\n') | |
316 return result | |
317 | |
318 | |
319 def GenerateWrapperForPPPMethod(self, iface, member): | |
320 result = [] | |
321 func_prefix = self.WrapperMethodPrefix(iface.node, iface.release) | |
322 sig = self.cgen.GetSignature(member, iface.release, 'store', | |
323 func_prefix, False) | |
324 result.append('static %s {\n' % sig) | |
325 result.append(' while(1) { /* Not implemented */ } \n') | |
326 result.append('}\n') | |
327 return result | |
328 | |
329 | |
330 def GenerateWrapperForMethods(self, iface_releases, comments=True): | |
331 """ Return a string representing the code for each wrapper method | |
332 (using a string rather than writing to the file directly for testing.) """ | |
333 result = [] | |
334 for iface in iface_releases: | |
335 if not iface.needs_wrapping: | |
336 if comments: | |
337 result.append('/* Not generating wrapper methods for %s */\n\n' % | |
338 iface.struct_name) | |
339 continue | |
340 if comments: | |
341 result.append('/* Begin wrapper methods for %s */\n\n' % | |
342 iface.struct_name) | |
343 generator = PPKind.ChoosePPFunc(iface, | |
344 self.GenerateWrapperForPPBMethod, | |
345 self.GenerateWrapperForPPPMethod) | |
346 for member in iface.node.GetListOf('Member'): | |
347 # Skip the method if it's not actually in the release. | |
348 if not member.InReleases([iface.release]): | |
349 continue | |
350 result.extend(generator(iface, member)) | |
351 if comments: | |
352 result.append('/* End wrapper methods for %s */\n\n' % | |
353 iface.struct_name) | |
354 return ''.join(result) | |
355 | |
356 | |
357 def GenerateWrapperInterfaces(self, iface_releases, out): | |
358 for iface in iface_releases: | |
359 if not iface.needs_wrapping: | |
360 out.Write('/* Not generating wrapper interface for %s */\n\n' % | |
361 iface.struct_name) | |
362 continue | |
363 | |
364 out.Write('struct %s %s_Wrappers_%s = {\n' % (iface.struct_name, | |
365 self.wrapper_prefix, | |
366 iface.struct_name)) | |
367 methods = [] | |
368 for member in iface.node.GetListOf('Member'): | |
369 # Skip the method if it's not actually in the release. | |
370 if not member.InReleases([iface.release]): | |
371 continue | |
372 prefix = self.WrapperMethodPrefix(iface.node, iface.release) | |
373 cast = self.cgen.GetSignature(member, iface.release, 'return', | |
374 prefix='', | |
375 func_as_ptr=True, | |
376 ptr_prefix='', | |
377 include_name=False) | |
378 methods.append(' .%s = (%s)&%s%s' % (member.GetName(), | |
379 cast, | |
380 prefix, | |
381 member.GetName())) | |
382 out.Write(' ' + ',\n '.join(methods) + '\n') | |
383 out.Write('};\n\n') | |
384 | |
385 | |
386 def GetWrapperInfoName(self, iface): | |
387 return '%s_WrapperInfo_%s' % (self.wrapper_prefix, iface.struct_name) | |
388 | |
389 | |
390 def GenerateWrapperInfoAndCollection(self, iface_releases, out): | |
391 for iface in iface_releases: | |
392 iface_macro = self.cgen.GetInterfaceMacro(iface.node, iface.version) | |
393 if iface.needs_wrapping: | |
394 wrap_iface = '(void *) &%s_Wrappers_%s' % (self.wrapper_prefix, | |
395 iface.struct_name) | |
396 else: | |
397 wrap_iface = 'NULL /* Still need slot for real_iface */' | |
398 out.Write("""static struct %s %s = { | |
399 .iface_macro = %s, | |
400 .wrapped_iface = %s, | |
401 .real_iface = NULL | |
402 };\n\n""" % (self.GetWrapperMetadataName(), | |
403 self.GetWrapperInfoName(iface), | |
404 iface_macro, | |
405 wrap_iface)) | |
406 | |
407 # Now generate NULL terminated arrays of the above wrapper infos. | |
408 ppb_wrapper_infos = [] | |
409 ppp_wrapper_infos = [] | |
410 for iface in iface_releases: | |
411 appender = PPKind.ChoosePPFunc(iface, | |
412 ppb_wrapper_infos.append, | |
413 ppp_wrapper_infos.append) | |
414 appender(' &%s' % self.GetWrapperInfoName(iface)) | |
415 ppb_wrapper_infos.append(' NULL') | |
416 ppp_wrapper_infos.append(' NULL') | |
417 out.Write( | |
418 'static struct %s *s_ppb_wrappers[] = {\n%s\n};\n\n' % | |
419 (self.GetWrapperMetadataName(), ',\n'.join(ppb_wrapper_infos))) | |
420 out.Write( | |
421 'static struct %s *s_ppp_wrappers[] = {\n%s\n};\n\n' % | |
422 (self.GetWrapperMetadataName(), ',\n'.join(ppp_wrapper_infos))) | |
423 | |
424 | |
425 def DeclareWrapperInfos(self, iface_releases, out): | |
426 """ The wrapper methods usually need access to the real_iface, so we must | |
427 declare these wrapper infos ahead of time (there is a circular dependency). | |
428 """ | |
429 out.Write('/* BEGIN Declarations for all Wrapper Infos */\n\n') | |
430 for iface in iface_releases: | |
431 out.Write('static struct %s %s;\n' % | |
432 (self.GetWrapperMetadataName(), self.GetWrapperInfoName(iface))) | |
433 out.Write('/* END Declarations for all Wrapper Infos. */\n\n') | |
434 | |
435 | |
436 def GenerateRange(self, ast, releases, options): | |
437 """ Generate shim code for a range of releases. """ | |
438 | |
439 # Remember to set the output filename before running this. | |
440 out_filename = self.output_file | |
441 if out_filename is None: | |
442 ErrOut.Log('Did not set filename for writing out wrapper\n') | |
443 return 1 | |
444 | |
445 out_header_filename = os.path.splitext(out_filename)[0] + '.h' | |
446 InfoOut.Log("Generating %s and %s for %s" % | |
447 (out_filename, out_header_filename, self.wrapper_prefix)) | |
448 | |
449 out_header = IDLOutFile(out_header_filename) | |
450 self.GenerateHeaderFile(out_header_filename, out_header) | |
451 out_header.Close() | |
452 | |
453 out = IDLOutFile(out_filename) | |
454 | |
455 # Get a list of all the interfaces along with metadata. | |
456 iface_releases = self.DetermineInterfaces(ast, releases) | |
457 | |
458 # Generate the includes. | |
459 self.GenerateIncludes(iface_releases, out_header_filename, out) | |
460 | |
461 # Write out static helper functions (mystrcmp). | |
462 self.GenerateHelperFunctions(out) | |
463 | |
464 # Declare list of WrapperInfo before actual wrapper methods, since | |
465 # they reference each other. | |
466 self.DeclareWrapperInfos(iface_releases, out) | |
467 | |
468 # Generate wrapper functions for each wrapped method in the interfaces. | |
469 result = self.GenerateWrapperForMethods(iface_releases) | |
470 out.Write(result) | |
471 | |
472 # Collect all the wrapper functions into interface structs. | |
473 self.GenerateWrapperInterfaces(iface_releases, out) | |
474 | |
475 # Generate a table of the wrapped interface structs that can be looked up. | |
476 self.GenerateWrapperInfoAndCollection(iface_releases, out) | |
477 | |
478 # Write out the IDL-invariant functions. | |
479 self.GenerateFixedFunctions(out) | |
480 out.Close() | |
481 return 0 | |
OLD | NEW |