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