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
45 self
._pnacl
_attribute
= '__attribute__((pnaclcall))'
47 ############################################################
49 def OwnHeaderFile(self
):
50 """Return the header file that specifies the API of this wrapper.
51 We do not generate the header files. """
52 return 'ppapi/generators/pnacl_shim.h'
54 def GetGuardStart(self
):
55 return ('\n/* The PNaCl PPAPI shims are only needed on x86-64 and arm. */\n'
56 '#if defined(__x86_64__) || defined(__arm__)\n\n')
58 def GetGuardEnd(self
):
61 def InterfaceNeedsWrapper(self
, iface
, releases
):
62 """Return true if the interface has ANY methods that need wrapping.
66 for release
in iface
.GetUniqueReleases(releases
):
67 version
= iface
.GetVersion(release
)
68 if self
.InterfaceVersionNeedsWrapping(iface
, version
):
73 def InterfaceVersionNeedsWrapping(self
, iface
, version
):
74 """Return true if the interface+version has ANY methods that
79 for member
in iface
.GetListOf('Member'):
80 release
= member
.GetRelease(version
)
81 if self
.MemberNeedsWrapping(member
, release
):
86 def MemberNeedsWrapping(self
, member
, release
):
87 """Return true if a particular member function at a particular
88 release needs wrapping.
92 if not member
.InReleases([release
]):
94 ret
, name
, array
, args_spec
= self
.cgen
.GetComponents(member
,
97 return self
.TypeNeedsWrapping(ret
, []) or self
.ArgsNeedWrapping(args_spec
)
100 def ArgsNeedWrapping(self
, args
):
101 """Return true if any parameter in the list needs wrapping.
104 (type_str
, name
, array_dims
, more_args
) = arg
105 if self
.TypeNeedsWrapping(type_str
, array_dims
):
110 def TypeNeedsWrapping(self
, type_node
, array_dims
):
111 """Return true if a parameter type needs wrapping.
112 Currently, this is true for byval aggregates.
114 is_aggregate
= type_node
.startswith('struct') or \
115 type_node
.startswith('union')
116 is_reference
= (type_node
.find('*') != -1 or array_dims
!= [])
117 return is_aggregate
and not is_reference
119 ############################################################
122 def GenerateWrapperForPPBMethod(self
, iface
, member
):
124 func_prefix
= self
.WrapperMethodPrefix(iface
.node
, iface
.release
)
125 sig
= self
.cgen
.GetSignature(member
, iface
.release
, 'store',
127 result
.append('static %s\n%s {\n' % (self
._pnacl
_attribute
, sig
))
128 result
.append(' const struct %s *iface = %s.real_iface;\n' %
129 (iface
.struct_name
, self
.GetWrapperInfoName(iface
)))
130 ret
, name
, array
, cspec
= self
.cgen
.GetComponents(member
,
133 ret_str
, args_str
= self
.GetReturnArgs(ret
, cspec
)
134 result
.append(' %siface->%s(%s);\n}\n\n' % (ret_str
,
135 member
.GetName(), args_str
))
139 def GenerateWrapperForPPPMethod(self
, iface
, member
):
141 func_prefix
= self
.WrapperMethodPrefix(iface
.node
, iface
.release
)
142 sig
= self
.cgen
.GetSignature(member
, iface
.release
, 'store',
144 result
.append('static %s {\n' % sig
)
145 result
.append(' const struct %s *iface = %s.real_iface;\n' %
146 (iface
.struct_name
, self
.GetWrapperInfoName(iface
)))
147 temp_fp
= self
.cgen
.GetSignature(member
, iface
.release
, 'return',
150 ptr_prefix
=self
._pnacl
_attribute
+ ' ',
152 cast
= self
.cgen
.GetSignature(member
, iface
.release
, 'return',
155 ptr_prefix
=self
._pnacl
_attribute
+ ' ',
157 result
.append(' %s = ((%s)iface->%s);\n' % (temp_fp
,
160 ret
, name
, array
, cspec
= self
.cgen
.GetComponents(member
,
163 ret_str
, args_str
= self
.GetReturnArgs(ret
, cspec
)
164 result
.append(' %stemp_fp(%s);\n}\n\n' % (ret_str
, args_str
))
168 def GenerateRange(self
, ast
, releases
, options
):
169 """Generate shim code for a range of releases.
171 self
._skip
_opt
= GetOption('disable_pnacl_opt')
172 self
.SetOutputFile(GetOption('pnaclshim'))
173 return WrapperGen
.GenerateRange(self
, ast
, releases
, options
)
175 pnaclgen
= PnaclGen()
177 ######################################################################
180 # Clean a string representing an object definition and return then string
181 # as a single space delimited set of tokens.
182 def CleanString(instr
):
183 instr
= instr
.strip()
184 instr
= instr
.split()
185 return ' '.join(instr
)
188 def PrintErrorDiff(old
, new
):
189 oldlines
= old
.split(';')
190 newlines
= new
.split(';')
192 diff
= d
.compare(oldlines
, newlines
)
193 ErrOut
.Log('Diff is:\n%s' % '\n'.join(diff
))
196 def GetOldTestOutput(ast
):
197 # Scan the top-level comments in the IDL file for comparison.
199 for filenode
in ast
.GetListOf('File'):
200 for node
in filenode
.GetChildren():
201 instr
= node
.GetOneOf('Comment')
202 if not instr
: continue
204 old
.append(instr
.GetName())
205 return CleanString(''.join(old
))
208 def TestFiles(filenames
, test_releases
):
209 ast
= ParseFiles(filenames
)
210 iface_releases
= pnaclgen
.DetermineInterfaces(ast
, test_releases
)
211 new_output
= CleanString(pnaclgen
.GenerateWrapperForMethods(
212 iface_releases
, comments
=False))
213 old_output
= GetOldTestOutput(ast
)
214 if new_output
!= old_output
:
215 PrintErrorDiff(old_output
, new_output
)
216 ErrOut
.Log('Failed pnacl generator test.')
219 InfoOut
.Log('Passed pnacl generator test.')
224 filenames
= ParseOptions(args
)
225 test_releases
= ['M13', 'M14', 'M15']
227 idldir
= os
.path
.split(sys
.argv
[0])[0]
228 idldir
= os
.path
.join(idldir
, 'test_gen_pnacl', '*.idl')
229 filenames
= glob
.glob(idldir
)
230 filenames
= sorted(filenames
)
231 if GetOption('test'):
233 return TestFiles(filenames
, test_releases
)
235 # Otherwise, generate the output file (for potential use as golden file).
236 ast
= ParseFiles(filenames
)
237 return pnaclgen
.GenerateRange(ast
, test_releases
, filenames
)
240 if __name__
== '__main__':
241 retval
= Main(sys
.argv
[1:])