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