Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(14)

Unified Diff: ppapi/generators/idl_gen_pnacl.py

Issue 8568025: Pnacl ppapi shim generator (from IDL), based on Noel's first cut. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Add code to skip wrapping Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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()

Powered by Google App Engine
This is Rietveld 408576698