diff options
author | jvoung@google.com <jvoung@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-30 17:59:14 +0000 |
---|---|---|
committer | jvoung@google.com <jvoung@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-30 17:59:14 +0000 |
commit | 2978339a797536e720a18ebdde2fd7f1b4e12c34 (patch) | |
tree | 0cdc35c2663cd0e6c1715fbb184fe3e623b0e98b /ppapi | |
parent | 8dc2405798db63428fa62d2affb1cf640783c388 (diff) | |
download | chromium_src-2978339a797536e720a18ebdde2fd7f1b4e12c34.zip chromium_src-2978339a797536e720a18ebdde2fd7f1b4e12c34.tar.gz chromium_src-2978339a797536e720a18ebdde2fd7f1b4e12c34.tar.bz2 |
Pnacl ppapi shim generator (from IDL), based on Noel's first cut.
BUG= http://code.google.com/p/nativeclient/issues/detail?id=2413
TEST= python idl_gen_pnacl.py --test --wnone
Also
** ./generator.py doesn't change
** ./generator.py --wnone --pnacl --pnaclshim=pnacl_shim.c
generates a shim that compiles and works in the NaCl repo.
Review URL: http://codereview.chromium.org/8568025
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@112244 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ppapi')
-rw-r--r-- | ppapi/PRESUBMIT.py | 48 | ||||
-rwxr-xr-x | ppapi/generators/generator.py | 8 | ||||
-rw-r--r-- | ppapi/generators/idl_c_proto.py | 53 | ||||
-rw-r--r-- | ppapi/generators/idl_gen_pnacl.py | 236 | ||||
-rw-r--r-- | ppapi/generators/idl_gen_wrapper.py | 456 | ||||
-rw-r--r-- | ppapi/generators/idl_generator.py | 1 | ||||
-rw-r--r-- | ppapi/generators/pnacl_shim.h | 29 | ||||
-rw-r--r-- | ppapi/generators/test_gen_pnacl/test_interfaces.idl | 150 |
8 files changed, 950 insertions, 31 deletions
diff --git a/ppapi/PRESUBMIT.py b/ppapi/PRESUBMIT.py index 90b809c..227cc41 100644 --- a/ppapi/PRESUBMIT.py +++ b/ppapi/PRESUBMIT.py @@ -6,9 +6,43 @@ import os import sys import subprocess +def RunCmdAndCheck(cmd, ppapi_dir, err_string, output_api): + results = [] + p = subprocess.Popen(cmd, cwd=os.path.join(ppapi_dir, 'generators'), + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + (p_stdout, p_stderr) = p.communicate() + if p.returncode: + results.append( + output_api.PresubmitError(err_string, + long_text=p_stderr)) + return results + + +def RunUnittests(input_api, output_api): + # Run some Generator unittests if the generator source was changed. + results = [] + files = input_api.LocalPaths() + generator_files = [] + for filename in files: + name_parts = filename.split(os.sep) + if name_parts[0:2] == ['ppapi', 'generators']: + generator_files.append(filename) + if generator_files != []: + cmd = [ sys.executable, 'idl_gen_pnacl.py', '--wnone', '--test'] + ppapi_dir = input_api.PresubmitLocalPath() + results.extend(RunCmdAndCheck(cmd, + ppapi_dir, + 'PPAPI IDL Pnacl unittest failed.', + output_api)) + return results + + def CheckChange(input_api, output_api): results = [] + results.extend(RunUnittests(input_api, output_api)) + # Verify all modified *.idl have a matching *.h files = input_api.LocalPaths() h_files = [] @@ -51,15 +85,10 @@ def CheckChange(input_api, output_api): # Only generate output for IDL files references (as *.h or *.idl) in this CL cmd.append('--out=' + ','.join([name + '.idl' for name in both])) - - p = subprocess.Popen(cmd, cwd=os.path.join(ppapi_dir, 'generators'), - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - (p_stdout, p_stderr) = p.communicate() - if p.returncode: - results.append( - output_api.PresubmitError('PPAPI IDL Diff detected: Run the generator.', - long_text=p_stderr)) + results.extend(RunCmdAndCheck(cmd, + ppapi_dir, + 'PPAPI IDL Diff detected: Run the generator.', + output_api)) return results def CheckChangeOnUpload(input_api, output_api): @@ -69,4 +98,3 @@ def CheckChangeOnUpload(input_api, output_api): def CheckChangeOnCommit(input_api, output_api): # return [] return CheckChange(input_api, output_api) - diff --git a/ppapi/generators/generator.py b/ppapi/generators/generator.py index a6065cd..3bb4ff5 100755 --- a/ppapi/generators/generator.py +++ b/ppapi/generators/generator.py @@ -6,14 +6,13 @@ import sys -from idl_ast import IDLAst +# Note: some of these files are imported to register cmdline options. from idl_generator import Generator -from idl_log import ErrOut, InfoOut, WarnOut -from idl_node import IDLAttribute, IDLNode -from idl_option import GetOption, Option, ParseOptions +from idl_option import ParseOptions from idl_outfile import IDLOutFile from idl_parser import ParseFiles from idl_c_header import HGen +from idl_gen_pnacl import PnaclGen def Main(args): @@ -29,4 +28,3 @@ if __name__ == '__main__': if not args: args = ['--wnone', '--cgen', '--range=start,end'] sys.exit(Main(args)) - diff --git a/ppapi/generators/idl_c_proto.py b/ppapi/generators/idl_c_proto.py index 584cfaa..861f86f 100644 --- a/ppapi/generators/idl_c_proto.py +++ b/ppapi/generators/idl_c_proto.py @@ -342,7 +342,7 @@ class CGen(object): # rtype - The store or return type of the object. # name - The name of the object. # arrays - A list of array dimensions as [] or [<fixed_num>]. - # args - None of not a function, otherwise a list of parameters. + # args - None if not a function, otherwise a list of parameters. # def GetComponents(self, node, release, mode): self.LogEnter('GetComponents mode %s for %s %s' % (mode, node, release)) @@ -375,17 +375,23 @@ class CGen(object): return (rtype, name, arrayspec, callspec) - def Compose(self, rtype, name, arrayspec, callspec, prefix, func_as_ptr): + def Compose(self, rtype, name, arrayspec, callspec, prefix, func_as_ptr, + ptr_prefix, include_name): self.LogEnter('Compose: %s %s' % (rtype, name)) arrayspec = ''.join(arrayspec) - name = '%s%s%s' % (prefix, name, arrayspec) + if not include_name: + name = prefix + arrayspec + else: + name = prefix + name + arrayspec if callspec is None: out = '%s %s' % (rtype, name) else: params = [] for ptype, pname, parray, pspec in callspec: - params.append(self.Compose(ptype, pname, parray, pspec, '', True)) - if func_as_ptr: name = '(*%s)' % name + params.append(self.Compose(ptype, pname, parray, pspec, '', True, + ptr_prefix='', include_name=True)) + if func_as_ptr: + name = '(%s*%s)' % (ptr_prefix, name) out = '%s %s(%s)' % (rtype, name, ', '.join(params)) self.LogExit('Exit Compose: %s' % out) return out @@ -396,11 +402,18 @@ class CGen(object): # Returns the 'C' style signature of the object # prefix - A prefix for the object's name # func_as_ptr - Formats a function as a function pointer + # ptr_prefix - A prefix that goes before the "*" for a function pointer + # include_name - If true, include member name in the signature. + # If false, leave it out. In any case, prefix and ptr_prefix + # are always included. # - def GetSignature(self, node, release, mode, prefix='', func_as_ptr=True): - self.LogEnter('GetSignature %s %s as func=%s' % (node, mode, func_as_ptr)) + def GetSignature(self, node, release, mode, prefix='', func_as_ptr=True, + ptr_prefix='', include_name=True): + self.LogEnter('GetSignature %s %s as func=%s' % + (node, mode, func_as_ptr)) rtype, name, arrayspec, callspec = self.GetComponents(node, release, mode) - out = self.Compose(rtype, name, arrayspec, callspec, prefix, func_as_ptr) + out = self.Compose(rtype, name, arrayspec, callspec, prefix, + func_as_ptr, ptr_prefix, include_name) self.LogExit('Exit GetSignature: %s' % out) return out @@ -451,12 +464,22 @@ class CGen(object): self.LogExit('Exit DefineMember') return out - def DefineStructInternals(self, node, release, suffix='', comment=True): + def GetStructName(self, node, release, include_version=False): + suffix = '' + if include_version: + ver_num = node.GetVersion(release) + suffix = ('_%s' % ver_num).replace('.', '_') + return node.GetName() + suffix + + def DefineStructInternals(self, node, release, + include_version=False, comment=True): out = '' if node.GetProperty('union'): - out += 'union %s%s {\n' % (node.GetName(), suffix) + out += 'union %s {\n' % ( + self.GetStructName(node, release, include_version)) else: - out += 'struct %s%s {\n' % (node.GetName(), suffix) + out += 'struct %s {\n' % ( + self.GetStructName(node, release, include_version)) # Generate Member Functions members = [] @@ -476,13 +499,13 @@ class CGen(object): build_list = node.GetUniqueReleases(releases) # Build the most recent one with comments - out = self.DefineStructInternals(node, build_list[-1], comment=True) + out = self.DefineStructInternals(node, build_list[-1], + include_version=False, comment=True) # Build the rest without comments and with the version number appended for rel in build_list[0:-1]: - ver_num = node.GetVersion(rel) - ver = ("_%s" % ver_num).replace('.', '_') - out += '\n' + self.DefineStructInternals(node, rel, suffix=ver, + out += '\n' + self.DefineStructInternals(node, rel, + include_version=True, comment=False) self.LogExit('Exit DefineStruct') 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) diff --git a/ppapi/generators/idl_gen_wrapper.py b/ppapi/generators/idl_gen_wrapper.py new file mode 100644 index 0000000..0f3cec8 --- /dev/null +++ b/ppapi/generators/idl_gen_wrapper.py @@ -0,0 +1,456 @@ +#!/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. + +"""Base class for generating wrapper functions for PPAPI methods. +""" + +from datetime import datetime +import os +import sys + +from idl_c_proto import CGen +from idl_generator import Generator +from idl_log import ErrOut, InfoOut, WarnOut +from idl_outfile import IDLOutFile + + +class PPKind(object): + @staticmethod + def ChoosePPFunc(iface, ppb_func, ppp_func): + name = iface.node.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. + + - struct_name: the struct type used by the ppapi headers to hold the + method pointers (the vtable). + - needs_wrapping: True if a method in the interface needs wrapping. + - header_file: the name of the header file that defined this interface. + """ + def __init__(self, interface_node, release, version, + struct_name, needs_wrapping, header_file): + self.node = interface_node + self.release = release + self.version = version + 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 WrapperGen(Generator): + """WrapperGen - An abstract class that generates wrappers for PPAPI methods. + + This generates a wrapper PPB and PPP GetInterface, which directs users + to wrapper PPAPI methods. Wrapper PPAPI methods may perform arbitrary + work before invoking the real PPAPI method (supplied by the original + GetInterface functions). + + Subclasses must implement GenerateWrapperForPPBMethod (and PPP). + Optionally, subclasses can implement InterfaceNeedsWrapper to + filter out interfaces that do not actually need wrappers (those + interfaces can jump directly to the original interface functions). + """ + + def __init__(self, wrapper_prefix, s1, s2, s3): + Generator.__init__(self, s1, s2, s3) + self.wrapper_prefix = wrapper_prefix + self._skip_opt = False + self.output_file = None + self.cgen = CGen() + + def SetOutputFile(self, fname): + self.output_file = fname + + + 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 GetWrapperMetadataName(self): + return '__%sWrapperInfo' % self.wrapper_prefix + + + def GenerateHelperFunctions(self, out): + """Generate helper functions to avoid dependencies on libc. + """ + out.Write("""/* Use local strcmp to avoid dependency on libc. */ +static int mystrcmp(const char* s1, const char *s2) { + while((*s1 && *s2) && (*s1++ == *s2++)); + return *(--s1) - *(--s2); +}\n +""") + + + def GenerateFixedFunctions(self, out): + """Write out the set of constant functions (those that do not depend on + the current Pepper IDL). + """ + out.Write(""" + +static PPB_GetInterface __real_PPBGetInterface; +static PPP_GetInterface_Type __real_PPPGetInterface; + +void __set_real_%(wrapper_prefix)s_PPBGetInterface(PPB_GetInterface real) { + __real_PPBGetInterface = real; +} + +void __set_real_%(wrapper_prefix)s_PPPGetInterface(PPP_GetInterface_Type real) { + __real_PPPGetInterface = real; +} + +/* Map interface string -> wrapper metadata */ +static struct %(wrapper_struct)s *%(wrapper_prefix)sPPBShimIface( + const char *name) { + struct %(wrapper_struct)s **next = s_ppb_wrappers; + while (*next != NULL) { + if (mystrcmp(name, (*next)->iface_macro) == 0) return *next; + ++next; + } + return NULL; +} + +/* Map interface string -> wrapper metadata */ +static struct %(wrapper_struct)s *%(wrapper_prefix)sPPPShimIface( + const char *name) { + struct %(wrapper_struct)s **next = s_ppp_wrappers; + while (*next != NULL) { + if (mystrcmp(name, (*next)->iface_macro) == 0) return *next; + ++next; + } + return NULL; +} + +const void *__%(wrapper_prefix)s_PPBGetInterface(const char *name) { + struct %(wrapper_struct)s *wrapper = %(wrapper_prefix)sPPBShimIface(name); + if (wrapper == NULL) { + /* We don't have an IDL for this, for some reason. Take our chances. */ + return (*__real_PPBGetInterface)(name); + } + + /* Initialize the real_iface if it hasn't been. The wrapper depends on it. */ + if (wrapper->real_iface == NULL) { + const void *iface = (*__real_PPBGetInterface)(name); + if (NULL == iface) return NULL; + wrapper->real_iface = iface; + } + + if (wrapper->wrapped_iface) { + return wrapper->wrapped_iface; + } else { + return wrapper->real_iface; + } +} + +const void *__%(wrapper_prefix)s_PPPGetInterface(const char *name) { + struct %(wrapper_struct)s *wrapper = %(wrapper_prefix)sPPPShimIface(name); + if (wrapper == NULL) { + /* We don't have an IDL for this, for some reason. Take our chances. */ + return (*__real_PPPGetInterface)(name); + } + + /* Initialize the real_iface if it hasn't been. The wrapper depends on it. */ + if (wrapper->real_iface == NULL) { + const void *iface = (*__real_PPPGetInterface)(name); + if (NULL == iface) return NULL; + wrapper->real_iface = iface; + } + + if (wrapper->wrapped_iface) { + return wrapper->wrapped_iface; + } else { + return wrapper->real_iface; + } +} +""" % { 'wrapper_struct' : self.GetWrapperMetadataName(), + 'wrapper_prefix' : self.wrapper_prefix, + } ) + + + ############################################################ + + def InterfaceNeedsWrapper(self, iface, releases): + """Return true if the interface has ANY methods that need wrapping. + """ + return True + + + def OwnHeaderFile(self): + """Return the header file that specifies the API of this wrapper. + We do not generate the header files. """ + raise Exception('Child class must implement this') + + + ############################################################ + + 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: + InfoOut.Log('WrapperGen: 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) + 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: + InfoOut.Log('Interface %s ver %s does not need wrapping' % + (struct_name, version)) + iface_releases.append( + Interface(iface, release, version, + struct_name, needs_wrap, file_name)) + return iface_releases + + + def GenerateIncludes(self, iface_releases, out): + """Generate the list of #include that define the original interfaces. + """ + self.WriteCopyrightGeneratedTime(out) + # First include own header. + out.Write('#include "%s"\n\n' % self.OwnHeaderFile()) + + # 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. We currently depend on the macro + # string for comparison, even when it is not wrapped, to decide when + # to use the original/real interface. + 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) + out.Write('\n') + + + def WrapperMethodPrefix(self, iface, release): + return '%s_%s_%s_' % (self.wrapper_prefix, 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 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' % sig) + result.append(' while(1) { /* Not implemented */ } \n') + result.append('}\n') + 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(' while(1) { /* Not implemented */ } \n') + result.append('}\n') + return result + + + def GenerateWrapperForMethods(self, iface_releases, comments=True): + """Return a string representing the code for each wrapper method + (using a string rather than writing to the file directly for testing.) + """ + result = [] + for iface in iface_releases: + if not iface.needs_wrapping: + if comments: + result.append('/* Not generating wrapper methods for %s */\n\n' % + iface.struct_name) + continue + if comments: + result.append('/* Begin wrapper methods for %s */\n\n' % + iface.struct_name) + generator = PPKind.ChoosePPFunc(iface, + self.GenerateWrapperForPPBMethod, + self.GenerateWrapperForPPPMethod) + 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 + result.extend(generator(iface, member)) + if comments: + result.append('/* End wrapper methods for %s */\n\n' % + iface.struct_name) + return ''.join(result) + + + def GenerateWrapperInterfaces(self, iface_releases, out): + for iface in iface_releases: + if not iface.needs_wrapping: + out.Write('/* Not generating wrapper interface for %s */\n\n' % + iface.struct_name) + continue + + out.Write('struct %s %s_Wrappers_%s = {\n' % (iface.struct_name, + self.wrapper_prefix, + 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') + + + def GetWrapperInfoName(self, iface): + return '%s_WrapperInfo_%s' % (self.wrapper_prefix, iface.struct_name) + + + def GenerateWrapperInfoAndCollection(self, iface_releases, out): + for iface in iface_releases: + iface_macro = self.cgen.GetInterfaceMacro(iface.node, iface.version) + if iface.needs_wrapping: + wrap_iface = '(void *) &%s_Wrappers_%s' % (self.wrapper_prefix, + iface.struct_name) + else: + wrap_iface = 'NULL /* Still need slot for real_iface */' + out.Write("""static struct %s %s = { + .iface_macro = %s, + .wrapped_iface = %s, + .real_iface = NULL +};\n\n""" % (self.GetWrapperMetadataName(), + self.GetWrapperInfoName(iface), + iface_macro, + wrap_iface)) + + # Now generate NULL terminated arrays of the above wrapper infos. + ppb_wrapper_infos = [] + ppp_wrapper_infos = [] + for iface in iface_releases: + appender = PPKind.ChoosePPFunc(iface, + ppb_wrapper_infos.append, + ppp_wrapper_infos.append) + appender(' &%s' % self.GetWrapperInfoName(iface)) + ppb_wrapper_infos.append(' NULL') + ppp_wrapper_infos.append(' NULL') + out.Write( + 'static struct %s *s_ppb_wrappers[] = {\n%s\n};\n\n' % + (self.GetWrapperMetadataName(), ',\n'.join(ppb_wrapper_infos))) + out.Write( + 'static struct %s *s_ppp_wrappers[] = {\n%s\n};\n\n' % + (self.GetWrapperMetadataName(), ',\n'.join(ppp_wrapper_infos))) + + + def DeclareWrapperInfos(self, iface_releases, out): + """The wrapper methods usually need access to the real_iface, so we must + declare these wrapper infos ahead of time (there is a circular dependency). + """ + out.Write('/* BEGIN Declarations for all Wrapper Infos */\n\n') + for iface in iface_releases: + out.Write('static struct %s %s;\n' % + (self.GetWrapperMetadataName(), self.GetWrapperInfoName(iface))) + out.Write('/* END Declarations for all Wrapper Infos. */\n\n') + + + def GenerateRange(self, ast, releases, options): + """Generate shim code for a range of releases. + """ + + # Remember to set the output filename before running this. + out_filename = self.output_file + if out_filename is None: + ErrOut.Log('Did not set filename for writing out wrapper\n') + return 1 + + InfoOut.Log("Generating %s for %s" % (out_filename, self.wrapper_prefix)) + + 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) + + # Write out static helper functions (mystrcmp). + self.GenerateHelperFunctions(out) + + # Declare list of WrapperInfo before actual wrapper methods, since + # they reference each other. + self.DeclareWrapperInfos(iface_releases, out) + + # Generate wrapper functions for each wrapped method in the interfaces. + result = self.GenerateWrapperForMethods(iface_releases) + out.Write(result) + + # Collect all the wrapper functions into interface structs. + self.GenerateWrapperInterfaces(iface_releases, out) + + # Generate a table of the wrapped interface structs that can be looked up. + self.GenerateWrapperInfoAndCollection(iface_releases, out) + + # Write out the IDL-invariant functions. + self.GenerateFixedFunctions(out) + out.Close() + return 0 diff --git a/ppapi/generators/idl_generator.py b/ppapi/generators/idl_generator.py index b6372ee..efc2325 100644 --- a/ppapi/generators/idl_generator.py +++ b/ppapi/generators/idl_generator.py @@ -262,4 +262,3 @@ def Main(args): if __name__ == '__main__': GeneratorReleaseTest('Test Gen', 'testgen', 'Generator Class Test.') sys.exit(Main(sys.argv[1:])) - diff --git a/ppapi/generators/pnacl_shim.h b/ppapi/generators/pnacl_shim.h new file mode 100644 index 0000000..1002d07 --- /dev/null +++ b/ppapi/generators/pnacl_shim.h @@ -0,0 +1,29 @@ +/* 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. + */ + +#ifndef PPAPI_GENERATORS_PNACL_SHIM_H_ +#define PPAPI_GENERATORS_PNACL_SHIM_H_ + +#include "ppapi/c/ppb.h" + +/** Defines the interface exposed by the generated wrapper code. */ + +/* There is not a typedef for PPP_GetInterface_type, but it is currently + * the same as PPB_GetInterface. */ +typedef PPB_GetInterface PPP_GetInterface_Type; + +void __set_real_Pnacl_PPBGetInterface(PPB_GetInterface real); +void __set_real_Pnacl_PPPGetInterface(PPP_GetInterface_Type real); + +const void *__Pnacl_PPBGetInterface(const char *name); +const void *__Pnacl_PPPGetInterface(const char *name); + +struct __PnaclWrapperInfo { + const char* iface_macro; + const void* wrapped_iface; /* If NULL then it was not wrapped! */ + const void* real_iface; +}; + +#endif /* PPAPI_GENERATORS_PNACL_SHIM_H_ */ diff --git a/ppapi/generators/test_gen_pnacl/test_interfaces.idl b/ppapi/generators/test_gen_pnacl/test_interfaces.idl new file mode 100644 index 0000000..731fce0 --- /dev/null +++ b/ppapi/generators/test_gen_pnacl/test_interfaces.idl @@ -0,0 +1,150 @@ +/* + * 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. + */ + +/** + * This file will test that the pnacl-generated wrapper functions match + * the comments in this IDL. + */ + +label Chrome { + M13 = 0.0, + M14 = 1.0, + M15 = 2.0 +}; + +describe { + void; + mem_t; + int32_t; +}; + +[passByValue, returnByValue] struct some_struct { + mem_t X; + int32_t Y; +}; + +struct some_struct2 { + mem_t X; + int32_t Y; +}; + +[union, passByValue, returnByValue] struct some_union { + mem_t X; + int32_t Y; +}; + +/* + * static __attribute__((pnaclcall)) int32_t + * Pnacl_M15_PPB_Iface_struct_wrap_foo1(int32_t a, struct some_struct b) { + * const struct PPB_Iface_struct_wrap *iface = + * Pnacl_WrapperInfo_PPB_Iface_struct_wrap.real_iface; + * return iface->foo1(a, b); + * } + */ +[version=2.0] +interface PPB_Iface_struct_wrap { + int32_t foo1(int32_t a, [in] some_struct b); +}; + +/* + * static __attribute__((pnaclcall)) int32_t + * Pnacl_M15_PPB_Iface_union_wrap_foo1(int32_t a, union some_union b) { + * const struct PPB_Iface_union_wrap *iface = + * Pnacl_WrapperInfo_PPB_Iface_union_wrap.real_iface; + * return iface->foo1(a, b); + * } + */ +[version=2.0] +interface PPB_Iface_union_wrap { + int32_t foo1(int32_t a, [in] some_union b); +}; + + +[version=2.0] +interface PPB_Iface_nowrap { + int32_t foo1(int32_t a, [in] some_struct2 b); +}; + + +/* + * static __attribute__((pnaclcall)) + * int32_t Pnacl_M13_PPB_SomeWrap_foo1(struct some_struct a) { + * const struct PPB_SomeWrap_0_0 *iface = + * Pnacl_WrapperInfo_PPB_SomeWrap_0_0.real_iface; + * return iface->foo1(a); + * } + * + * static __attribute__((pnaclcall)) + * struct some_struct Pnacl_M13_PPB_SomeWrap_foo2(int32_t a) { + * const struct PPB_SomeWrap_0_0 *iface = + * Pnacl_WrapperInfo_PPB_SomeWrap_0_0.real_iface; + * return iface->foo2(a); + * } + */ +[version=0.0] +interface PPB_SomeWrap { + int32_t foo1([in] some_struct a); + some_struct foo2([in] int32_t a); + + /* Not generating wrapper methods for PPB_SomeWrap_1_0 */ + [version=1.0] + int32_t foo1([in] some_struct[] a); + [version=1.0] + void foo2([in] int32_t a, [out] some_struct b); + + /* Not generating wrapper methods for PPB_SomeWrap */ + [version=2.0] + int32_t foo1([in] some_struct2 a); +}; + + +/* + * static int32_t Pnacl_M13_PPP_SomeWrap_foo1(struct some_struct a) { + * const struct PPP_SomeWrap_0_0 *iface = + * Pnacl_WrapperInfo_PPP_SomeWrap_0_0.real_iface; + * int32_t (__attribute__((pnaclcall)) *temp_fp)(struct some_struct a) = + * ((int32_t (__attribute__((pnaclcall)) *)(struct some_struct a))iface->foo1); + * return temp_fp(a); + * } + * + * static struct some_struct Pnacl_M13_PPP_SomeWrap_foo2(int32_t a) { + * const struct PPP_SomeWrap_0_0 *iface = + * Pnacl_WrapperInfo_PPP_SomeWrap_0_0.real_iface; + * struct some_struct (__attribute__((pnaclcall)) *temp_fp)(int32_t a) = + * ((struct some_struct (__attribute__((pnaclcall)) *)(int32_t a))iface->foo2); + * return temp_fp(a); + * } + * + * static struct some_struct Pnacl_M14_PPP_SomeWrap_foo2(int32_t a) { + * const struct PPP_SomeWrap_1_0 *iface = + * Pnacl_WrapperInfo_PPP_SomeWrap_1_0.real_iface; + * struct some_struct (__attribute__((pnaclcall)) *temp_fp)(int32_t a) = + * ((struct some_struct (__attribute__((pnaclcall)) *)(int32_t a))iface->foo2); + * return temp_fp(a); + * } + * + * static int32_t Pnacl_M14_PPP_SomeWrap_foo1(const struct some_struct a[]) { + * const struct PPP_SomeWrap_1_0 *iface = + * Pnacl_WrapperInfo_PPP_SomeWrap_1_0.real_iface; + * int32_t (__attribute__((pnaclcall)) *temp_fp)(const struct some_struct a[]) = + * ((int32_t (__attribute__((pnaclcall)) *)(const struct some_struct a[]))iface->foo1); + * return temp_fp(a); + * } + */ +[version=0.0] +interface PPP_SomeWrap { + int32_t foo1([in] some_struct a); + some_struct foo2([in] int32_t a); + + [version=1.0] + int32_t foo1([in] some_struct[] a); + + /* Not generating wrapper interface for PPP_SomeWrap */ + [version=2.0] + int32_t foo1([in] some_struct2 a); + [version=2.0] + void foo2([in] int32_t a); +}; |