Send activityLogPrivate to stable
[chromium-blink-merge.git] / ppapi / generators / idl_gen_pnacl.py
blob2dc631b68728cf566500edffc0a0f73c0340383b
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/generators/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 for member in iface.GetListOf('Member'):
63 release = member.GetRelease(version)
64 if self.MemberNeedsWrapping(member, release):
65 return True
66 return False
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)
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
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
102 ############################################################
105 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
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
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)
135 def GenerateWrapperForPPBMethod(self, iface, member):
136 result = []
137 func_prefix = self.WrapperMethodPrefix(iface.node, iface.release)
138 ret, name, array, cspec = self.cgen.GetComponents(member,
139 iface.release,
140 'store')
141 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)))
152 return_prefix = ''
153 if wrap_return:
154 return_prefix = '*_struct_result = '
155 elif ret != 'void':
156 return_prefix = 'return '
158 result.append(' %siface->%s(%s);\n}\n\n' % (return_prefix,
159 member.GetName(),
160 self.FormatArgs('*', cspec)))
161 return result
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)))
172 ret, name, array, cspec = self.cgen.GetComponents(member,
173 iface.release,
174 'store')
175 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 '
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')
201 return result
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 ######################################################################
214 # Tests.
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(';')
227 d = difflib.Differ()
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.
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))
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.')
253 return 1
254 else:
255 InfoOut.Log('Passed pnacl generator test.')
256 return 0
259 def 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)
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:])
278 sys.exit(retval)