idl_gen_wrapper.py revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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  if (wrapper->wrapped_iface) {
172    return wrapper->wrapped_iface;
173  } else {
174    return wrapper->real_iface;
175  }
176}
177
178const void *__%(wrapper_prefix)s_PPPGetInterface(const char *name) {
179  struct %(wrapper_struct)s *wrapper = %(wrapper_prefix)sPPPShimIface(name);
180  if (wrapper == NULL) {
181    /* We did not generate a wrapper for this, so return the real interface. */
182    return (*__real_PPPGetInterface)(name);
183  }
184
185  /* Initialize the real_iface if it hasn't been. The wrapper depends on it. */
186  if (wrapper->real_iface == NULL) {
187    const void *iface = (*__real_PPPGetInterface)(name);
188    if (NULL == iface) return NULL;
189    wrapper->real_iface = iface;
190  }
191
192  if (wrapper->wrapped_iface) {
193    return wrapper->wrapped_iface;
194  } else {
195    return wrapper->real_iface;
196  }
197}
198""" % { 'wrapper_struct' : self.GetWrapperMetadataName(),
199        'wrapper_prefix' : self.wrapper_prefix,
200        } )
201
202
203  ############################################################
204
205  def OwnHeaderFile(self):
206    """Return the header file that specifies the API of this wrapper.
207    We do not generate the header files.  """
208    raise Exception('Child class must implement this')
209
210
211  ############################################################
212
213  def DetermineInterfaces(self, ast, releases):
214    """Get a list of interfaces along with whatever metadata we need.
215    """
216    iface_releases = []
217    for filenode in ast.GetListOf('File'):
218      # If this file has errors, skip it
219      if filenode in self.skip_list:
220        if GetOption('verbose'):
221          InfoOut.Log('WrapperGen: Skipping %s due to errors\n' %
222                      filenode.GetName())
223        continue
224
225      file_name = self.GetHeaderName(filenode.GetName())
226      ifaces = filenode.GetListOf('Interface')
227      for iface in ifaces:
228        releases_for_iface = iface.GetUniqueReleases(releases)
229        for release in releases_for_iface:
230          version = iface.GetVersion(release)
231          struct_name = self.cgen.GetStructName(iface, release,
232                                                include_version=True)
233          needs_wrap = self.InterfaceVersionNeedsWrapping(iface, version)
234          if not needs_wrap:
235            if GetOption('verbose'):
236              InfoOut.Log('Interface %s ver %s does not need wrapping' %
237                          (struct_name, version))
238          iface_releases.append(
239              Interface(iface, release, version,
240                        struct_name, needs_wrap, file_name))
241    return iface_releases
242
243
244  def GenerateIncludes(self, iface_releases, out):
245    """Generate the list of #include that define the original interfaces.
246    """
247    self.WriteCopyright(out)
248    # First include own header.
249    out.Write('#include "%s"\n\n' % self.OwnHeaderFile())
250
251    # Get typedefs for PPB_GetInterface.
252    out.Write('#include "%s"\n' % self.GetHeaderName('ppb.h'))
253
254    # Get a conservative list of all #includes that are needed,
255    # whether it requires wrapping or not. We currently depend on the macro
256    # string for comparison, even when it is not wrapped, to decide when
257    # to use the original/real interface.
258    header_files = set()
259    for iface in iface_releases:
260      header_files.add(iface.header_file)
261    for header in sorted(header_files):
262      out.Write('#include "%s"\n' % header)
263    out.Write('\n')
264
265
266  def WrapperMethodPrefix(self, iface, release):
267    return '%s_%s_%s_' % (self.wrapper_prefix, release, iface.GetName())
268
269
270  def GenerateWrapperForPPBMethod(self, iface, member):
271    result = []
272    func_prefix = self.WrapperMethodPrefix(iface.node, iface.release)
273    sig = self.cgen.GetSignature(member, iface.release, 'store',
274                                 func_prefix, False)
275    result.append('static %s {\n' % sig)
276    result.append(' while(1) { /* Not implemented */ } \n')
277    result.append('}\n')
278    return result
279
280
281  def GenerateWrapperForPPPMethod(self, iface, member):
282    result = []
283    func_prefix = self.WrapperMethodPrefix(iface.node, iface.release)
284    sig = self.cgen.GetSignature(member, iface.release, 'store',
285                                 func_prefix, False)
286    result.append('static %s {\n' % sig)
287    result.append(' while(1) { /* Not implemented */ } \n')
288    result.append('}\n')
289    return result
290
291
292  def GenerateWrapperForMethods(self, iface_releases, comments=True):
293    """Return a string representing the code for each wrapper method
294    (using a string rather than writing to the file directly for testing.)
295    """
296    result = []
297    for iface in iface_releases:
298      if not iface.needs_wrapping:
299        if comments:
300          result.append('/* Not generating wrapper methods for %s */\n\n' %
301                        iface.struct_name)
302        continue
303      if comments:
304        result.append('/* Begin wrapper methods for %s */\n\n' %
305                      iface.struct_name)
306      generator =  PPKind.ChoosePPFunc(iface,
307                                       self.GenerateWrapperForPPBMethod,
308                                       self.GenerateWrapperForPPPMethod)
309      for member in iface.node.GetListOf('Member'):
310        # Skip the method if it's not actually in the release.
311        if not member.InReleases([iface.release]):
312          continue
313        result.extend(generator(iface, member))
314      if comments:
315        result.append('/* End wrapper methods for %s */\n\n' %
316                      iface.struct_name)
317    return ''.join(result)
318
319
320  def GenerateWrapperInterfaces(self, iface_releases, out):
321    for iface in iface_releases:
322      if not iface.needs_wrapping:
323        out.Write('/* Not generating wrapper interface for %s */\n\n' %
324                  iface.struct_name)
325        continue
326
327      out.Write('static struct %s %s_Wrappers_%s = {\n' % (iface.struct_name,
328                                                           self.wrapper_prefix,
329                                                           iface.struct_name))
330      methods = []
331      for member in iface.node.GetListOf('Member'):
332        # Skip the method if it's not actually in the release.
333        if not member.InReleases([iface.release]):
334          continue
335        prefix = self.WrapperMethodPrefix(iface.node, iface.release)
336        # Casts are necessary for the PPB_* wrappers because we must
337        # cast away "__attribute__((pnaclcall))".  The PPP_* wrappers
338        # must match the default calling conventions and so don't have
339        # the attribute, so omitting casts for them provides a little
340        # extra type checking.
341        if iface.node.GetName().startswith('PPB_'):
342          cast = '(%s)' % self.cgen.GetSignature(
343              member, iface.release, 'return',
344              prefix='',
345              func_as_ptr=True,
346              include_name=False)
347        else:
348          cast = ''
349        methods.append('  .%s = %s&%s%s' % (member.GetName(),
350                                            cast,
351                                            prefix,
352                                            member.GetName()))
353      out.Write('  ' + ',\n  '.join(methods) + '\n')
354      out.Write('};\n\n')
355
356
357  def GetWrapperInfoName(self, iface):
358    return '%s_WrapperInfo_%s' % (self.wrapper_prefix, iface.struct_name)
359
360
361  def GenerateWrapperInfoAndCollection(self, iface_releases, out):
362    for iface in iface_releases:
363      iface_macro = self.cgen.GetInterfaceMacro(iface.node, iface.version)
364      if iface.needs_wrapping:
365        wrap_iface = '(void *) &%s_Wrappers_%s' % (self.wrapper_prefix,
366                                                   iface.struct_name)
367        out.Write("""static struct %s %s = {
368  .iface_macro = %s,
369  .wrapped_iface = %s,
370  .real_iface = NULL
371};\n\n""" % (self.GetWrapperMetadataName(),
372             self.GetWrapperInfoName(iface),
373             iface_macro,
374             wrap_iface))
375
376    # Now generate NULL terminated arrays of the above wrapper infos.
377    ppb_wrapper_infos = []
378    ppp_wrapper_infos = []
379    for iface in iface_releases:
380      if iface.needs_wrapping:
381        appender = PPKind.ChoosePPFunc(iface,
382                                       ppb_wrapper_infos.append,
383                                       ppp_wrapper_infos.append)
384        appender('  &%s' % self.GetWrapperInfoName(iface))
385    ppb_wrapper_infos.append('  NULL')
386    ppp_wrapper_infos.append('  NULL')
387    out.Write(
388        'static struct %s *s_ppb_wrappers[] = {\n%s\n};\n\n' %
389        (self.GetWrapperMetadataName(), ',\n'.join(ppb_wrapper_infos)))
390    out.Write(
391        'static struct %s *s_ppp_wrappers[] = {\n%s\n};\n\n' %
392        (self.GetWrapperMetadataName(), ',\n'.join(ppp_wrapper_infos)))
393
394
395  def DeclareWrapperInfos(self, iface_releases, out):
396    """The wrapper methods usually need access to the real_iface, so we must
397    declare these wrapper infos ahead of time (there is a circular dependency).
398    """
399    out.Write('/* BEGIN Declarations for all Wrapper Infos */\n\n')
400    for iface in iface_releases:
401      if iface.needs_wrapping:
402        out.Write('static struct %s %s;\n' %
403                  (self.GetWrapperMetadataName(),
404                   self.GetWrapperInfoName(iface)))
405    out.Write('/* END Declarations for all Wrapper Infos. */\n\n')
406
407
408  def GenerateRange(self, ast, releases, options):
409    """Generate shim code for a range of releases.
410    """
411
412    # Remember to set the output filename before running this.
413    out_filename = self.output_file
414    if out_filename is None:
415      ErrOut.Log('Did not set filename for writing out wrapper\n')
416      return 1
417
418    InfoOut.Log("Generating %s for %s" % (out_filename, self.wrapper_prefix))
419
420    out = IDLOutFile(out_filename)
421
422    # Get a list of all the interfaces along with metadata.
423    iface_releases = self.DetermineInterfaces(ast, releases)
424
425    # Generate the includes.
426    self.GenerateIncludes(iface_releases, out)
427
428    # Write out static helper functions (mystrcmp).
429    self.GenerateHelperFunctions(out)
430
431    # Declare list of WrapperInfo before actual wrapper methods, since
432    # they reference each other.
433    self.DeclareWrapperInfos(iface_releases, out)
434
435    # Generate wrapper functions for each wrapped method in the interfaces.
436    result = self.GenerateWrapperForMethods(iface_releases)
437    out.Write(result)
438
439    # Collect all the wrapper functions into interface structs.
440    self.GenerateWrapperInterfaces(iface_releases, out)
441
442    # Generate a table of the wrapped interface structs that can be looked up.
443    self.GenerateWrapperInfoAndCollection(iface_releases, out)
444
445    # Write out the IDL-invariant functions.
446    self.GenerateFixedFunctions(out)
447
448    out.Close()
449    return 0
450