summaryrefslogtreecommitdiffstats
path: root/ppapi
diff options
context:
space:
mode:
authorjvoung@google.com <jvoung@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2011-11-30 17:59:14 +0000
committerjvoung@google.com <jvoung@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2011-11-30 17:59:14 +0000
commit2978339a797536e720a18ebdde2fd7f1b4e12c34 (patch)
tree0cdc35c2663cd0e6c1715fbb184fe3e623b0e98b /ppapi
parent8dc2405798db63428fa62d2affb1cf640783c388 (diff)
downloadchromium_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.py48
-rwxr-xr-xppapi/generators/generator.py8
-rw-r--r--ppapi/generators/idl_c_proto.py53
-rw-r--r--ppapi/generators/idl_gen_pnacl.py236
-rw-r--r--ppapi/generators/idl_gen_wrapper.py456
-rw-r--r--ppapi/generators/idl_generator.py1
-rw-r--r--ppapi/generators/pnacl_shim.h29
-rw-r--r--ppapi/generators/test_gen_pnacl/test_interfaces.idl150
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);
+};