blob: 2dc631b68728cf566500edffc0a0f73c0340383b [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. """
51 return 'ppapi/generators/pnacl_shim.h'
52
[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]2978339a72011-11-30 17:59:1462 for member in iface.GetListOf('Member'):
63 release = member.GetRelease(version)
64 if self.MemberNeedsWrapping(member, release):
65 return True
66 return False
67
68
69 def MemberNeedsWrapping(self, member, release):
70 """Return true if a particular member function at a particular
71 release needs wrapping.
72 """
73 if self._skip_opt:
74 return True
75 if not member.InReleases([release]):
76 return False
77 ret, name, array, args_spec = self.cgen.GetComponents(member,
78 release,
79 'store')
80 return self.TypeNeedsWrapping(ret, []) or self.ArgsNeedWrapping(args_spec)
81
82
83 def ArgsNeedWrapping(self, args):
84 """Return true if any parameter in the list needs wrapping.
85 """
86 for arg in args:
87 (type_str, name, array_dims, more_args) = arg
88 if self.TypeNeedsWrapping(type_str, array_dims):
89 return True
90 return False
91
92
93 def TypeNeedsWrapping(self, type_node, array_dims):
94 """Return true if a parameter type needs wrapping.
95 Currently, this is true for byval aggregates.
96 """
97 is_aggregate = type_node.startswith('struct') or \
98 type_node.startswith('union')
99 is_reference = (type_node.find('*') != -1 or array_dims != [])
100 return is_aggregate and not is_reference
101
102 ############################################################
103
104
[email protected]c3064ec2013-04-26 23:38:35105 def ConvertByValueReturnType(self, ret, args_spec):
106 if self.TypeNeedsWrapping(ret, array_dims=[]):
107 args_spec = [(ret, '_struct_result', [], None)] + args_spec
108 ret2 = 'void'
109 wrap_return = True
110 else:
111 ret2 = ret
112 wrap_return = False
113 return wrap_return, ret2, args_spec
114
115
116 def ConvertByValueArguments(self, args_spec):
117 args = []
118 for type_str, name, array_dims, more_args in args_spec:
119 if self.TypeNeedsWrapping(type_str, array_dims):
120 type_str += '*'
121 args.append((type_str, name, array_dims, more_args))
122 return args
123
124
125 def FormatArgs(self, c_operator, args_spec):
126 args = []
127 for type_str, name, array_dims, more_args in args_spec:
128 if self.TypeNeedsWrapping(type_str, array_dims):
129 args.append(c_operator + name)
130 else:
131 args.append(name)
132 return ', '.join(args)
133
134
[email protected]2978339a72011-11-30 17:59:14135 def GenerateWrapperForPPBMethod(self, iface, member):
136 result = []
137 func_prefix = self.WrapperMethodPrefix(iface.node, iface.release)
[email protected]2978339a72011-11-30 17:59:14138 ret, name, array, cspec = self.cgen.GetComponents(member,
139 iface.release,
140 'store')
[email protected]c3064ec2013-04-26 23:38:35141 wrap_return, ret2, cspec2 = self.ConvertByValueReturnType(ret, cspec)
142 cspec2 = self.ConvertByValueArguments(cspec2)
143 sig = self.cgen.Compose(ret2, name, array, cspec2,
144 prefix=func_prefix,
145 func_as_ptr=False,
146 include_name=True,
147 unsized_as_ptr=False)
148 result.append('static %s {\n' % sig)
149 result.append(' const struct %s *iface = %s.real_iface;\n' %
150 (iface.struct_name, self.GetWrapperInfoName(iface)))
151
152 return_prefix = ''
153 if wrap_return:
154 return_prefix = '*_struct_result = '
155 elif ret != 'void':
156 return_prefix = 'return '
157
158 result.append(' %siface->%s(%s);\n}\n\n' % (return_prefix,
159 member.GetName(),
160 self.FormatArgs('*', cspec)))
[email protected]2978339a72011-11-30 17:59:14161 return result
162
163
164 def GenerateWrapperForPPPMethod(self, iface, member):
165 result = []
166 func_prefix = self.WrapperMethodPrefix(iface.node, iface.release)
167 sig = self.cgen.GetSignature(member, iface.release, 'store',
168 func_prefix, False)
169 result.append('static %s {\n' % sig)
170 result.append(' const struct %s *iface = %s.real_iface;\n' %
171 (iface.struct_name, self.GetWrapperInfoName(iface)))
[email protected]2978339a72011-11-30 17:59:14172 ret, name, array, cspec = self.cgen.GetComponents(member,
173 iface.release,
174 'store')
[email protected]c3064ec2013-04-26 23:38:35175 wrap_return, ret2, cspec = self.ConvertByValueReturnType(ret, cspec)
176 cspec2 = self.ConvertByValueArguments(cspec)
177 temp_fp = self.cgen.Compose(ret2, name, array, cspec2,
178 prefix='temp_fp',
179 func_as_ptr=True,
180 include_name=False,
181 unsized_as_ptr=False)
182 cast = self.cgen.Compose(ret2, name, array, cspec2,
183 prefix='',
184 func_as_ptr=True,
185 include_name=False,
186 unsized_as_ptr=False)
187 result.append(' %s =\n ((%s)iface->%s);\n' % (temp_fp,
188 cast,
189 member.GetName()))
190 return_prefix = ''
191 if wrap_return:
192 result.append(' %s _struct_result;\n' % ret)
193 elif ret != 'void':
194 return_prefix = 'return '
195
196 result.append(' %stemp_fp(%s);\n' % (return_prefix,
197 self.FormatArgs('&', cspec)))
198 if wrap_return:
199 result.append(' return _struct_result;\n')
200 result.append('}\n\n')
[email protected]2978339a72011-11-30 17:59:14201 return result
202
203
204 def GenerateRange(self, ast, releases, options):
205 """Generate shim code for a range of releases.
206 """
207 self._skip_opt = GetOption('disable_pnacl_opt')
208 self.SetOutputFile(GetOption('pnaclshim'))
209 return WrapperGen.GenerateRange(self, ast, releases, options)
210
211pnaclgen = PnaclGen()
212
213######################################################################
214# Tests.
215
216# Clean a string representing an object definition and return then string
217# as a single space delimited set of tokens.
218def CleanString(instr):
219 instr = instr.strip()
220 instr = instr.split()
221 return ' '.join(instr)
222
223
224def PrintErrorDiff(old, new):
225 oldlines = old.split(';')
226 newlines = new.split(';')
227 d = difflib.Differ()
228 diff = d.compare(oldlines, newlines)
229 ErrOut.Log('Diff is:\n%s' % '\n'.join(diff))
230
231
232def GetOldTestOutput(ast):
233 # Scan the top-level comments in the IDL file for comparison.
234 old = []
235 for filenode in ast.GetListOf('File'):
236 for node in filenode.GetChildren():
237 instr = node.GetOneOf('Comment')
238 if not instr: continue
239 instr.Dump()
240 old.append(instr.GetName())
241 return CleanString(''.join(old))
242
243
244def TestFiles(filenames, test_releases):
245 ast = ParseFiles(filenames)
246 iface_releases = pnaclgen.DetermineInterfaces(ast, test_releases)
247 new_output = CleanString(pnaclgen.GenerateWrapperForMethods(
248 iface_releases, comments=False))
249 old_output = GetOldTestOutput(ast)
250 if new_output != old_output:
251 PrintErrorDiff(old_output, new_output)
252 ErrOut.Log('Failed pnacl generator test.')
253 return 1
254 else:
255 InfoOut.Log('Passed pnacl generator test.')
256 return 0
257
258
259def Main(args):
260 filenames = ParseOptions(args)
261 test_releases = ['M13', 'M14', 'M15']
262 if not filenames:
263 idldir = os.path.split(sys.argv[0])[0]
264 idldir = os.path.join(idldir, 'test_gen_pnacl', '*.idl')
265 filenames = glob.glob(idldir)
266 filenames = sorted(filenames)
267 if GetOption('test'):
268 # Run the tests.
269 return TestFiles(filenames, test_releases)
270
271 # Otherwise, generate the output file (for potential use as golden file).
272 ast = ParseFiles(filenames)
273 return pnaclgen.GenerateRange(ast, test_releases, filenames)
274
275
276if __name__ == '__main__':
277 retval = Main(sys.argv[1:])
278 sys.exit(retval)