Infobar material design refresh: bg color
[chromium-blink-merge.git] / ppapi / generators / idl_gen_pnacl.py
blobdd1ce0cd9eb7ef909e70882be4f2e5ad7a2a3165
1 #!/usr/bin/env python
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
10 import difflib
11 import glob
12 import os
13 import sys
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).
35 """
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
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
56 need wrapping.
57 """
58 if self._skip_opt:
59 return True
60 if iface.GetName().endswith('Trusted'):
61 return False
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"):
67 return False
68 for member in iface.GetListOf('Member'):
69 release = member.GetRelease(version)
70 if self.MemberNeedsWrapping(member, release):
71 return True
72 return False
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)
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
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
114 ret2 = 'void'
115 wrap_return = True
116 else:
117 ret2 = ret
118 wrap_return = False
119 return wrap_return, ret2, args_spec
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
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)
141 def GenerateWrapperForPPBMethod(self, iface, member):
142 result = []
143 func_prefix = self.WrapperMethodPrefix(iface.node, iface.release)
144 ret, name, array, cspec = self.cgen.GetComponents(member,
145 iface.release,
146 'store')
147 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)))
158 return_prefix = ''
159 if wrap_return:
160 return_prefix = '*_struct_result = '
161 elif ret != 'void':
162 return_prefix = 'return '
164 result.append(' %siface->%s(%s);\n}\n\n' % (return_prefix,
165 member.GetName(),
166 self.FormatArgs('*', cspec)))
167 return result
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)))
178 ret, name, array, cspec = self.cgen.GetComponents(member,
179 iface.release,
180 'store')
181 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 '
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')
207 return result
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 ######################################################################
220 # Tests.
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(';')
233 d = difflib.Differ()
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.
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))
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.')
259 return 1
260 else:
261 InfoOut.Log('Passed pnacl generator test.')
262 return 0
265 def 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)
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:])
284 sys.exit(retval)