blob: dd1ce0cd9eb7ef909e70882be4f2e5ad7a2a3165 [file] [log] [blame]
[email protected]3e5eb102012-04-23 18:07:541#!/usr/bin/env python
2# Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]2978339a72011-11-30 17:59:143# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Generator for Pnacl Shim functions that bridges the calling conventions
7between GCC and PNaCl. """
8
9from datetime import datetime
10import difflib
11import glob
12import os
13import sys
14
15from idl_c_proto import CGen
16from idl_gen_wrapper import Interface, WrapperGen
17from idl_log import ErrOut, InfoOut, WarnOut
18from idl_option import GetOption, Option, ParseOptions
19from idl_parser import ParseFiles
20
21Option('pnaclshim', 'Name of the pnacl shim file.',
22 default='temp_pnacl_shim.c')
23
24Option('disable_pnacl_opt', 'Turn off optimization of pnacl shim.')
25
26
27class PnaclGen(WrapperGen):
28 """PnaclGen generates shim code to bridge the Gcc ABI with PNaCl.
29
30 This subclass of WrapperGenerator takes the IDL sources and
31 generates shim methods for bridging the calling conventions between GCC
32 and PNaCl (LLVM). Some of the PPAPI methods do not need shimming, so
33 this will also detect those situations and provide direct access to the
34 original PPAPI methods (rather than the shim methods).
35 """
36
37 def __init__(self):
38 WrapperGen.__init__(self,
39 'Pnacl',
40 'Pnacl Shim Gen',
41 'pnacl',
42 'Generate the PNaCl shim.')
43 self.cgen = CGen()
44 self._skip_opt = False
[email protected]2978339a72011-11-30 17:59:1445
46 ############################################################
47
48 def OwnHeaderFile(self):
49 """Return the header file that specifies the API of this wrapper.
50 We do not generate the header files. """
[email protected]c2eb0bc82014-02-25 17:56:3151 return 'ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.h'
[email protected]2978339a72011-11-30 17:59:1452
[email protected]2978339a72011-11-30 17:59:1453
54 def InterfaceVersionNeedsWrapping(self, iface, version):
55 """Return true if the interface+version has ANY methods that
56 need wrapping.
57 """
58 if self._skip_opt:
59 return True
[email protected]99a2d132013-06-20 18:15:1460 if iface.GetName().endswith('Trusted'):
61 return False
[email protected]36fc8172014-05-31 05:01:4562 # TODO(dmichael): We have no way to wrap PPP_ interfaces without an
63 # interface string. If any ever need wrapping, we'll need to figure out a
64 # way to get the plugin-side of the Pepper proxy (within the IRT) to access
65 # and use the wrapper.
66 if iface.GetProperty("no_interface_string"):
67 return False
[email protected]2978339a72011-11-30 17:59:1468 for member in iface.GetListOf('Member'):
69 release = member.GetRelease(version)
70 if self.MemberNeedsWrapping(member, release):
71 return True
72 return False
73
74
75 def MemberNeedsWrapping(self, member, release):
76 """Return true if a particular member function at a particular
77 release needs wrapping.
78 """
79 if self._skip_opt:
80 return True
81 if not member.InReleases([release]):
82 return False
83 ret, name, array, args_spec = self.cgen.GetComponents(member,
84 release,
85 'store')
86 return self.TypeNeedsWrapping(ret, []) or self.ArgsNeedWrapping(args_spec)
87
88
89 def ArgsNeedWrapping(self, args):
90 """Return true if any parameter in the list needs wrapping.
91 """
92 for arg in args:
93 (type_str, name, array_dims, more_args) = arg
94 if self.TypeNeedsWrapping(type_str, array_dims):
95 return True
96 return False
97
98
99 def TypeNeedsWrapping(self, type_node, array_dims):
100 """Return true if a parameter type needs wrapping.
101 Currently, this is true for byval aggregates.
102 """
103 is_aggregate = type_node.startswith('struct') or \
104 type_node.startswith('union')
105 is_reference = (type_node.find('*') != -1 or array_dims != [])
106 return is_aggregate and not is_reference
107
108 ############################################################
109
110
[email protected]c3064ec2013-04-26 23:38:35111 def ConvertByValueReturnType(self, ret, args_spec):
112 if self.TypeNeedsWrapping(ret, array_dims=[]):
113 args_spec = [(ret, '_struct_result', [], None)] + args_spec
114 ret2 = 'void'
115 wrap_return = True
116 else:
117 ret2 = ret
118 wrap_return = False
119 return wrap_return, ret2, args_spec
120
121
122 def ConvertByValueArguments(self, args_spec):
123 args = []
124 for type_str, name, array_dims, more_args in args_spec:
125 if self.TypeNeedsWrapping(type_str, array_dims):
126 type_str += '*'
127 args.append((type_str, name, array_dims, more_args))
128 return args
129
130
131 def FormatArgs(self, c_operator, args_spec):
132 args = []
133 for type_str, name, array_dims, more_args in args_spec:
134 if self.TypeNeedsWrapping(type_str, array_dims):
135 args.append(c_operator + name)
136 else:
137 args.append(name)
138 return ', '.join(args)
139
140
[email protected]2978339a72011-11-30 17:59:14141 def GenerateWrapperForPPBMethod(self, iface, member):
142 result = []
143 func_prefix = self.WrapperMethodPrefix(iface.node, iface.release)
[email protected]2978339a72011-11-30 17:59:14144 ret, name, array, cspec = self.cgen.GetComponents(member,
145 iface.release,
146 'store')
[email protected]c3064ec2013-04-26 23:38:35147 wrap_return, ret2, cspec2 = self.ConvertByValueReturnType(ret, cspec)
148 cspec2 = self.ConvertByValueArguments(cspec2)
149 sig = self.cgen.Compose(ret2, name, array, cspec2,
150 prefix=func_prefix,
151 func_as_ptr=False,
152 include_name=True,
153 unsized_as_ptr=False)
154 result.append('static %s {\n' % sig)
155 result.append(' const struct %s *iface = %s.real_iface;\n' %
156 (iface.struct_name, self.GetWrapperInfoName(iface)))
157
158 return_prefix = ''
159 if wrap_return:
160 return_prefix = '*_struct_result = '
161 elif ret != 'void':
162 return_prefix = 'return '
163
164 result.append(' %siface->%s(%s);\n}\n\n' % (return_prefix,
165 member.GetName(),
166 self.FormatArgs('*', cspec)))
[email protected]2978339a72011-11-30 17:59:14167 return result
168
169
170 def GenerateWrapperForPPPMethod(self, iface, member):
171 result = []
172 func_prefix = self.WrapperMethodPrefix(iface.node, iface.release)
173 sig = self.cgen.GetSignature(member, iface.release, 'store',
174 func_prefix, False)
175 result.append('static %s {\n' % sig)
176 result.append(' const struct %s *iface = %s.real_iface;\n' %
177 (iface.struct_name, self.GetWrapperInfoName(iface)))
[email protected]2978339a72011-11-30 17:59:14178 ret, name, array, cspec = self.cgen.GetComponents(member,
179 iface.release,
180 'store')
[email protected]c3064ec2013-04-26 23:38:35181 wrap_return, ret2, cspec = self.ConvertByValueReturnType(ret, cspec)
182 cspec2 = self.ConvertByValueArguments(cspec)
183 temp_fp = self.cgen.Compose(ret2, name, array, cspec2,
184 prefix='temp_fp',
185 func_as_ptr=True,
186 include_name=False,
187 unsized_as_ptr=False)
188 cast = self.cgen.Compose(ret2, name, array, cspec2,
189 prefix='',
190 func_as_ptr=True,
191 include_name=False,
192 unsized_as_ptr=False)
193 result.append(' %s =\n ((%s)iface->%s);\n' % (temp_fp,
194 cast,
195 member.GetName()))
196 return_prefix = ''
197 if wrap_return:
198 result.append(' %s _struct_result;\n' % ret)
199 elif ret != 'void':
200 return_prefix = 'return '
201
202 result.append(' %stemp_fp(%s);\n' % (return_prefix,
203 self.FormatArgs('&', cspec)))
204 if wrap_return:
205 result.append(' return _struct_result;\n')
206 result.append('}\n\n')
[email protected]2978339a72011-11-30 17:59:14207 return result
208
209
210 def GenerateRange(self, ast, releases, options):
211 """Generate shim code for a range of releases.
212 """
213 self._skip_opt = GetOption('disable_pnacl_opt')
214 self.SetOutputFile(GetOption('pnaclshim'))
215 return WrapperGen.GenerateRange(self, ast, releases, options)
216
217pnaclgen = PnaclGen()
218
219######################################################################
220# Tests.
221
222# Clean a string representing an object definition and return then string
223# as a single space delimited set of tokens.
224def CleanString(instr):
225 instr = instr.strip()
226 instr = instr.split()
227 return ' '.join(instr)
228
229
230def PrintErrorDiff(old, new):
231 oldlines = old.split(';')
232 newlines = new.split(';')
233 d = difflib.Differ()
234 diff = d.compare(oldlines, newlines)
235 ErrOut.Log('Diff is:\n%s' % '\n'.join(diff))
236
237
238def GetOldTestOutput(ast):
239 # Scan the top-level comments in the IDL file for comparison.
240 old = []
241 for filenode in ast.GetListOf('File'):
242 for node in filenode.GetChildren():
243 instr = node.GetOneOf('Comment')
244 if not instr: continue
245 instr.Dump()
246 old.append(instr.GetName())
247 return CleanString(''.join(old))
248
249
250def TestFiles(filenames, test_releases):
251 ast = ParseFiles(filenames)
252 iface_releases = pnaclgen.DetermineInterfaces(ast, test_releases)
253 new_output = CleanString(pnaclgen.GenerateWrapperForMethods(
254 iface_releases, comments=False))
255 old_output = GetOldTestOutput(ast)
256 if new_output != old_output:
257 PrintErrorDiff(old_output, new_output)
258 ErrOut.Log('Failed pnacl generator test.')
259 return 1
260 else:
261 InfoOut.Log('Passed pnacl generator test.')
262 return 0
263
264
265def Main(args):
266 filenames = ParseOptions(args)
267 test_releases = ['M13', 'M14', 'M15']
268 if not filenames:
269 idldir = os.path.split(sys.argv[0])[0]
270 idldir = os.path.join(idldir, 'test_gen_pnacl', '*.idl')
271 filenames = glob.glob(idldir)
272 filenames = sorted(filenames)
273 if GetOption('test'):
274 # Run the tests.
275 return TestFiles(filenames, test_releases)
276
277 # Otherwise, generate the output file (for potential use as golden file).
278 ast = ParseFiles(filenames)
279 return pnaclgen.GenerateRange(ast, test_releases, filenames)
280
281
282if __name__ == '__main__':
283 retval = Main(sys.argv[1:])
284 sys.exit(retval)