Fix the no password save issue for ajax login
[chromium-blink-merge.git] / ppapi / generators / idl_thunk.py
blob3358f188f0a5641dcb66b9f64a24b6d0caf67137
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 C++ style thunks """
8 import glob
9 import os
10 import re
11 import sys
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
22 Option('thunkroot', 'Base directory of output',
23 default=os.path.join('..', 'thunk'))
26 class TGenError(Exception):
27 def __init__(self, msg):
28 self.value = msg
30 def __str__(self):
31 return repr(self.value)
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()
40 def AddApi(self, api):
41 self._apis.add(api)
43 def Apis(self):
44 return self._apis
46 def AddInclude(self, include):
47 self._includes.add(include)
49 def Includes(self):
50 return self._includes
53 def _GetBaseFileName(filenode):
54 """Returns the base name for output files, given the filenode.
56 Examples:
57 'dev/ppb_find_dev.h' -> 'ppb_find_dev'
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 return name
65 def _GetHeaderFileName(filenode):
66 """Returns the name for the header for this file."""
67 path, name = os.path.split(filenode.GetProperty('NAME'))
68 name = os.path.splitext(name)[0]
69 if path:
70 header = "ppapi/c/%s/%s.h" % (path, name)
71 else:
72 header = "ppapi/c/%s.h" % name
73 return header
76 def _GetThunkFileName(filenode, relpath):
77 """Returns the thunk file name."""
78 path = os.path.split(filenode.GetProperty('NAME'))[0]
79 name = _GetBaseFileName(filenode)
80 # We don't reattach the path for thunk.
81 if relpath: name = os.path.join(relpath, name)
82 name = '%s%s' % (name, '_thunk.cc')
83 return name
86 def _MakeEnterLine(filenode, interface, arg, handle_errors, callback, meta):
87 """Returns an EnterInstance/EnterResource string for a function."""
88 if arg[0] == 'PP_Instance':
89 if callback is None:
90 return 'EnterInstance enter(%s);' % arg[1]
91 else:
92 return 'EnterInstance enter(%s, %s);' % (arg[1], callback)
93 elif arg[0] == 'PP_Resource':
94 api_name = interface.GetName()
95 if api_name.endswith('Trusted'):
96 api_name = api_name[:-len('Trusted')]
97 if api_name.endswith('_Dev'):
98 api_name = api_name[:-len('_Dev')]
99 api_name += '_API'
101 enter_type = 'EnterResource<%s>' % api_name
102 # The API header matches the file name, not the interface name.
103 api_basename = _GetBaseFileName(filenode)
104 if api_basename.endswith('_dev'):
105 # Clip off _dev suffix.
106 api_basename = api_basename[:-len('_dev')]
107 if api_basename.endswith('_trusted'):
108 # Clip off _trusted suffix.
109 api_basename = api_basename[:-len('_trusted')]
110 meta.AddApi(api_basename + '_api')
112 if callback is None:
113 return '%s enter(%s, %s);' % (enter_type, arg[1],
114 str(handle_errors).lower())
115 else:
116 return '%s enter(%s, %s, %s);' % (enter_type, arg[1],
117 callback,
118 str(handle_errors).lower())
119 else:
120 raise TGenError("Unknown type for _MakeEnterLine: %s" % arg[0])
123 def _GetShortName(interface, filter_suffixes):
124 """Return a shorter interface name that matches Is* and Create* functions."""
125 parts = interface.GetName().split('_')[1:]
126 tail = parts[len(parts) - 1]
127 if tail in filter_suffixes:
128 parts = parts[:-1]
129 return ''.join(parts)
132 def _IsTypeCheck(interface, node):
133 """Returns true if node represents a type-checking function."""
134 return node.GetName() == 'Is%s' % _GetShortName(interface, ['Dev', 'Private'])
137 def _GetCreateFuncName(interface):
138 """Returns the creation function name for an interface."""
139 return 'Create%s' % _GetShortName(interface, ['Dev'])
142 def _GetDefaultFailureValue(t):
143 """Returns the default failure value for a given type.
145 Returns None if no default failure value exists for the type.
147 values = {
148 'PP_Bool': 'PP_FALSE',
149 'PP_Resource': '0',
150 'struct PP_Var': 'PP_MakeUndefined()',
151 'float': '0.0f',
152 'int32_t': 'enter.retval()',
153 'uint16_t': '0',
154 'uint32_t': '0',
155 'uint64_t': '0',
157 if t in values:
158 return values[t]
159 return None
162 def _MakeCreateMemberBody(interface, member, args):
163 """Returns the body of a Create() function.
165 Args:
166 interface - IDLNode for the interface
167 member - IDLNode for member function
168 args - List of arguments for the Create() function
170 if args[0][0] == 'PP_Resource':
171 body = 'Resource* object =\n'
172 body += ' PpapiGlobals::Get()->GetResourceTracker()->'
173 body += 'GetResource(%s);\n' % args[0][1]
174 body += 'if (!object)\n'
175 body += ' return 0;\n'
176 body += 'EnterResourceCreation enter(object->pp_instance());\n'
177 elif args[0][0] == 'PP_Instance':
178 body = 'EnterResourceCreation enter(%s);\n' % args[0][1]
179 else:
180 raise TGenError('Unknown arg type for Create(): %s' % args[0][0])
182 body += 'if (enter.failed())\n'
183 body += ' return 0;\n'
184 arg_list = ', '.join([a[1] for a in args])
185 if member.GetProperty('create_func'):
186 create_func = member.GetProperty('create_func')
187 else:
188 create_func = _GetCreateFuncName(interface)
189 body += 'return enter.functions()->%s(%s);' % (create_func,
190 arg_list)
191 return body
194 def _MakeNormalMemberBody(filenode, release, node, member, rtype, args,
195 include_version, meta):
196 """Returns the body of a typical function.
198 Args:
199 filenode - IDLNode for the file
200 release - release to generate body for
201 node - IDLNode for the interface
202 member - IDLNode for the member function
203 rtype - Return type for the member function
204 args - List of 4-tuple arguments for the member function
205 include_version - whether to include the version in the invocation
206 meta - ThunkBodyMetadata for header hints
208 is_callback_func = args[len(args) - 1][0] == 'struct PP_CompletionCallback'
210 if is_callback_func:
211 call_args = args[:-1] + [('', 'enter.callback()', '', '')]
212 meta.AddInclude('ppapi/c/pp_completion_callback.h')
213 else:
214 call_args = args
216 if args[0][0] == 'PP_Instance':
217 call_arglist = ', '.join(a[1] for a in call_args)
218 function_container = 'functions'
219 else:
220 call_arglist = ', '.join(a[1] for a in call_args[1:])
221 function_container = 'object'
223 function_name = member.GetName()
224 if include_version:
225 version = node.GetVersion(release).replace('.', '_')
226 function_name += version
228 invocation = 'enter.%s()->%s(%s)' % (function_container,
229 function_name,
230 call_arglist)
232 handle_errors = not (member.GetProperty('report_errors') == 'False')
233 if is_callback_func:
234 body = '%s\n' % _MakeEnterLine(filenode, node, args[0], handle_errors,
235 args[len(args) - 1][1], meta)
236 body += 'if (enter.failed())\n'
237 value = member.GetProperty('on_failure')
238 if value is None:
239 value = 'enter.retval()'
240 body += ' return %s;\n' % value
241 body += 'return enter.SetResult(%s);' % invocation
242 elif rtype == 'void':
243 body = '%s\n' % _MakeEnterLine(filenode, node, args[0], handle_errors,
244 None, meta)
245 body += 'if (enter.succeeded())\n'
246 body += ' %s;' % invocation
247 else:
248 value = member.GetProperty('on_failure')
249 if value is None:
250 value = _GetDefaultFailureValue(rtype)
251 if value is None:
252 raise TGenError('No default value for rtype %s' % rtype)
254 body = '%s\n' % _MakeEnterLine(filenode, node, args[0], handle_errors,
255 None, meta)
256 body += 'if (enter.failed())\n'
257 body += ' return %s;\n' % value
258 body += 'return %s;' % invocation
259 return body
262 def DefineMember(filenode, node, member, release, include_version, meta):
263 """Returns a definition for a member function of an interface.
265 Args:
266 filenode - IDLNode for the file
267 node - IDLNode for the interface
268 member - IDLNode for the member function
269 release - release to generate
270 include_version - include the version in emitted function name.
271 meta - ThunkMetadata for header hints
272 Returns:
273 A string with the member definition.
275 cgen = CGen()
276 rtype, name, arrays, args = cgen.GetComponents(member, release, 'return')
278 if _IsTypeCheck(node, member):
279 body = '%s\n' % _MakeEnterLine(filenode, node, args[0], False, None, meta)
280 body += 'return PP_FromBool(enter.succeeded());'
281 elif member.GetName() == 'Create':
282 body = _MakeCreateMemberBody(node, member, args)
283 else:
284 body = _MakeNormalMemberBody(filenode, release, node, member, rtype, args,
285 include_version, meta)
287 signature = cgen.GetSignature(member, release, 'return', func_as_ptr=False,
288 include_version=include_version)
289 return '%s\n%s\n}' % (cgen.Indent('%s {' % signature, tabs=0),
290 cgen.Indent(body, tabs=1))
293 def _IsNewestMember(member, members, releases):
294 """Returns true if member is the newest node with its name in members.
296 Currently, every node in the AST only has one version. This means that we
297 will have two sibling nodes with the same name to represent different
298 versions.
299 See http://crbug.com/157017 .
301 Special handling is required for nodes which share their name with others,
302 but aren't the newest version in the IDL.
304 Args:
305 member - The member which is checked if it's newest
306 members - The list of members to inspect
307 releases - The set of releases to check for versions in.
309 build_list = member.GetUniqueReleases(releases)
310 release = build_list[0] # Pick the oldest release.
311 same_name_siblings = filter(
312 lambda n: str(n) == str(member) and n != member, members)
314 for s in same_name_siblings:
315 sibling_build_list = s.GetUniqueReleases(releases)
316 sibling_release = sibling_build_list[0]
317 if sibling_release > release:
318 return False
319 return True
322 class TGen(GeneratorByFile):
323 def __init__(self):
324 Generator.__init__(self, 'Thunk', 'tgen', 'Generate the C++ thunk.')
326 def GenerateFile(self, filenode, releases, options):
327 savename = _GetThunkFileName(filenode, GetOption('thunkroot'))
328 my_min, my_max = filenode.GetMinMax(releases)
329 if my_min > releases[-1] or my_max < releases[0]:
330 if os.path.isfile(savename):
331 print "Removing stale %s for this range." % filenode.GetName()
332 os.remove(os.path.realpath(savename))
333 return False
334 do_generate = filenode.GetProperty('generate_thunk')
335 if not do_generate:
336 return False
338 thunk_out = IDLOutFile(savename)
339 body, meta = self.GenerateBody(thunk_out, filenode, releases, options)
340 self.WriteHead(thunk_out, filenode, releases, options, meta)
341 thunk_out.Write('\n\n'.join(body))
342 self.WriteTail(thunk_out, filenode, releases, options)
343 return thunk_out.Close()
345 def WriteHead(self, out, filenode, releases, options, meta):
346 __pychecker__ = 'unusednames=options'
347 cgen = CGen()
349 cright_node = filenode.GetChildren()[0]
350 assert(cright_node.IsA('Copyright'))
351 out.Write('%s\n' % cgen.Copyright(cright_node, cpp_style=True))
353 # Wrap the From ... modified ... comment if it would be >80 characters.
354 from_text = 'From %s' % (
355 filenode.GetProperty('NAME').replace(os.sep,'/'))
356 modified_text = 'modified %s.' % (
357 filenode.GetProperty('DATETIME'))
358 if len(from_text) + len(modified_text) < 74:
359 out.Write('// %s %s\n\n' % (from_text, modified_text))
360 else:
361 out.Write('// %s,\n// %s\n\n' % (from_text, modified_text))
364 # TODO(teravest): Don't emit includes we don't need.
365 includes = ['ppapi/c/pp_errors.h',
366 'ppapi/shared_impl/tracked_callback.h',
367 'ppapi/thunk/enter.h',
368 'ppapi/thunk/ppb_instance_api.h',
369 'ppapi/thunk/resource_creation_api.h',
370 'ppapi/thunk/thunk.h']
371 includes.append(_GetHeaderFileName(filenode))
372 for api in meta.Apis():
373 includes.append('ppapi/thunk/%s.h' % api.lower())
374 for i in meta.Includes():
375 includes.append(i)
376 for include in sorted(includes):
377 out.Write('#include "%s"\n' % include)
378 out.Write('\n')
379 out.Write('namespace ppapi {\n')
380 out.Write('namespace thunk {\n')
381 out.Write('\n')
382 out.Write('namespace {\n')
383 out.Write('\n')
385 def GenerateBody(self, out, filenode, releases, options):
386 """Generates a member function lines to be written and metadata.
388 Returns a tuple of (body, meta) where:
389 body - a list of lines with member function bodies
390 meta - a ThunkMetadata instance for hinting which headers are needed.
392 __pychecker__ = 'unusednames=options'
393 out_members = []
394 meta = ThunkBodyMetadata()
395 for node in filenode.GetListOf('Interface'):
396 # Skip if this node is not in this release
397 if not node.InReleases(releases):
398 print "Skipping %s" % node
399 continue
401 # Generate Member functions
402 if node.IsA('Interface'):
403 members = node.GetListOf('Member')
404 for child in members:
405 build_list = child.GetUniqueReleases(releases)
406 # We have to filter out releases this node isn't in.
407 build_list = filter(lambda r: child.InReleases([r]), build_list)
408 if len(build_list) == 0:
409 continue
410 assert(len(build_list) == 1)
411 release = build_list[-1]
412 include_version = not _IsNewestMember(child, members, releases)
413 member = DefineMember(filenode, node, child, release, include_version,
414 meta)
415 if not member:
416 continue
417 out_members.append(member)
418 return (out_members, meta)
420 def WriteTail(self, out, filenode, releases, options):
421 __pychecker__ = 'unusednames=options'
422 cgen = CGen()
424 version_list = []
425 out.Write('\n\n')
426 for node in filenode.GetListOf('Interface'):
427 build_list = node.GetUniqueReleases(releases)
428 for build in build_list:
429 version = node.GetVersion(build).replace('.', '_')
430 thunk_name = 'g_' + node.GetName().lower() + '_thunk_' + \
431 version
432 thunk_type = '_'.join((node.GetName(), version))
433 version_list.append((thunk_type, thunk_name))
435 declare_line = 'const %s %s = {' % (thunk_type, thunk_name)
436 if len(declare_line) > 80:
437 declare_line = 'const %s\n %s = {' % (thunk_type, thunk_name)
438 out.Write('%s\n' % declare_line)
439 generated_functions = []
440 members = node.GetListOf('Member')
441 for child in members:
442 rtype, name, arrays, args = cgen.GetComponents(
443 child, build, 'return')
444 if not _IsNewestMember(child, members, releases):
445 version = node.GetVersion(build).replace('.', '_')
446 name += '_' + version
447 if child.InReleases([build]):
448 generated_functions.append(name)
449 out.Write(',\n'.join([' &%s' % f for f in generated_functions]))
450 out.Write('\n};\n\n')
452 out.Write('} // namespace\n')
453 out.Write('\n')
454 for thunk_type, thunk_name in version_list:
455 thunk_decl = 'const %s* Get%s_Thunk() {\n' % (thunk_type, thunk_type)
456 if len(thunk_decl) > 80:
457 thunk_decl = 'const %s*\n Get%s_Thunk() {\n' % (thunk_type,
458 thunk_type)
459 out.Write(thunk_decl)
460 out.Write(' return &%s;\n' % thunk_name)
461 out.Write('}\n')
462 out.Write('\n')
463 out.Write('} // namespace thunk\n')
464 out.Write('} // namespace ppapi\n')
467 tgen = TGen()
470 def Main(args):
471 # Default invocation will verify the golden files are unchanged.
472 failed = 0
473 if not args:
474 args = ['--wnone', '--diff', '--test', '--thunkroot=.']
476 ParseOptions(args)
478 idldir = os.path.split(sys.argv[0])[0]
479 idldir = os.path.join(idldir, 'test_thunk', '*.idl')
480 filenames = glob.glob(idldir)
481 ast = ParseFiles(filenames)
482 if tgen.GenerateRange(ast, ['M13', 'M14'], {}):
483 print "Golden file for M13-M14 failed."
484 failed = 1
485 else:
486 print "Golden file for M13-M14 passed."
488 return failed
491 if __name__ == '__main__':
492 sys.exit(Main(sys.argv[1:]))