Chromium Code Reviews| 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..fbdd8469c939b90dd3a1d189e2659896775e1368 |
| --- /dev/null |
| +++ b/ppapi/generators/idl_gen_pnacl.py |
| @@ -0,0 +1,422 @@ |
| +#!/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('pnaclglue', 'Name of the pnacl glue file.', |
|
sehr (please use chromium)
2011/11/16 00:44:01
We should use either 'glue' or 'shim' consistently
jvoung - send to chromium...
2011/11/16 01:45:22
Changed to shim.
|
| + default=os.path.join('..', |
| + 'native_client', |
| + 'src', |
| + 'shared', |
| + 'ppapi_proxy', |
| + 'pnacl_glue.c')) |
| + |
| +# TODO(jvoung): this doesn't do anything yet (except change some dbg messages). |
| +Option('disable_pnacl_opt', 'Turn off optimization of pnacl glue.') |
| + |
| + |
| +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 InterfaceVersion(object): |
| + """ Tracks information about a particular interface version, including |
| + the ID assigned to it in the PNaCl glue namespace. """ |
| + def __init__(self, interface_node, release, version, pnacl_id): |
| + self.node = interface_node |
| + self.release = release |
| + self.version = version |
| + self.pnacl_id = pnacl_id |
| + |
| + |
| +class PnaclGen(Generator): |
| + """PnaclGen - A subclass of Generator which takes the IDL sources and |
| + generates glue code for bridging the calling conventions between GCC |
| + and PNaCl (LLVM). |
| + """ |
| + |
| + def __init__(self): |
| + Generator.__init__(self, |
| + 'Pnacl Glue Gen', |
| + 'pnacl', |
| + 'Generate the PNaCl glue.') |
| + 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 GenerateHeaderFile(self, out): |
| + """ Writes out an interface header file for the exported symbols. """ |
| + out.Write(""" |
| +/* 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. |
| + */ |
| + |
| +/* Last generated: %(timestamp)s. */ |
| + |
| +#ifndef %(include_guard)s |
| +#define %(include_guard)s |
| + |
| +typedef void *(*get_interface_fp)(const char *interface_name); |
| + |
| +get_interface_fp real_PPBGetInterface; |
| +get_interface_fp real_PPPGetInterface; |
| + |
| +void set_real_PPBGetInterface(get_interface_fp real); |
| +void set_real_PPPGetInterface(get_interface_fp real); |
| + |
| +void *Pnacl_PPBGetInterface(const char *name); |
| +void *Pnacl_PPPGetInterface(const char *name); |
| + |
| +#endif /* %(include_guard)s */ |
| +""" % { |
| + "include_guard" : "PNACL_GLUE_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(get_interface_fp real) { |
| + real_PPBGetInterface = real; |
| +} |
| + |
| +void set_real_PPPGetInterface(get_interface_fp real) { |
| + real_PPPGetInterface = real; |
| +} |
| + |
| +void *Pnacl_PPBGetInterface(const char *name) { |
| + int id = PnaclGlueIfaceID(name); |
| + if (id < 0) return NULL; |
| + |
| + if (s_realPtrs[id] == NULL) { |
| + void *iface = (*real_PPBGetInterface)(name); |
| + if (NULL == iface) return NULL; |
| + s_realPtrs[id] = iface; |
| + } |
| + return s_wrapPtrs[id]; |
| +} |
| + |
| +void *Pnacl_PPPGetInterface(const char *name) { |
| + int id = PnaclGlueIfaceID(name); |
| + if (id < 0) return NULL; |
| + |
| + if (s_realPtrs[id] == NULL) { |
| + void *iface = (*real_PPPGetInterface)(name); |
| + if (NULL == iface) return NULL; |
| + s_realPtrs[id] = iface; |
| + } |
| + return s_wrapPtrs[id]; |
| +} |
| +""") |
| + |
| + 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 |
| + 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: |
| + if self.TypeNeedsWrapping(arg[0]): |
| + return True |
| + return False |
| + |
| + |
| + def TypeNeedsWrapping(self, type_node): |
| + """ Return true if a parameter type needs wrapping. |
| + Currently, this is true for byval aggregates. """ |
| + return ((type_node.startswith('struct') or type_node.startswith('union')) |
| + and not (type_node.find('*') != -1)) |
| + |
| + |
| + def GenerateIncludes(self, ast, releases, options, out): |
| + """ Write out the includes and other headers and return a list |
| + of the interface nodes that were ultimately included. """ |
| + out.Write('#include "pnacl_glue.h"\n\n') |
| + |
| + # Get a conservative list of all the interfaces, as we are generating |
| + # #includes for each. |
| + # TODO(jvoung): Filter out interfaces that don't need wrapping. |
| + # TODO(jvoung): Separate PPB and PPP interfaces to slightly shorten the |
| + # big strcmp function. |
| + wrapped_iface_list = [] |
| + skipped_iface_list = [] |
| + for filenode in ast.GetListOf('File'): |
| + # If this file has errors, skip it |
| + if filenode in self.skip_list: continue |
| + |
| + name = self.GetHeaderName(filenode.GetName()) |
| + ifaces = filenode.GetListOf('Interface') |
| + for iface in ifaces: |
| + if not self.InterfaceNeedsWrapper(iface, releases): |
| + print 'Interface %s does not need wrapping!' % iface.GetName() |
| + skipped_iface_list.append(iface) |
| + else: |
| + print 'Interface %s does need wrapping!' % iface.GetName() |
| + |
| + # TODO(jvoung): Don't #include if we didn't wrap any of the |
| + # ifaces in the list / header file |
| + if ifaces: |
| + out.Write('#include "%s"\n' % name) |
| + wrapped_iface_list.extend(ifaces) |
| + return wrapped_iface_list, skipped_iface_list |
| + |
| + |
| + def PnaclIdForInterfaceVersion(self, iface, version): |
| + return 'PNACL_%s_%s' % (iface.GetName(), str(version).replace('.', '_')) |
| + |
| + |
| + def EnumerateInterfaces(self, wrapped_iface_list, releases, out): |
| + iface_releases = [] |
| + out.Write('\nenum PnaclId {\n') |
| + for iface in wrapped_iface_list: |
| + for release in iface.GetUniqueReleases(releases): |
| + version = iface.GetVersion(release) |
| + pnacl_id = self.PnaclIdForInterfaceVersion(iface, version) |
| + iface_releases.append( |
| + InterfaceVersion(iface, release, version, pnacl_id)) |
| + out.Write(' %s,\n' % pnacl_id) |
| + out.Write(' PNACL_IFACE_COUNT\n') |
| + out.Write('};\n\n') |
| + out.Write('static 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 PnaclGlueIfaceID(const char *name) {\n') |
| + for iface in iface_releases: |
| + out.Write(' if (!strcmp(name, %s)) return %s;\n' % |
| + (self.cgen.GetInterfaceMacro( |
| + iface.node, iface.version), iface.pnacl_id)) |
| + out.Write(' return -1;\n}\n\n') |
| + return iface_releases |
| + |
| + |
| + 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, is_latest_release, out): |
| + struct_name = self.cgen.GetStructName(iface.node, |
| + iface.release, |
| + is_latest_release=is_latest_release) |
| + 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(' struct %s *iface = s_realPtrs[%s];\n' % ( |
| + 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, is_latest_release, out): |
| + struct_name = self.cgen.GetStructName(iface.node, |
| + iface.release, |
| + is_latest_release=is_latest_release) |
| + 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(' struct %s *iface = s_realPtrs[%s];\n' % ( |
| + struct_name, iface.pnacl_id)) |
| + temp_fp = self.cgen.GetSignature(member, iface.release, 'return', |
| + 'temp_fp', |
| + func_attributes=self._pnacl_attribute, |
| + func_as_ptr=True) |
| + cast = self.cgen.GetSignature(member, iface.release, 'return', |
| + prefix='', |
| + func_attributes=self._pnacl_attribute, |
| + func_as_ptr=True, |
| + is_cast=True) |
| + 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(%s);\n}\n\n' % (ret_str, |
| + member.GetName(), |
| + args_str)) |
| + |
| + |
| + def GenerateWrapperForMethods(self, iface_releases, releases, out): |
| + for iface in iface_releases: |
| + is_latest_release = (iface.release == |
| + iface.node.GetUniqueReleases(releases)[-1]) |
| + generator = PPKind.ChoosePPFunc(iface.node, |
| + self.GenerateWrapperForPPBMethods, |
| + self.GenerateWrapperForPPPMethods) |
| + generator(iface, is_latest_release, out) |
| + |
| + |
| + def GenerateWrapperInterfaces(self, iface_releases, releases, out): |
| + for iface in iface_releases: |
| + is_latest_release = (iface.release == |
| + iface.node.GetUniqueReleases(releases)[-1]) |
| + struct_name = self.cgen.GetStructName(iface.node, |
| + iface.release, |
| + is_latest_release=is_latest_release) |
| + out.Write('struct %s PNACL_Wrappers_%s = {\n' % (struct_name, |
| + 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_attributes='', |
| + func_as_ptr=True, |
| + is_cast=True) |
| + 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 void *s_wrapPtrs[PNACL_IFACE_COUNT + 1] = {\n') |
| + for iface in iface_releases: |
| + is_latest_release = (iface.release == |
| + iface.node.GetUniqueReleases(releases)[-1]) |
| + struct_name = self.cgen.GetStructName(iface.node, |
| + iface.release, |
| + is_latest_release=is_latest_release) |
| + out.Write(' (void *) &PNACL_Wrappers_%s,\n' % struct_name) |
| + out.Write(' NULL\n};\n\n') |
| + |
| + |
| + def GenerateRange(self, ast, releases, options): |
| + """ Generate glue code for a range of releases. """ |
| + |
| + self._skip_opt = GetOption('disable_pnacl_opt') |
| + |
| + out_filename = GetOption('pnaclglue') |
| + 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) |
| + |
| + # Generate the includes. |
| + wrapped_iface_list, skipped_iface_list = \ |
| + self.GenerateIncludes(ast, releases, options, out) |
| + |
| + # Generate a list of interfaces and assign each an ID number which |
| + # is listed in an ENUM. |
| + iface_releases = self.EnumerateInterfaces(wrapped_iface_list, 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() |