2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
6 """Generator for Pnacl Shim functions that bridges the calling conventions
7 between GCC and PNaCl. """
9 from datetime
import datetime
15 from idl_c_proto
import CGen
16 from idl_gen_wrapper
import Interface
, WrapperGen
17 from idl_log
import ErrOut
, InfoOut
, WarnOut
18 from idl_option
import GetOption
, Option
, ParseOptions
19 from idl_parser
import ParseFiles
21 Option('pnaclshim', 'Name of the pnacl shim file.',
22 default
='temp_pnacl_shim.c')
24 Option('disable_pnacl_opt', 'Turn off optimization of pnacl shim.')
27 class PnaclGen(WrapperGen
):
28 """PnaclGen generates shim code to bridge the Gcc ABI with PNaCl.
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).
38 WrapperGen
.__init
__(self
,
42 'Generate the PNaCl shim.')
44 self
._skip
_opt
= False
46 ############################################################
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'
54 def InterfaceVersionNeedsWrapping(self
, iface
, version
):
55 """Return true if the interface+version has ANY methods that
60 if iface
.GetName().endswith('Trusted'):
62 for member
in iface
.GetListOf('Member'):
63 release
= member
.GetRelease(version
)
64 if self
.MemberNeedsWrapping(member
, release
):
69 def MemberNeedsWrapping(self
, member
, release
):
70 """Return true if a particular member function at a particular
71 release needs wrapping.
75 if not member
.InReleases([release
]):
77 ret
, name
, array
, args_spec
= self
.cgen
.GetComponents(member
,
80 return self
.TypeNeedsWrapping(ret
, []) or self
.ArgsNeedWrapping(args_spec
)
83 def ArgsNeedWrapping(self
, args
):
84 """Return true if any parameter in the list needs wrapping.
87 (type_str
, name
, array_dims
, more_args
) = arg
88 if self
.TypeNeedsWrapping(type_str
, array_dims
):
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.
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
102 ############################################################
105 def ConvertByValueReturnType(self
, ret
, args_spec
):
106 if self
.TypeNeedsWrapping(ret
, array_dims
=[]):
107 args_spec
= [(ret
, '_struct_result', [], None)] + args_spec
113 return wrap_return
, ret2
, args_spec
116 def ConvertByValueArguments(self
, args_spec
):
118 for type_str
, name
, array_dims
, more_args
in args_spec
:
119 if self
.TypeNeedsWrapping(type_str
, array_dims
):
121 args
.append((type_str
, name
, array_dims
, more_args
))
125 def FormatArgs(self
, c_operator
, args_spec
):
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
)
132 return ', '.join(args
)
135 def GenerateWrapperForPPBMethod(self
, iface
, member
):
137 func_prefix
= self
.WrapperMethodPrefix(iface
.node
, iface
.release
)
138 ret
, name
, array
, cspec
= self
.cgen
.GetComponents(member
,
141 wrap_return
, ret2
, cspec2
= self
.ConvertByValueReturnType(ret
, cspec
)
142 cspec2
= self
.ConvertByValueArguments(cspec2
)
143 sig
= self
.cgen
.Compose(ret2
, name
, array
, cspec2
,
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
)))
154 return_prefix
= '*_struct_result = '
156 return_prefix
= 'return '
158 result
.append(' %siface->%s(%s);\n}\n\n' % (return_prefix
,
160 self
.FormatArgs('*', cspec
)))
164 def GenerateWrapperForPPPMethod(self
, iface
, member
):
166 func_prefix
= self
.WrapperMethodPrefix(iface
.node
, iface
.release
)
167 sig
= self
.cgen
.GetSignature(member
, iface
.release
, 'store',
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
)))
172 ret
, name
, array
, cspec
= self
.cgen
.GetComponents(member
,
175 wrap_return
, ret2
, cspec
= self
.ConvertByValueReturnType(ret
, cspec
)
176 cspec2
= self
.ConvertByValueArguments(cspec
)
177 temp_fp
= self
.cgen
.Compose(ret2
, name
, array
, cspec2
,
181 unsized_as_ptr
=False)
182 cast
= self
.cgen
.Compose(ret2
, name
, array
, cspec2
,
186 unsized_as_ptr
=False)
187 result
.append(' %s =\n ((%s)iface->%s);\n' % (temp_fp
,
192 result
.append(' %s _struct_result;\n' % ret
)
194 return_prefix
= 'return '
196 result
.append(' %stemp_fp(%s);\n' % (return_prefix
,
197 self
.FormatArgs('&', cspec
)))
199 result
.append(' return _struct_result;\n')
200 result
.append('}\n\n')
204 def GenerateRange(self
, ast
, releases
, options
):
205 """Generate shim code for a range of releases.
207 self
._skip
_opt
= GetOption('disable_pnacl_opt')
208 self
.SetOutputFile(GetOption('pnaclshim'))
209 return WrapperGen
.GenerateRange(self
, ast
, releases
, options
)
211 pnaclgen
= PnaclGen()
213 ######################################################################
216 # Clean a string representing an object definition and return then string
217 # as a single space delimited set of tokens.
218 def CleanString(instr
):
219 instr
= instr
.strip()
220 instr
= instr
.split()
221 return ' '.join(instr
)
224 def PrintErrorDiff(old
, new
):
225 oldlines
= old
.split(';')
226 newlines
= new
.split(';')
228 diff
= d
.compare(oldlines
, newlines
)
229 ErrOut
.Log('Diff is:\n%s' % '\n'.join(diff
))
232 def GetOldTestOutput(ast
):
233 # Scan the top-level comments in the IDL file for comparison.
235 for filenode
in ast
.GetListOf('File'):
236 for node
in filenode
.GetChildren():
237 instr
= node
.GetOneOf('Comment')
238 if not instr
: continue
240 old
.append(instr
.GetName())
241 return CleanString(''.join(old
))
244 def 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.')
255 InfoOut
.Log('Passed pnacl generator test.')
260 filenames
= ParseOptions(args
)
261 test_releases
= ['M13', 'M14', 'M15']
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'):
269 return TestFiles(filenames
, test_releases
)
271 # Otherwise, generate the output file (for potential use as golden file).
272 ast
= ParseFiles(filenames
)
273 return pnaclgen
.GenerateRange(ast
, test_releases
, filenames
)
276 if __name__
== '__main__':
277 retval
= Main(sys
.argv
[1:])