cc: Added inline to Tile::IsReadyToDraw
[chromium-blink-merge.git] / ppapi / native_client / src / tools / srpcgen.py
blob91901c10e2b2e84ab53b33a17251f10a7b863a47
1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Native Client 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 """Build "SRPC" interfaces from specifications.
8 SRPC interfaces consist of one or more interface classes, typically defined
9 in a set of .srpc files. The specifications are Python dictionaries, with a
10 top level 'name' element and an 'rpcs' element. The rpcs element is a list
11 containing a number of rpc methods, each of which has a 'name', an 'inputs',
12 and an 'outputs' element. These elements are lists of input or output
13 parameters, which are lists pairs containing a name and type. The set of
14 types includes all the SRPC basic types.
16 These SRPC specifications are used to generate a header file and either a
17 server or client stub file, as determined by the command line flag -s or -c.
18 """
20 import getopt
21 import sys
22 import os
24 COPYRIGHT_AND_AUTOGEN_COMMENT = """\
25 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
26 // Use of this source code is governed by a BSD-style license that can be
27 // found in the LICENSE file.
29 // WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
31 // Automatically generated code. See srpcgen.py
33 // NaCl Simple Remote Procedure Call interface abstractions.
34 """
36 HEADER_INCLUDE_GUARD_START = """\
37 #ifndef %(include_guard)s
38 #define %(include_guard)s
39 """
41 HEADER_INCLUDE_GUARD_END = """\
42 \n\n#endif // %(include_guard)s
43 """
45 HEADER_FILE_INCLUDES = """\
46 #ifndef __native_client__
47 #include "native_client/src/include/portability.h"
48 #endif // __native_client__
49 %(EXTRA_INCLUDES)s
50 """
52 SOURCE_FILE_INCLUDES = """\
53 #include "%(srpcgen_h)s"
54 #ifdef __native_client__
55 #ifndef UNREFERENCED_PARAMETER
56 #define UNREFERENCED_PARAMETER(P) do { (void) P; } while (0)
57 #endif // UNREFERENCED_PARAMETER
58 #else
59 #include "native_client/src/include/portability.h"
60 #endif // __native_client__
61 %(EXTRA_INCLUDES)s
62 """
64 # For both .cc and .h files.
65 EXTRA_INCLUDES = [
66 '#include "native_client/src/shared/srpc/nacl_srpc.h"',
69 types = {'bool': ['b', 'bool', 'u.bval', ''],
70 'char[]': ['C', 'char*', 'arrays.carr', 'u.count'],
71 'double': ['d', 'double', 'u.dval', ''],
72 'double[]': ['D', 'double*', 'arrays.darr', 'u.count'],
73 'handle': ['h', 'NaClSrpcImcDescType', 'u.hval', ''],
74 'int32_t': ['i', 'int32_t', 'u.ival', ''],
75 'int32_t[]': ['I', 'int32_t*', 'arrays.iarr', 'u.count'],
76 'int64_t': ['l', 'int64_t', 'u.lval', ''],
77 'int64_t[]': ['L', 'int64_t', 'arrays.larr', 'u.count'],
78 'PP_Instance': ['i', 'PP_Instance', 'u.ival', ''],
79 'PP_Module': ['i', 'PP_Module', 'u.ival', ''],
80 'PP_Resource': ['i', 'PP_Resource', 'u.ival', ''],
81 'string': ['s', 'const char*', 'arrays.str', ''],
84 def AddInclude(name):
85 """Adds an include to the include section of both .cc and .h files."""
86 EXTRA_INCLUDES.append('#include "%s"' % name)
89 def HeaderFileIncludes():
90 """Includes are sorted alphabetically."""
91 EXTRA_INCLUDES.sort()
92 return HEADER_FILE_INCLUDES % {
93 'EXTRA_INCLUDES': '\n'.join(EXTRA_INCLUDES),
97 def SourceFileIncludes(srpcgen_h_file):
98 """Includes are sorted alphabetically."""
99 EXTRA_INCLUDES.sort()
100 return SOURCE_FILE_INCLUDES % {
101 'EXTRA_INCLUDES': '\n'.join(EXTRA_INCLUDES),
102 'srpcgen_h': srpcgen_h_file
106 def PrintHeaderFileTop(output, include_guard):
107 """Prints the header of the .h file including copyright,
108 header comment, include guard and includes."""
109 print >>output, COPYRIGHT_AND_AUTOGEN_COMMENT
110 print >>output, HEADER_INCLUDE_GUARD_START % {'include_guard': include_guard}
111 print >>output, HeaderFileIncludes()
114 def PrintHeaderFileBottom(output, include_guard):
115 """Prints the footer of the .h file including copyright,
116 header comment, include guard and includes."""
117 print >>output, HEADER_INCLUDE_GUARD_END % {'include_guard': include_guard}
120 def PrintSourceFileTop(output, srpcgen_h_file):
121 """Prints the header of the .cc file including copyright,
122 header comment and includes."""
123 print >>output, COPYRIGHT_AND_AUTOGEN_COMMENT
124 print >>output, SourceFileIncludes(srpcgen_h_file)
127 def CountName(name):
128 """Returns the name of the auxiliary count member used for array typed."""
129 return '%s_bytes' % name
132 def FormatRpcPrototype(is_server, class_name, indent, rpc):
133 """Returns a string for the prototype of an individual RPC."""
135 def FormatArgs(is_output, args):
136 """Returns a string containing the formatted arguments for an RPC."""
138 def FormatArg(is_output, arg):
139 """Returns a string containing a formatted argument to an RPC."""
140 if is_output:
141 suffix = '* '
142 else:
143 suffix = ' '
144 s = ''
145 type_info = types[arg[1]]
146 if type_info[3]:
147 s += 'nacl_abi_size_t%s%s, %s %s' % (suffix,
148 CountName(arg[0]),
149 type_info[1],
150 arg[0])
151 else:
152 s += '%s%s%s' % (type_info[1], suffix, arg[0])
153 return s
154 s = ''
155 for arg in args:
156 s += ',\n %s%s' % (indent, FormatArg(is_output, arg))
157 return s
158 if is_server:
159 ret_type = 'void'
160 else:
161 ret_type = 'NaClSrpcError'
162 s = '%s %s%s(\n' % (ret_type, class_name, rpc['name'])
163 # Until SRPC uses RPC/Closure on the client side, these must be different.
164 if is_server:
165 s += ' %sNaClSrpcRpc* rpc,\n' % indent
166 s += ' %sNaClSrpcClosure* done' % indent
167 else:
168 s += ' %sNaClSrpcChannel* channel' % indent
169 s += '%s' % FormatArgs(False, rpc['inputs'])
170 s += '%s' % FormatArgs(True, rpc['outputs'])
171 s += ')'
172 return s
175 def PrintHeaderFile(output, is_server, guard_name, interface_name, specs):
176 """Prints out the header file containing the prototypes for the RPCs."""
177 PrintHeaderFileTop(output, guard_name)
178 s = ''
179 # iterate over all the specified interfaces
180 if is_server:
181 suffix = 'Server'
182 else:
183 suffix = 'Client'
184 for spec in specs:
185 class_name = spec['name'] + suffix
186 rpcs = spec['rpcs']
187 s += 'class %s {\n public:\n' % class_name
188 for rpc in rpcs:
189 s += ' static %s;\n' % FormatRpcPrototype(is_server, '', ' ', rpc)
190 s += '\n private:\n %s();\n' % class_name
191 s += ' %s(const %s&);\n' % (class_name, class_name)
192 s += ' void operator=(const %s);\n' % class_name
193 s += '}; // class %s\n\n' % class_name
194 if is_server:
195 s += 'class %s {\n' % interface_name
196 s += ' public:\n'
197 s += ' static NaClSrpcHandlerDesc srpc_methods[];\n'
198 s += '}; // class %s' % interface_name
199 print >>output, s
200 PrintHeaderFileBottom(output, guard_name)
203 def PrintServerFile(output, header_name, interface_name, specs):
204 """Print the server (stub) .cc file."""
206 def FormatDispatchPrototype(indent, rpc):
207 """Format the prototype of a dispatcher method."""
208 s = '%sstatic void %sDispatcher(\n' % (indent, rpc['name'])
209 s += '%s NaClSrpcRpc* rpc,\n' % indent
210 s += '%s NaClSrpcArg** inputs,\n' % indent
211 s += '%s NaClSrpcArg** outputs,\n' % indent
212 s += '%s NaClSrpcClosure* done\n' % indent
213 s += '%s)' % indent
214 return s
216 def FormatMethodString(rpc):
217 """Format the SRPC text string for a single rpc method."""
219 def FormatTypes(args):
220 s = ''
221 for arg in args:
222 s += types[arg[1]][0]
223 return s
224 s = ' { "%s:%s:%s", %sDispatcher },\n' % (rpc['name'],
225 FormatTypes(rpc['inputs']),
226 FormatTypes(rpc['outputs']),
227 rpc['name'])
228 return s
230 def FormatCall(class_name, indent, rpc):
231 """Format a call from a dispatcher method to its stub."""
233 def FormatArgs(is_output, args):
234 """Format the arguments passed to the stub."""
236 def FormatArg(is_output, num, arg):
237 """Format an argument passed to a stub."""
238 if is_output:
239 prefix = 'outputs[' + str(num) + ']->'
240 addr_prefix = '&('
241 addr_suffix = ')'
242 else:
243 prefix = 'inputs[' + str(num) + ']->'
244 addr_prefix = ''
245 addr_suffix = ''
246 type_info = types[arg[1]]
247 if type_info[3]:
248 s = '%s%s%s%s, %s%s' % (addr_prefix,
249 prefix,
250 type_info[3],
251 addr_suffix,
252 prefix,
253 type_info[2])
254 else:
255 s = '%s%s%s%s' % (addr_prefix, prefix, type_info[2], addr_suffix)
256 return s
257 # end FormatArg
258 s = ''
259 num = 0
260 for arg in args:
261 s += ',\n%s %s' % (indent, FormatArg(is_output, num, arg))
262 num += 1
263 return s
264 # end FormatArgs
265 s = '%s::%s(\n%s rpc,\n' % (class_name, rpc['name'], indent)
266 s += '%s done' % indent
267 s += FormatArgs(False, rpc['inputs'])
268 s += FormatArgs(True, rpc['outputs'])
269 s += '\n%s)' % indent
270 return s
271 # end FormatCall
273 PrintSourceFileTop(output, header_name)
274 s = 'namespace {\n\n'
275 for spec in specs:
276 class_name = spec['name'] + 'Server'
277 rpcs = spec['rpcs']
278 for rpc in rpcs:
279 s += '%s {\n' % FormatDispatchPrototype('', rpc)
280 if rpc['inputs'] == []:
281 s += ' UNREFERENCED_PARAMETER(inputs);\n'
282 if rpc['outputs'] == []:
283 s += ' UNREFERENCED_PARAMETER(outputs);\n'
284 s += ' %s;\n' % FormatCall(class_name, ' ', rpc)
285 s += '}\n\n'
286 s += '} // namespace\n\n'
287 s += 'NaClSrpcHandlerDesc %s::srpc_methods[] = {\n' % interface_name
288 for spec in specs:
289 class_name = spec['name'] + 'Server'
290 rpcs = spec['rpcs']
291 for rpc in rpcs:
292 s += FormatMethodString(rpc)
293 s += ' { NULL, NULL }\n};\n'
294 print >>output, s
297 def PrintClientFile(output, header_name, specs, thread_check):
298 """Prints the client (proxy) .cc file."""
300 def InstanceInputArg(rpc):
301 """Returns the name of the PP_Instance arg or None if there is none."""
302 for arg in rpc['inputs']:
303 if arg[1] == 'PP_Instance':
304 return arg[0]
305 return None
307 def DeadNexeHandling(rpc, retval):
308 """Generates the code necessary to handle death of a nexe during the rpc
309 call. This is only possible if PP_Instance arg is present, otherwise"""
310 instance = InstanceInputArg(rpc);
311 if instance is not None:
312 check = (' if (%s == NACL_SRPC_RESULT_INTERNAL)\n'
313 ' ppapi_proxy::CleanUpAfterDeadNexe(%s);\n')
314 return check % (retval, instance)
315 return '' # No handling
318 def FormatCall(rpc):
319 """Format a call to the generic dispatcher, NaClSrpcInvokeBySignature."""
321 def FormatTypes(args):
322 """Format a the type signature string for either inputs or outputs."""
323 s = ''
324 for arg in args:
325 s += types[arg[1]][0]
326 return s
327 def FormatArgs(args):
328 """Format the arguments for the call to the generic dispatcher."""
330 def FormatArg(arg):
331 """Format a single argument for the call to the generic dispatcher."""
332 s = ''
333 type_info = types[arg[1]]
334 if type_info[3]:
335 s += '%s, ' % CountName(arg[0])
336 s += arg[0]
337 return s
338 # end FormatArg
339 s = ''
340 for arg in args:
341 s += ',\n %s' % FormatArg(arg)
342 return s
343 #end FormatArgs
344 s = '(\n channel,\n "%s:%s:%s"' % (rpc['name'],
345 FormatTypes(rpc['inputs']),
346 FormatTypes(rpc['outputs']))
347 s += FormatArgs(rpc['inputs'])
348 s += FormatArgs(rpc['outputs']) + '\n )'
349 return s
350 # end FormatCall
352 # We need this to handle dead nexes.
353 if header_name.startswith('trusted'):
354 AddInclude('native_client/src/shared/ppapi_proxy/browser_globals.h')
355 if thread_check:
356 AddInclude('native_client/src/shared/ppapi_proxy/plugin_globals.h')
357 AddInclude('ppapi/c/ppb_core.h')
358 AddInclude('native_client/src/shared/platform/nacl_check.h')
359 PrintSourceFileTop(output, header_name)
360 s = ''
362 for spec in specs:
363 class_name = spec['name'] + 'Client'
364 rpcs = spec['rpcs']
365 for rpc in rpcs:
366 s += '%s {\n' % FormatRpcPrototype('', class_name + '::', '', rpc)
367 if thread_check and rpc['name'] not in ['PPB_GetInterface',
368 'PPB_Core_CallOnMainThread']:
369 error = '"%s: PPAPI calls are not supported off the main thread\\n"'
370 s += ' VCHECK(ppapi_proxy::PPBCoreInterface()->IsMainThread(),\n'
371 s += ' (%s,\n' % error
372 s += ' __FUNCTION__));\n'
373 s += ' NaClSrpcError retval;\n'
374 s += ' retval = NaClSrpcInvokeBySignature%s;\n' % FormatCall(rpc)
375 if header_name.startswith('trusted'):
376 s += DeadNexeHandling(rpc, 'retval')
377 s += ' return retval;\n'
378 s += '}\n\n'
379 print >>output, s
381 def MakePath(name):
382 paths = name.split(os.sep)
383 path = os.sep.join(paths[:-1])
384 try:
385 os.makedirs(path)
386 except OSError:
387 return
390 def main(argv):
391 usage = 'Usage: srpcgen.py <-c | -s> [--include=<name>] [--ppapi]'
392 usage = usage + ' <iname> <gname> <.h> <.cc> <specs>'
394 mode = None
395 ppapi = False
396 thread_check = False
397 try:
398 long_opts = ['include=', 'ppapi', 'thread-check']
399 opts, pargs = getopt.getopt(argv[1:], 'cs', long_opts)
400 except getopt.error, e:
401 print >>sys.stderr, 'Illegal option:', str(e)
402 print >>sys.stderr, usage
403 return 1
405 # Get the class name for the interface.
406 interface_name = pargs[0]
407 # Get the name for the token used as a multiple inclusion guard in the header.
408 include_guard_name = pargs[1]
409 # Get the name of the header file to be generated.
410 h_file_name = pargs[2]
411 MakePath(h_file_name)
412 # Note we open output files in binary mode so that on Windows the files
413 # will always get LF line-endings rather than CRLF.
414 h_file = open(h_file_name, 'wb')
415 # Get the name of the source file to be generated. Depending upon whether
416 # -c or -s is generated, this file contains either client or server methods.
417 cc_file_name = pargs[3]
418 MakePath(cc_file_name)
419 cc_file = open(cc_file_name, 'wb')
420 # The remaining arguments are the spec files to be compiled.
421 spec_files = pargs[4:]
423 for opt, val in opts:
424 if opt == '-c':
425 mode = 'client'
426 elif opt == '-s':
427 mode = 'server'
428 elif opt == '--include':
429 h_file_name = val
430 elif opt == '--ppapi':
431 ppapi = True
432 elif opt == '--thread-check':
433 thread_check = True
435 if ppapi:
436 AddInclude("ppapi/c/pp_instance.h")
437 AddInclude("ppapi/c/pp_module.h")
438 AddInclude("ppapi/c/pp_resource.h")
440 # Convert to forward slash paths if needed
441 h_file_name = "/".join(h_file_name.split("\\"))
443 # Verify we picked server or client mode
444 if not mode:
445 print >>sys.stderr, 'Neither -c nor -s specified'
446 usage()
447 return 1
449 # Combine the rpc specs from spec_files into rpcs.
450 specs = []
451 for spec_file in spec_files:
452 code_obj = compile(open(spec_file, 'r').read(), 'file', 'eval')
453 specs.append(eval(code_obj))
454 # Print out the requested files.
455 if mode == 'client':
456 PrintHeaderFile(h_file, False, include_guard_name, interface_name, specs)
457 PrintClientFile(cc_file, h_file_name, specs, thread_check)
458 elif mode == 'server':
459 PrintHeaderFile(h_file, True, include_guard_name, interface_name, specs)
460 PrintServerFile(cc_file, h_file_name, interface_name, specs)
462 return 0
465 if __name__ == '__main__':
466 sys.exit(main(sys.argv))