idl_gen_wrapper.py revision 46d4c2bc3267f3f028f39e7e311b0f89aba2e4fd
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 = self.cgen.GetInterfaceMacro(iface.node, iface.version)
353      if iface.needs_wrapping:
354        wrap_iface = '(const void *) &%s_Wrappers_%s' % (self.wrapper_prefix,
355                                                         iface.struct_name)
356        out.Write("""static struct %s %s = {
357  .iface_macro = %s,
358  .wrapped_iface = %s,
359  .real_iface = NULL
360};\n\n""" % (self.GetWrapperMetadataName(),
361             self.GetWrapperInfoName(iface),
362             iface_macro,
363             wrap_iface))
364
365    # Now generate NULL terminated arrays of the above wrapper infos.
366    ppb_wrapper_infos = []
367    ppp_wrapper_infos = []
368    for iface in iface_releases:
369      if iface.needs_wrapping:
370        appender = PPKind.ChoosePPFunc(iface,
371                                       ppb_wrapper_infos.append,
372                                       ppp_wrapper_infos.append)
373        appender('  &%s' % self.GetWrapperInfoName(iface))
374    ppb_wrapper_infos.append('  NULL')
375    ppp_wrapper_infos.append('  NULL')
376    out.Write(
377        'static struct %s *s_ppb_wrappers[] = {\n%s\n};\n\n' %
378        (self.GetWrapperMetadataName(), ',\n'.join(ppb_wrapper_infos)))
379    out.Write(
380        'static struct %s *s_ppp_wrappers[] = {\n%s\n};\n\n' %
381        (self.GetWrapperMetadataName(), ',\n'.join(ppp_wrapper_infos)))
382
383
384  def DeclareWrapperInfos(self, iface_releases, out):
385    """The wrapper methods usually need access to the real_iface, so we must
386    declare these wrapper infos ahead of time (there is a circular dependency).
387    """
388    out.Write('/* BEGIN Declarations for all Wrapper Infos */\n\n')
389    for iface in iface_releases:
390      if iface.needs_wrapping:
391        out.Write('static struct %s %s;\n' %
392                  (self.GetWrapperMetadataName(),
393                   self.GetWrapperInfoName(iface)))
394    out.Write('/* END Declarations for all Wrapper Infos. */\n\n')
395
396
397  def GenerateRange(self, ast, releases, options):
398    """Generate shim code for a range of releases.
399    """
400
401    # Remember to set the output filename before running this.
402    out_filename = self.output_file
403    if out_filename is None:
404      ErrOut.Log('Did not set filename for writing out wrapper\n')
405      return 1
406
407    InfoOut.Log("Generating %s for %s" % (out_filename, self.wrapper_prefix))
408
409    out = IDLOutFile(out_filename)
410
411    # Get a list of all the interfaces along with metadata.
412    iface_releases = self.DetermineInterfaces(ast, releases)
413
414    # Generate the includes.
415    self.GenerateIncludes(iface_releases, out)
416
417    # Write out static helper functions (mystrcmp).
418    self.GenerateHelperFunctions(out)
419
420    # Declare list of WrapperInfo before actual wrapper methods, since
421    # they reference each other.
422    self.DeclareWrapperInfos(iface_releases, out)
423
424    # Generate wrapper functions for each wrapped method in the interfaces.
425    result = self.GenerateWrapperForMethods(iface_releases)
426    out.Write(result)
427
428    # Collect all the wrapper functions into interface structs.
429    self.GenerateWrapperInterfaces(iface_releases, out)
430
431    # Generate a table of the wrapped interface structs that can be looked up.
432    self.GenerateWrapperInfoAndCollection(iface_releases, out)
433
434    # Write out the IDL-invariant functions.
435    self.GenerateFixedFunctions(out)
436
437    out.Close()
438    return 0
439