diff options
Diffstat (limited to 'ppapi/generators/idl_gen_pnacl.py')
-rw-r--r-- | ppapi/generators/idl_gen_pnacl.py | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/ppapi/generators/idl_gen_pnacl.py b/ppapi/generators/idl_gen_pnacl.py new file mode 100644 index 0000000..8116ae8 --- /dev/null +++ b/ppapi/generators/idl_gen_pnacl.py @@ -0,0 +1,236 @@ +#!/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 bridges the calling conventions +between GCC and PNaCl. """ + +from datetime import datetime +import difflib +import glob +import os +import sys + +from idl_c_proto import CGen +from idl_gen_wrapper import Interface, WrapperGen +from idl_log import ErrOut, InfoOut, WarnOut +from idl_option import GetOption, Option, ParseOptions +from idl_parser import ParseFiles + +Option('pnaclshim', 'Name of the pnacl shim file.', + default='temp_pnacl_shim.c') + +Option('disable_pnacl_opt', 'Turn off optimization of pnacl shim.') + + +class PnaclGen(WrapperGen): + """PnaclGen generates shim code to bridge the Gcc ABI with PNaCl. + + This subclass of WrapperGenerator takes the IDL sources and + generates shim methods for bridging the calling conventions between GCC + and PNaCl (LLVM). Some of the PPAPI methods do not need shimming, so + this will also detect those situations and provide direct access to the + original PPAPI methods (rather than the shim methods). + """ + + def __init__(self): + WrapperGen.__init__(self, + 'Pnacl', + 'Pnacl Shim Gen', + 'pnacl', + 'Generate the PNaCl shim.') + self.cgen = CGen() + self._skip_opt = False + self._pnacl_attribute = '__attribute__((pnaclcall))' + + ############################################################ + + def OwnHeaderFile(self): + """Return the header file that specifies the API of this wrapper. + We do not generate the header files. """ + return 'ppapi/generators/pnacl_shim.h' + + 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, array_dims): + return True + return False + + + def TypeNeedsWrapping(self, type_node, array_dims): + """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 or array_dims != []) + return is_aggregate and not is_reference + + ############################################################ + + + def GenerateWrapperForPPBMethod(self, iface, member): + result = [] + func_prefix = self.WrapperMethodPrefix(iface.node, iface.release) + sig = self.cgen.GetSignature(member, iface.release, 'store', + func_prefix, False) + result.append('static %s\n%s {\n' % (self._pnacl_attribute, sig)) + result.append(' const struct %s *iface = %s.real_iface;\n' % + (iface.struct_name, self.GetWrapperInfoName(iface))) + ret, name, array, cspec = self.cgen.GetComponents(member, + iface.release, + 'store') + ret_str, args_str = self.GetReturnArgs(ret, cspec) + result.append(' %siface->%s(%s);\n}\n\n' % (ret_str, + member.GetName(), args_str)) + return result + + + def GenerateWrapperForPPPMethod(self, iface, member): + result = [] + func_prefix = self.WrapperMethodPrefix(iface.node, iface.release) + sig = self.cgen.GetSignature(member, iface.release, 'store', + func_prefix, False) + result.append('static %s {\n' % sig) + result.append(' const struct %s *iface = %s.real_iface;\n' % + (iface.struct_name, self.GetWrapperInfoName(iface))) + 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) + result.append(' %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) + result.append(' %stemp_fp(%s);\n}\n\n' % (ret_str, args_str)) + return result + + + def GenerateRange(self, ast, releases, options): + """Generate shim code for a range of releases. + """ + self._skip_opt = GetOption('disable_pnacl_opt') + self.SetOutputFile(GetOption('pnaclshim')) + return WrapperGen.GenerateRange(self, ast, releases, options) + +pnaclgen = PnaclGen() + +###################################################################### +# Tests. + +# Clean a string representing an object definition and return then string +# as a single space delimited set of tokens. +def CleanString(instr): + instr = instr.strip() + instr = instr.split() + return ' '.join(instr) + + +def PrintErrorDiff(old, new): + oldlines = old.split(';') + newlines = new.split(';') + d = difflib.Differ() + diff = d.compare(oldlines, newlines) + ErrOut.Log('Diff is:\n%s' % '\n'.join(diff)) + + +def GetOldTestOutput(ast): + # Scan the top-level comments in the IDL file for comparison. + old = [] + for filenode in ast.GetListOf('File'): + for node in filenode.GetChildren(): + instr = node.GetOneOf('Comment') + if not instr: continue + instr.Dump() + old.append(instr.GetName()) + return CleanString(''.join(old)) + + +def TestFiles(filenames, test_releases): + ast = ParseFiles(filenames) + iface_releases = pnaclgen.DetermineInterfaces(ast, test_releases) + new_output = CleanString(pnaclgen.GenerateWrapperForMethods( + iface_releases, comments=False)) + old_output = GetOldTestOutput(ast) + if new_output != old_output: + PrintErrorDiff(old_output, new_output) + ErrOut.Log('Failed pnacl generator test.') + return 1 + else: + InfoOut.Log('Passed pnacl generator test.') + return 0 + + +def Main(args): + filenames = ParseOptions(args) + test_releases = ['M13', 'M14', 'M15'] + if not filenames: + idldir = os.path.split(sys.argv[0])[0] + idldir = os.path.join(idldir, 'test_gen_pnacl', '*.idl') + filenames = glob.glob(idldir) + filenames = sorted(filenames) + if GetOption('test'): + # Run the tests. + return TestFiles(filenames, test_releases) + + # Otherwise, generate the output file (for potential use as golden file). + ast = ParseFiles(filenames) + return pnaclgen.GenerateRange(ast, test_releases, filenames) + + +if __name__ == '__main__': + retval = Main(sys.argv[1:]) + sys.exit(retval) |