Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(238)

Side by Side Diff: third_party/chrome/ppapi/generators/idl_thunk.py

Issue 12300042: Update idlsync.py to pull in dependencies required for chrome api generation. (Closed) Base URL: git://github.com/dart-lang/bleeding_edge.git@master
Patch Set: From another checkout Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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.
5
6 """ Generator for C++ style thunks """
7
8 import glob
9 import os
10 import re
11 import sys
12
13 from idl_log import ErrOut, InfoOut, WarnOut
14 from idl_node import IDLAttribute, IDLNode
15 from idl_ast import IDLAst
16 from idl_option import GetOption, Option, ParseOptions
17 from idl_outfile import IDLOutFile
18 from idl_parser import ParseFiles
19 from idl_c_proto import CGen, GetNodeComments, CommentLines, Comment
20 from idl_generator import Generator, GeneratorByFile
21
22 Option('thunkroot', 'Base directory of output',
23 default=os.path.join('..', 'thunk'))
24
25
26 class TGenError(Exception):
27 def __init__(self, msg):
28 self.value = msg
29
30 def __str__(self):
31 return repr(self.value)
32
33
34 class ThunkBodyMetadata(object):
35 """Metadata about thunk body. Used for selecting which headers to emit."""
36 def __init__(self):
37 self._apis = set()
38 self._includes = set()
39
40 def AddApi(self, api):
41 self._apis.add(api)
42
43 def Apis(self):
44 return self._apis
45
46 def AddInclude(self, include):
47 self._includes.add(include)
48
49 def Includes(self):
50 return self._includes
51
52
53 def _GetBaseFileName(filenode):
54 """Returns the base name for output files, given the filenode.
55
56 Examples:
57 'dev/ppb_find_dev.h' -> 'ppb_find'
58 'trusted/ppb_buffer_trusted.h' -> 'ppb_buffer_trusted'
59 """
60 path, name = os.path.split(filenode.GetProperty('NAME'))
61 name = os.path.splitext(name)[0]
62 if name.endswith('_dev'):
63 # Clip off _dev suffix.
64 name = name[:-len('_dev')]
65 return name
66
67
68 def _GetHeaderFileName(filenode):
69 """Returns the name for the header for this file."""
70 path, name = os.path.split(filenode.GetProperty('NAME'))
71 name = os.path.splitext(name)[0]
72 if path:
73 header = "ppapi/c/%s/%s.h" % (path, name)
74 else:
75 header = "ppapi/c/%s.h" % name
76 return header
77
78
79 def _GetThunkFileName(filenode, relpath):
80 """Returns the thunk file name."""
81 path = os.path.split(filenode.GetProperty('NAME'))[0]
82 name = _GetBaseFileName(filenode)
83 # We don't reattach the path for thunk.
84 if relpath: name = os.path.join(relpath, name)
85 name = '%s%s' % (name, '_thunk.cc')
86 return name
87
88
89 def _MakeEnterLine(filenode, interface, arg, handle_errors, callback, meta):
90 """Returns an EnterInstance/EnterResource string for a function."""
91 if arg[0] == 'PP_Instance':
92 if callback is None:
93 return 'EnterInstance enter(%s);' % arg[1]
94 else:
95 return 'EnterInstance enter(%s, %s);' % (arg[1], callback)
96 elif arg[0] == 'PP_Resource':
97 api_name = interface.GetName()
98 if api_name.endswith('_Dev'):
99 api_name = api_name[:-len('_Dev')]
100 api_name += '_API'
101
102 enter_type = 'EnterResource<%s>' % api_name
103 # The API header matches the file name, not the interface name.
104 meta.AddApi(_GetBaseFileName(filenode) + '_api')
105
106 if callback is None:
107 return '%s enter(%s, %s);' % (enter_type, arg[1],
108 str(handle_errors).lower())
109 else:
110 return '%s enter(%s, %s, %s);' % (enter_type, arg[1],
111 callback,
112 str(handle_errors).lower())
113 else:
114 raise TGenError("Unknown type for _MakeEnterLine: %s" % arg[0])
115
116
117 def _GetShortName(interface, filter_suffixes):
118 """Return a shorter interface name that matches Is* and Create* functions."""
119 parts = interface.GetName().split('_')[1:]
120 tail = parts[len(parts) - 1]
121 if tail in filter_suffixes:
122 parts = parts[:-1]
123 return ''.join(parts)
124
125
126 def _IsTypeCheck(interface, node):
127 """Returns true if node represents a type-checking function."""
128 return node.GetName() == 'Is%s' % _GetShortName(interface, ['Dev', 'Private'])
129
130
131 def _GetCreateFuncName(interface):
132 """Returns the creation function name for an interface."""
133 return 'Create%s' % _GetShortName(interface, ['Dev'])
134
135
136 def _GetDefaultFailureValue(t):
137 """Returns the default failure value for a given type.
138
139 Returns None if no default failure value exists for the type.
140 """
141 values = {
142 'PP_Bool': 'PP_FALSE',
143 'PP_Resource': '0',
144 'struct PP_Var': 'PP_MakeUndefined()',
145 'int32_t': 'enter.retval()',
146 'uint16_t': '0',
147 'uint32_t': '0',
148 'uint64_t': '0',
149 }
150 if t in values:
151 return values[t]
152 return None
153
154
155 def _MakeCreateMemberBody(interface, member, args):
156 """Returns the body of a Create() function.
157
158 Args:
159 interface - IDLNode for the interface
160 member - IDLNode for member function
161 args - List of arguments for the Create() function
162 """
163 if args[0][0] == 'PP_Resource':
164 body = ' Resource* object =\n'
165 body += ' PpapiGlobals::Get()->GetResourceTracker()->'
166 body += 'GetResource(%s);\n' % args[0][1]
167 body += ' if (!object)\n'
168 body += ' return 0;\n'
169 body += ' EnterResourceCreation enter(object->pp_instance());\n'
170 elif args[0][0] == 'PP_Instance':
171 body = ' EnterResourceCreation enter(%s);\n' % args[0][1]
172 else:
173 raise TGenError('Unknown arg type for Create(): %s' % args[0][0])
174
175 body += ' if (enter.failed())\n'
176 body += ' return 0;\n'
177 arg_list = ', '.join([a[1] for a in args])
178 if member.GetProperty('create_func'):
179 create_func = member.GetProperty('create_func')
180 else:
181 create_func = _GetCreateFuncName(interface)
182 body += ' return enter.functions()->%s(%s);' % (create_func,
183 arg_list)
184 return body
185
186
187 def _MakeNormalMemberBody(filenode, node, member, rtype, args, meta):
188 """Returns the body of a typical function.
189
190 Args:
191 filenode - IDLNode for the file
192 node - IDLNode for the interface
193 member - IDLNode for the member function
194 rtype - Return type for the member function
195 args - List of 4-tuple arguments for the member function
196 meta - ThunkBodyMetadata for header hints
197 """
198 is_callback_func = args[len(args) - 1][0] == 'struct PP_CompletionCallback'
199
200 if is_callback_func:
201 call_args = args[:-1] + [('', 'enter.callback()', '', '')]
202 meta.AddInclude('ppapi/c/pp_completion_callback.h')
203 else:
204 call_args = args
205
206 if args[0][0] == 'PP_Instance':
207 call_arglist = ', '.join(a[1] for a in call_args)
208 function_container = 'functions'
209 else:
210 call_arglist = ', '.join(a[1] for a in call_args[1:])
211 function_container = 'object'
212
213 invocation = 'enter.%s()->%s(%s)' % (function_container,
214 member.GetName(),
215 call_arglist)
216
217 handle_errors = not (member.GetProperty('report_errors') == 'False')
218 if is_callback_func:
219 body = ' %s\n' % _MakeEnterLine(filenode, node, args[0], handle_errors,
220 args[len(args) - 1][1], meta)
221 body += ' if (enter.failed())\n'
222 value = member.GetProperty('on_failure')
223 if value is None:
224 value = 'enter.retval()'
225 body += ' return %s;\n' % value
226 body += ' return enter.SetResult(%s);' % invocation
227 elif rtype == 'void':
228 body = ' %s\n' % _MakeEnterLine(filenode, node, args[0], handle_errors,
229 None, meta)
230 body += ' if (enter.succeeded())\n'
231 body += ' %s;' % invocation
232 else:
233 value = member.GetProperty('on_failure')
234 if value is None:
235 value = _GetDefaultFailureValue(rtype)
236 if value is None:
237 raise TGenError('No default value for rtype %s' % rtype)
238
239 body = ' %s\n' % _MakeEnterLine(filenode, node, args[0], handle_errors,
240 None, meta)
241 body += ' if (enter.failed())\n'
242 body += ' return %s;\n' % value
243 body += ' return %s;' % invocation
244 return body
245
246
247 def DefineMember(filenode, node, member, release, include_version, meta):
248 """Returns a definition for a member function of an interface.
249
250 Args:
251 filenode - IDLNode for the file
252 node - IDLNode for the interface
253 member - IDLNode for the member function
254 release - release to generate
255 include_version - include the version in emitted function name.
256 meta - ThunkMetadata for header hints
257 Returns:
258 A string with the member definition.
259 """
260 cgen = CGen()
261 rtype, name, arrays, args = cgen.GetComponents(member, release, 'return')
262
263 if _IsTypeCheck(node, member):
264 body = ' %s\n' % _MakeEnterLine(filenode, node, args[0], False, None, meta)
265 body += ' return PP_FromBool(enter.succeeded());'
266 elif member.GetName() == 'Create':
267 body = _MakeCreateMemberBody(node, member, args)
268 else:
269 body = _MakeNormalMemberBody(filenode, node, member, rtype, args, meta)
270
271 signature = cgen.GetSignature(member, release, 'return', func_as_ptr=False,
272 include_version=include_version)
273 member_code = '%s {\n%s\n}' % (signature, body)
274 return cgen.Indent(member_code, tabs=0)
275
276
277 class TGen(GeneratorByFile):
278 def __init__(self):
279 Generator.__init__(self, 'Thunk', 'tgen', 'Generate the C++ thunk.')
280
281 def GenerateFile(self, filenode, releases, options):
282 savename = _GetThunkFileName(filenode, GetOption('thunkroot'))
283 my_min, my_max = filenode.GetMinMax(releases)
284 if my_min > releases[-1] or my_max < releases[0]:
285 if os.path.isfile(savename):
286 print "Removing stale %s for this range." % filenode.GetName()
287 os.remove(os.path.realpath(savename))
288 return False
289 do_generate = filenode.GetProperty('generate_thunk')
290 if not do_generate:
291 return False
292
293 thunk_out = IDLOutFile(savename)
294 body, meta = self.GenerateBody(thunk_out, filenode, releases, options)
295 self.WriteHead(thunk_out, filenode, releases, options, meta)
296 thunk_out.Write('\n\n'.join(body))
297 self.WriteTail(thunk_out, filenode, releases, options)
298 return thunk_out.Close()
299
300 def WriteHead(self, out, filenode, releases, options, meta):
301 __pychecker__ = 'unusednames=options'
302 cgen = CGen()
303
304 cright_node = filenode.GetChildren()[0]
305 assert(cright_node.IsA('Copyright'))
306 out.Write('%s\n' % cgen.Copyright(cright_node, cpp_style=True))
307
308 # Wrap the From ... modified ... comment if it would be >80 characters.
309 from_text = 'From %s' % (
310 filenode.GetProperty('NAME').replace(os.sep,'/'))
311 modified_text = 'modified %s.' % (
312 filenode.GetProperty('DATETIME'))
313 if len(from_text) + len(modified_text) < 74:
314 out.Write('// %s %s\n\n' % (from_text, modified_text))
315 else:
316 out.Write('// %s,\n// %s\n\n' % (from_text, modified_text))
317
318
319 # TODO(teravest): Don't emit includes we don't need.
320 includes = ['ppapi/c/pp_errors.h',
321 'ppapi/shared_impl/tracked_callback.h',
322 'ppapi/thunk/enter.h',
323 'ppapi/thunk/ppb_instance_api.h',
324 'ppapi/thunk/resource_creation_api.h',
325 'ppapi/thunk/thunk.h']
326 includes.append(_GetHeaderFileName(filenode))
327 for api in meta.Apis():
328 includes.append('ppapi/thunk/%s.h' % api.lower())
329 for i in meta.Includes():
330 includes.append(i)
331 for include in sorted(includes):
332 out.Write('#include "%s"\n' % include)
333 out.Write('\n')
334 out.Write('namespace ppapi {\n')
335 out.Write('namespace thunk {\n')
336 out.Write('\n')
337 out.Write('namespace {\n')
338 out.Write('\n')
339
340 def GenerateBody(self, out, filenode, releases, options):
341 """Generates a member function lines to be written and metadata.
342
343 Returns a tuple of (body, meta) where:
344 body - a list of lines with member function bodies
345 meta - a ThunkMetadata instance for hinting which headers are needed.
346 """
347 __pychecker__ = 'unusednames=options'
348 members = []
349 meta = ThunkBodyMetadata()
350 for node in filenode.GetListOf('Interface'):
351 # Skip if this node is not in this release
352 if not node.InReleases(releases):
353 print "Skipping %s" % node
354 continue
355
356 # Generate Member functions
357 if node.IsA('Interface'):
358 for child in node.GetListOf('Member'):
359 build_list = child.GetUniqueReleases(releases)
360 # We have to filter out releases this node isn't in.
361 build_list = filter(lambda r: child.InReleases([r]), build_list)
362 if len(build_list) == 0:
363 continue
364 release = build_list[-1] # Pick the newest release.
365 member = DefineMember(filenode, node, child, release, False, meta)
366 if not member:
367 continue
368 members.append(member)
369 for build in build_list[:-1]:
370 member = DefineMember(filenode, node, child, build, True, meta)
371 if not member:
372 continue
373 members.append(member)
374 return (members, meta)
375
376 def WriteTail(self, out, filenode, releases, options):
377 __pychecker__ = 'unusednames=options'
378 cgen = CGen()
379
380 version_list = []
381 out.Write('\n\n')
382 for node in filenode.GetListOf('Interface'):
383 build_list = node.GetUniqueReleases(releases)
384 for build in build_list:
385 version = node.GetVersion(build).replace('.', '_')
386 thunk_name = 'g_' + node.GetName().lower() + '_thunk_' + \
387 version
388 thunk_type = '_'.join((node.GetName(), version))
389 version_list.append((thunk_type, thunk_name))
390
391 out.Write('const %s %s = {\n' % (thunk_type, thunk_name))
392 generated_functions = []
393 for child in node.GetListOf('Member'):
394 rtype, name, arrays, args = cgen.GetComponents(
395 child, build, 'return')
396 if child.InReleases([build]):
397 generated_functions.append(name)
398 out.Write(',\n'.join([' &%s' % f for f in generated_functions]))
399 out.Write('\n};\n\n')
400
401 out.Write('} // namespace\n')
402 out.Write('\n')
403 for thunk_type, thunk_name in version_list:
404 thunk_decl = 'const %s* Get%s_Thunk() {\n' % (thunk_type, thunk_type)
405 if len(thunk_decl) > 80:
406 thunk_decl = 'const %s*\n Get%s_Thunk() {\n' % (thunk_type,
407 thunk_type)
408 out.Write(thunk_decl)
409 out.Write(' return &%s;\n' % thunk_name)
410 out.Write('}\n')
411 out.Write('\n')
412 out.Write('} // namespace thunk\n')
413 out.Write('} // namespace ppapi\n')
414
415
416 tgen = TGen()
417
418
419 def Main(args):
420 # Default invocation will verify the golden files are unchanged.
421 failed = 0
422 if not args:
423 args = ['--wnone', '--diff', '--test', '--thunkroot=.']
424
425 ParseOptions(args)
426
427 idldir = os.path.split(sys.argv[0])[0]
428 idldir = os.path.join(idldir, 'test_thunk', '*.idl')
429 filenames = glob.glob(idldir)
430 ast = ParseFiles(filenames)
431 if tgen.GenerateRange(ast, ['M13', 'M14'], {}):
432 print "Golden file for M13-M14 failed."
433 failed = 1
434 else:
435 print "Golden file for M13-M14 passed."
436
437 return failed
438
439
440 if __name__ == '__main__':
441 sys.exit(Main(sys.argv[1:]))
OLDNEW
« no previous file with comments | « third_party/chrome/ppapi/generators/idl_tests.py ('k') | third_party/chrome/ppapi/generators/idl_visitor.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698