From: David Howells Date: Fri, 4 Sep 2015 10:22:01 +0000 (+0100) Subject: Rewrite rxgen in python using the ply module X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=refs%2Fheads%2Fmaster;p=users%2Fdhowells%2Fkafs-utils.git Rewrite rxgen in python using the ply module Signed-off-by: David Howells --- diff --git a/rxgen/emit_c_struct.py b/rxgen/emit_c_struct.py new file mode 100644 index 0000000..2078cda --- /dev/null +++ b/rxgen/emit_c_struct.py @@ -0,0 +1,121 @@ +#!/usr/bin/python3 +# +# Emit C structure +# -*- coding: utf-8 -*- + +__copyright__ = """ +Copyright (C) 2015 Red Hat, Inc. All Rights Reserved. +Written by David Howells (dhowells@redhat.com) + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public Licence version 2 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public Licence for more details. + +You should have received a copy of the GNU General Public Licence +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +""" + +from rxgen_bits import * + +############################################################################### +# +# Emit structure encoders and decoders predeclarations +# +############################################################################### +def emit_struct_encdec_decl(o, struct): + o.rxsrc("/* ", struct.name, " XDR size ", struct.xdr_size, " */\n") + +############################################################################### +# +# Emit structure encoders and decoders +# +############################################################################### +def emit_struct_encdec(o, struct): + # Write out a C structure definition for this type + o.rxhdr("struct ", struct.name, " {\n") + for m in struct.members: + ty = m.typespec + o.where(struct.name + "::" + m.name) + if ty.is_single(): + o.rxhdr("\t", ty.c_name, "\t", m.name) + elif ty.is_int_array() or ty.is_struct_array(): + o.rxhdr("\t", ty.c_name, "\t", m.name, "[", ty.dim, "]") + else: + o.error("Unsupported type '", ty, "'\n") + o.rxhdr(";\n") + o.rxhdr("};\n") + + # Write an encoding function + o.rxhdr("extern void rxgen_encode_", struct.name, + "(struct rx_call *call, const struct ", struct.name, " *p);\n") + + o.rxsrc("void rxgen_encode_", struct.name, + "(struct rx_call *call, const struct ", struct.name, " *p)\n") + o.rxsrc("{\n") + + for m in struct.members: + ty = m.typespec + if ty.is_array(): + o.rxsrc("\tint i;\n\n") + break + + for m in struct.members: + ty = m.typespec + o.where(struct.name + "::" + m.name) + if ty.is_single_int32(): + o.rxsrc("\trxrpc_enc(call, p->", m.name, ");\n") + elif ty.is_single_struct(): + o.rxsrc("\trxgen_encode_", ty.name, "(call, &p->", m.name, ");\n") + elif ty.is_array(): + o.rxsrc("\tfor (i = 0; i < ", ty.dim.name, "; i++)\n") + if ty.is_int32_array(): + o.rxsrc("\t\trxrpc_enc(call, p->", m.name, "[i]);\n") + elif ty.is_struct_array(): + o.rxsrc("\t\trxgen_encode_", ty.name, "(call, &p->", m.name, "[i]);\n") + else: + o.error("No encoding for array type '", ty, "'") + else: + o.error("No encoding for type '", ty, "'") + + o.rxsrc("}\n") + o.rxsrc("\n") + + # Write a decoding function + o.rxhdr("extern void rxgen_decode_", struct.name, + "(struct rx_call *call, struct ", struct.name, " *p);\n") + + o.rxsrc("void rxgen_decode_", struct.name, + "(struct rx_call *call, struct ", struct.name, " *p)\n") + o.rxsrc("{\n") + + for m in struct.members: + ty = m.typespec + if ty.is_array(): + o.rxsrc("\tint i;\n\n") + break + + for m in struct.members: + ty = m.typespec + o.where(struct.name + "::" + m.name) + if ty.is_single_int32(): + o.rxsrc("\tp->", m.name, " = rxrpc_dec(call);\n") + elif ty.is_single_struct(): + o.rxsrc("\trxgen_decode_", ty.name, "(call, &p->", m.name, ");\n") + elif ty.is_array(): + o.rxsrc("\tfor (i = 0; i < ", ty.dim.name, "; i++)\n") + if ty.is_int32_array(): + o.rxsrc("\t\tp->", m.name, "[i] = rxrpc_dec(call);\n") + elif ty.is_struct_array(): + o.rxsrc("\t\trxgen_decode_", ty.name, "(call, &p->", m.name, "[i]);\n") + else: + o.error("No decoding for array type '", ty, "'") + else: + o.error("No decoding for type '", ty, "'") + + o.rxsrc("}\n") diff --git a/rxgen/emit_c_sync_funcs.pm b/rxgen/emit_c_sync_funcs.pm index 493eeac..942bce5 100644 --- a/rxgen/emit_c_sync_funcs.pm +++ b/rxgen/emit_c_sync_funcs.pm @@ -639,7 +639,7 @@ sub emit_func_decode($$$$) } } - if ($phase->{type} ne "blob" || $phase->{type} ne "bulk") { + if ($phase->{type} ne "blob" && $phase->{type} ne "bulk") { print RXOUT "\t\tif (rxrpc_post_dec(call) < 0)\n"; print RXOUT "\t\t\treturn -1;\n"; } @@ -764,20 +764,6 @@ sub emit_func_send($$) } elsif ($p->{class} eq "blob") { print RXOUT "\trxrpc_enc_blob(call, ", $p->{name}, ", nr__", $p->{name}, ");\n"; print RXOUT "\trxrpc_enc_align(call);\n"; - } elsif ($p->{class} eq "blob") { - print RXOUT "\trxrpc_enc(call, nr__", $p->{name}, ");\n"; - print RXOUT "\tcall->blob_size = nr__", $p->{name}, ";\n"; - print RXOUT "\tfor (call->blob_offset = 0; call->blob_offset < call->blob_size; call->blob_offset++) {\n"; - if ($p->{elem}->{class} eq "struct") { - print RXOUT "\t\tstruct ", $p->{elem}->{type}, " x;\n"; - } else { - print RXOUT "\t\t", $p->{elem}->{type}, " x;\n"; - } - print RXOUT "\t\tcall->blob = &x;\n"; - print RXOUT "\t\tif (get__", $p->{name}, "(call, token__", $p->{name}, ") < 0)\n"; - print RXOUT "\t\t\tgoto error;\n"; - die $p->{where}, "No decoding for array type '$type'"; - print RXOUT "\t}\n"; } elsif ($p->{class} eq "bulk") { print RXOUT "\trxrpc_enc(call, nr__", $p->{name}, ");\n"; print RXOUT "\tcall->bulk_count = nr__", $p->{name}, ";\n"; diff --git a/rxgen/emit_c_sync_funcs.py b/rxgen/emit_c_sync_funcs.py new file mode 100644 index 0000000..79da197 --- /dev/null +++ b/rxgen/emit_c_sync_funcs.py @@ -0,0 +1,555 @@ +#!/usr/bin/python3 +# +# Emit C synchronous functions +# -*- coding: utf-8 -*- + +__copyright__ = """ +Copyright (C) 2015 Red Hat, Inc. All Rights Reserved. +Written by David Howells (dhowells@redhat.com) + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public Licence version 2 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public Licence for more details. + +You should have received a copy of the GNU General Public Licence +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +""" + +from rxgen_bits import * + +############################################################################### +# +# Calculate the C function prototypes +# +############################################################################### +def emit_func_prototype(o, func): + # Function prototype lists (we add commas and the closing bracket later) + protos = list() + protos.append("int " + func.name + "(\n") + send_request_protos = list() + send_response_protos = list() + recv_request_protos = list() + recv_response_protos = list() + + # Arguments to pass when sending a call or processing a reply + send_args = list() + recv_args = list() + + for p in func.params: + o.where(func.name + ":" + p.name) + ty = p.typespec + enclines = list() + declines = list() + args = list() + + if ty.is_array(): + raise RuntimeError("Array arg not supported") + elif ty.is_bulk(): + # Encode + if ty.is_bulk_struct(): + proto = "int (*get__" + p.name + ")(struct rx_call *call, void *token)" + else: + proto = "int (*get__" + p.name + ")(struct rx_call *call, void *token)" + + enclines.append(proto) + enclines.append("void *token__" + p.name) + enclines.append("size_t nr__" + p.name) + args.append("get__" + p.name) + args.append("token__" + p.name) + args.append("nr__" + p.name) + + # Decode + if ty.is_bulk_struct(): + proto = "int (*alloc__" + p.name + ")(struct rx_call *call, void **token)" + args.append("alloc__" + p.name) + else: + proto = "int (*store__" + p.name + ")(struct rx_call *call, void **token)" + args.append("store__" + p.name) + + declines.append(proto) + declines.append("void *token__" + p.name) + declines.append("size_t nr__" + p.name) + args.append("token__" + p.name) + elif ty.is_single_blob(): + # Do we want to insert a "*" in the following? + proto = ty.c_name + " *" + p.name + enclines.append("size_t nr__" + p.name) + enclines.append("const " + proto) + + declines.append("size_t nr__" + p.name) + declines.append("void *token__" + p.name) + declines.append("int (*alloc__" + p.name + ")(struct rx_call *call, void **token)") + args.append("nr__" + p.name) + args.append(p.name) + args.append("alloc__" + p.name) + else: + enc_const = "" + if not ty.is_single_int(): + enc_const = "const " + proto = ty.c_name + " " + if not ty.is_single_int(): + proto += "*" + proto += p.name + enclines.append(enc_const + proto) + declines.append(proto) + args.append(p.name) + + if p.direction == xdr_direction.IN or p.direction == xdr_direction.INOUT: + send_request_protos += enclines + recv_request_protos += declines + send_args += args + if p.direction == xdr_direction.OUT or p.direction == xdr_direction.INOUT: + send_response_protos += enclines + recv_response_protos += declines + recv_args += args + + o.rxhdr("\n") + o.rxhdr("/*\n") + o.rxhdr(" * ", func.name, "\n") + o.rxhdr(" */\n") + + if recv_request_protos: + o.rxhdr("struct ", func.name, "_request {\n") + for p in recv_request_protos: + o.rxhdr("\t", p, ";\n") + o.rxhdr("};\n") + + o.rxhdr("\n") + if recv_response_protos: + o.rxhdr("struct ", func.name, "_response {\n") + for p in recv_response_protos: + o.rxhdr("\t", p, ";\n") + o.rxhdr("};\n") + + # # Terminate each line with a comma, excepting the last, which we terminate + # # with a closing bracket. + # for (my $i = 1; $i < $#protos; $i++: + # protos[$i] .= ",\n") + # } + # protos[$#protos] .= ")") + + # for (my $i = 1; $i < $#send_protos; $i++: + # $send_protos[$i] .= ",\n") + # } + # $send_protos[$#send_protos] .= ")") + + # for (my $i = 1; $i < $#recv_protos; $i++: + # $recv_protos[$i] .= ",\n") + # } + # $recv_protos[$#recv_protos] .= ")") + + func.protos = protos + func.send_request_protos = send_request_protos + func.recv_request_protos = recv_request_protos + func.send_response_protos = send_response_protos + func.recv_response_protos = recv_response_protos + func.send_args = send_args + func.recv_args = recv_args + +############################################################################### +# +# Emit a function to decode a block in a way that can be used from asynchronous +# code. The opcode is expected to have been removed from the incoming call on +# the server side. +# +############################################################################### +class decode_phase: + def __init__(self, form="flat", size=0, xdr_size=0, name=None): + self.form = form + self.size = size + self.params = list() + self.xdr_size = xdr_size + self.elem_count = 0 + self.name = name + +def emit_func_decode(o, func, side, subname, params): + # We fetch the data in a number of phases. Each phase receives a chunk of + # data of a certain size. A phase's size might be dependent on a variable + # in the previous phase. Variable-sized bulk arrays are split across + # multiple phases, with the length being at the end of the first phase and + # the data in the second. + phases = list() + phase = None + have_bulk = False + + for p in params: + o.where(func.name + ":" + p.name) + ty = p.typespec + + if not phase: + phase = decode_phase() + phases.append(phase) + + if ty.is_single_int() or ty.is_single_struct(): + phase.size += ty.xdr_size + phase.params.append(p) + elif ty.is_single_blob(): + have_bulk = True + + # Blob objects begin with a size for which we institute a special + # parameter + phase.elem_count = phase.size + phase.size += 4 + + count_type = xdr_type(o.xdr, base=o.xdr.get_type("uint32_t")) + pseudoparam = xdr_member("nr__" + p.name, count_type, o.xdr) + pseudoparam.special = "blob_size" + phase.params.append(pseudoparam) + + # Create a new phase + phase = decode_phase(form="blob", name=p.name, size=4, xdr_size=ty.xdr_size) + phase.params.append(p) + phases.append(phase) + phase = None + + elif ty.is_bulk(): + have_bulk = True + + # Bulk objects begin with an element count for which we institute a + # special parameter + phase.elem_count = phase.size + phase.size += 4 + + count_type = xdr_type(o.xdr, base=o.xdr.get_type("uint32_t")) + pseudoparam = xdr_member("nr__" + p.name, count_type, o.xdr) + pseudoparam.special = "bulk_size" + phase.params.append(pseudoparam) + + # Create a new phase + phase = decode_phase(form="bulk", name=p.name, size=4, xdr_size=ty.xdr_size) + phase.params.append(p) + phases.append(phase) + + # We don't want to be asking recvmsg() for one object at a time if + # they're really small. + n_buf = 1 + if ty.xdr_size < 1020: + n_buf = int(1020 / ty.xdr_size) + n_buf *= ty.xdr_size + phase.size = ty.xdr_size + phase = None + + else: + raise RuntimeError("Reply array not supported") + + # Function definition and arguments + o.rxsrc("\n") + o.rxsrc("static int rxgen_decode_", func.name, "_", subname, "(struct rx_call *call)\n") + o.rxsrc("{\n") + + if not params: + o.rxsrc("\treturn 0;\n") + o.rxsrc("}\n") + return + + # Local variables + o.rxsrc("\tstruct ", func.name, "_", subname, " *obj = call->decoder_private;\n") + o.rxsrc("\tunsigned count;\n") + o.rxsrc("\tunsigned phase = call->phase;\n") + + # Deal with each phase + o.rxsrc("\n") + if have_bulk: + o.rxsrc("select_phase:\n") + o.rxsrc("\tcount = call->data_count;\n") + o.rxsrc("\tswitch (phase) {\n") + + o.rxsrc("\tcase 0:\n") + + next_phase_id = 1 + for phase in phases: + phase.phase_id = next_phase_id + next_phase_id += 1 + + phase_goto_label = None + for phase in phases: + phase_id = phase.phase_id + o.rxsrc("\n") + o.rxsrc("\t\t/* --- Phase ", phase_id, " --- */\n") + + if phase_goto_label == phase_id: + o.rxsrc("\tphase_", phase_id, ":\n") + phase_goto_label = None + + # Determine how big bulk objects are + if phase.form == "blob": + p = phase.params[0] + o.rxsrc("\t\tcall->blob_size = obj->nr__", p.name, ";\n") + o.rxsrc("\t\tcall->blob_offset = UINT_MAX;\n") + o.rxsrc("\t\tif (obj->alloc__", p.name, "(call, &obj->token__", p.name, ") < 0)\n") + o.rxsrc("\t\t\treturn -1;\n") + o.rxsrc("\t\tif (call->blob_size == 0)\n") + o.rxsrc("\t\t\tgoto phase_", phase_id + 1, ";\n") + phase_goto_label = phase_id + 1 + o.rxsrc("\t\tcall->blob_offset = 0;\n") + elif phase.form == "bulk": + p = phase.params[0] + o.rxsrc("\t\tcall->bulk_count = obj->nr__", p.name, ";\n") + o.rxsrc("\t\tcall->bulk_index = UINT_MAX;\n") + + if ty.is_bulk_int(): + o.rxsrc("\t\tif (obj->store__", p.name, "(call, &obj->token__", p.name, ") < 0)\n") + else: + o.rxsrc("\t\tif (obj->alloc__", p.name, "(call, &obj->token__", p.name, ") < 0)\n") + + o.rxsrc("\t\t\treturn -1;\n") + o.rxsrc("\t\tif (call->bulk_count == 0)\n") + o.rxsrc("\t\t\tgoto phase_", phase_id + 1, ";\n") + phase_goto_label = phase_id + 1 + o.rxsrc("\t\tcall->bulk_index = 0;\n") + else: + o.rxsrc("\t\tcall->need_size = ", phase.size, ";\n") + + # Entry point for a phase + o.rxsrc("\t\tcall->phase = ", phase_id, ";\n") + o.rxsrc("\tcase ", phase_id, ":\n") + + o.rxsrc("\t\tif (count < ", phase.size, ")") + if phase.form == "bulk" and phase.xdr_size <= 512: + o.rxsrc(" {\n") + o.rxsrc("\t\t\tunsigned n = call->bulk_count - call->bulk_index;\n") + o.rxsrc("\t\t\tn = MIN(n, ", int(1024 / phase.xdr_size), ");\n") + o.rxsrc("\t\t\tcall->need_size = n * ", phase.xdr_size, ";\n") + o.rxsrc("\t\t\treturn 1;\n") + o.rxsrc("\t\t}") + else: + o.rxsrc("\n") + o.rxsrc("\t\t\treturn 1;\n") + + # Unmarshal the data + o.rxsrc("\n") + for p in phase.params: + o.where(func.name + ":" + p.name) + ty = p.typespec + if p.special == None: + pass + elif p.special == "blob_size" or p.special == "bulk_size": + o.rxsrc("\t\tobj->", p.name, " = rxrpc_dec(call);\n") + continue + else: + raise RuntimeError + + if ty.is_bulk_int(): + if ty.is_bulk_int32(): + o.rxsrc("\t\tcall->bulk_u32 = rxrpc_dec(call);\n") + o.rxsrc("\t\tif (obj->store__", p.name, "(call, &obj->token__", p.name, ") < 0)\n") + elif ty.is_bulk_int64(): + o.rxsrc("\t\tcall->bulk_u64 = (uint64_t)rxrpc_dec(call) << 32;\n") + o.rxsrc("\t\tcall->bulk_u64 |= (uint64_t)rxrpc_dec(call);\n") + o.rxsrc("\t\tif (obj->store__", p.name, "(call, &obj->token__", p.name, ") < 0)\n") + else: + raise RuntimeError + o.rxsrc("\t\t\treturn -1;\n") + o.rxsrc("\t\tcall->bulk_index++;\n") + + elif ty.is_bulk_struct(): + o.rxsrc("\t\tif (obj->alloc__", p.name, "(call, &obj->token__", p.name, ") < 0)\n") + o.rxsrc("\t\t\treturn -1;\n") + o.rxsrc("\t\trxgen_decode_", ty.name, "(call, call->bulk_item);\n") + o.rxsrc("\t\tcall->bulk_index++;\n") + elif ty.is_single_blob(): + o.rxsrc("\t\trxrpc_dec_blob(call);\n") + o.rxsrc("\t\trxrpc_dec_align(call);\n") + elif ty.is_single_int32(): + o.rxsrc("\t\tobj->", p.name, " = rxrpc_dec(call);\n") + elif ty.is_single_int64(): + o.rxsrc("\t\tobj->", p.name, " = (uint64_t)rxrpc_dec(call) << 32;\n") + o.rxsrc("\t\tobj->", p.name, " |= (uint64_t)rxrpc_dec(call);\n") + elif ty.is_single_struct(): + o.rxsrc("\t\trxgen_decode_", ty.name, "(call, obj->", p.name, ");\n") + else: + raise RuntimeError("Unsupported type in decode " + str(ty)) + + if ty.is_single_blob(): + o.rxsrc("\t\tif (rxrpc_post_dec(call) < 0)\n") + o.rxsrc("\t\t\treturn -1;\n") + o.rxsrc("\t\tif (call->blob_offset < call->blob_size) {\n") + o.rxsrc("\t\t\tphase = ", phase_id, ";\n") + o.rxsrc("\t\t\tgoto select_phase;\n") + o.rxsrc("\t\t}\n") + elif ty.is_bulk(): + o.rxsrc("\t\tif (rxrpc_post_dec(call) < 0)\n") + o.rxsrc("\t\t\treturn -1;\n") + o.rxsrc("\t\tif (call->bulk_index < call->bulk_count) {\n") + o.rxsrc("\t\t\tphase = ", phase_id, ";\n") + o.rxsrc("\t\t\tgoto select_phase;\n") + o.rxsrc("\t\t}\n") + + # --- TODO: Check the following condition as it must always be true + if phase.form != "blob" or phase.form != "bulk": + o.rxsrc("\t\tif (rxrpc_post_dec(call) < 0)\n") + o.rxsrc("\t\t\treturn -1;\n") + + o.rxsrc("\n") + o.rxsrc("\t\t/* --- Phase ", next_phase_id, " --- */\n") + if phase_goto_label: + o.rxsrc("\tphase_", next_phase_id, ":\n") + o.rxsrc("\t\tcall->phase = ", next_phase_id, ";\n") + o.rxsrc("\t\tcall->need_size = 0;\n") + o.rxsrc("\tdefault:\n") + o.rxsrc("\t\treturn 0;\n") + o.rxsrc("\t}\n") + o.rxsrc("}\n") + + +############################################################################### +# +# Emit a function to encode and dispatch a request or a response +# +############################################################################### +def emit_func_send(o, func, what): + # Function definition and arguments + if what == "request": + protos = func.send_request_protos + params = func.request + bad_ret = "NULL" + else: + protos = func.send_response_protos + params = func.response + bad_ret = "-1" + + o.rxsrc("\n") + if what == "request": + o.rxsrc("struct rx_call *", func.name + "(\n") + o.rxsrc("\tstruct rx_connection *z_conn") + else: + o.rxsrc("int respond_to_", func.name + "(\n") + o.rxsrc("\tstruct rx_call *call") + + for proto in protos: + o.rxsrc(",\n\t", proto) + + if what == "request" and func.response: + o.rxsrc(",\n") + o.rxsrc("\tstruct ", func.name, "_response *response") + + o.rxsrc(")\n") + o.rxsrc("{\n") + + if what == "request": + o.rxsrc("\tstruct rx_call *call;\n") + + blob_params = list() + bulk_params = list() + for p in params: + ty = p.typespec + if ty.is_single_blob(): + blob_params.append(p) + if ty.is_bulk(): + bulk_params.append(p) + + # Local variables + o.rxsrc("\tint ret;\n") + + # Check lengths + if blob_params or bulk_params: + o.rxsrc("\n") + o.rxsrc("\tif (") + first = True + for p in blob_params: + o.where(func.name + ":" + p.name) + ty = p.typespec + if first: + first = False + else: + o.rxsrc(" ||\n\t ") + o.rxsrc("!", p.name) + if ty.max_size: + o.rxsrc(" || nr__", p.name, " > ", ty.max_size.name) + + for p in bulk_params: + o.where(func.name + ":" + p.name) + ty = p.typespec + if first: + first = False + else: + o.rxsrc(" ||\n\t ") + o.rxsrc("!get__", p.name) + if ty.max_size: + o.rxsrc(" || nr__", p.name, " > ", ty.max_size.name) + + o.rxsrc(") {\n") + o.rxsrc("\t\terrno = EINVAL;\n") + o.rxsrc("\t\treturn ", bad_ret, ";\n") + o.rxsrc("\t};\n") + + # Allocate call + if what == "request": + o.rxsrc("\n") + o.rxsrc("\tcall = rxrpc_alloc_call(z_conn, 0);\n") + o.rxsrc("\tif (!call)\n") + o.rxsrc("\t\treturn ", bad_ret, ";\n") + o.rxsrc("\tcall->decoder = rxgen_decode_", func.name, "_response;\n") + if func.response: + o.rxsrc("\tcall->decoder_private = response;\n") + + # Marshal the data + if what == "request" or params: + o.rxsrc("\n") + if what == "request": + o.rxsrc("\trxrpc_enc(call, ", func.opcode.name, ");\n") + + for p in params: + o.where(func.name + ":" + p.name) + ty = p.typespec + if ty.is_single_int32(): + o.rxsrc("\trxrpc_enc(call, ", p.name, ");\n") + elif ty.is_single_int64(): + o.rxsrc("\trxrpc_enc(call, (uint32_t)", p.name, ");\n") + o.rxsrc("\trxrpc_enc(call, (uint32_t)(", p.name, " >> 32));\n") + elif ty.is_single_struct(): + o.rxsrc("\trxgen_encode_", ty.name, "(call, ", p.name, ");\n") + elif ty.is_single_blob(): + o.rxsrc("\trxrpc_enc_blob(call, ", p.name, ", nr__", p.name, ");\n") + o.rxsrc("\trxrpc_enc_align(call);\n") + elif ty.is_bulk(): + o.rxsrc("\trxrpc_enc(call, nr__", p.name, ");\n") + o.rxsrc("\tcall->bulk_count = nr__", p.name, ";\n") + o.rxsrc("\tfor (call->bulk_index = 0; call->bulk_index < call->bulk_count; call->bulk_index++) {\n") + o.rxsrc("\t\t", ty.c_name, " x;\n") + + o.rxsrc("\t\tcall->bulk_item = &x;\n") + o.rxsrc("\t\tif (get__", p.name, "(call, token__", p.name, ") < 0)\n") + o.rxsrc("\t\t\tgoto error;\n") + if ty.is_bulk_int32(): + if not ty.name.startswith("u"): + o.rxsrc("\t\trxrpc_enc(call, (u", ty.name, ")x);\n") + else: + o.rxsrc("\t\trxrpc_enc(call, x);\n") + elif ty.is_bulk_int64(): + o.rxsrc("\t\trxrpc_enc(call, (uint32_t)", p.name, ");\n") + o.rxsrc("\t\trxrpc_enc(call, (uint32_t)(", p.name, " >> 32));\n") + elif ty.is_bulk_struct(): + o.rxsrc("\t\trxgen_encode_", ty.name, "(call, &x);\n") + else: + raise RuntimeError("No decoding for array type '" + str(ty) + "'") + + o.rxsrc("\t}\n") + else: + raise RuntimeError("Unsupported param encoding") + + o.rxsrc("\tif (rxrpc_post_enc(call) < 0)\n") + o.rxsrc("\t\tgoto error;\n") + o.rxsrc("\tcall->more_send = 0;\n") + + # Send the message + o.rxsrc("\n") + o.rxsrc("\tret = rxrpc_send_data(call);\n") + o.rxsrc("\tif (ret < 0)\n") + o.rxsrc("\t\tgoto error;\n") + if what == "request": + o.rxsrc("\treturn call;\n") + else: + o.rxsrc("\treturn 0;\n") + + o.rxsrc("\n") + o.rxsrc("error:\n") + o.rxsrc("\trxrpc_terminate_call(call, 0);\n") + o.rxsrc("\treturn ", bad_ret, ";\n") + o.rxsrc("}\n") diff --git a/rxgen/emit_py_module.py b/rxgen/emit_py_module.py new file mode 100644 index 0000000..d2277cb --- /dev/null +++ b/rxgen/emit_py_module.py @@ -0,0 +1,175 @@ +#!/usr/bin/python3 +# +# Emit python module definition. +# -*- coding: utf-8 -*- + +__copyright__ = """ +Copyright (C) 2015 Red Hat, Inc. All Rights Reserved. +Written by David Howells (dhowells@redhat.com) + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public Licence version 2 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public Licence for more details. + +You should have received a copy of the GNU General Public Licence +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +""" + +from rxgen_bits import * + +############################################################################### +# +# Emit python module definition. +# +############################################################################### +def emit_py_module(o): + # We want an exception we can raise when we get a remote abort + o.pyhdr("extern PyObject *kafs_remote_abort;\n") + + o.pysrc("\n") + o.pysrc("/*\n") + o.pysrc(" * The remote-abort exception.\n") + o.pysrc(" */\n") + o.pysrc("PyObject *kafs_remote_abort;\n") + + pkgnames = list(o.xdr.packages.keys()) + pkgnames.sort() + + for pkgname in pkgnames: + pkg = o.xdr.packages[pkgname] + codes = pkg.abort_codes + if codes: + o.pysrc("PyObject *kafs_", pkg.name, "_abort;\n") + + o.pysrc("\n") + o.pyhdr("extern struct kafs_abort_list kafs_abort_map[", o.xdr.abort_count, "];\n") + + index = 0 + o.pysrc("struct kafs_abort_list kafs_abort_map[", o.xdr.abort_count, "] = {\n") + abort_ids = list(o.xdr.abort_ids.keys()) + abort_ids.sort() + for aid in abort_ids: + abort = o.xdr.abort_ids[aid] + o.pysrc("\t{ .id = ", abort.name) + #if abort.msg: + # o.pysrc(", .msg = \"", abort.msg, "\"") + o.pysrc(" }, /* ", abort.u32, " */\n") + abort.index = index + index += 1 + + o.pysrc("};\n\n") + + # Emit python structure wrapper static method table + o.pysrc("\n") + o.pysrc("/*\n") + o.pysrc(" * The static methods.\n") + o.pysrc(" */\n") + o.pysrc("static PyMethodDef module_methods[] = {\n") + + o.pysrc("\t{\"rx_new_connection\", (PyCFunction)kafs_py_rx_new_connection, METH_VARARGS, \"\" },\n") + o.pysrc("\t{\"afs_string_to_key\", (PyCFunction)kafs_py_string_to_key, METH_VARARGS, \"\" },\n") + + for pyf in o.xdr.py_func_defs: + o.pysrc("\t{\"", pyf.name, "\", (PyCFunction)", pyf.c_func, ", METH_VARARGS,") + o.pysrc(" \"", pyf.doc, "\" },\n") + + o.pysrc("\t{}\n") + o.pysrc("};\n") + + # Emit python structure wrapper loader + o.pysrc("\n") + + o.pysrc("static PyModuleDef kafs_module = {\n") + o.pysrc("\t.m_base = PyModuleDef_HEAD_INIT,\n") + o.pysrc("\t.m_name = \"kafs\",\n") + o.pysrc("\t.m_doc = \"AFS stuff.\",\n") + o.pysrc("\t.m_size = -1,\n") + o.pysrc("\t.m_methods = module_methods,\n") + o.pysrc("};\n") + + o.pyhdr("\n") + o.pyhdr("extern PyObject *pykafs_load_wrappers(void);\n") + + o.pysrc("\n") + o.pysrc("PyObject *pykafs_load_wrappers(void)\n") + o.pysrc("{\n") + o.pysrc("\tPyObject *m;\n") + + # Load types + if o.xdr.py_type_defs: + o.pysrc("\tif (") + o.pysrc("PyType_Ready(&py_rx_connectionType) < 0 ||\n\t ") + o.pysrc("PyType_Ready(&py_rx_split_infoType) < 0") + for pyt in o.xdr.py_type_defs: + o.pysrc(" ||\n\t ") + o.pysrc("PyType_Ready(&", pyt.c_type, ") < 0") + o.pysrc(")\n") + o.pysrc("\t\treturn NULL;\n") + + + o.pysrc("\n") + o.pysrc("\tm = PyModule_Create(&kafs_module);\n") + o.pysrc("\tif (!m)\n") + o.pysrc("\t\treturn NULL;\n") + + if o.xdr.constants: + o.pysrc("\n") + con_names = list(o.xdr.constants.keys()) + con_names.sort() + for c in con_names: + o.pysrc("\tPyModule_AddIntConstant(m, \"", c, "\", ", c, ");\n") + + if o.xdr.py_type_defs: + o.pysrc("\n") + for pyt in o.xdr.py_type_defs: + o.pysrc("\tPy_INCREF(&", pyt.c_type, ");\n") + o.pysrc("\tPyModule_AddObject(m, \"", pyt.name, "\", (PyObject *)&", pyt.c_type, ");\n") + + # Emit a base remote abort class that all others can be subclassed off + o.pysrc("\n") + o.pysrc("\tkafs_remote_abort = PyErr_NewException(\"kafs.RemoteAbort\", NULL, NULL);\n") + o.pysrc("\tif (!kafs_remote_abort)\n") + o.pysrc("\t\treturn NULL;\n") + o.pysrc("\tPy_INCREF(kafs_remote_abort);\n") + o.pysrc("\tPyModule_AddObject(m, \"RemoteAbort\", kafs_remote_abort);\n") + + for pkgname in pkgnames: + pkg = o.xdr.packages[pkgname] + abort_codes = pkg.abort_codes + if not abort_codes: + continue + + pkg_abort = pkg.name + "Abort" + pkg_sym = "kafs_" + pkg.name + "_abort" + + o.pysrc("\n") + o.pysrc("\t", pkg_sym, " = PyErr_NewException(\"kafs.", pkg_abort, "\", kafs_remote_abort, NULL);\n") + o.pysrc("\tif (!", pkg_sym, ")\n") + o.pysrc("\t\treturn NULL;\n") + o.pysrc("\tPy_INCREF(", pkg_sym, ");\n") + o.pysrc("\tPyModule_AddObject(m, \"", pkg_abort, "\", ", pkg_sym, ");\n") + + def get_constant_value(abort): + return abort.u32 + abort_codes.sort(key=get_constant_value) + + for abort in abort_codes: + abort_name = "Abort" + abort.name + abort_var = "kafs_abort_map[" + str(abort.index) + "].obj" + + o.pysrc("\n") + o.pysrc("\t", abort_var, " = PyErr_NewException(\"kafs.", abort_name, "\", ", pkg_sym, ", NULL);\n") + o.pysrc("\tif (!", abort_var, ")\n") + o.pysrc("\t\treturn NULL;\n") + o.pysrc("\tPy_INCREF(", abort_var, ");\n") + o.pysrc("\tPyModule_AddObject(m, \"", abort_name, "\", ", abort_var, ");\n") + + o.pysrc("\n") + o.pysrc("\treturn m;\n") + o.pysrc("}\n") diff --git a/rxgen/emit_py_sync_funcs.py b/rxgen/emit_py_sync_funcs.py new file mode 100644 index 0000000..1c8ac71 --- /dev/null +++ b/rxgen/emit_py_sync_funcs.py @@ -0,0 +1,752 @@ +#!/usr/bin/python3 +# +# Emit C synchronous functions +# -*- coding: utf-8 -*- + +__copyright__ = """ +Copyright (C) 2015 Red Hat, Inc. All Rights Reserved. +Written by David Howells (dhowells@redhat.com) + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public Licence version 2 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public Licence for more details. + +You should have received a copy of the GNU General Public Licence +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +""" + +from rxgen_bits import * + +class decode_phase: + def __init__(self, form="flat", size=0, xdr_size=0, name=None): + self.form = form + self.size = size + self.params = list() + self.xdr_size = xdr_size + self.elem_count = 0 + self.name = name + +bulk_get_helpers = dict(); +bulk_set_helpers = dict(); + +c_to_py_type_map = dict([("char", "T_CHAR"), + ("int8_t", "T_BYTE"), + ("int16_t", "T_SHORT"), + ("int32_t", "T_INT"), + ("int64_t", "T_LONGLONG"), + ("uint8_t", "T_UBYTE"), + ("uint16_t", "T_USHORT"), + ("uint32_t", "T_UINT"), + ("uint64_t", "T_ULONGLONG") + ]) + +############################################################################### +# +# Emit python objects to represent received parameter sets and received +# response sets for RPC calls. +# +############################################################################### +def emit_py_func_param_object(o, func, way): + struct_req = "py_" + func.name + "_" + way; + basic_params = list() + complex_params = list() + params = list() + division = "" + + global c_to_py_type_map + + t = py_type_def(func.name + "_" + way, struct_req + "Type") + o.xdr.py_type_defs.append(t) + + if way == "request": + params = func.request + division = "calls" + else: + params = func.response + division = "responses" + + # Define a C structure to hold the python object header and the data. + o.pyhdr("\n") + o.pyhdr("struct ", struct_req, " {\n") + o.pyhdr("\tstruct py_rx_", way, " common;\n") + if params: + have_opaque = False + o.pyhdr("\tstruct {\n") + for p in params: + o.where(func.name + ":" + p.name) + ty = p.typespec + if ty.is_single_int(): + basic_params.append(p) + o.pyhdr("\t\t", ty.name, "\t", p.name, ";\n") + else: + complex_params.append(p) + o.pyhdr("\t\tPyObject\t*", p.name, ";\n") + + if ty.is_single_opaque(): + have_opaque = True + + o.pyhdr("\t} x;\n") + if have_opaque: + o.pyhdr("\tPy_buffer dec_buf;\n") + + o.pyhdr("};\n") + + # We need to have a new function if the object is to be allocatable by the + # Python interpreter + o.pysrc("\n") + o.pysrc("static PyObject *\n") + o.pysrc(struct_req, "_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds)\n") + o.pysrc("{\n") + o.pysrc("\tPyObject *obj;\n") + o.pysrc("\n") + o.pysrc("\tobj = subtype->tp_alloc(subtype, 1);\n") + if params: + o.pysrc("\tif (obj) {\n") + o.pysrc("\t\tstruct ", struct_req, " *self = (struct ", struct_req, " *)obj;\n") + o.pysrc("\t\tmemset(&self->x, 0, sizeof(self->x));\n") + o.pysrc("\t}\n") + o.pysrc("\treturn obj;\n") + o.pysrc("}\n") + + # We have to have a deallocation function + o.pysrc("\n") + o.pysrc("static void ", struct_req, "_dealloc(struct ", struct_req, " *self)\n") + o.pysrc("{\n") + for p in complex_params: + o.pysrc("\tPy_XDECREF(self->x.", p.name, ");\n") + o.pysrc("\tPy_TYPE(self)->tp_free((PyObject *)self);\n") + o.pysrc("}\n") + + # All elements are made directly accessible to the Python interpreter, + # either as integer types or as object types. + if params: + o.pysrc("\n") + o.pysrc("static PyMemberDef ", struct_req, "_members[] = {\n") + for p in params: + o.where(func.name + ":" + p.name) + ty = p.typespec + o.pysrc("\t{ \"", p.name, "\", ") + if ty.is_single_blob(): o.pysrc("T_OBJECT_EX") + elif ty.is_bulk(): o.pysrc("T_OBJECT_EX") + elif ty.is_single_int(): o.pysrc(c_to_py_type_map[ty.name]) + else: + o.pysrc("T_OBJECT_EX") + o.pysrc(", offsetof(struct ", struct_req, ", x.", p.name, "), 0, \"\"},\n") + + o.pysrc("\t{}\n") + o.pysrc("};\n") + + # Emit the Python type definition + o.pysrc("\n") + o.pysrc("static PyTypeObject ", struct_req, "Type = {\n") + o.pysrc("\tPyVarObject_HEAD_INIT(NULL, 0)\n") + o.pysrc("\t\"kafs.", func.name, "_", way, "\",\t\t/*tp_name*/\n") + o.pysrc("\tsizeof(struct ", struct_req, "),\t/*tp_basicsize*/\n") + o.pysrc("\t0,\t\t\t\t/*tp_itemsize*/\n") + o.pysrc("\t(destructor)", struct_req, "_dealloc, /*tp_dealloc*/\n") + o.pysrc("\t0,\t\t\t\t/*tp_print*/\n") + o.pysrc("\t0,\t\t\t\t/*tp_getattr*/\n") + o.pysrc("\t0,\t\t\t\t/*tp_setattr*/\n") + o.pysrc("\t0,\t\t\t\t/*tp_compare*/\n") + o.pysrc("\t0,\t\t\t\t/*tp_repr*/\n") + o.pysrc("\t0,\t\t\t\t/*tp_as_number*/\n") + o.pysrc("\t0,\t\t\t\t/*tp_as_sequence*/\n") + o.pysrc("\t0,\t\t\t\t/*tp_as_mapping*/\n") + o.pysrc("\t0,\t\t\t\t/*tp_hash */\n") + o.pysrc("\t0,\t\t\t\t/*tp_call */\n") + o.pysrc("\t0,\t\t\t\t/*tp_str*/\n") + o.pysrc("\t0,\t\t\t\t/*tp_getattro*/\n") + o.pysrc("\t0,\t\t\t\t/*tp_setattro*/\n") + o.pysrc("\t0,\t\t\t\t/*tp_as_buffer*/\n") + o.pysrc("\tPy_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/\n") + o.pysrc("\t\"\",\t\t\t\t/* tp_doc */\n") + o.pysrc("\t0,\t\t\t\t/* tp_traverse */\n") + o.pysrc("\t0,\t\t\t\t/* tp_clear */\n") + o.pysrc("\t0,\t\t\t\t/* tp_richcompare */\n") + o.pysrc("\t0,\t\t\t\t/* tp_weaklistoffset */\n") + o.pysrc("\t0,\t\t\t\t/* tp_iter */\n") + o.pysrc("\t0,\t\t\t\t/* tp_iternext */\n") + o.pysrc("\t0,\t\t\t\t/* tp_methods */\n") + if params: + o.pysrc("\t", struct_req, "_members,\n") + else: + o.pysrc("\t0,\t\t\t\t/* tp_members */\n") + + o.pysrc("\t0,\t\t\t\t/* tp_getset */\n") + o.pysrc("\t0,\t\t\t\t/* tp_base */\n") + o.pysrc("\t0,\t\t\t\t/* tp_dict */\n") + o.pysrc("\t0,\t\t\t\t/* tp_descr_get */\n") + o.pysrc("\t0,\t\t\t\t/* tp_descr_set */\n") + o.pysrc("\t0,\t\t\t\t/* tp_dictoffset */\n") + o.pysrc("\t0,\t\t\t\t/* tp_init */\n") + o.pysrc("\t0,\t\t\t\t/* tp_alloc */\n") + o.pysrc("\t", struct_req, "_new,\n") + o.pysrc("};\n") + + +############################################################################### +# +# Emit functions to help deal with bulk lists +# +############################################################################### +def emit_py_func_bulk_helper(o, func): + for p in func.params: + o.where(func.name + ":" + p.name) + ty = p.typespec + if not ty.is_bulk(): + continue + + # Data encoding + if ty.name not in bulk_get_helpers: + bulk_get_helpers[ty.name] = True + + o.pysrc("\n") + o.pysrc("static __attribute__((unused))\n") + o.pysrc("int py_encode_bulk_", ty.name, "(struct rx_call *call, PyObject *list)\n") + o.pysrc("{\n") + o.pysrc("\tPyObject *item;\n") + o.pysrc("\tunsigned count, i;\n") + o.pysrc("\n") + o.pysrc("\tcount = PyList_Size(list);\n") + o.pysrc("\trxrpc_enc(call, count);\n") + o.pysrc("\n") + o.pysrc("\tfor (i = 0; i < count; i++) {\n") + o.pysrc("\t\titem = PyList_GetItem(list, i);\n") + o.pysrc("\t\tif (!item)\n") + o.pysrc("\t\t\treturn -1;\n") + + o.pysrc("\n") + if ty.is_bulk_int(): + o.pysrc("\t\tif (!PyLong_Check(item)) {\n") + o.pysrc("\t\t\tPyErr_SetString(PyExc_TypeError, \"Expected list of ", ty.name, "\");\n") + o.pysrc("\t\t\treturn -1;\n") + o.pysrc("\t\t}\n") + else: + o.pysrc("\t\tif (py_premarshal_", ty.name, "(item))\n") + o.pysrc("\t\t\treturn -1;\n") + + if ty.is_bulk_int(): + if ty.name == "int64_t": + o.pysrc("\t\tuint64_t x = PyLong_AsLongLong(item);\n") + o.pysrc("\t\trxrpc_enc(call, x >> 32);\n") + o.pysrc("\t\trxrpc_enc(call, x);\n") + elif ty.name == "uint64_t": + o.pysrc("\t\tuint64_t x = PyLong_AsUnsignedLongLong(item);\n") + o.pysrc("\t\trxrpc_enc(call, x >> 32);\n") + o.pysrc("\t\trxrpc_enc(call, x);\n") + elif ty.name.startswith("int"): + o.pysrc("\t\trxrpc_enc(call, PyLong_AsLong(item));\n") + elif ty.name.startswith("uint") or ty.name.startswith("char"): + o.pysrc("\t\trxrpc_enc(call, PyLong_AsUnsignedLong(item));\n") + else: + raise RuntimeError + else: + o.pysrc("\t\trxgen_encode_", ty.name, "(call, &((struct py_", ty.name, " *)item)->x);\n") + + o.pysrc("\t}\n") + o.pysrc("\treturn 0;\n") + o.pysrc("}\n") + +############################################################################### +# +# Emit a python wrapper function to make a simple synchronous call +# +############################################################################### +def emit_py_func_simple_sync_call(o, func): + + o.xdr.py_func_defs.append(py_func_def(func.name, "kafs_" + func.name)) + + o.pysrc("\n") + o.pysrc("PyObject *\n") + o.pysrc("kafs_", func.name, "(PyObject *_self, PyObject *args)\n") + o.pysrc("{\n") + + # Local variable declarations representing parameters to send + o.pysrc("\tstruct rx_call *call;\n") + o.pysrc("\tstruct py_rx_connection *z_conn;\n") + o.pysrc("\tstruct py_", func.name, "_response *response;\n") + for p in func.request: + o.where(func.name + ":" + p.name) + ty = p.typespec + if ty.is_single_blob(): + o.pysrc("\tPy_buffer param_", p.name, ";\n") + elif ty.is_single_int(): + o.pysrc("\t", ty.name, " param_", p.name, ";\n") + elif ty.is_single_struct(): + o.pysrc("\tstruct py_", ty.name, " *param_", p.name, ";\n") + elif ty.is_bulk(): + if p.direction != "OUT": + o.pysrc("\tPyObject *param_", p.name, ";\n") + else: + raise RuntimeError("Unsupported type \"" + str(ty) + "\"") + + if func.split: + o.pysrc("\tPyObject *split_callback, *split_info;\n") + o.pysrc("\tPyObject *res = NULL;\n") + o.pysrc("\tint ret;\n") + + # Make use of the tuple parser to extract the arguments and check their + # types for us. + o.pysrc("\n") + o.pysrc("\tif (!PyArg_ParseTuple(args, \"O!") + + for p in func.request: + o.where(func.name + ":" + p.name) + ty = p.typespec + if ty.is_bulk(): o.pysrc("O!") + elif ty.is_array(): raise RuntimeError + elif ty.name == "int8_t": o.pysrc("B") + elif ty.name == "int16_t": o.pysrc("h") + elif ty.name == "int32_t": o.pysrc("i") + elif ty.name == "int64_t": o.pysrc("L") + elif ty.name == "uint8_t": o.pysrc("b") + elif ty.name == "uint16_t": o.pysrc("H") + elif ty.name == "uint32_t": o.pysrc("I") + elif ty.name == "uint64_t": o.pysrc("K") + elif ty.is_single_struct(): o.pysrc("O!") + elif ty.is_single_string(): o.pysrc("s*") + elif ty.is_single_opaque(): o.pysrc("z*") + else: + raise RuntimeError("No py parse for param") + + if func.split: + o.pysrc("O") + o.pysrc("\",\n") + o.pysrc("\t\t\t &py_rx_connectionType, &z_conn") + + for p in func.request: + o.where(func.name + ":" + p.name) + ty = p.typespec + o.pysrc(",\n") + o.pysrc("\t\t\t ") + if ty.is_single_int(): + o.pysrc("¶m_", p.name) + elif ty.is_single_struct(): + o.pysrc("&py_", ty.name, "Type, ¶m_", p.name) + elif ty.is_single_blob(): + o.pysrc("¶m_", p.name) + elif ty.is_bulk(): + o.pysrc("&PyList_Type, ¶m_", p.name) + else: + raise RuntimeError(": Unsupported type \"" + str(ty) + "\"") + + if func.split: + o.pysrc(",\n\t\t\t &split_callback") + o.pysrc("))\n") + o.pysrc("\t\treturn NULL;\n") + + if func.split: + o.pysrc("\n") + o.pysrc("\tsplit_info = py_rxgen_split_client_prepare();\n") + o.pysrc("\tif (!split_info)\n") + o.pysrc("\t\treturn NULL;\n") + + o.pysrc("\n") + o.pysrc("\tcall = rxrpc_alloc_call(z_conn->x, 0);\n") + o.pysrc("\tif (!call) {\n") + if func.split: + o.pysrc("\t\tPy_XDECREF(split_info);\n"); + o.pysrc("\t\treturn PyErr_NoMemory();\n") + o.pysrc("\t}\n") + o.pysrc("\tcall->decoder_cleanup = py_rxgen_decoder_cleanup;\n") + if func.split: + o.pysrc("\tpy_rxgen_split_client_set(call, split_callback, split_info);\n") + + # Marshal the arguments + o.pysrc("\n") + o.pysrc("\trxrpc_enc(call, ", func.opcode.name, ");\n") + for p in func.request: + o.where(func.name + ":" + p.name) + ty = p.typespec + if ty.is_blob(): + dim = -1 + if ty.max_size: + dim = ty.max_size.name + o.pysrc("\tif (py_enc_buffer(call, ¶m_", p.name, ", ", dim, ") < 0) {\n") + o.pysrc("\t\trxrpc_terminate_call(call, EINVAL);\n") + o.pysrc("\t\treturn NULL;\n") + o.pysrc("\t}\n") + elif ty.is_bulk(): + o.pysrc("\tif (py_encode_bulk_", ty.name, "(call, param_", p.name, ") < 0)\n") + o.pysrc("\t\tgoto error;\n") + elif ty.is_single_int32(): + o.pysrc("\trxrpc_enc(call, param_", p.name, ");\n") + elif ty.is_single_int64(): + o.pysrc("\trxrpc_enc(call, param_", p.name, " >> 32);\n") + o.pysrc("\trxrpc_enc(call, param_", p.name, ");\n") + elif ty.is_single_struct(): + o.pysrc("\tif (py_premarshal_", ty.name, "((PyObject *)param_", p.name, ")) {\n") + o.pysrc("\t\trxrpc_terminate_call(call, EINVAL);\n") + o.pysrc("\t\treturn NULL;\n") + o.pysrc("\t}\n") + o.pysrc("\trxgen_encode_", ty.name, "(call, ¶m_", p.name, "->x);\n") + else: + raise RuntimeError("Unsupported type in decode " + str(ty)) + + o.pysrc("\tif (rxrpc_post_enc(call) < 0)\n") + o.pysrc("\t\tgoto error_no_res;\n") + + # Allocate a reply object + o.pysrc("\n") + o.pysrc("\tres = _PyObject_New(&py_", func.name, "_responseType);\n") + o.pysrc("\tresponse = (struct py_", func.name, "_response *)res;\n") + o.pysrc("\tif (!response)\n") + o.pysrc("\t\tgoto enomem;\n") + if func.response: + o.pysrc("\tmemset(&response->x, 0, sizeof(response->x));\n") + o.pysrc("\tcall->decoder = py_", func.name, "_decode_response;\n") + o.pysrc("\tcall->decoder_private = response;\n") + + # Transmit the split data + if func.split: + o.pysrc("\tif (py_rxgen_split_transmit(call) < 0)\n") + o.pysrc("\t\tgoto error_no_res;\n") + else: + o.pysrc("\tcall->more_send = 0;\n") + + # Make the call + o.pysrc("\n") + o.pysrc("\tret = rxrpc_send_data(call);\n") + o.pysrc("\tif (ret == -1)\n") + o.pysrc("\t\tgoto error;\n") + + # Wait for the reply + # + # If we're dealing with a split function or are in asynchronous mode, we + # need to return the call here. + # + o.pysrc("\n") + o.pysrc("\tret = rxrpc_run_sync_call(call);\n") + o.pysrc("\tif (ret == -1)\n") + o.pysrc("\t\tgoto error;\n") + + # Successful return + o.pysrc("\n") + o.pysrc("\trxrpc_terminate_call(call, 0);\n") + o.pysrc("\treturn res;\n") + + # Error cleanups + o.pysrc("\n") + o.pysrc("error:\n") + o.pysrc("\tPy_XDECREF(res);\n") + o.pysrc("error_no_res:\n") + o.pysrc("\tif (errno == ENOMEM)\n") + o.pysrc("enomem:\n") + o.pysrc("\t\tres = PyErr_NoMemory();\n") + o.pysrc("\telse if (errno == ECONNABORTED)\n") + o.pysrc("\t\tres = py_rxgen_received_abort(call);\n") + o.pysrc("\telse\n") + o.pysrc("\t\tres = PyErr_SetFromErrno(PyExc_IOError);\n") + o.pysrc("\trxrpc_terminate_call(call, ENOMEM);\n") + o.pysrc("\treturn res;\n") + + # End the function + o.pysrc("}\n") + +############################################################################### +# +# Emit a function to decode a block into a python object in a way that can be +# used from asynchronous code. The opcode is expected to have been removed +# from the incoming call on the server side. +# +############################################################################### +def emit_py_func_decode(o, func, side, subname, params): + ptr = "obj->" + + # We fetch the data in a number of phases. Each phase receives a chunk of + # data of a certain size. A phase's size might be dependent on a variable + # in the previous phase. Variable-sized bulk arrays are split across + # multiple phases, with the length being at the end of the first phase and + # the data in the second. + # + # We also need to interpolate a phase to deal with decoding split-op + # auxiliary data. This comes last when decoding the request and first when + # decoding the response. + # + phases = list() + phase = None + have_bulk = False + want_item = False + + if func.split and subname == "response": + phase = decode_phase(form="split", + size="py_rxgen_split_receive(call)") + phases.append(phase) + phase = None + have_bulk = True + + for p in params: + o.where(func.name + ":" + p.name) + ty = p.typespec + + if not phase: + phase = decode_phase(form="flat") + phases.append(phase) + + if ty.is_single_int() or ty.is_single_struct(): + phase.size += ty.xdr_size + phase.params.append(p) + elif ty.is_single_blob(): + have_bulk = True + + # Bulk objects begin with an element count + phase.elem_count = phase.size + phase.size += 4 + + count_type = xdr_type(o.xdr, base=o.xdr.get_type("uint32_t")) + pseudoparam = xdr_member("nr__" + p.name, count_type, o.xdr) + pseudoparam.special = "blob_size" + phase.params.append(pseudoparam) + + # Create a new phase + phase = decode_phase(form="blob", name=p.name, size=4, xdr_size=ty.xdr_size) + phase.params.append(p) + phases.append(phase) + + # We don't want to be asking recvmsg() for one object at a time if + # they're really small. + phase.size = ty.xdr_size + phase = None + elif ty.is_bulk(): + have_bulk = True + + # Bulk objects begin with an element count + phase.elem_count = phase.size + phase.size += 4 + + count_type = xdr_type(o.xdr, base=o.xdr.get_type("uint32_t")) + pseudoparam = xdr_member("nr__" + p.name, count_type, o.xdr) + pseudoparam.special = "bulk_size" + phase.params.append(pseudoparam) + + # Create a new phase + phase = decode_phase(form="bulk", name=p.name, size=4, xdr_size=ty.xdr_size) + phase.params.append(p) + phases.append(phase) + + want_item = True + + # We don't want to be asking recvmsg() for one object at a time if + # they're really small. + phase.size = ty.xdr_size + phase = None + else: + raise RuntimeError("Reply array not supported") + + if func.split and subname == "request": + phase = decode_phase(form="split", + size="py_rxgen_split_receive(call)") + phases.append(phase) + phase = None + have_bulk = True + + # Function definition and arguments + o.pysrc("\n") + o.pysrc("int py_", func.name, "_decode_", subname, "(struct rx_call *call)\n") + o.pysrc("{\n") + + if not params and not func.split: + o.pysrc("\treturn 0;\n") + o.pysrc("}\n") + return + + # Local variables + if params: + o.pysrc("\tstruct py_", func.name, "_", subname, " *obj = call->decoder_private;\n") + if want_item: + o.pysrc("\tPyObject *item;\n") + o.pysrc("\tunsigned phase = call->phase;\n") + o.pysrc("\tunsigned count;\n") + + # Deal with each phase + o.pysrc("\n") + if have_bulk: + o.pysrc("select_phase:\n") + o.pysrc("\tcount = call->data_count;\n") + #o.pysrc("\tprintf(\"-- Phase %u (%u) --\\n\", phase, count);\n") + o.pysrc("\tswitch (phase) {\n") + + o.pysrc("\tcase 0:\n") + + next_phase_id = 1 + for phase in phases: + phase.phase_id = next_phase_id + next_phase_id += 1 + + phase_goto_label = None + for phase in phases: + phase_id = phase.phase_id + o.pysrc("\n") + o.pysrc("\t\t/* --- Phase ", phase_id, " --- */\n") + + if phase_goto_label == phase_id: + o.pysrc("\tphase_", phase_id, ":\n") + phase_goto_label = None + + # Determine how big bulk objects are + if phase.form == "blob": + p = phase.params[0] + ty = p.typespec + if ty.is_single_string(): + o.pysrc("\t\tswitch (py_dec_init_string(call, &obj->x.", p.name, ")) {\n") + elif ty.is_single_opaque(): + o.pysrc("\t\tobj->x.", p.name, " = PyByteArray_FromStringAndSize(\"\", 0);\n") + o.pysrc("\t\tif (!obj->x.", p.name, ")\n") + o.pysrc("\t\t\treturn -1;\n") + o.pysrc("\t\tif (PyByteArray_Resize(obj->x.", p.name, ", call->blob_size) == -1)\n") + o.pysrc("\t\t\treturn -1;\n") + + o.pysrc("\t\tswitch (py_dec_init_opaque(call, obj->x.", p.name, ")) {\n") + else: + raise RuntimeError("Unsupported blob type " + str(ty)) + + o.pysrc("\t\tcase -1: return -1;\n") + o.pysrc("\t\tcase 0: goto phase_", phase_id + 1, ";\n") + o.pysrc("\t\tcase 1: break;\n") + o.pysrc("\t\t}\n") + phase_goto_label = phase_id + 1 + + elif phase.form == "bulk": + p = phase.params[0] + ty = p.typespec + if ty.is_bulk_int() or ty.is_bulk_struct(): + o.pysrc("\t\tobj->x.", p.name, " = PyList_New(call->bulk_count);\n") + o.pysrc("\t\tif (!obj->x.", p.name, ")\n") + o.pysrc("\t\t\treturn -1;\n") + else: + raise RuntimeError + + o.pysrc("\t\tif (call->bulk_count == 0)\n") + o.pysrc("\t\t\tgoto phase_", phase_id + 1, ";\n") + phase_goto_label = phase_id + 1 + o.pysrc("\t\tcall->bulk_index = 0;\n") + + # Entry point for a phase + elif phase.form == "split": + o.pysrc("\t\tif (py_rxgen_split_receive(call, 1) < 0)\n") + o.pysrc("\t\t\treturn -1;\n") + o.pysrc("\t\tif (call->need_size == 0)\n") + o.pysrc("\t\t\tgoto phase_", phase_id + 1, ";\n") + phase_goto_label = phase_id + 1 + + o.pysrc("\t\tcall->phase = ", phase_id, ";\n") + o.pysrc("\tcase ", phase_id, ":\n") + + if phase.form != "split": + o.pysrc("\t\tcall->need_size = ", phase.size, ";\n") + o.pysrc("\t\tif (count < call->need_size)\n") + o.pysrc("\t\t\treturn 1;\n") + else: + o.pysrc("\t\tif (call->need_size == UINT_MAX ? count == 0 : count < call->need_size) {\n") + #o.pysrc("\t\t\tprintf(\"NEED %u (phase %u)\\n\", call->need_size, phase);\n") + o.pysrc("\t\t\treturn 1;\n") + o.pysrc("\t\t}\n") + + # Unmarshal the data + o.pysrc("\n") + for p in phase.params: + o.where(func.name + ":" + p.name) + ty = p.typespec + if p.special == None: + pass + elif p.special == "blob_size": + o.pysrc("\t\tcall->blob_size = rxrpc_dec(call);\n") + continue + elif p.special == "bulk_size": + o.pysrc("\t\tcall->bulk_count = rxrpc_dec(call);\n") + continue + else: + raise RuntimeError + + if ty.is_bulk(): + if ty.is_bulk_struct(): + o.pysrc("\t\titem = py_decode_", ty.name, "(call);\n") + elif ty.is_bulk_int32() and ty.name.startswith("u"): + o.pysrc("\t\titem = PyLong_FromUnsignedLong((", ty.name, ")rxrpc_dec(call));\n") + elif ty.is_bulk_int32(): + o.pysrc("\t\titem = PyLong_FromLong((", ty.name, ")rxrpc_dec(call));\n") + elif ty.is_bulk_int64() and ty.name.startswith("u"): + o.pysrc("\t\tcall->bulk_u64 = (uint64_t)rxrpc_dec(call) << 32;\n") + o.pysrc("\t\tcall->bulk_u64 |= (uint64_t)rxrpc_dec(call);\n") + o.pysrc("\t\titem = PyLong_FromUnsignedLongLong(call->bulk_u64);\n") + elif ty.is_bulk_int64(): + o.pysrc("\t\tcall->bulk_s64 = (int64_t)rxrpc_dec(call) << 32;\n") + o.pysrc("\t\tcall->bulk_s64 |= (int64_t)rxrpc_dec(call);\n") + o.pysrc("\t\titem = PyLong_FromLongLong(call->bulk_s64);\n") + else: + raise RuntimeError + + o.pysrc("\t\tif (!item)\n") + o.pysrc("\t\t\treturn -1;\n") + o.pysrc("\t\tif (PyList_SetItem(obj->x.", p.name, ", call->bulk_index, item) < 0)\n") + o.pysrc("\t\t\treturn -1;\n") + o.pysrc("\t\tcall->bulk_index++;\n") + + elif ty.is_single_blob(): + if ty.is_single_string(): + o.pysrc("\t\tswitch (py_dec_into_string(call)) {\n") + else: + o.pysrc("\t\tswitch (py_dec_into_buffer(call)) {\n") + o.pysrc("\t\tcase -1: return -1;\n") + o.pysrc("\t\tcase 0: break;\n") + o.pysrc("\t\tcase 1: phase = ", phase_id, "; goto select_phase;\n") + o.pysrc("\t\t}\n") + elif ty.is_single_int32(): + o.pysrc("\t\tobj->x.", p.name, " = (", ty.name, ")rxrpc_dec(call);\n") + elif ty.is_single_int64(): + o.pysrc("\t\tobj->x.", p.name, " = (", ty.name, ")rxrpc_dec(call) << 32;\n") + o.pysrc("\t\tobj->x.", p.name, " |= (", ty.name, ")rxrpc_dec(call) << 32;\n") + elif ty.is_single_struct(): + o.pysrc("\t\tobj->x.", p.name, " = py_decode_", ty.name, "(call);\n") + else: + raise RuntimeError("Unsupported type in decode") + + if ty.is_single_string(): + o.pysrc("\t\tif (rxrpc_post_dec(call) < 0)\n") + o.pysrc("\t\t\treturn -1;\n") + o.pysrc("\t\tif (call->blob_offset < call->blob_size) {\n") + o.pysrc("\t\t\tphase = ", phase_id, ";\n") + o.pysrc("\t\t\tgoto select_phase;\n") + o.pysrc("\t\t}\n") + if ty.is_bulk(): + o.pysrc("\t\tif (rxrpc_post_dec(call) < 0)\n") + o.pysrc("\t\t\treturn -1;\n") + o.pysrc("\t\tif (call->bulk_index < call->bulk_count) {\n") + o.pysrc("\t\t\tphase = ", phase_id, ";\n") + o.pysrc("\t\t\tgoto select_phase;\n") + o.pysrc("\t\t}\n") + + if phase.form == "split": + o.pysrc("\t\tswitch (py_rxgen_split_receive(call, 0)) {\n") + o.pysrc("\t\tcase -1: return -1;\n") + o.pysrc("\t\tcase 0: break;\n") + o.pysrc("\t\tcase 1: phase = ", phase_id, "; goto select_phase;\n") + o.pysrc("\t\t}\n") + #o.pysrc("\t\tif (rxrpc_post_dec(call) < 0)\n") + #o.pysrc("\t\t\treturn -1;\n") + #o.pysrc("\t\tif (call->need_size != 0) {\n") + #o.pysrc("\t\t\tphase = ", phase_id, ";\n") + #o.pysrc("\t\t\tgoto select_phase;\n") + #o.pysrc("\t\t}\n") + + if phase.form != "bulk" and phase.form != "blob": + o.pysrc("\t\tif (rxrpc_post_dec(call) < 0)\n") + o.pysrc("\t\t\treturn -1;\n") + + o.pysrc("\n") + o.pysrc("\t\t/* --- Phase ", next_phase_id, " --- */\n") + if phase_goto_label: + o.pysrc("\tphase_", next_phase_id, ":\n") + o.pysrc("\t\tcall->phase = ", next_phase_id, ";\n") + o.pysrc("\t\tcall->need_size = 0;\n") + o.pysrc("\tdefault:\n") + o.pysrc("\t\treturn 0;\n") + o.pysrc("\t}\n") + + o.pysrc("}\n") diff --git a/rxgen/emit_py_types.py b/rxgen/emit_py_types.py new file mode 100644 index 0000000..529303a --- /dev/null +++ b/rxgen/emit_py_types.py @@ -0,0 +1,387 @@ +# Emission of Python type wrappers +# -*- coding: utf-8 -*- + +__copyright__ = """ +Copyright (C) 2015 Red Hat, Inc. All Rights Reserved. +Written by David Howells (dhowells@redhat.com) + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public Licence version 2 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public Licence for more details. + +You should have received a copy of the GNU General Public Licence +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +""" + +from rxgen_bits import * + +############################################################################### +# +# Emit python type wrapper declarations +# +############################################################################### +def emit_py_type_wrapper_decls(o, s): + o.pysrc("static PyTypeObject py_", s.name, "Type;\n") + + +############################################################################### +# +# Emit python type wrappers for C structs. +# +############################################################################### +def emit_py_type_wrapper(o, struct): + o.xdr.py_type_defs.append(py_type_def(struct.name, "py_" + struct.name + "Type")) + + # Divide the struct members into single ints, single structs, char arrays + # (strings) and other arrays + single_ints = list(); + single_structs = list(); + char_arrays = list(); + arrays = list(); + for m in struct.members: + ty = m.typespec + o.where(struct.name + "::" + m.name) + if ty.is_char_array(): + char_arrays.append(m) + elif ty.is_single_basic(): + single_ints.append(m) + elif ty.is_single_struct(): + single_structs.append(m) + elif ty.is_array(): + arrays.append(m) + else: + o.error(": Unsupported struct member type") + + # Write a python wrapper struct + # + # We have a copy of the raw struct and we also have caches for python + # objects for non-integer, non-array bits of the struct. We populate the + # caches when these bits are called for and then fold their contents back + # into the raw struct when we're about to marshal it. + # + o.pyhdr("\n") + o.pyhdr("struct py_", struct.name, " {\n") + o.pyhdr("\tPyObject_HEAD\n") + o.pyhdr("\tstruct ", struct.name, " x;\n") + if single_structs or arrays: + o.pyhdr("\tstruct {\n") + for m in single_structs + arrays: + o.pyhdr("\t\tPyObject *", m.name, ";\n") + o.pyhdr("\t} c;\n") + o.pyhdr("};\n") + + # We need to have a new function if the object is to be allocatable by the + # Python interpreter + o.pysrc("\n") + o.pysrc("static PyObject *\n") + o.pysrc("py_", struct.name, "_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds)\n") + o.pysrc("{\n") + o.pysrc("\treturn subtype->tp_alloc(subtype, 1);\n;") + o.pysrc("}\n") + + # We have to have a deallocation function + o.pysrc("\n") + o.pysrc("static void\n") + o.pysrc("py_", struct.name, "_dealloc(struct py_", struct.name, " *self)\n") + o.pysrc("{\n") + for m in single_structs + arrays: + o.pysrc("\tPy_XDECREF(self->c.", m.name, ");\n") + o.pysrc("\tPy_TYPE(self)->tp_free((PyObject *)self);\n") + o.pysrc("}\n") + + # Any integer non-array elements are made directly accessible to the Python + # interpreter + if single_ints: + o.pysrc("\n") + o.pysrc("static PyMemberDef py_", struct.name, "_members[] = {\n") + for m in single_ints: + ty = m.typespec + o.where(struct.name + "::" + m.name) + o.pysrc("\t{ \"", m.name, "\", ") + if ty.name == "char": + o.pysrc("T_CHAR") + elif ty.name == "int8_t": + o.pysrc("T_BYTE") + elif ty.name == "int16_t": + o.pysrc("T_SHORT") + elif ty.name == "int32_t": + o.pysrc("T_INT") + elif ty.name == "int64_t": + o.pysrc("T_LONGLONG") + elif ty.name == "uint8_t": + o.pysrc("T_UBYTE") + elif ty.name == "uint16_t": + o.pysrc("T_USHORT") + elif ty.name == "uint32_t": + o.pysrc("T_UINT") + elif ty.name == "uint64_t": + o.pysrc("T_ULONGLONG") + else: + o.error(": Unsupported type \"", ty.name, "\"") + o.pysrc(", offsetof(struct py_", struct.name, ", x.", m.name, "), 0, \"\"},\n") + o.pysrc("\t{}\n") + o.pysrc("};\n") + + # Non-single integer elements need to be turned into their respective + # Python types and returned. + + # Array elements have to be accessed through ->tp_[sg]etattro() as + # tuples (int[]/uint[]/struct[]) or strings (char[]) + # + attro_list = char_arrays + single_structs + arrays + if attro_list: + # The attribute get function + o.pysrc("\n") + o.pysrc("static PyObject *\n") + o.pysrc("py_", struct.name, "_getattro(PyObject *_self, PyObject *name)\n") + o.pysrc("{\n") + o.pysrc("\tstruct py_", struct.name, " *self = (struct py_", struct.name, " *)_self;\n") + o.pysrc("\n") + o.pysrc("\tif (PyUnicode_Check(name)) {\n") + + for m in attro_list: + ty = m.typespec + o.where(struct.name + "::" + m.name) + o.pysrc("\t\tif (PyUnicode_CompareWithASCIIString(name, \"", m.name, "\") == 0)\n") + if ty.is_single_struct(): + o.pysrc("\t\t\treturn py_rxgen_get_struct(&self->x.", m.name, ",\n") + o.pysrc("\t\t\t\t\t\t &self->c.", m.name, ",\n") + o.pysrc("\t\t\t\t\t\t py_data_to_", ty.name, ");\n") + elif not ty.is_array(): + raise RuntimeError("Unsupported basic type \"" + str(ty) + "\"") + elif ty.is_struct_array(): + o.pysrc("\t\t\treturn py_rxgen_get_structs(&self->x.", m.name, ", ", ty.dim.name, ",\n") + o.pysrc("\t\t\t\t\t\t sizeof(struct ", ty.name, "),\n") + o.pysrc("\t\t\t\t\t\t &self->c.", m.name, ",\n") + o.pysrc("\t\t\t\t\t\t py_data_to_", ty.name, ");\n") + elif not ty.is_int_array(): + o.error(": Unsupported array type class \"", ty, "\"") + elif ty.name == "char": + o.pysrc("\t\t\treturn py_rxgen_get_string(&self->x.", m.name, ", ", ty.dim.name, ");\n") + elif ty.name == "uint8_t": + o.pysrc("\t\t\treturn py_rxgen_get_uint8(&self->x.", m.name, ", ", ty.dim.name, ",\n") + o.pysrc("\t\t\t\t\t\t &self->c.", m.name, ");\n") + elif ty.name == "uint16_t": + o.pysrc("\t\t\treturn py_rxgen_get_uint16(&self->x.", m.name, ", ", ty.dim.name, ",\n") + o.pysrc("\t\t\t\t\t\t &self->c.", m.name, ");\n") + elif ty.name == "uint32_t": + o.pysrc("\t\t\treturn py_rxgen_get_uint32(&self->x.", m.name, ", ", ty.dim.name, ",\n") + o.pysrc("\t\t\t\t\t\t &self->c.", m.name, ");\n") + elif ty.name == "int8_t": + o.pysrc("\t\t\treturn py_rxgen_get_int8(&self->x.", m.name, ", ", ty.dim.name, ",\n") + o.pysrc("\t\t\t\t\t\t &self->c.", m.name, ");\n") + elif ty.name == "int16_t": + o.pysrc("\t\t\treturn py_rxgen_get_int16(&self->x.", m.name, ", ", ty.dim.name, ",\n") + o.pysrc("\t\t\t\t\t\t &self->c.", m.name, ");\n") + elif ty.name == "int32_t": + o.pysrc("\t\t\treturn py_rxgen_get_int32(&self->x.", m.name, ", ", ty.dim.name, ",\n") + o.pysrc("\t\t\t\t\t\t &self->c.", m.name, ");\n") + else: + raise RuntimeError("Unsupported array type \"" + str(ty) + "\"") + + o.pysrc("\t}\n") + o.pysrc("\n") + o.pysrc("\treturn PyObject_GenericGetAttr(_self, name);\n") + o.pysrc("}\n") + o.pysrc("\n") + + # The attribute set function + o.pysrc("static int\n") + o.pysrc("py_", struct.name, "_setattro(PyObject *_self, PyObject *name, PyObject *val)\n") + o.pysrc("{\n") + o.pysrc("\tstruct py_", struct.name, " *self = (struct py_", struct.name, " *)_self;\n") + o.pysrc("\n") + o.pysrc("\tif (PyUnicode_Check(name)) {\n") + + for m in attro_list: + ty = m.typespec + o.where(struct.name + "::" + m.name) + o.pysrc("\t\tif (PyUnicode_CompareWithASCIIString(name, \"", m.name, "\") == 0)\n") + if ty.is_single_struct(): + o.pysrc("\t\t\treturn py_rxgen_set_struct(&self->c.", m.name, ",\n") + o.pysrc("\t\t\t\t\t\t &py_", ty.name, "Type, val);\n") + elif not ty.is_array(): + raise RuntimeError("Unsupported basic type \"" + str(ty) + "\"") + elif ty.is_char_array(): + o.pysrc("\t\t\treturn py_rxgen_set_string(&self->x.", m.name, ", ", ty.dim.name, ", val);\n") + elif ty.is_int_array() or ty.is_struct_array(): + o.pysrc("\t\t\treturn py_rxgen_set_array(", ty.dim.name, ", &self->c.", m.name, ", val);\n") + else: + raise RuntimeError("Unsupported array type \"" + str(ty) + "\"") + + o.pysrc("\t}\n") + o.pysrc("\n") + o.pysrc("\treturn PyObject_GenericSetAttr(_self, name, val);\n") + o.pysrc("}\n") + o.pysrc("\n") + + # Emit the Python type definition + o.pysrc("static PyTypeObject py_", struct.name, "Type = {\n") + o.pysrc("\tPyVarObject_HEAD_INIT(NULL, 0)\n") + o.pysrc("\t\"kafs.", struct.name, "\",\t\t/*tp_name*/\n") + o.pysrc("\tsizeof(struct py_", struct.name, "),\t/*tp_basicsize*/\n") + o.pysrc("\t0,\t\t\t\t/*tp_itemsize*/\n") + o.pysrc("\t(destructor)py_", struct.name, "_dealloc, /*tp_dealloc*/\n") + o.pysrc("\t0,\t\t\t\t/*tp_print*/\n") + o.pysrc("\t0,\t\t\t\t/*tp_getattr*/\n") + o.pysrc("\t0,\t\t\t\t/*tp_setattr*/\n") + o.pysrc("\t0,\t\t\t\t/*tp_compare*/\n") + o.pysrc("\t0,\t\t\t\t/*tp_repr*/\n") + o.pysrc("\t0,\t\t\t\t/*tp_as_number*/\n") + o.pysrc("\t0,\t\t\t\t/*tp_as_sequence*/\n") + o.pysrc("\t0,\t\t\t\t/*tp_as_mapping*/\n") + o.pysrc("\t0,\t\t\t\t/*tp_hash */\n") + o.pysrc("\t0,\t\t\t\t/*tp_call*/\n") + o.pysrc("\t0,\t\t\t\t/*tp_str*/\n") + if attro_list: + o.pysrc("\tpy_", struct.name, "_getattro,\n") + o.pysrc("\tpy_", struct.name, "_setattro,\n") + else: + o.pysrc("\t0,\t\t\t\t/*tp_getattro*/\n") + o.pysrc("\t0,\t\t\t\t/*tp_setattro*/\n") + o.pysrc("\t0,\t\t\t\t/*tp_as_buffer*/\n") + o.pysrc("\tPy_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/\n") + o.pysrc("\t\"\",\t\t\t/* tp_doc */\n") + o.pysrc("\t0,\t\t\t\t/* tp_traverse */\n") + o.pysrc("\t0,\t\t\t\t/* tp_clear */\n") + o.pysrc("\t0,\t\t\t\t/* tp_richcompare */\n") + o.pysrc("\t0,\t\t\t\t/* tp_weaklistoffset */\n") + o.pysrc("\t0,\t\t\t\t/* tp_iter */\n") + o.pysrc("\t0,\t\t\t\t/* tp_iternext */\n") + o.pysrc("\t0,\t\t\t\t/* tp_methods */\n") + if single_ints: + o.pysrc("\tpy_", struct.name, "_members,\n") + else: + o.pysrc("\t0,\t\t\t/* tp_members */\n") + o.pysrc("\t0,\t\t\t\t/* tp_getset */\n") + o.pysrc("\t0,\t\t\t\t/* tp_base */\n") + o.pysrc("\t0,\t\t\t\t/* tp_dict */\n") + o.pysrc("\t0,\t\t\t\t/* tp_descr_get */\n") + o.pysrc("\t0,\t\t\t\t/* tp_descr_set */\n") + o.pysrc("\t0,\t\t\t\t/* tp_dictoffset */\n") + o.pysrc("\t0,\t\t\t\t/* tp_init */\n") + o.pysrc("\t0,\t\t\t\t/* tp_alloc */\n") + o.pysrc("\tpy_", struct.name, "_new,\n") + o.pysrc("};\n") + + # Emit a function to allocate such a type + o.pyhdr("extern PyObject *kafs_new_py_", struct.name, "(PyObject *, PyObject *);\n") + + o.pysrc("\n") + o.pysrc("PyObject *\n") + o.pysrc("kafs_new_py_", struct.name, "(PyObject *_self, PyObject *args)\n") + o.pysrc("{\n") + o.pysrc("\tPyObject *obj = _PyObject_New(&py_", struct.name, "Type);\n") + o.pysrc("\tstruct py_", struct.name, " *self = (struct py_", struct.name, " *)obj;\n") + o.pysrc("\tif (!obj)\n") + o.pysrc("\t\treturn PyExc_MemoryError;\n") + o.pysrc("\tmemset(&self->x, 0, sizeof(self->x));\n") + if single_structs or arrays: + o.pysrc("\tmemset(&self->c, 0, sizeof(self->c));\n") + o.pysrc("\treturn obj;\n") + o.pysrc("}\n") + + # Emit a function to create an object of this type from raw data + o.pyhdr("extern PyObject *py_data_to_", struct.name, "(const void *);\n") + + o.pysrc("\n") + o.pysrc("PyObject *py_data_to_", struct.name, "(const void *data)\n") + o.pysrc("{\n") + o.pysrc("\tPyObject *obj = _PyObject_New(&py_", struct.name, "Type);\n") + o.pysrc("\tstruct py_", struct.name, " *self = (struct py_", struct.name, " *)obj;\n") + o.pysrc("\tif (!obj)\n") + o.pysrc("\t\treturn PyExc_MemoryError;\n") + o.pysrc("\tmemcpy(&self->x, data, sizeof(self->x));\n") + if single_structs or arrays: + o.pysrc("\tmemset(&self->c, 0, sizeof(self->c));\n") + o.pysrc("\treturn obj;\n") + o.pysrc("}\n") + + # Emit a function to unmarshal on object of this type. + o.pysrc("\n") + o.pysrc("PyObject *py_decode_", struct.name, "(struct rx_call *call)\n") + o.pysrc("{\n") + o.pysrc("\tPyObject *obj = _PyObject_New(&py_", struct.name, "Type);\n") + o.pysrc("\tstruct py_", struct.name, " *self = (struct py_", struct.name, " *)obj;\n") + o.pysrc("\tif (!obj)\n") + o.pysrc("\t\treturn PyExc_MemoryError;\n") + o.pysrc("\trxgen_decode_", struct.name, "(call, &self->x);\n") + if single_structs or arrays: + o.pysrc("\tmemset(&self->c, 0, sizeof(self->c));\n") + o.pysrc("\treturn obj;\n") + o.pysrc("}\n") + + # Emit a function to premarshal such a type. This checks the Python object + # type and folds the contents of the cached Python objects into their raw + # fields. + # + o.pyhdr("extern int py_premarshal_", struct.name, "(PyObject *);\n") + + o.pysrc("\n") + o.pysrc("int py_premarshal_", struct.name, "(PyObject *_self)\n") + o.pysrc("{\n") + o.pysrc("\tstruct py_", struct.name, " *self = (struct py_", struct.name, " *)_self;\n") + + # Check that the type we've been given is the right one + o.pysrc("\n") + o.pysrc("\tif (!PyObject_TypeCheck(self, &py_", struct.name, "Type)) {\n") + o.pysrc("\t\tPyErr_Format(PyExc_TypeError, \"Expected object of type ", struct.name, "\");\n") + o.pysrc("\t\treturn -1;\n") + o.pysrc("\t}\n") + + if single_structs or arrays: + o.pysrc("\n") + first = 1 + for m in single_structs + arrays: + ty = m.typespec + o.where(struct.name + "::" + m.name) + if first: + o.pysrc("\tif (") + first = 0 + else: + o.pysrc(" ||\n") + o.pysrc("\t ") + + if ty.is_single_struct(): + o.pysrc("py_rxgen_premarshal_struct(&self->x.", m.name, ",\n") + o.pysrc("\t\t\t\t sizeof(struct ", ty.name, "),\n") + o.pysrc("\t\t\t\t offsetof(struct py_", ty.name, ", x),\n") + o.pysrc("\t\t\t\t self->c.", m.name, ",\n") + o.pysrc("\t\t\t\t py_premarshal_", ty.name, ") < 0") + elif not ty.is_array(): + raise RuntimeError("Unsupported basic type \"" + str(ty) + "\"") + elif ty.is_struct_array(): + o.pysrc("py_rxgen_premarshal_structs(&self->x.", m.name, ",\n") + o.pysrc("\t\t\t\t\t", ty.dim.name, ", sizeof(struct ", ty.name, "),\n") + o.pysrc("\t\t\t\t\toffsetof(struct py_", ty.name, ", x),\n") + o.pysrc("\t\t\t\t\tself->c.", m.name, ",\n") + o.pysrc("\t\t\t\t\tpy_premarshal_", ty.name, ") < 0") + elif not ty.is_int_array(): + raise RuntimeError("Unsupported array type \"", ty, "\"") + elif ty.name == "uint8_t": + o.pysrc("py_rxgen_premarshal_uint8(&self->x.", m.name, ", ", ty.dim.name, ", self->c.", m.name, ") < 0") + elif ty.name == "uint16_t": + o.pysrc("py_rxgen_premarshal_uint16(&self->x.", m.name, ", ", ty.dim.name, ", self->c.", m.name, ") < 0") + elif ty.name == "uint32_t": + o.pysrc("py_rxgen_premarshal_uint32(&self->x.", m.name, ", ", ty.dim.name, ", self->c.", m.name, ") < 0") + elif ty.name == "int8_t": + o.pysrc("py_rxgen_premarshal_int8(&self->x.", m.name, ", ", ty.dim.name, ", self->c.", m.name, ") < 0") + elif ty.name == "int16_t": + o.pysrc("py_rxgen_premarshal_int16(&self->x.", m.name, ", ", ty.dim.name, ", self->c.", m.name, ") < 0") + elif ty.name == "int32_t": + o.pysrc("py_rxgen_premarshal_int32(&self->x.", m.name, ", ", ty.dim.name, ", self->c.", m.name, ") < 0") + else: + o.error(": Unsupported array type \"", ty.name, "\"") + + o.pysrc(")\n") + o.pysrc("\t\treturn -1;\n") + + o.pysrc("\treturn 0;\n") + o.pysrc("}\n") + diff --git a/rxgen/rxgen.py b/rxgen/rxgen.py new file mode 100755 index 0000000..7142037 --- /dev/null +++ b/rxgen/rxgen.py @@ -0,0 +1,745 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# +# Tool for processing an RxRPC-based RPC API definition in a C header file to +# produce (un)marshalling code and RPC functions to implement that API. +# +# It also produces a python module containing wrappers for the types, RPC +# functions and constants in the API definition. + +__copyright__ = """ +Copyright (C) 2015 Red Hat, Inc. All Rights Reserved. +Written by David Howells (dhowells@redhat.com) + +Yacc & Lex bits based on pynfs rpcgen.py code: + + Written by Fred Isaman + Copyright (C) 2004 University of Michigan, Center for + Information Technology Integration + Based on version written by Peter Astrand + Copyright (C) 2001 Cendio Systems AB (http://www.cendio.se) + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public Licence version 2 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public Licence for more details. + +You should have received a copy of the GNU General Public Licence +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +""" + +import sys +import keyword +import time +import os +from rxgen_bits import * +from emit_c_struct import * +from emit_c_sync_funcs import * +from emit_py_types import * +from emit_py_sync_funcs import * +from emit_py_module import * + +xdr = None # Current context + +########################################################################## +# # +# Lexical analysis # +# # +########################################################################## +import ply.lex as lex + +basic_types = ( + ( "char", "char", xdr_basic.int32, 4 ), + ( "int8_t", "int8_t", xdr_basic.int32, 4 ), + ( "int16_t", "int16_t", xdr_basic.int32, 4 ), + ( "int32_t", "int32_t", xdr_basic.int32, 4 ), + ( "int64_t", "int64_t", xdr_basic.int64, 8 ), + ( "uint8_t", "uint8_t", xdr_basic.int32, 4 ), + ( "uint16_t", "uint16_t", xdr_basic.int32, 4 ), + ( "uint32_t", "uint32_t", xdr_basic.int32, 4 ), + ( "uint64_t", "uint64_t", xdr_basic.int64, 8 ), + ( "string", "char", xdr_basic.string, 4 ), + ( "opaque", "void", xdr_basic.opaque, 4 ), + ) + +def load_basic_types(xdr): + global basic_types + for i in basic_types: + name = i[0] + t = xdr_type(xdr, name=name, c_name=i[1], basic=i[2], xdr_size=i[3]) + xdr.types[name] = t + +keywords = ( + "IN", + "INOUT", + "OUT", + "const", + "enum", + "multi", + "package", + "split", + "struct", + "typedef", +) + tuple([i[0] for i in basic_types]) + +# Required by lex. Each token also allows a function t_. +tokens = tuple([t.upper() for t in keywords]) + ( + "ID", "CONST10", "CONST8", "CONST16", + # ( ) [ ] { } + "LPAREN", "RPAREN", "LSQUARE", "RSQUARE", "LBRACE", "RBRACE", + # ; : < > * = , + "SEMI", "LT", "GT", "STAR", "EQUALS", "COMMA", + "BEGIN_ERROR_CODES", "END_ERROR_CODES", "NEWFILE" +) + +# t_ functions are used by lex. They are called with t.value==, and t.type==. They expect a return value +# with attribute type= + +# Tell lexer to ignore Whitespace. +t_ignore = " \t" + +def t_NEWFILE(t): + r'__NEWFILE__ [^\n]*' + t.lexer.source = t.value[12:] + t.lexer.lineno = 0 + return t + +def t_ID(t): + r'[A-Za-z][A-Za-z0-9_]*' + if t.value in keywords: + t.type = t.value.upper() + return t + +def t_CONST16(t): + r'0x[0-9a-fA-F]+' + return t + +def t_CONST8(t): + r'0[0-7]+' + return t + +def t_CONST10(t): + r'-?(([1-9]\d*)|0)' + return t + +# Tokens +t_LPAREN = r'\(' +t_RPAREN = r'\)' +t_LSQUARE = r'\[' +t_RSQUARE = r'\]' +t_LBRACE = r'\{' +t_RBRACE = r'\}' +t_SEMI = r';' +t_LT = r'<' +t_GT = r'>' +t_STAR = r'\*' +t_EQUALS = r'=' +t_COMMA = r',' +t_BEGIN_ERROR_CODES = r'__BEGIN_ERROR_CODES__' +t_END_ERROR_CODES = r'__END_ERROR_CODES__' + +def t_newline(t): + r'\n+' + t.lexer.lineno += t.value.count("\n") + +def t_error(t): + print("Illegal character {:s} at {:d} type {:s}".format(repr(t.value[0]), + t.lineno, t.type)) + t.lexer.skip(1) + +# Build the lexer +lex.lex(debug=0) + +########################################################################## +# # +# Yacc Parsing Info # +# # +########################################################################## + +def p_specification(t): + '''specification : NEWFILE definition_list''' + +def p_definition_list(t): + '''definition_list : definition definition_list + | empty''' + +def p_definition(t): + '''definition : package_def + | constant_def + | enum_def + | type_def + | error_code_list_def + | proc_spec''' + +def p_package_def(t): + '''package_def : PACKAGE ID''' + prefix = t[2] + name = t[2] + if name.endswith("_"): + name = name[:-1] + global xdr + xdr.lineno = t.lineno(1) + xdr.add_package(name, prefix) + +############################################################################### +# +# Constants and values +# +def p_constant_def(t): + '''constant_def : CONST ID EQUALS constant SEMI''' + name = t[2] + value = t[4] + global xdr + xdr.lineno = t.lineno(1) + t[0] = xdr.add_constant(name, value) + +def p_constant(t): + '''constant : CONST10 + | CONST8 + | CONST16''' + value = t[1] + if len(value) > 9: + value = value + 'L' + global xdr + xdr.lineno = t.lineno(1) + t[0] = xdr.add_number(value) + +def p_value_1(t): + '''value : constant''' + t[0] = t[1] + +def p_value_2(t): + '''value : ID''' + name = t[1] + global xdr + xdr.lineno = t.lineno(1) + t[0] = xdr.get_constant(name) + +def p_optional_value(t): + '''optional_value : value + | empty''' + # return value or None. + t[0] = t[1] + +def p_constant_list_1(t): + '''constant_list : constant_def constant_list''' + t[0] = [t[1]] + t[2] + +def p_constant_list_2(t): + '''constant_list : constant_def''' + t[0] = [t[1]] + +def p_error_code_list_def(t): + '''error_code_list_def : BEGIN_ERROR_CODES constant_list END_ERROR_CODES''' + global xdr + xdr.lineno = t.lineno(1) + xdr.add_error_codes(t[2]) + +def p_enum_def(t): + '''enum_def : ENUM ID LBRACE enum_list RBRACE SEMI''' + '''enum_def : ENUM ID LBRACE enum_list COMMA RBRACE SEMI''' + name = t[2] + global xdr + xdr.lineno = t.lineno(1) + enum = xdr_type(xdr, base=xdr.get_type("int32_t")) + t[0] = xdr.add_type(name, enum) + +def p_enum_list_1(t): + '''enum_list : enum_term''' + t[0] = [t[1]] + +def p_enum_list_2(t): + '''enum_list : enum_term COMMA enum_list''' + t[0] = [t[1]] + t[3] + +def p_enum_term(t): + '''enum_term : ID EQUALS constant''' + name = t[1] + value = t[3] + global xdr + xdr.lineno = t.lineno(1) + t[0] = xdr.add_constant(name, value) + +############################################################################### +# +# Type definition +# +def p_type_def_1(t): + '''type_def : TYPEDEF declaration SEMI''' + name = t[2].name + typespec = t[2].typespec + global xdr + xdr.lineno = t.lineno(1) + alias = xdr_type_alias(name, typespec, xdr) + xdr.add_type_alias(name, alias) + #xdr.debug("Typedef", name) + +def p_type_def_2(t): + '''type_def : STRUCT ID struct_body SEMI''' + name = t[2] + body = t[3] + global xdr + xdr.lineno = t.lineno(1) + if name in xdr.structs: + xdr.error("Structure {:s} already exists".format(name)) + else: + s = xdr_type(xdr, name=name, c_name="struct " + name, + basic=xdr_basic.struct, members=body) + xdr.structs[name] = s + xdr.all_structs.append(s) + xdr.add_type(name, s) + xdr.debug("New struct", name) + +############################################################################### +# +# Type specification +# +def p_declaration_1(t): + '''declaration : type_specifier ID''' + typespec = t[1] + name = t[2] + global xdr + xdr.lineno = t.lineno(1) + t[0] = xdr_member(name, typespec, xdr); + +def p_declaration_2(t): + '''declaration : type_specifier STAR ID''' + # We ignore the pointer type marker + typespec = t[1] + name = t[3] + global xdr + xdr.lineno = t.lineno(1) + ty = xdr_type(xdr, base=typespec) + t[0] = xdr_member(name, ty, xdr) + +def p_declaration_3(t): + '''declaration : type_specifier ID LSQUARE value RSQUARE''' + typespec = t[1] + name = t[2] + array_size = t[4] + if not isinstance(typespec, xdr_type): + raise RuntimeError("Type is not type" + str(typespec)) + global xdr + xdr.lineno = t.lineno(1) + ty = xdr_type(xdr, base=typespec, array=xdr_array.fixed, dim=array_size) + t[0] = xdr_member(name, ty, xdr) + +def p_declaration_4(t): + '''declaration : type_specifier ID LT optional_value GT''' + typespec = t[1] + name = t[2] + max_size = t[4] + global xdr + xdr.lineno = t.lineno(1) + if typespec.is_single_blob(): + ty = xdr_type(xdr, base=typespec, max_size=max_size) + else: + ty = xdr_type(xdr, base=typespec, array=xdr_array.bulk, max_size=max_size) + t[0] = xdr_member(name, ty, xdr) + +def p_declaration_5(t): + '''declaration : type_specifier STAR ID LT optional_value GT''' + typespec = t[1] + name = t[3] + max_size = t[5] + global xdr + xdr.lineno = t.lineno(1) + if typespec.is_single_blob(): + ty = xdr_type(xdr, base=typespec, max_size=max_size) + else: + ty = xdr_type(xdr, base=typespec, array=xdr_array.bulk, max_size=max_size) + t[0] = xdr_member(name, ty, xdr) + +def p_declaration_6(t): + '''declaration : type_specifier LT optional_value GT STAR ID''' + typespec = t[1] + max_size = t[3] + name = t[6] + global xdr + xdr.lineno = t.lineno(1) + ty = xdr_type(xdr, base=typespec, array=xdr_array.bulk, max_size=max_size) + t[0] = xdr_member(name, ty, xdr) + +def p_type_specifier_1(t): + '''type_specifier : CHAR + | INT8_T + | INT16_T + | INT32_T + | INT64_T + | UINT8_T + | UINT16_T + | UINT32_T + | UINT64_T + | STRING + | OPAQUE''' + name = t[1] + global xdr + xdr.lineno = t.lineno(1) + t[0] = xdr.types[name] + if not isinstance(t[0], xdr_type): + raise RuntimeError("Type is not type" + typespec); + +def p_type_specifier_2(t): + '''type_specifier : ID''' + name = t[1] + global xdr + xdr.lineno = t.lineno(1) + t[0] = xdr.get_type(t[1]) + +def p_type_specifier_3(t): + '''type_specifier : struct_type_spec''' + t[0] = t[1] + +def p_type_specifier_4(t): + '''type_specifier : STRUCT ID''' + name = t[2] + global xdr + xdr.lineno = t.lineno(1) + t[0] = xdr.get_type(name) + + +############################################################################### +# +# Structure specification +# +def p_struct_type_spec(t): + '''struct_type_spec : STRUCT struct_body''' + t[0] = xdr_type(xdr, name="".format(t.lineno), c_name="struct", + compound=xdr_compound.struct, basic_type="struct", members=t[2]) + +def p_struct_body(t): + '''struct_body : LBRACE member_list RBRACE''' + t[0] = t[2] + +def p_member_list_1(t): + '''member_list : declaration SEMI''' + t[0] = [t[1]] + +def p_member_list_2(t): + '''member_list : declaration SEMI member_list''' + t[0] = [t[1]] + t[3] + +############################################################################### +# +# Procedure specification +# +def p_proc_spec_1(t): + '''proc_spec : ID LPAREN parameters RPAREN EQUALS value SEMI''' + name = t[1] + params = t[3] + op = t[6] + global xdr + proc = xdr_proc(name, xdr, params, op) + xdr.add_proc(proc) + +def p_proc_spec_2(t): + '''proc_spec : ID LPAREN parameters RPAREN SPLIT EQUALS value SEMI''' + name = t[1] + params = t[3] + op = t[7] + global xdr + proc = xdr_proc(name, xdr, params, op, split=True) + xdr.add_proc(proc) + +def p_proc_spec_3(t): + '''proc_spec : ID LPAREN parameters RPAREN MULTI EQUALS value SEMI''' + name = t[1] + params = t[3] + op = t[7] + global xdr + proc = xdr_proc(name, xdr, params, op, multi=True) + xdr.add_proc(proc) + +def p_parameters_0(t): + '''parameters : ''' + t[0] = [] + +def p_parameters_1(t): + '''parameters : parameter''' + t[0] = [t[1]] + +def p_parameters_2(t): + '''parameters : parameter COMMA parameters''' + t[0] = [t[1]] + t[3] + +def p_parameter_1(t): + '''parameter : IN declaration''' + t[2].direction = xdr_direction.IN + t[0] = t[2] + +def p_parameter_2(t): + '''parameter : INOUT declaration''' + t[2].direction = xdr_direction.INOUT + t[0] = t[2] + +def p_parameter_3(t): + '''parameter : OUT declaration''' + t[2].direction = xdr_direction.OUT + t[0] = t[2] + +############################################################################### +# +# Miscellany +# +def p_empty(t): + 'empty :' + +def p_error(t): + global error_occurred + error_occurred = True + if t: + print(t) + print("Syntax error at '{:s}' (lineno {:d})".format(t.value, t.lineno)) + else: + print("Syntax error: unexpectedly hit EOF") + +########################################################################## +# # +# Global Variables # +# # +########################################################################## + +error_occurred = False # Parsing of infile status + + + +########################################################################## +# # +# C Preprocessor # +# # +########################################################################## +def cpp(data, filename): + # Remove C comments + lines = data.splitlines() + inside_C_comment = False + inside_error_code_list = False + + for i in range(0, len(lines)): + l = lines[i] + begin = None + if inside_C_comment: + begin = 0 + cursor = 0 + + # We need to capture the error code list so that we can build a set of + # Python exceptions for it - but the only way to do that is to snoop + # the comments + # + if not inside_C_comment: + if l == "/* Error codes */" and not inside_error_code_list: + inside_error_code_list = True + lines[i] = "__BEGIN_ERROR_CODES__" + continue + + if inside_error_code_list and l == "": + inside_error_code_list = False + lines[i] = "__END_ERROR_CODES__" + continue + + while True: + if inside_C_comment: + p = l.find("*/", cursor) + if p == -1: + l = l[:begin] + lines[i] = l + break + l = l[:begin] + l[p + 2:] + lines[i] = l + cursor = begin + begin = None + inside_C_comment = False + else: + p = l.find("/*"); + if p == -1: + break + inside_C_comment = True + begin = p + cursor += 2 + + if inside_error_code_list: + lines.append("__END_ERROR_CODES__") + + # Remove C++ comments + for i in range(0, len(lines)): + l = lines[i] + p = l.find("//") + if p != -1: + lines[i] = l[:p] + + # Remove directives + skipping = False + for i in range(0, len(lines)): + l = lines[i] + if skipping: + if l == "#endif": + skipping = False; + lines[i] = "" + continue + if l == "#if 0": + skipping = True; + lines[i] = "" + continue + if (l.startswith("#include") or + l.startswith("#define") or + l.startswith("%")): + lines[i] = "" + continue + + lines.insert(0, "__NEWFILE__ {:s}".format(filename)) + return "\n".join(lines) + +########################################################################## +# # +# Main Loop # +# # +########################################################################## +xdr = xdr_context() +load_basic_types(xdr) +xdr.add_constant("RXRPC_SECURITY_PLAIN", "0") +xdr.add_constant("RXRPC_SECURITY_AUTH", "1") +xdr.add_constant("RXRPC_SECURITY_ENCRYPT", "2") + +# Parse the input data with yacc +import ply.yacc as yacc +yacc.yacc(debug=0) + +def parse(infile, debug=False): + global xdr + xdr.source = infile + xdr.lineno = 0 + + f = open(infile) + data = f.read() + f.close() + + data = cpp(data, infile) + + print("Parsing", infile); + global yacc + yacc.parse(data, debug=debug) + + if error_occurred: + print + print("Error occurred, did not write output files") + return False + return True + +# +# Section: main +# +if __name__ == "__main__": + if len(sys.argv) < 2: + print("Usage: {:s} *".format(sys.argv[0])) + sys.exit(1) + + for f in sys.argv[1:]: + if not parse(f): + break + + xdr.finished_parsing() + + o = file_generator(xdr) + o.rxhdr("/* AUTOGENERATED */\n") + #o.rxhdr("#define _XOPEN_SOURCE\n"; + o.rxhdr("#include \n") + o.rxhdr("#include \"rxgen.h\"\n") + + o.rxsrc("/* AUTOGENERATED */\n") + o.rxsrc("#include \"afs_xg.h\"\n") + o.rxsrc("#include \n") + o.rxsrc("#include \n") + o.rxsrc("#include \n") + o.rxsrc("#include \n") + o.rxsrc("#include \n") + o.rxsrc("#include \n") + o.rxsrc("#include \n") + o.rxsrc("#include \n") + o.rxsrc("\n") + + o.pyhdr("/* AUTOGENERATED */\n") + o.pyhdr("#include \n") + o.pyhdr("#include \"afs_xg.h\"\n") + o.pyhdr("#include \"py_rxgen.h\"\n") + + o.pysrc("/* AUTOGENERATED */\n") + o.pysrc("#include \n") + o.pysrc("#include \"structmember.h\"\n") + o.pysrc("#include \"afs_py.h\"\n") + o.pysrc("#include \n") + o.pysrc("\n") + + # Declare constants + o.rxhdr("\n") + for name in xdr.all_constants: + c = xdr.constants[name] + o.rxhdr("#define ", c.name, " ", c.value, "\n") + + # Declare structure types + for s in xdr.all_structs: + emit_struct_encdec_decl(o, s) + emit_py_type_wrapper_decls(o, s) + + for s in xdr.all_structs: + emit_struct_encdec(o, s); + emit_py_type_wrapper(o, s); + + # Emit RPC call functions. For this we need to classify parameters according + # to input and output usage and work out how big the RPC messages will be. + # + for f in xdr.funcs: + # Dump the banner comment block + o.rxsrc("/*\n") + o.rxsrc(" * RPC Call ", f.name, "\n") + o.rxsrc(" */\n") + + # Find the Operation ID + if not f.opcode: + raise RuntimeError("Operation ID unspecified for " + f.name) + + # Filter the parameters into request and response + f.request = list() + f.response = list() + + for p in f.params: + o.where(f.name + ":" + p.name) + ty = p.typespec + if ty.is_single_basic(): + pass + elif ty.is_struct(): + assert(ty.xdr_size) + elif ty.is_single_blob(): + # Could validate max_size attribute + pass + elif ty.is_bulk(): + assert(ty.xdr_size) + else: + raise RuntimeError("Unsupported param type \"" + str(ty) + "\"") + + if p.direction == xdr_direction.IN: + f.request.append(p) + elif p.direction == xdr_direction.OUT: + f.response.append(p) + elif p.direction == xdr_direction.INOUT: + f.request.append(p) + f.response.append(p) + + emit_func_prototype(o, f) + emit_func_decode(o, f, "client", "response", f.response) + emit_func_send(o, f, "request") + #emit_func_decode(f, "server", "request", request) + #emit_func_send(f, "response") + + emit_py_func_param_object(o, f, "request") + emit_py_func_param_object(o, f, "response") + emit_py_func_bulk_helper(o, f) + emit_py_func_decode(o, f, "client", "response", f.response) + emit_py_func_decode(o, f, "server", "request", f.request) + emit_py_func_simple_sync_call(o, f) + + emit_py_module(o); diff --git a/rxgen/rxgen_bits.py b/rxgen/rxgen_bits.py new file mode 100644 index 0000000..a8ad012 --- /dev/null +++ b/rxgen/rxgen_bits.py @@ -0,0 +1,550 @@ +# Bits for rxgen implementation +# -*- coding: utf-8 -*- + +__copyright__ = """ +Copyright (C) 2015 Red Hat, Inc. All Rights Reserved. +Written by David Howells (dhowells@redhat.com) + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public Licence version 2 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public Licence for more details. + +You should have received a copy of the GNU General Public Licence +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +""" + +import sys +from enum import Enum + +class xdr_basic(Enum): + int32 = 1 + int64 = 2 + string = 3 + opaque = 4 + struct = 5 + +class xdr_array(Enum): + # single object is represented by None + fixed = 1 + bulk = 2 + +class xdr_direction(Enum): + IN = 1 + OUT = 2 + INOUT = 3 + +class xdr_context: + def __init__(self): + self.source = None # Current source file + self.lineno = None # Current line number + self.types = dict() # List of types + self.typedefs = dict() # List of type aliases + self.structs = dict() # Structure definitions + self.struct_sizes = dict() # Structure sizes + self.all_structs = list() # Structures in order of declaration + self.funcs = list() # Functions in declaration order + self.func_names = dict() # Function name uniquifier + self.constants = dict() # Constants + self.packages = dict() # Packages + self.pkg = None # Current package + self.abort_syms = dict() # Abort symbol to code map + self.abort_ids = dict() # Abort code to symbol map + self.abort_count = 0 # Number of abort codes + self.error_codes = False # True if parsing error code list + self.py_type_defs = list() # Python type definitions + self.py_func_defs = list() # Python function definitions + self.debug_level = False + self.zero = xdr_constant("0", "0", self) + self.typespecs = dict() + + def debug(self, *s): + if (self.debug_level): + print(*s) + + def error(self, *s): + print("{:s}:{:d}: Error".format(self.source, self.lineno), *s) + + def add_package(self, name, prefix): + if name in self.packages: + self.error("Package {:s} already exists".format(name)) + else: + pkg = xdr_package(name, prefix, self) + self.packages[name] = pkg + self.pkg = pkg + self.debug("New package", name) + + def add_error_codes(self, codes): + self.abort_count += len(codes) + self.pkg.abort_codes += codes + for code in codes: + if not isinstance(code, xdr_constant): + raise TypeError("Expecting xdr_constant, got " + str(type(code))) + self.abort_syms[code.name] = code + value = code.value + suffix = "" + if value.endswith("L"): + value = value[0:-1] + suffix = "L" + if int(value) < 0: + value = str(int(value) + 0x100000000) + code.u32 = int(value) + self.abort_ids[code.u32] = code + + def add_constant(self, name, value): + if name in self.constants: + self.error("Constant {:s} already exists".format(name)) + elif isinstance(value, xdr_constant): + self.constants[name] = xdr_constant(name, value.value, self) + else: + self.constants[name] = xdr_constant(name, value, self) + self.debug("New constant", name) + return self.constants[name] + + def add_number(self, value): + if value == 0: + return self.zero + return xdr_constant(value, value, self) + + def get_constant(self, name): + if name not in self.constants: + self.error("Constant {:s} undefined".format(name)) + return self.zero + return self.constants[name] + + def add_type(self, name, typespec): + if name in self.types: + self.error("Type {:s} already exists".format(name)) + else: + self.types[name] = typespec + self.debug("New type", name) + return self.types[name] + + def add_type_alias(self, name, alias): + if name in self.typedefs: + self.error("Type alias {:s} already exists".format(name)) + return self.typedefs[name] + if name in self.structs: + self.error("struct {:s} already exists, cannot shadow with alias".format(name)) + return alias + + self.typedefs[name] = alias + self.debug("New typedef", name) + + def get_type(self, name): + if name in self.types: + typespec = self.types[name] + elif name in self.typedefs: + typespec = self.typedefs[name].typespec + else: + raise RuntimeError("Undefined type requested" + name); + if not isinstance(typespec, xdr_type): + raise TypeError("Retrieved type object is not xdr_type" + name + str(typespec)); + typespec.referenced = True + return typespec + + def add_proc(self, proc): + if not isinstance(proc, xdr_proc): + raise KeyError("proc is not an xdr_proc", name, proc); + name = proc.name + if name in self.func_names: + self.error("Proc {:s} already exists".format(name)) + else: + self.func_names[name] = proc + self.funcs.append(proc) + self.debug("New proc", name) + return self.func_names[name] + + def finished_parsing(self): + self.all_constants = list(self.constants.keys()) + self.all_constants.sort() + self.all_types = list(self.types.keys()) + self.all_types.sort() + + +############################################################################### +# +# Token base class +# +############################################################################### +class token_base(object): + def __init__(self): + self.source = None + self.lineno = None + self.name = None + self.type = None + + def __str__(self): + return "{:s} {:s} at {:s}:{:d}".format( + self.type, self.name, self.source, self.lineno) + +############################################################################### +# +# XDR package +# +############################################################################### +class xdr_package(token_base): + """The result of 'package ID'""" + def __init__(self, name, prefix, xdr): + self.type = 'package' + self.name = name + self.source = xdr.source + self.lineno = xdr.lineno + self.prefix = prefix + self.abort_codes = list() + +############################################################################### +# +# XDR constant +# +############################################################################### +class xdr_constant(token_base): + """The result of 'CONST ID EQUALS constant SEMI'""" + def __init__(self, name, value, xdr): + self.type = 'const' + self.name = name + self.value = value + self.source = xdr.source + self.lineno = xdr.lineno + if not isinstance(value, str): + raise RuntimeError("Value should be a string"); + + def get_value(self): + if isinstance(self.value, xdr_constant): + return self.value.get_value() + return int(self.value) + + def __str__(self): + return self.value + + def __repr__(self): + return "constant {:s}={:s} at {:s}:{:d}".format( + self.name, self.value, self.source, self.lineno) + +############################################################################### +# +# XDR type definition +# +# Each type is specified by a hash of the following elements: +# +# name Name of non-anonymous type (char, {u,}int{8,16,32,64}_t, struct name) +# basic Basic XDR type (int32, int64, string, opaque, struct) +# array Array class (single, array, bulk) +# dim Number of elements in fixed-size array (or None) +# max_size Max elements in bulk array (if array/bulk) +# members Members of struct +# xdr_size Size of XDR encoded object +# source/lineno Where defined in which file +# +# Members/parameters take a copy of their parent type's hash and add: +# +# name Member or parameter name (overrides type name) +# direction Direction of parameter (IN, OUT or INOUT) +# +############################################################################### +class xdr_type(token_base): + def __init__(self, xdr, base=None, name=None, c_name=None, + basic=None, array=None, dim=None, max_size=None, + xdr_size=None, + members=None): + if not isinstance(xdr, xdr_context): + raise TypeError("XDR context isn't") + if array: + if not isinstance(array, xdr_array): + raise TypeError("Invalid array class") + if array == xdr_array.fixed and not dim: + raise RuntimeError("Dimension required for fixed-size array") + if dim and max_size: + raise RuntimeError("Can't be both variable and fixed-size array") + elif basic == "string" or basic == "opaque" or base and base.is_blob(): + if dim: + raise RuntimeError("Can't specify fixed dimension limits on string/opaque") + else: + if dim or max_size: + raise RuntimeError("Can't specify dimension limits on non-array") + + self.source = xdr.source + self.lineno = xdr.lineno + self.referenced = False + + if base: + if name: + raise RuntimeError("Type basic name can't be changed on an extant type") + if c_name: + raise RuntimeError("Type C name can't be changed on an extant type") + if basic: + raise RuntimeError("Basic type can't be changed on an extant type") + if members: + raise RuntimeError("Members can't be added to an extant type") + if not isinstance(base, xdr_type): + raise TypeError("Base type is not a type", type(base)) + base.referenced = True + self.referenced = True + self.name = base.name + self.c_name = base.c_name + self.basic = base.basic + self.array = base.array + self.dim = base.dim + self.max_size = base.max_size + self.xdr_size = base.xdr_size + self.members = base.members + + if array: + if base.array: + xdr.error("Array-of-array not supported") + self.array = array + self.dim = dim + self.max_size = max_size + if self.xdr_size and dim: + self.xdr_size *= int(str(dim)) + elif self.is_single_blob() and max_size: + if self.max_size: + xdr.error("Maximum size already set on string/opaque") + self.max_size = max_size + else: + if name and not isinstance(name, str): + raise TypeError("Type name is not a string") + if c_name and not isinstance(c_name, str): + raise TypeError("C type name is not a string") + if basic and not isinstance(basic, xdr_basic): + raise TypeError("Invalid basic XDR type") + self.name = name + self.c_name = c_name + self.basic = basic + self.array = array + self.xdr_size = xdr_size + self.array = array + self.dim = dim + self.max_size = max_size + self.members = members + self.xdr_size = xdr_size + if members: + if not isinstance(members, list): + raise RuntimeError("Members should be a list") + self.xdr_size = 0 + for i in members: + if i.typespec.xdr_size: + self.xdr_size += i.typespec.xdr_size + + if not self.basic: + raise RuntimeError("basic type unset") + + typespec = str(self) + if typespec not in xdr.typespecs: + xdr.typespecs[typespec] = self + + + def is_int(self): + return self.basic == xdr_basic.int32 or self.basic == xdr_basic.int64 + + def is_int32(self): + return self.basic == xdr_basic.int32 + + def is_int64(self): + return self.basic == xdr_basic.int64 + + def is_string(self): + return self.basic == xdr_basic.string + + def is_opaque(self): + return self.basic == xdr_basic.opaque + + def is_struct(self): + return self.basic == xdr_basic.struct + + def is_blob(self): + return self.is_string() or self.is_opaque() + + def is_single_int(self): + return not self.array and self.is_int() + + def is_single_int32(self): + return not self.array and self.is_int32() + + def is_single_int64(self): + return not self.array and self.is_int64() + + def is_single_string(self): + return not self.array and self.is_string() + + def is_single_opaque(self): + return not self.array and self.is_opaque() + + def is_single_blob(self): + return not self.array and self.is_blob() + + def is_single_basic(self): + return not self.array and not self.is_struct() + + def is_single_struct(self): + return not self.array and self.is_struct() + + def is_single(self): + return not self.array + + def is_array(self): + return self.array == xdr_array.fixed + + def is_char_array(self): + return self.array == xdr_array.fixed and self.name == "char" + + def is_int32_array(self): + return self.array == xdr_array.fixed and self.is_int32() + + def is_int_array(self): + return self.array == xdr_array.fixed and self.is_int() + + def is_struct_array(self): + return self.array == xdr_array.fixed and self.is_struct() + + def is_bulk(self): + return self.array == xdr_array.bulk + + def is_bulk_char(self): + return self.is_bulk() and self.name == "char" + + def is_bulk_int32(self): + return self.is_bulk() and self.is_int32() + + def is_bulk_int64(self): + return self.is_bulk() and self.is_int64() + + def is_bulk_int(self): + return self.is_bulk() and self.is_int() + + def is_bulk_struct(self): + return self.is_bulk() and self.is_struct() + + def __str__(self): + t = self.name + if self.is_array(): + t += "[{:s}]".format(str(self.dim)) + elif self.is_bulk() or self.is_blob(): + if self.max_size: + t += "<{:s}>".format(str(self.max_size)) + else: + t += "<>" + return t + +############################################################################### +# +# XDR structure member definition +# +############################################################################### +class xdr_member(token_base): + """A structure member""" + def __init__(self, name, typespec, xdr): + self.typespec = typespec + self.name = name + self.source = xdr.source + self.lineno = xdr.lineno + self.special = None + +############################################################################### +# +# XDR type alias definition +# +############################################################################### +class xdr_type_alias(token_base): + """A type alias""" + def __init__(self, name, typespec, xdr): + if not isinstance(typespec, xdr_type): + raise TypeError("Base type is not a type") + self.name = name + self.typespec = typespec + self.source = xdr.source + self.lineno = xdr.lineno + +############################################################################### +# +# XDR procedure member definition +# +############################################################################### +class xdr_proc(token_base): + """An XDR procedure""" + def __init__(self, name, xdr, params, opcode, multi=False, split=False): + self.name = xdr.pkg.name + "_" + name + self.source = xdr.source + self.lineno = xdr.lineno + self.params = params + self.opcode = opcode + self.multi = multi + self.split = split + +############################################################################### +# +# Generated file writer class +# +############################################################################### +class file_generator: + """File generator class""" + def __init__(self, xdr): + self.xdr = xdr + self._rxhdr = open("2_afs_xg.h", "w", encoding="utf-8") + self._rxsrc = open("2_afs_xg.c", "w", encoding="utf-8") + self._pyhdr = open("2_afs_py.h", "w", encoding="utf-8") + self._pysrc = open("2_afs_py.c", "w", encoding="utf-8") + + def rxhdr(self, *va): + for i in va: + self._rxhdr.write(str(i)) + + def rxsrc(self, *va): + for i in va: + self._rxsrc.write(str(i)) + + def pyhdr(self, *va): + for i in va: + self._pyhdr.write(str(i)) + + def pysrc(self, *va): + for i in va: + self._pysrc.write(str(i)) + + def rxhdrf(self, fmt, *va): + self._rxhdr.write(fmt.format(*va)) + + def rxsrcf(self, fmt, *va): + self._rxsrc.write(fmt.format(*va)) + + def pyhdrf(self, fmt, *va): + self._pyhdr.write(fmt.format(*va)) + + def pysrcf(self, fmt, *va): + self._pysrc.write(fmt.format(*va)) + + def where(self, loc): + self._where = loc + ": " + + def error(self, *va): + if self._where: + sys.stdout.write(self._where) + for i in va: + sys.stdout.write(str(i)) + sys.stdout.write("\n") + +############################################################################### +# +# Python type def +# +############################################################################### +class py_type_def: + def __init__(self, name, c_type): + self.name = name + self.c_type = c_type + +############################################################################### +# +# Python function def +# +############################################################################### +class py_func_def: + def __init__(self, name, c_func, doc=""): + self.name = name + self.c_func = c_func + self.doc = doc