| Index: ppapi/generators/idl_gen_pnacl.py
|
| diff --git a/ppapi/generators/idl_gen_pnacl.py b/ppapi/generators/idl_gen_pnacl.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..39933421580578d92a33f06c55138022c60a2e74
|
| --- /dev/null
|
| +++ b/ppapi/generators/idl_gen_pnacl.py
|
| @@ -0,0 +1,474 @@
|
| +#!/usr/bin/python
|
| +#
|
| +# Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
| +# Use of this source code is governed by a BSD-style license that can be
|
| +# found in the LICENSE file.
|
| +
|
| +""" Generator for Pnacl Shim functions that bridge the calling conventions
|
| +between GCC and PNaCl. """
|
| +
|
| +from datetime import datetime
|
| +import os
|
| +import sys
|
| +
|
| +from idl_log import ErrOut, InfoOut, WarnOut
|
| +from idl_ast import IDLAst
|
| +from idl_option import GetOption, Option, ParseOptions
|
| +from idl_outfile import IDLOutFile
|
| +from idl_c_proto import CGen
|
| +from idl_generator import Generator
|
| +
|
| +Option('pnaclshim', 'Name of the pnacl shim file.',
|
| + default=os.path.join('..',
|
| + 'native_client',
|
| + 'src',
|
| + 'shared',
|
| + 'ppapi_proxy',
|
| + 'pnacl_shim.c'))
|
| +
|
| +
|
| +# TODO(jvoung): this doesn't do anything yet (except change some dbg messages).
|
| +Option('disable_pnacl_opt', 'Turn off optimization of pnacl shim.')
|
| +
|
| +
|
| +class PPKind(object):
|
| + @staticmethod
|
| + def ChoosePPFunc(iface, ppb_func, ppp_func):
|
| + name = iface.GetName()
|
| + if name.startswith("PPP"):
|
| + return ppp_func
|
| + elif name.startswith("PPB"):
|
| + return ppb_func
|
| + else:
|
| + raise Exception('Unknown PPKind for ' + name)
|
| +
|
| +
|
| +class Interface(object):
|
| + """ Tracks information about a particular interface version
|
| + - pnacl_id: the ID assigned to it in the PNaCl shim namespace
|
| + - struct_name: the struct type used by the ppapi headers to hold the
|
| + method pointers (the vtable).
|
| + - needs_wrapping: True if there is a method in the interface that needs
|
| + shimming
|
| + - header_file: the name of the header file that defined this interface
|
| + """
|
| + def __init__(self, interface_node, release, version,
|
| + pnacl_id, struct_name, needs_wrapping, header_file):
|
| + self.node = interface_node
|
| + self.release = release
|
| + self.version = version
|
| + self.pnacl_id = pnacl_id
|
| + self.struct_name = struct_name
|
| + # We may want finer grained filtering (method level), but it is not
|
| + # yet clear how to actually do that.
|
| + self.needs_wrapping = needs_wrapping
|
| + self.header_file = header_file
|
| +
|
| +
|
| +class PnaclGen(Generator):
|
| + """PnaclGen - A subclass of Generator which takes the IDL sources and
|
| + generates shim code for bridging the calling conventions between GCC
|
| + and PNaCl (LLVM).
|
| + """
|
| +
|
| + def __init__(self):
|
| + Generator.__init__(self,
|
| + 'Pnacl Shim Gen',
|
| + 'pnacl',
|
| + 'Generate the PNaCl shim.')
|
| + self.cgen = CGen()
|
| + self._skip_opt = False
|
| + self._pnacl_attribute = ' __attribute__((pnaclcall)) '
|
| +
|
| +
|
| + def GenerateRelease(self, ast, release, options):
|
| + return self.GenerateRange(ast, [release], options)
|
| +
|
| +
|
| + @staticmethod
|
| + def GetHeaderName(name):
|
| + """ Get the corresponding ppapi .h file from each IDL filename. """
|
| + name = os.path.splitext(name)[0] + '.h'
|
| + return 'ppapi/c/' + name
|
| +
|
| + def WriteCopyrightGeneratedTime(self, out):
|
| + now = datetime.now()
|
| + c = """/* Copyright (c) %s The Chromium Authors. All rights reserved.
|
| + * Use of this source code is governed by a BSD-style license that can be
|
| + * found in the LICENSE file.
|
| + */
|
| +
|
| +/* Last generated from IDL: %s. */
|
| +""" % (now.year, datetime.ctime(now))
|
| + out.Write(c)
|
| +
|
| + def GenerateHeaderFile(self, out):
|
| + """ Writes out an interface header file for the exported symbols. """
|
| + self.WriteCopyrightGeneratedTime(out)
|
| + out.Write("""
|
| +#ifndef %(include_guard)s
|
| +#define %(include_guard)s
|
| +
|
| +#include "ppapi/c/ppb.h"
|
| +
|
| +PPB_GetInterface real_PPBGetInterface;
|
| +/* There is not a typedef for PPP_GetInterface, but it is currently the same as
|
| + PPB_GetInterface. */
|
| +PPB_GetInterface real_PPPGetInterface;
|
| +
|
| +void set_real_PPBGetInterface(PPB_GetInterface real);
|
| +void set_real_PPPGetInterface(PPB_GetInterface real);
|
| +
|
| +const void *Pnacl_PPBGetInterface(const char *name);
|
| +const void *Pnacl_PPPGetInterface(const char *name);
|
| +
|
| +#endif /* %(include_guard)s */
|
| +""" % {
|
| + "include_guard" : "PNACL_SHIM_H_",
|
| + "timestamp" : datetime.ctime(datetime.now()),
|
| + })
|
| +
|
| +
|
| + def GenerateFixedFunctions(self, out):
|
| + """ Write out the set of constant functions (do not depend on the
|
| + current Pepper IDL. """
|
| + out.Write("""
|
| +
|
| +void set_real_PPBGetInterface(PPB_GetInterface real) {
|
| + real_PPBGetInterface = real;
|
| +}
|
| +
|
| +void set_real_PPPGetInterface(PPB_GetInterface real) {
|
| + real_PPPGetInterface = real;
|
| +}
|
| +
|
| +const void *Pnacl_PPBGetInterface(const char *name) {
|
| + int wrapped;
|
| + int id = PnaclShimIfaceID(name, &wrapped);
|
| + if (id < 0) return NULL;
|
| +
|
| + if (s_realPtrs[id] == NULL) {
|
| + const void *iface = (*real_PPBGetInterface)(name);
|
| + if (NULL == iface) return NULL;
|
| + s_realPtrs[id] = iface;
|
| + }
|
| +
|
| + if (wrapped) {
|
| + return s_wrapPtrs[id];
|
| + } else {
|
| + return s_realPtrs[id];
|
| + }
|
| +}
|
| +
|
| +const void *Pnacl_PPPGetInterface(const char *name) {
|
| + int wrapped;
|
| + int id = PnaclShimIfaceID(name, &wrapped);
|
| + if (id < 0) return NULL;
|
| +
|
| + if (s_realPtrs[id] == NULL) {
|
| + const void *iface = (*real_PPPGetInterface)(name);
|
| + if (NULL == iface) return NULL;
|
| + s_realPtrs[id] = iface;
|
| + }
|
| +
|
| + if (wrapped) {
|
| + return s_wrapPtrs[id];
|
| + } else {
|
| + return s_realPtrs[id];
|
| + }
|
| +}
|
| +""")
|
| +
|
| + def PnaclIdForInterfaceVersion(self, iface, version):
|
| + return 'PNACL_%s_%s' % (iface.GetName(), str(version).replace('.', '_'))
|
| +
|
| +
|
| + def InterfaceNeedsWrapper(self, iface, releases):
|
| + """ Return true if the interface has ANY methods that need wrapping. """
|
| + if self._skip_opt:
|
| + return True
|
| + for release in iface.GetUniqueReleases(releases):
|
| + version = iface.GetVersion(release)
|
| + if self.InterfaceVersionNeedsWrapping(iface, version):
|
| + return True
|
| + return False
|
| +
|
| +
|
| + def InterfaceVersionNeedsWrapping(self, iface, version):
|
| + """ Return true if the interface+version has ANY methods that
|
| + need wrapping. """
|
| + if self._skip_opt:
|
| + return True
|
| + for member in iface.GetListOf('Member'):
|
| + release = member.GetRelease(version)
|
| + if self.MemberNeedsWrapping(member, release):
|
| + return True
|
| + return False
|
| +
|
| +
|
| + def MemberNeedsWrapping(self, member, release):
|
| + """ Return true if a particular member function at a particular
|
| + release needs wrapping. """
|
| + if self._skip_opt:
|
| + return True
|
| + if not member.InReleases([release]):
|
| + return False
|
| + ret, name, array, args_spec = self.cgen.GetComponents(member,
|
| + release,
|
| + 'store')
|
| + return self.TypeNeedsWrapping(ret) or self.ArgsNeedWrapping(args_spec)
|
| +
|
| +
|
| + def ArgsNeedWrapping(self, args):
|
| + """ Return true if any parameter in the list needs wrapping. """
|
| + for arg in args:
|
| + (type_str, name, array_dims, more_args) = arg
|
| + if self.TypeNeedsWrapping(type_str):
|
| + return True
|
| + return False
|
| +
|
| +
|
| + def TypeNeedsWrapping(self, type_node):
|
| + """ Return true if a parameter type needs wrapping.
|
| + Currently, this is true for byval aggregates. """
|
| + is_aggregate = type_node.startswith('struct') or \
|
| + type_node.startswith('union')
|
| + is_reference = type_node.find('*') != -1
|
| + return is_aggregate and not is_reference
|
| +
|
| +
|
| + def DetermineInterfaces(self, ast, releases):
|
| + """ Get a list of interfaces along with whatever metadata we need. """
|
| + iface_releases = []
|
| + for filenode in ast.GetListOf('File'):
|
| + # If this file has errors, skip it
|
| + if filenode in self.skip_list:
|
| + print 'PnaclGen: Skipping %s due to errors\n' % filenode.GetName()
|
| + continue
|
| +
|
| + file_name = self.GetHeaderName(filenode.GetName())
|
| + ifaces = filenode.GetListOf('Interface')
|
| + for iface in ifaces:
|
| + releases_for_iface = iface.GetUniqueReleases(releases)
|
| + for release in releases_for_iface:
|
| + version = iface.GetVersion(release)
|
| + pnacl_id = self.PnaclIdForInterfaceVersion(iface, version)
|
| + not_latest = release != releases_for_iface[-1]
|
| + struct_name = self.cgen.GetStructName(iface, release,
|
| + include_version=not_latest)
|
| + needs_wrap = self.InterfaceVersionNeedsWrapping(iface, version)
|
| + if not needs_wrap:
|
| + print 'Interface %s does not need wrapping' % pnacl_id
|
| + iface_releases.append(
|
| + Interface(iface, release, version, pnacl_id,
|
| + struct_name, needs_wrap, file_name))
|
| + return iface_releases
|
| +
|
| +
|
| + def GenerateIncludes(self, iface_releases, out_header_filename, out):
|
| + """ Write out the includes and other headers and return a list
|
| + of the interface nodes that were ultimately included. """
|
| + self.WriteCopyrightGeneratedTime(out)
|
| + # First include own header.
|
| + out.Write('#include "%s"\n\n' % out_header_filename)
|
| +
|
| + # Include libc headers for strcmp.
|
| + out.Write('#include <string.h>\n\n')
|
| +
|
| + # Get typedefs for PPB_GetInterface.
|
| + out.Write('#include "%s"\n' % self.GetHeaderName('ppb.h'))
|
| +
|
| + # Get a conservative list of all #includes that are needed,
|
| + # whether it requires wrapping or not.
|
| + header_files = set()
|
| + for iface in iface_releases:
|
| + header_files.add(iface.header_file)
|
| + for header in sorted(header_files):
|
| + out.Write('#include "%s"\n' % header)
|
| +
|
| +
|
| + def EnumerateInterfaces(self, iface_releases, out):
|
| + # Get a conservative list of all IDs needed (even it not wrapped).
|
| + # We will need the IDs anyway to write the "real" version out.
|
| + out.Write('\nenum PnaclId {\n')
|
| + for iface in iface_releases:
|
| + out.Write(' %s,\n' % iface.pnacl_id)
|
| + out.Write(' PNACL_IFACE_COUNT\n')
|
| + out.Write('};\n\n')
|
| + out.Write('static const void *s_realPtrs[PNACL_IFACE_COUNT];\n\n');
|
| +
|
| + # Also create a function that maps interface name to ID. The ID will be
|
| + # used to look up the wrapped interface in a table
|
| + # (so really it's string -> interface, but there is an ID in the middle).
|
| + out.Write('/* Map interface string -> pnacl ID */\n')
|
| + out.Write('static enum PnaclId PnaclShimIfaceID(const char *name, ' +
|
| + 'int *wrapped) {\n')
|
| + for iface in iface_releases:
|
| + if iface.needs_wrapping:
|
| + wrap_str = '*wrapped = 1'
|
| + else:
|
| + wrap_str = '*wrapped = 0'
|
| + iface_macro = self.cgen.GetInterfaceMacro(iface.node, iface.version)
|
| + out.Write(""" if (!strcmp(name, %s)) {
|
| + %s;
|
| + return %s;
|
| + }\n""" % (iface_macro, wrap_str, iface.pnacl_id))
|
| +
|
| + out.Write(' return -1;\n}\n\n')
|
| +
|
| +
|
| + def WrapperMethodPrefix(self, iface, release):
|
| + return 'PNACL_%s_%s_' % (release, iface.GetName())
|
| +
|
| +
|
| + def GetReturnArgs(self, ret_type, args_spec):
|
| + if ret_type != 'void':
|
| + ret = 'return '
|
| + else:
|
| + ret = ''
|
| + if args_spec:
|
| + args = []
|
| + for arg in args_spec:
|
| + args.append(arg[1])
|
| + args = ', '.join(args)
|
| + else:
|
| + args = ''
|
| + return (ret, args)
|
| +
|
| +
|
| + def GenerateWrapperForPPBMethods(self, iface, out):
|
| + for member in iface.node.GetListOf('Member'):
|
| + # Skip the method if it's not actually in the release.
|
| + if not member.InReleases([iface.release]):
|
| + continue
|
| + func_prefix = self.WrapperMethodPrefix(iface.node, iface.release)
|
| + sig = self.cgen.GetSignature(member, iface.release, 'store',
|
| + func_prefix, False)
|
| + out.Write('static %s %s {\n' % (self._pnacl_attribute, sig))
|
| + out.Write(' const struct %s *iface = s_realPtrs[%s];\n' % (
|
| + iface.struct_name, iface.pnacl_id))
|
| + ret, name, array, cspec = self.cgen.GetComponents(member,
|
| + iface.release,
|
| + 'store')
|
| + ret_str, args_str = self.GetReturnArgs(ret, cspec)
|
| + out.Write(' %siface->%s(%s);\n}\n\n' % (ret_str,
|
| + member.GetName(), args_str))
|
| +
|
| +
|
| + def GenerateWrapperForPPPMethods(self, iface, out):
|
| + for member in iface.node.GetListOf('Member'):
|
| + # Skip the method if it's not actually in the release.
|
| + if not member.InReleases([iface.release]):
|
| + continue
|
| + func_prefix = self.WrapperMethodPrefix(iface.node, iface.release)
|
| + sig = self.cgen.GetSignature(member, iface.release, 'store',
|
| + func_prefix, False)
|
| + out.Write('static %s {\n' % sig)
|
| + out.Write(' const struct %s *iface = s_realPtrs[%s];\n' % (
|
| + iface.struct_name, iface.pnacl_id))
|
| + temp_fp = self.cgen.GetSignature(member, iface.release, 'return',
|
| + 'temp_fp',
|
| + func_as_ptr=True,
|
| + ptr_prefix=self._pnacl_attribute,
|
| + include_name=False)
|
| + cast = self.cgen.GetSignature(member, iface.release, 'return',
|
| + prefix='',
|
| + func_as_ptr=True,
|
| + ptr_prefix=self._pnacl_attribute,
|
| + include_name=False)
|
| + out.Write(' %s = ((%s)iface->%s);\n' % (temp_fp,
|
| + cast,
|
| + member.GetName()))
|
| + ret, name, array, cspec = self.cgen.GetComponents(member,
|
| + iface.release,
|
| + 'store')
|
| + ret_str, args_str = self.GetReturnArgs(ret, cspec)
|
| + out.Write(' %stemp_fp(%s);\n}\n\n' % (ret_str,
|
| + args_str))
|
| +
|
| +
|
| + def GenerateWrapperForMethods(self, iface_releases, releases, out):
|
| + for iface in iface_releases:
|
| + if not iface.needs_wrapping:
|
| + out.Write('/* Not generating wrappers for %s */\n' % iface.struct_name)
|
| + continue
|
| + generator = PPKind.ChoosePPFunc(iface.node,
|
| + self.GenerateWrapperForPPBMethods,
|
| + self.GenerateWrapperForPPPMethods)
|
| + generator(iface, out)
|
| +
|
| +
|
| + def GenerateWrapperInterfaces(self, iface_releases, releases, out):
|
| + for iface in iface_releases:
|
| + if not iface.needs_wrapping:
|
| + out.Write('/* Not generating wrappers for %s */\n' % iface.struct_name)
|
| + continue
|
| +
|
| + out.Write('struct %s PNACL_Wrappers_%s = {\n' % (iface.struct_name,
|
| + iface.struct_name))
|
| + methods = []
|
| + for member in iface.node.GetListOf('Member'):
|
| + # Skip the method if it's not actually in the release.
|
| + if not member.InReleases([iface.release]):
|
| + continue
|
| + prefix = self.WrapperMethodPrefix(iface.node, iface.release)
|
| + cast = self.cgen.GetSignature(member, iface.release, 'return',
|
| + prefix='',
|
| + func_as_ptr=True,
|
| + ptr_prefix='',
|
| + include_name=False)
|
| + methods.append(' .%s = (%s)&%s%s' % (member.GetName(),
|
| + cast,
|
| + prefix,
|
| + member.GetName()))
|
| + out.Write(' ' + ',\n '.join(methods) + '\n')
|
| + out.Write('};\n\n')
|
| +
|
| + # Write out a collection of the pnacl wrapper functions.
|
| + out.Write('static const void *s_wrapPtrs[PNACL_IFACE_COUNT + 1] = {\n')
|
| + for iface in iface_releases:
|
| + if not iface.needs_wrapping:
|
| + out.Write('/* Not generating wrappers for %s */\n' % iface.struct_name)
|
| + continue
|
| + out.Write(' (void *) &PNACL_Wrappers_%s,\n' % iface.struct_name)
|
| + out.Write(' NULL\n};\n\n')
|
| +
|
| +
|
| + def GenerateRange(self, ast, releases, options):
|
| + """ Generate shim code for a range of releases. """
|
| +
|
| + self._skip_opt = GetOption('disable_pnacl_opt')
|
| +
|
| + out_filename = GetOption('pnaclshim')
|
| + out_header_filename = os.path.splitext(out_filename)[0] + '.h'
|
| + print "Generating %s and %s" % (out_filename, out_header_filename)
|
| +
|
| + out_header = IDLOutFile(out_header_filename)
|
| + self.GenerateHeaderFile(out_header)
|
| + out_header.Close()
|
| +
|
| + out = IDLOutFile(out_filename)
|
| +
|
| + # Get a list of all the interfaces along with metadata.
|
| + iface_releases = self.DetermineInterfaces(ast, releases)
|
| +
|
| + # Generate the includes.
|
| + self.GenerateIncludes(iface_releases, out_header_filename, out)
|
| +
|
| + # Assign each interface an ID number which is listed in an ENUM.
|
| + self.EnumerateInterfaces(iface_releases, out)
|
| +
|
| + # Generate wrapper functions for each wrapped method in the interfaces.
|
| + self.GenerateWrapperForMethods(iface_releases, releases, out)
|
| +
|
| + # Collect all the wrapper functions into interface structs. Then generate
|
| + # a table of the wrapped interface structs that can be looked up.
|
| + self.GenerateWrapperInterfaces(iface_releases, releases, out)
|
| +
|
| + # Write out the IDL-invariant functions.
|
| + self.GenerateFixedFunctions(out)
|
| + out.Close()
|
| + return 0
|
| +
|
| +
|
| +pnaclgen = PnaclGen()
|
|
|