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/native_client/src/untrusted/pnacl_irt_shim/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 # 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"):
68 for member
in iface
.GetListOf('Member'):
69 release
= member
.GetRelease(version
)
70 if self
.MemberNeedsWrapping(member
, release
):
75 def MemberNeedsWrapping(self
, member
, release
):
76 """Return true if a particular member function at a particular
77 release needs wrapping.
81 if not member
.InReleases([release
]):
83 ret
, name
, array
, args_spec
= self
.cgen
.GetComponents(member
,
86 return self
.TypeNeedsWrapping(ret
, []) or self
.ArgsNeedWrapping(args_spec
)
89 def ArgsNeedWrapping(self
, args
):
90 """Return true if any parameter in the list needs wrapping.
93 (type_str
, name
, array_dims
, more_args
) = arg
94 if self
.TypeNeedsWrapping(type_str
, array_dims
):
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.
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
108 ############################################################
111 def ConvertByValueReturnType(self
, ret
, args_spec
):
112 if self
.TypeNeedsWrapping(ret
, array_dims
=[]):
113 args_spec
= [(ret
, '_struct_result', [], None)] + args_spec
119 return wrap_return
, ret2
, args_spec
122 def ConvertByValueArguments(self
, args_spec
):
124 for type_str
, name
, array_dims
, more_args
in args_spec
:
125 if self
.TypeNeedsWrapping(type_str
, array_dims
):
127 args
.append((type_str
, name
, array_dims
, more_args
))
131 def FormatArgs(self
, c_operator
, args_spec
):
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
)
138 return ', '.join(args
)
141 def GenerateWrapperForPPBMethod(self
, iface
, member
):
143 func_prefix
= self
.WrapperMethodPrefix(iface
.node
, iface
.release
)
144 ret
, name
, array
, cspec
= self
.cgen
.GetComponents(member
,
147 wrap_return
, ret2
, cspec2
= self
.ConvertByValueReturnType(ret
, cspec
)
148 cspec2
= self
.ConvertByValueArguments(cspec2
)
149 sig
= self
.cgen
.Compose(ret2
, name
, array
, cspec2
,
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
)))
160 return_prefix
= '*_struct_result = '
162 return_prefix
= 'return '
164 result
.append(' %siface->%s(%s);\n}\n\n' % (return_prefix
,
166 self
.FormatArgs('*', cspec
)))
170 def GenerateWrapperForPPPMethod(self
, iface
, member
):
172 func_prefix
= self
.WrapperMethodPrefix(iface
.node
, iface
.release
)
173 sig
= self
.cgen
.GetSignature(member
, iface
.release
, 'store',
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
)))
178 ret
, name
, array
, cspec
= self
.cgen
.GetComponents(member
,
181 wrap_return
, ret2
, cspec
= self
.ConvertByValueReturnType(ret
, cspec
)
182 cspec2
= self
.ConvertByValueArguments(cspec
)
183 temp_fp
= self
.cgen
.Compose(ret2
, name
, array
, cspec2
,
187 unsized_as_ptr
=False)
188 cast
= self
.cgen
.Compose(ret2
, name
, array
, cspec2
,
192 unsized_as_ptr
=False)
193 result
.append(' %s =\n ((%s)iface->%s);\n' % (temp_fp
,
198 result
.append(' %s _struct_result;\n' % ret
)
200 return_prefix
= 'return '
202 result
.append(' %stemp_fp(%s);\n' % (return_prefix
,
203 self
.FormatArgs('&', cspec
)))
205 result
.append(' return _struct_result;\n')
206 result
.append('}\n\n')
210 def GenerateRange(self
, ast
, releases
, options
):
211 """Generate shim code for a range of releases.
213 self
._skip
_opt
= GetOption('disable_pnacl_opt')
214 self
.SetOutputFile(GetOption('pnaclshim'))
215 return WrapperGen
.GenerateRange(self
, ast
, releases
, options
)
217 pnaclgen
= PnaclGen()
219 ######################################################################
222 # Clean a string representing an object definition and return then string
223 # as a single space delimited set of tokens.
224 def CleanString(instr
):
225 instr
= instr
.strip()
226 instr
= instr
.split()
227 return ' '.join(instr
)
230 def PrintErrorDiff(old
, new
):
231 oldlines
= old
.split(';')
232 newlines
= new
.split(';')
234 diff
= d
.compare(oldlines
, newlines
)
235 ErrOut
.Log('Diff is:\n%s' % '\n'.join(diff
))
238 def GetOldTestOutput(ast
):
239 # Scan the top-level comments in the IDL file for comparison.
241 for filenode
in ast
.GetListOf('File'):
242 for node
in filenode
.GetChildren():
243 instr
= node
.GetOneOf('Comment')
244 if not instr
: continue
246 old
.append(instr
.GetName())
247 return CleanString(''.join(old
))
250 def 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.')
261 InfoOut
.Log('Passed pnacl generator test.')
266 filenames
= ParseOptions(args
)
267 test_releases
= ['M13', 'M14', 'M15']
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'):
275 return TestFiles(filenames
, test_releases
)
277 # Otherwise, generate the output file (for potential use as golden file).
278 ast
= ParseFiles(filenames
)
279 return pnaclgen
.GenerateRange(ast
, test_releases
, filenames
)
282 if __name__
== '__main__':
283 retval
= Main(sys
.argv
[1:])