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 """ 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 |