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

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

Issue 11417010: Add support for generating thunk source from IDL. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Removed an unnecessary change Created 8 years, 1 month 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 def _GetBaseName(filenode):
35 """Returns the base name for an interface, given the filename."""
noelallen1 2012/11/15 22:03:18 FYI: Interface name does not always match source f
teravest 2012/11/16 17:33:36 I changed the method name to _GetBaseFileName to m
dmichael (off chromium) 2012/11/16 18:24:44 Maybe explain what "base name" means? Just an exam
teravest 2012/11/16 18:52:36 Examples added.
36 path, name = os.path.split(filenode.GetProperty('NAME'))
37 name = os.path.splitext(name)[0]
38 if name.endswith('_dev'):
39 # Clip off _dev suffix.
40 name = name[:-len('_dev')]
41 return name
42
43
44 def _GetHeaderFileName(filenode):
45 """Returns the name for the header for this file."""
46 path, name = os.path.split(filenode.GetProperty('NAME'))
47 name = os.path.splitext(name)[0]
48 if path:
49 header = "ppapi/c/%s/%s.h" % (path, name)
50 else:
51 header = "ppapi/c/%s.h" % name
52 return header
53
54
55 def _GetThunkFileName(filenode, relpath):
56 """Returns the thunk file name."""
57 path = os.path.split(filenode.GetProperty('NAME'))[0]
58 name = _GetBaseName(filenode)
59 # We don't reattach the path for thunk.
60 if relpath: name = os.path.join(relpath, name)
61 name = '%s%s' % (name, '_thunk.cc')
62 return name
63
64
65 def _MakeEnterLine(filenode, interface, arg, handle_errors, callback):
66 """Returns an EnterInstance/EnterResource string for a function."""
67 if arg[0] == 'PP_Instance':
68 if callback is None:
69 return 'EnterInstance enter(%s);' % arg[1]
70 else:
71 return 'EnterInstance enter(%s, %s);' % (arg[1], callback)
72 elif arg[0] == 'PP_Resource':
73 api_name = interface.GetName()
74 if api_name.endswith('_Dev'):
75 api_name = api_name[:-len('_Dev')]
76 api_name += '_API'
77
78 enter_type = 'EnterResource<%s>' % api_name
79 if callback is None:
80 return '%s enter(%s, %s);' % (enter_type, arg[1],
81 str(handle_errors).lower())
82 else:
83 return '%s enter(%s, %s, %s);' % (enter_type, arg[1],
84 callback,
85 str(handle_errors).lower())
86 else:
87 raise TGenError("Unknown type for _MakeEnterLine: %s" % arg[0])
88
89
90 def _GetShortName(interface, filter_suffixes):
91 """Return a shorter interface name that matches Is* and Create* functions."""
92 parts = interface.GetName().split('_')[1:]
93 tail = parts[len(parts) - 1]
94 if tail in filter_suffixes:
95 parts = parts[:-1]
96 return ''.join(parts)
97
98
99 def _IsTypeCheck(interface, node):
100 """Returns true if node represents a type-checking function."""
101 return node.GetName() == 'Is%s' % _GetShortName(interface, ['Dev', 'Private'])
102
103
104 def _GetCreateFuncName(interface):
105 """Returns the creation function name for an interface."""
106 return 'Create%s' % _GetShortName(interface, ['Dev'])
107
108
109 def _GetDefaultFailureValue(t):
110 """Returns the default failure value for a given type.
111
112 Returns None if no default failure value exists for the type.
113 """
114 values = {
115 'PP_Bool': 'PP_FALSE',
116 'PP_Resource': '0',
117 'struct PP_Var': 'PP_MakeUndefined()',
118 'int32_t': 'enter.retval()',
119 'uint16_t': '0',
120 'uint32_t': '0',
121 'uint64_t': '0',
122 }
123 if t in values:
124 return values[t]
125 return None
126
127
128 def _MakeCreateMemberBody(interface, member, args):
129 """Returns the body of a Create() function.
130
131 Args:
132 interface - IDLNode for the interface
133 member - IDLNode for member function
134 args - List of arguments for the Create() function
135 """
136 if args[0][0] == 'PP_Resource':
137 body = ' Resource* object =\n'
138 body += ' PpapiGlobals::Get()->GetResourceTracker()->'
139 body += 'GetResource(%s);\n' % args[0][1]
140 body += ' if (!object)\n'
141 body += ' return 0;\n'
142 body += ' EnterResourceCreation enter(object->pp_instance());\n'
143 elif args[0][0] == 'PP_Instance':
144 body = ' EnterResourceCreation enter(%s);\n' % args[0][1]
145 else:
146 raise TGenError('Unknown arg type for Create(): %s' % args[0][0])
147
148 body += ' if (enter.failed())\n'
149 body += ' return 0;\n'
150 arg_list = ', '.join([a[1] for a in args])
151 if member.GetProperty('create_func'):
152 create_func = member.GetProperty('create_func')
153 else:
154 create_func = _GetCreateFuncName(interface)
155 body += ' return enter.functions()->%s(%s);' % (create_func,
156 arg_list)
157 return body
158
159
160 def _MakeNormalMemberBody(filenode, node, member, rtype, args):
161 """Returns the body of a typical function.
162
163 Args:
164 filenode - IDLNode for the file
165 node - IDLNode for the interface
166 member - IDLNode for the member function
167 rtype - Return type for the member function
168 args - List of arguments for the member function
dmichael (off chromium) 2012/11/16 18:24:44 maybe mention that it's a list of 2-element lists,
teravest 2012/11/16 18:52:36 It's actually a list of 4 element tuples, which is
noelallen1 2012/11/16 20:27:28 I've been meaning to make that change for a while.
169 """
170 is_callback_func = args[len(args) - 1][0] == 'struct PP_CompletionCallback'
171
172 if is_callback_func:
173 call_args = args[:-1] + [('', 'enter.callback()', '', '')]
174 else:
175 call_args = args
176
177 if args[0][0] == 'PP_Instance':
178 call_arglist = ', '.join(a[1] for a in call_args)
179 function_container = 'functions'
180 else:
181 call_arglist = ', '.join(a[1] for a in call_args[1:])
182 function_container = 'object'
183
184 invocation = 'enter.%s()->%s(%s)' % (function_container,
185 member.GetName(),
186 call_arglist)
187
188 handle_errors = not (member.GetProperty('report_errors') == 'False')
189 if is_callback_func:
190 body = ' %s\n' % _MakeEnterLine(filenode, node, args[0], handle_errors,
191 args[len(args) - 1][1])
192 body += ' if (enter.failed())\n'
193 value = member.GetProperty('on_failure')
194 if value is None:
195 value = 'enter.retval()'
196 body += ' return %s;\n' % value
197 body += ' return enter.SetResult(%s);\n' % invocation
198 elif rtype == 'void':
199 body = ' %s\n' % _MakeEnterLine(filenode, node, args[0], handle_errors,
200 None)
201 body += ' if (enter.succeeded())\n'
202 body += ' %s;' % invocation
203 else:
204 value = member.GetProperty('on_failure')
205 if value is None:
206 value = _GetDefaultFailureValue(rtype)
207 if value is None:
208 raise TGenError('No default value for rtype %s' % rtype)
209
210 body = ' %s\n' % _MakeEnterLine(filenode, node, args[0], handle_errors,
211 None)
212 body += ' if (enter.failed())\n'
213 body += ' return %s;\n' % value
214 body += ' return %s;' % invocation
215 return body
216
217
218 def DefineMember(filenode, node, member, release, include_version):
219 """Returns a definition for a member function of an interface.
220
221 Args:
222 filenode - IDLNode for the file
223 node - IDLNode for the interface
224 member - IDLNode for the member function
225 release - release to generate
226 include_version - include the version in emitted function name.
227 Returns:
228 A string with the member definition.
229 """
230 cgen = CGen()
231 rtype, name, arrays, args = cgen.GetComponents(member, release, 'return')
232
233 if _IsTypeCheck(node, member):
234 body = ' %s\n' % _MakeEnterLine(filenode, node, args[0], False, None)
235 body += ' return PP_FromBool(enter.succeeded());'
236 elif member.GetName() == 'Create':
237 body = _MakeCreateMemberBody(node, member, args)
238 else:
239 body = _MakeNormalMemberBody(filenode, node, member, rtype, args)
240
241 signature = cgen.GetSignature(member, release, 'return', func_as_ptr=False,
242 include_version=include_version)
243 member_code = '%s {\n%s\n}' % (signature, body)
244 return cgen.Indent(member_code, tabs=0)
245
246
247 class TGen(GeneratorByFile):
248 def __init__(self):
249 Generator.__init__(self, 'Thunk', 'tgen', 'Generate the C++ thunk.')
250
251 def GenerateFile(self, filenode, releases, options):
252 savename = _GetThunkFileName(filenode, GetOption('thunkroot'))
253 my_min, my_max = filenode.GetMinMax(releases)
254 if my_min > releases[-1] or my_max < releases[0]:
255 if os.path.isfile(savename):
256 print "Removing stale %s for this range." % filenode.GetName()
257 os.remove(os.path.realpath(savename))
258 return False
259 do_generate = filenode.GetProperty('generate_thunk')
260 if not do_generate:
261 return False
262
263 thunk_out = IDLOutFile(savename)
264 self.GenerateHead(thunk_out, filenode, releases, options)
265 self.GenerateBody(thunk_out, filenode, releases, options)
266 self.GenerateTail(thunk_out, filenode, releases, options)
267 return thunk_out.Close()
268
269 def GenerateHead(self, out, filenode, releases, options):
270 __pychecker__ = 'unusednames=options'
271 cgen = CGen()
272
273 cright_node = filenode.GetChildren()[0]
274 assert(cright_node.IsA('Copyright'))
275 out.Write('%s\n' % cgen.Copyright(cright_node))
276
277 # Wrap the From ... modified ... comment if it would be >80 characters.
278 from_text = 'From %s' % (
279 filenode.GetProperty('NAME').replace(os.sep,'/'))
280 modified_text = 'modified %s.' % (
281 filenode.GetProperty('DATETIME'))
282 if len(from_text) + len(modified_text) < 74:
283 out.Write('/* %s %s */\n\n' % (from_text, modified_text))
284 else:
285 out.Write('/* %s,\n * %s\n */\n\n' % (from_text, modified_text))
286
287
288 # TODO(teravest): Don't emit includes we don't need.
289 includes = ['ppapi/c/pp_errors.h',
290 'ppapi/shared_impl/tracked_callback.h',
291 'ppapi/thunk/enter.h',
292 'ppapi/thunk/ppb_instance_api.h',
293 'ppapi/thunk/resource_creation_api.h',
294 'ppapi/thunk/thunk.h']
295 includes.append(_GetHeaderFileName(filenode))
296 includes.append('ppapi/thunk/%s_api.h' % _GetBaseName(filenode))
297 for include in sorted(includes):
298 out.Write('#include "%s"\n' % include)
299 out.Write('\n')
300 out.Write('namespace ppapi {\n')
301 out.Write('namespace thunk {\n')
302 out.Write('\n')
303 out.Write('namespace {\n')
304 out.Write('\n')
305
306 def GenerateBody(self, out, filenode, releases, options):
307 __pychecker__ = 'unusednames=options'
308 for node in filenode.GetListOf('Interface'):
309 # Skip if this node is not in this release
310 if not node.InReleases(releases):
311 print "Skipping %s" % node
312 continue
313
314 # Generate Member functions
315 if node.IsA('Interface'):
316 members = []
317 for child in node.GetListOf('Member'):
318 build_list = child.GetUniqueReleases(releases)
319 # We have to filter out releases this node isn't in.
320 build_list = filter(lambda r: child.InReleases([r]), build_list)
321 if len(build_list) == 0:
322 continue
323 release = build_list[-1] # Pick the newest release.
324 member = DefineMember(filenode, node, child, release, False)
325 if not member:
326 continue
327 members.append(member)
328 for build in build_list[:-1]:
329 member = DefineMember(filenode, node, child, build, True)
330 if not member:
331 continue
332 members.append(member)
333 out.Write('\n\n'.join(members))
334
335 def GenerateTail(self, out, filenode, releases, options):
336 __pychecker__ = 'unusednames=options'
337 cgen = CGen()
338
339 version_list = []
340 out.Write('\n\n')
341 for node in filenode.GetListOf('Interface'):
342 build_list = node.GetUniqueReleases(releases)
343 for build in build_list:
344 version = node.GetVersion(build).replace('.', '_')
345 thunk_name = 'g_' + node.GetName().lower() + '_thunk_' + \
346 version
347 thunk_type = '_'.join((node.GetName(), version))
348 version_list.append((thunk_type, thunk_name))
349
350 out.Write('const %s %s = {\n' % (thunk_type, thunk_name))
351 for child in node.GetListOf('Member'):
352 rtype, name, arrays, args = cgen.GetComponents(
353 child, build, 'return')
354 if child.InReleases([build]): # TEST
355 out.Write(' &%s,\n' % name)
356 out.Write('};\n\n')
357
358 out.Write('} // namespace\n')
359 out.Write('\n')
360 for thunk_type, thunk_name in version_list:
361 thunk_decl = 'const %s* Get%s_Thunk() {\n' % (thunk_type, thunk_type)
362 if len(thunk_decl) > 80:
363 thunk_decl = 'const %s*\n Get%s_Thunk() {\n' % (thunk_type,
364 thunk_type)
365 out.Write(thunk_decl)
366 out.Write(' return &%s;\n' % thunk_name)
367 out.Write('}\n')
368 out.Write('\n')
369 out.Write('} // namespace thunk\n')
370 out.Write('} // namespace ppapi\n')
371
372
373 tgen = TGen()
374
375
376 def Main(args):
377 # Default invocation will verify the golden files are unchanged.
378 failed = 0
379 if not args:
380 args = ['--wnone', '--diff', '--test', '--thunkroot=.']
381
382 ParseOptions(args)
383
384 idldir = os.path.split(sys.argv[0])[0]
385 idldir = os.path.join(idldir, 'test_thunk', '*.idl')
386 filenames = glob.glob(idldir)
387 ast = ParseFiles(filenames)
388 if tgen.GenerateRange(ast, ['M13', 'M14'], {}):
389 print "Golden file for M13-M14 failed."
390 failed = 1
391 else:
392 print "Golden file for M13-M14 passed."
393
394 return failed
395
396
397 if __name__ == '__main__':
398 sys.exit(Main(sys.argv[1:]))
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698