#include "../../backend.h" #include "../../interface.h" #include "../../msg.h" #include "../../type.h" #include #include #include #include #include struct emit_ctx { b_stream *ctx_out; }; static void emit(struct emit_ctx *ctx, const char *format, ...) { va_list arg; va_start(arg, format); b_stream_write_vfmt(ctx->ctx_out, NULL, format, arg); va_end(arg); } static void emit_indent(struct emit_ctx *ctx) { b_stream_push_indent(ctx->ctx_out, 4); } static void emit_indent_zero(struct emit_ctx *ctx) { b_stream_push_indent(ctx->ctx_out, -100); } static void emit_unindent(struct emit_ctx *ctx) { b_stream_pop_indent(ctx->ctx_out); } static int emit_include(struct emit_ctx *ctx, const char *path, bool system) { if (system) { emit(ctx, "#include <%s>\n", path); } else { emit(ctx, "#include \"%s\"\n", path); } return 0; } static int emit_header_guard_start( struct emit_ctx *ctx, const struct interface_definition *iface) { b_string *header_guard = b_string_create(); b_string_append_cstrf(header_guard, "%s_H_", iface->if_name); b_string_toupper(header_guard); emit(ctx, "#ifndef %s\n#define %s\n\n", b_string_ptr(header_guard), b_string_ptr(header_guard)); b_string_unref(header_guard); return 0; } static int emit_header_guard_end( struct emit_ctx *ctx, const struct interface_definition *iface) { emit(ctx, "\n#endif"); return 0; } static int emit_header_lib_impl(struct emit_ctx *ctx) { emit(ctx, "kern_status_t msg_recv_generic(\n"); emit_indent(ctx); emit(ctx, "kern_handle_t channel,\nmsgid_t *out_id,\nstruct msg_header " "*out_hdr)\n"); emit_unindent(ctx); emit(ctx, "{\n"); emit_indent(ctx); /* clang-format off */ emit(ctx, "struct iovec iov = IOVEC(out_hdr, sizeof *out_hdr);\n" "kern_status_t status = msg_recv(channel, out_id, &iov, 1);\n" "if (status != KERN_OK) return status;\n" "if (out_hdr->hdr_magic != MSG_MAGIC) return KERN_INVALID_ARGUMENT;\n" "return KERN_OK;\n"); /* clang-format on */ emit_unindent(ctx); emit(ctx, "}\n\n"); emit(ctx, "kern_status_t msg_read_generic(\n"); emit_indent(ctx); emit(ctx, "kern_handle_t channel,\nmsgid_t id,\nstruct msg_header " "*out_hdr)\n"); emit_unindent(ctx); emit(ctx, "{\n"); emit_indent(ctx); /* clang-format off */ emit(ctx, "struct iovec iov = IOVEC(out_hdr, sizeof *out_hdr);\n" "size_t r = 0;\n" "kern_status_t status = msg_read(channel, id, 0, &iov, 1, &r);\n" "if (status != KERN_OK) return status;\n" "if (r != sizeof *out_hdr) return KERN_INVALID_ARGUMENT;\n" "if (out_hdr->hdr_magic != MSG_MAGIC) return KERN_INVALID_ARGUMENT;\n" "return KERN_OK;\n"); /* clang-format on */ emit_unindent(ctx); emit(ctx, "}\n\n"); emit(ctx, "kern_status_t msg_reply_generic(\n"); emit_indent(ctx); emit(ctx, "kern_handle_t channel,\nmsgid_t id,\nconst struct msg_header " "*msg, uint16_t status)\n"); emit_unindent(ctx); emit(ctx, "{\n"); emit_indent(ctx); /* clang-format off */ emit(ctx, "struct msg_header resp_data = {\n"); emit_indent(ctx); emit(ctx, ".hdr_magic = MSG_MAGIC,\n.hdr_status = status,\n"); emit_unindent(ctx); emit(ctx, "};\n"); emit(ctx, "if (msg) {\n"); emit_indent(ctx); emit(ctx, "resp_data.hdr_protocol = msg->hdr_protocol;\n" "resp_data.hdr_func = msg->hdr_func;\n"); emit_unindent(ctx); emit(ctx, "}\n"); emit(ctx, "struct iovec iov = IOVEC(&resp_data, sizeof resp_data);\n" "return msg_reply(channel, id, &iov, 1);\n"); /* clang-format on */ emit_unindent(ctx); emit(ctx, "}\n\n"); return 0; } static int emit_header_lib_definitions(struct emit_ctx *ctx) { emit(ctx, "#if !defined(MSG_STRINGBUF_SIZE)\n"); emit(ctx, "#define MSG_STRINGBUF_SIZE 256\n"); emit(ctx, "#endif\n\n"); emit(ctx, "#if !defined(MSG_LIB_DEFINED)\n"); emit(ctx, "#define MSG_LIB_DEFINED\n\n"); emit(ctx, "#define MSG_MAGIC 0x9AB07D10U\n\n"); emit(ctx, "struct msg_header {\n"); emit_indent(ctx); emit(ctx, "uint32_t hdr_magic;\n"); emit(ctx, "uint32_t hdr_protocol;\n"); emit(ctx, "uint16_t hdr_func;\n"); emit(ctx, "uint16_t hdr_status;\n"); emit_unindent(ctx); emit(ctx, "};\n\n"); emit(ctx, "struct msg_string {\n"); emit_indent(ctx); emit(ctx, "char *str_buf;\n"); emit(ctx, "size_t str_len;\n"); emit(ctx, "size_t str_max;\n"); emit_unindent(ctx); emit(ctx, "};\n\n"); emit(ctx, "extern kern_status_t msg_recv_generic(\n"); emit_indent(ctx); emit(ctx, "kern_handle_t channel,\nmsgid_t *out_id,\nstruct msg_header " "*out_hdr);\n", NULL); emit_unindent(ctx); emit(ctx, "extern kern_status_t msg_read_generic(\n"); emit_indent(ctx); emit(ctx, "kern_handle_t channel,\nmsgid_t id,\nstruct msg_header " "*out_hdr);\n", NULL); emit_unindent(ctx); emit(ctx, "extern kern_status_t msg_reply_generic(\n"); emit_indent(ctx); emit(ctx, "kern_handle_t channel,\nmsgid_t id,\nconst struct msg_header " "*msg, uint16_t status);\n", NULL); emit_unindent(ctx); emit(ctx, "#if defined(MSG_IMPLEMENTATION)\n\n"); emit_header_lib_impl(ctx); emit(ctx, "#endif\n\n"); emit(ctx, "#endif\n\n"); return 0; } static int emit_interface_id_macros( struct emit_ctx *ctx, const struct interface_definition *iface) { b_string *iface_define_name = b_string_create(); b_string_append_cstr(iface_define_name, iface->if_name); b_string_toupper(iface_define_name); uint32_t protocol_id = b_hash_cstr(iface->if_name) & 0xFFFFFFFF; emit(ctx, "/* %s protocol ID */\n#define PROTOCOL_%s 0x%08xU\n\n", iface->if_name, b_string_ptr(iface_define_name), protocol_id); b_string_unref(iface_define_name); b_string *msg_name = b_string_create(); b_queue_entry *entry = b_queue_first(&iface->if_msg); while (entry) { const struct msg_definition *msg = b_unbox(struct msg_definition, entry, msg_entry); b_string_clear(msg_name); b_string_append_cstrf( msg_name, "%s.%s", iface->if_name, msg->msg_name); emit(ctx, "/* %s message ID */\n", b_string_ptr(msg_name)); uint16_t msg_id = b_hash_cstr(b_string_ptr(msg_name)) & 0xFFFF; b_string_clear(msg_name); b_string_append_cstrf( msg_name, "%s_%s", iface->if_name, msg->msg_name); b_string_toupper(msg_name); emit(ctx, "#define MSG_%s 0x%04xU\n", b_string_ptr(msg_name), msg_id); entry = b_queue_next(entry); } emit(ctx, "\n"); b_string_unref(msg_name); return 0; } static int emit_msg_struct_member( struct emit_ctx *ctx, const struct msg_parameter *param) { switch (param->p_type->ty_id) { case TYPE_INT: emit(ctx, "uint32_t %s;\n", param->p_name); break; case TYPE_STRING: emit(ctx, "uint16_t %s_offset;\n", param->p_name); emit(ctx, "uint16_t %s_len;\n", param->p_name); break; default: break; } return 0; } static int emit_msg_struct_params( struct emit_ctx *ctx, const struct msg_definition *msg) { b_queue_entry *entry = b_queue_first(&msg->msg_params); while (entry) { const struct msg_parameter *param = b_unbox(struct msg_parameter, entry, p_entry); emit_msg_struct_member(ctx, param); entry = b_queue_next(entry); } entry = b_queue_first(&msg->msg_results); while (entry) { const struct msg_parameter *param = b_unbox(struct msg_parameter, entry, p_entry); if (param->p_type->ty_id != TYPE_STRING) { entry = b_queue_next(entry); continue; } emit(ctx, "uint16_t %s_max;\n", param->p_name); entry = b_queue_next(entry); } return 0; } static int emit_msg_struct_results( struct emit_ctx *ctx, const struct msg_definition *msg) { b_queue_entry *entry = b_queue_first(&msg->msg_results); while (entry) { const struct msg_parameter *param = b_unbox(struct msg_parameter, entry, p_entry); emit_msg_struct_member(ctx, param); entry = b_queue_next(entry); } return 0; } static int emit_interface_msg_struct( struct emit_ctx *ctx, const struct interface_definition *iface, const struct msg_definition *msg) { b_string *tmp = b_string_create(); emit(ctx, "struct msg_%s_%s {\n", iface->if_name, msg->msg_name); emit_indent(ctx); emit(ctx, "struct msg_header msg_header;\n"); emit(ctx, "union {\n"); emit_indent(ctx); /* msg_request inner struct */ emit(ctx, "struct {\n"); emit_indent(ctx); emit_msg_struct_params(ctx, msg); emit_unindent(ctx); emit(ctx, "} msg_request;\n"); /* msg_response inner struct */ emit(ctx, "struct {\n"); emit_indent(ctx); emit_msg_struct_results(ctx, msg); emit_unindent(ctx); emit(ctx, "} msg_response;\n"); emit_unindent(ctx); emit(ctx, "};\n"); emit_unindent(ctx); emit(ctx, "};\n"); b_string_unref(tmp); return 0; } static int emit_interface_msg_structs( struct emit_ctx *ctx, const struct interface_definition *iface) { b_queue_entry *entry = b_queue_first(&iface->if_msg); while (entry) { const struct msg_definition *msg = b_unbox(struct msg_definition, entry, msg_entry); emit_interface_msg_struct(ctx, iface, msg); entry = b_queue_next(entry); } emit(ctx, "\n"); return 0; } static int emit_interface_msg_function_send_impl( struct emit_ctx *ctx, const struct interface_definition *iface, const struct msg_definition *msg) { b_string *iface_ucase = b_string_create_from_cstr(iface->if_name); b_string_toupper(iface_ucase); b_string *msg_ucase = b_string_create_from_cstr(msg->msg_name); b_string_toupper(msg_ucase); emit(ctx, "struct msg_%s_%s msg = {0};\n", iface->if_name, msg->msg_name); emit(ctx, "msg.msg_header.hdr_magic = MSG_MAGIC;\n"); emit(ctx, "msg.msg_header.hdr_protocol = PROTOCOL_%s;\n", b_string_ptr(iface_ucase)); emit(ctx, "msg.msg_header.hdr_func = MSG_%s_%s;\n\n", b_string_ptr(iface_ucase), b_string_ptr(msg_ucase)); b_string_unref(iface_ucase); b_string_unref(msg_ucase); emit(ctx, "uint16_t offset = sizeof msg;\n\n"); b_queue_entry *entry = b_queue_first(&msg->msg_params); while (entry) { struct msg_parameter *param = b_unbox(struct msg_parameter, entry, p_entry); switch (param->p_type->ty_id) { case TYPE_INT: emit(ctx, "msg.msg_request.%s = %s;\n\n", param->p_name, param->p_name); break; case TYPE_STRING: emit(ctx, "msg.msg_request.%s_offset = offset;\n", param->p_name); emit(ctx, "msg.msg_request.%s_len = strlen(%s);\n", param->p_name, param->p_name); emit(ctx, "offset += msg.msg_request.%s_len;\n", param->p_name); break; default: break; } entry = b_queue_next(entry); } entry = b_queue_first(&msg->msg_results); while (entry) { struct msg_parameter *param = b_unbox(struct msg_parameter, entry, p_entry); switch (param->p_type->ty_id) { case TYPE_STRING: emit(ctx, "msg.msg_request.%s_max = out_%s->str_max;\n", param->p_name, param->p_name); break; default: break; } entry = b_queue_next(entry); } emit(ctx, "struct iovec req_iov[] = {\n"); emit_indent(ctx); emit(ctx, "IOVEC(&msg, sizeof msg),\n"); size_t nr_req_iov = 1; entry = b_queue_first(&msg->msg_params); while (entry) { struct msg_parameter *param = b_unbox(struct msg_parameter, entry, p_entry); if (param->p_type->ty_id != TYPE_STRING) { entry = b_queue_next(entry); continue; } emit(ctx, "IOVEC(%s, msg.msg_request.%s_len),\n", param->p_name, param->p_name); nr_req_iov++; entry = b_queue_next(entry); } emit_unindent(ctx); emit(ctx, "};\n\n"); emit(ctx, "struct iovec resp_iov[] = {\n"); emit_indent(ctx); emit(ctx, "IOVEC(&msg, sizeof msg),\n"); size_t nr_resp_iov = 1; entry = b_queue_first(&msg->msg_results); while (entry) { struct msg_parameter *param = b_unbox(struct msg_parameter, entry, p_entry); if (param->p_type->ty_id != TYPE_STRING) { entry = b_queue_next(entry); continue; } emit(ctx, "IOVEC(out_%s->str_buf, out_%s->str_max),\n", param->p_name, param->p_name); nr_resp_iov++; entry = b_queue_next(entry); } emit_unindent(ctx); emit(ctx, "};\n\n"); emit(ctx, "kern_status_t status = msg_send(port, req_iov, %zu, resp_iov, %zu);\n", nr_req_iov, nr_resp_iov); emit(ctx, "if (status != KERN_OK) return status;\n\n"); entry = b_queue_first(&msg->msg_results); while (entry) { struct msg_parameter *param = b_unbox(struct msg_parameter, entry, p_entry); switch (param->p_type->ty_id) { case TYPE_STRING: emit(ctx, "out_%s->str_len = msg.msg_response.%s_len;\n", param->p_name, param->p_name); emit(ctx, "out_%s->str_buf[out_%s->str_len < " "out_%s->str_max ? out_%s->str_len : " "(out_%s->str_max - 1)] = '\\0';\n", param->p_name, param->p_name, param->p_name, param->p_name, param->p_name); break; default: emit(ctx, "*out_%s = msg.msg_response.%s;\n", param->p_name, param->p_name); break; } entry = b_queue_next(entry); } emit(ctx, "return status;\n"); return 0; } static int emit_interface_msg_function_recv_impl( struct emit_ctx *ctx, const struct interface_definition *iface, const struct msg_definition *msg) { emit(ctx, "struct msg_%s_%s msg;\n", iface->if_name, msg->msg_name); emit(ctx, "struct iovec iov = IOVEC(&msg, sizeof msg);\n"); emit(ctx, "size_t r = 0;\n\n"); emit(ctx, "kern_status_t status = msg_read(channel, id, 0, &iov, 1, " "&r);\n"); emit(ctx, "if (status != KERN_OK) return status;\n"); emit(ctx, "if (r != sizeof msg) return KERN_INVALID_ARGUMENT;\n\n"); b_queue_entry *entry = b_queue_first(&msg->msg_params); while (entry) { struct msg_parameter *param = b_unbox(struct msg_parameter, entry, p_entry); switch (param->p_type->ty_id) { case TYPE_INT: emit(ctx, "*out_%s = msg.msg_request.%s;\n", param->p_name, param->p_name); break; case TYPE_STRING: emit(ctx, "out_%s->str_len = msg.msg_request.%s_len;\n", param->p_name, param->p_name); emit(ctx, "if (out_%s->str_max > 1) {\n", param->p_name); emit_indent(ctx); emit(ctx, "iov.io_base = (virt_addr_t)out_%s->str_buf;\n", param->p_name); emit(ctx, "iov.io_len = out_%s->str_max - 1;\n", param->p_name); emit(ctx, "if (iov.io_len > out_%s->str_len) iov.io_len " "= out_%s->str_len;\n", param->p_name, param->p_name); emit(ctx, "status = msg_read(\n"); emit_indent(ctx); emit(ctx, "channel,\n"); emit(ctx, "id,\n"); emit(ctx, "msg.msg_request.%s_offset,\n", param->p_name); emit(ctx, "&iov,\n"); emit(ctx, "1,\n"); emit(ctx, "&r);\n"); emit_unindent(ctx); emit(ctx, "if (status != KERN_OK) return status;\n\n"); emit(ctx, "if (r != iov.io_len) return " "KERN_INVALID_ARGUMENT;\n\n"); emit(ctx, "out_%s->str_buf[iov.io_len] = '\\0';\n", param->p_name); emit_unindent(ctx); emit(ctx, "} else if (out_%s->str_max > 0) {\n"); emit_indent(ctx); emit(ctx, "out_%s->str_buf[0] = '\\0';\n", param->p_name); emit_unindent(ctx); emit(ctx, "}\n\n"); break; default: break; } entry = b_queue_next(entry); } emit(ctx, "return status;\n"); return 0; } static int emit_interface_msg_function_reply_impl( struct emit_ctx *ctx, const struct interface_definition *iface, const struct msg_definition *msg) { b_string *iface_ucase = b_string_create_from_cstr(iface->if_name); b_string_toupper(iface_ucase); b_string *msg_ucase = b_string_create_from_cstr(msg->msg_name); b_string_toupper(msg_ucase); emit(ctx, "struct msg_%s_%s msg = {0};\n", iface->if_name, msg->msg_name); emit(ctx, "msg.msg_header.hdr_magic = MSG_MAGIC;\n"); emit(ctx, "msg.msg_header.hdr_protocol = PROTOCOL_%s;\n", b_string_ptr(iface_ucase)); emit(ctx, "msg.msg_header.hdr_func = MSG_%s_%s;\n\n", b_string_ptr(iface_ucase), b_string_ptr(msg_ucase)); b_string_unref(iface_ucase); b_string_unref(msg_ucase); b_queue_entry *entry = b_queue_first(&msg->msg_results); while (entry) { struct msg_parameter *param = b_unbox(struct msg_parameter, entry, p_entry); switch (param->p_type->ty_id) { case TYPE_INT: emit(ctx, "msg.msg_response.%s = %s;\n\n", param->p_name, param->p_name); break; case TYPE_STRING: emit(ctx, "msg.msg_response.%s_len = strlen(%s);\n", param->p_name, param->p_name); break; default: break; } entry = b_queue_next(entry); } emit(ctx, "\nstruct iovec iov[] = {\n"); emit_indent(ctx); emit(ctx, "IOVEC(&msg, sizeof msg),\n"); size_t nr_iov = 1; entry = b_queue_first(&msg->msg_results); while (entry) { struct msg_parameter *param = b_unbox(struct msg_parameter, entry, p_entry); switch (param->p_type->ty_id) { case TYPE_STRING: emit(ctx, "IOVEC(%s, msg.msg_response.%s_len),\n", param->p_name, param->p_name); nr_iov++; break; default: break; } entry = b_queue_next(entry); } emit_unindent(ctx); emit(ctx, "};\n\n"); emit(ctx, "return msg_reply(channel, id, iov, %zu);\n", nr_iov); return 0; } static int emit_interface_msg_function_send( struct emit_ctx *ctx, const struct interface_definition *iface, const struct msg_definition *msg, bool prototype_only) { if (prototype_only) { emit(ctx, "extern "); } emit(ctx, "kern_status_t %s_%s(\n", iface->if_name, msg->msg_name); emit_indent(ctx); emit(ctx, "kern_handle_t port"); b_queue_entry *entry = b_queue_first(&msg->msg_params); while (entry) { struct msg_parameter *param = b_unbox(struct msg_parameter, entry, p_entry); emit(ctx, ",\n"); switch (param->p_type->ty_id) { case TYPE_INT: emit(ctx, "int %s", param->p_name); break; case TYPE_STRING: emit(ctx, "const char *%s", param->p_name); break; default: break; } entry = b_queue_next(entry); } entry = b_queue_first(&msg->msg_results); while (entry) { struct msg_parameter *param = b_unbox(struct msg_parameter, entry, p_entry); emit(ctx, ",\n"); switch (param->p_type->ty_id) { case TYPE_INT: emit(ctx, "int *out_%s", param->p_name); break; case TYPE_STRING: emit(ctx, "struct msg_string *out_%s", param->p_name); break; default: break; } entry = b_queue_next(entry); } emit(ctx, ")"); emit_unindent(ctx); if (prototype_only) { emit(ctx, ";\n"); return 0; } emit(ctx, "\n{\n"); emit_indent(ctx); emit_interface_msg_function_send_impl(ctx, iface, msg); emit_unindent(ctx); emit(ctx, "}\n\n"); return 0; } static int emit_interface_msg_function_recv( struct emit_ctx *ctx, const struct interface_definition *iface, const struct msg_definition *msg, bool prototype_only) { if (prototype_only) { emit(ctx, "extern "); } emit(ctx, "kern_status_t %s_%s_recv(\n", iface->if_name, msg->msg_name); emit_indent(ctx); emit(ctx, "kern_handle_t channel,\nmsgid_t id"); b_queue_entry *entry = b_queue_first(&msg->msg_params); while (entry) { struct msg_parameter *param = b_unbox(struct msg_parameter, entry, p_entry); emit(ctx, ",\n"); switch (param->p_type->ty_id) { case TYPE_INT: emit(ctx, "int *out_%s", param->p_name); break; case TYPE_STRING: emit(ctx, "struct msg_string *out_%s", param->p_name); break; default: break; } entry = b_queue_next(entry); } emit(ctx, ")"); emit_unindent(ctx); if (prototype_only) { emit(ctx, ";\n"); return 0; } emit(ctx, "\n{\n"); emit_indent(ctx); emit_interface_msg_function_recv_impl(ctx, iface, msg); emit_unindent(ctx); emit(ctx, "}\n\n"); return 0; } static int emit_interface_msg_function_reply( struct emit_ctx *ctx, const struct interface_definition *iface, const struct msg_definition *msg, bool prototype_only) { if (prototype_only) { emit(ctx, "extern "); } emit(ctx, "kern_status_t %s_%s_reply(\n", iface->if_name, msg->msg_name); emit_indent(ctx); emit(ctx, "kern_handle_t channel,\nmsgid_t id"); b_queue_entry *entry = b_queue_first(&msg->msg_results); while (entry) { struct msg_parameter *param = b_unbox(struct msg_parameter, entry, p_entry); emit(ctx, ",\n"); switch (param->p_type->ty_id) { case TYPE_INT: emit(ctx, "int %s", param->p_name); break; case TYPE_STRING: emit(ctx, "const char *%s", param->p_name); break; default: break; } entry = b_queue_next(entry); } emit(ctx, ")"); emit_unindent(ctx); if (prototype_only) { emit(ctx, ";\n"); return 0; } emit(ctx, "\n{\n"); emit_indent(ctx); emit_interface_msg_function_reply_impl(ctx, iface, msg); emit_unindent(ctx); emit(ctx, "}\n\n"); return 0; } static int emit_interface_msg_functions( struct emit_ctx *ctx, const struct interface_definition *iface, const struct msg_definition *msg, bool prototype_only) { emit_interface_msg_function_send(ctx, iface, msg, prototype_only); emit_interface_msg_function_recv(ctx, iface, msg, prototype_only); emit_interface_msg_function_reply(ctx, iface, msg, prototype_only); return 0; } static int emit_string_destructor_list( struct emit_ctx *ctx, b_queue_entry *param_entry) { while (param_entry) { struct msg_parameter *param = b_unbox(struct msg_parameter, param_entry, p_entry); if (param->p_type->ty_id == TYPE_STRING) { emit(ctx, "free(%s.str_buf);\n", param->p_name); } param_entry = b_queue_prev(param_entry); } return 0; } static int emit_interface_dispatcher_impl_msg( struct emit_ctx *ctx, const struct interface_definition *iface, const struct msg_definition *msg) { emit(ctx, "if (!vtable->%s) return msg_reply_generic(channel, id, msg, " "KERN_UNIMPLEMENTED);\n", msg->msg_name); b_queue_entry *entry = b_queue_first(&msg->msg_params); while (entry) { struct msg_parameter *param = b_unbox(struct msg_parameter, entry, p_entry); switch (param->p_type->ty_id) { case TYPE_INT: emit(ctx, "int %s;\n", param->p_name); break; case TYPE_STRING: emit(ctx, "struct msg_string %s = {0};\n", param->p_name); emit_indent_zero(ctx); emit(ctx, "#if defined(MSG_NO_MALLOC)\n"); emit_unindent(ctx); emit(ctx, "char %s_buf[MSG_STRINGBUF_SIZE];\n", param->p_name); emit(ctx, "%s.str_buf = %s_buf;\n", param->p_name, param->p_name); emit(ctx, "%s.str_max = MSG_STRINGBUF_SIZE;\n", param->p_name); emit_indent_zero(ctx); emit(ctx, "#endif\n"); emit_unindent(ctx); break; default: break; } entry = b_queue_next(entry); } entry = b_queue_first(&msg->msg_results); while (entry) { struct msg_parameter *param = b_unbox(struct msg_parameter, entry, p_entry); switch (param->p_type->ty_id) { case TYPE_INT: emit(ctx, "int %s;\n", param->p_name); break; case TYPE_STRING: emit(ctx, "struct msg_string %s = {0};\n", param->p_name); emit_indent_zero(ctx); emit(ctx, "#if defined(MSG_NO_MALLOC)\n"); emit_unindent(ctx); emit(ctx, "char %s_buf[MSG_STRINGBUF_SIZE];\n", param->p_name); emit(ctx, "%s.str_buf = %s_buf;\n", param->p_name, param->p_name); emit(ctx, "%s.str_max = MSG_STRINGBUF_SIZE;\n", param->p_name); emit_indent_zero(ctx); emit(ctx, "#endif\n"); emit_unindent(ctx); break; default: break; } entry = b_queue_next(entry); } emit(ctx, "status = %s_%s_recv(\n", iface->if_name, msg->msg_name); emit_indent(ctx); emit(ctx, "channel,\nid"); entry = b_queue_first(&msg->msg_params); while (entry) { struct msg_parameter *param = b_unbox(struct msg_parameter, entry, p_entry); emit(ctx, ",\n&%s", param->p_name); entry = b_queue_next(entry); } emit(ctx, ");\n"); emit_unindent(ctx); emit(ctx, "if (status != KERN_OK) {\n"); emit_indent(ctx); emit(ctx, "msg_reply_generic(channel, id, msg, status);\n"); emit(ctx, "return status;\n"); emit_unindent(ctx); emit(ctx, "}\n"); emit_indent_zero(ctx); emit(ctx, "#if !defined(MSG_NO_MALLOC)\n"); emit_unindent(ctx); entry = b_queue_first(&msg->msg_params); while (entry) { struct msg_parameter *param = b_unbox(struct msg_parameter, entry, p_entry); switch (param->p_type->ty_id) { case TYPE_STRING: emit(ctx, "%s.str_max = %s.str_len + 1;\n", param->p_name, param->p_name); emit(ctx, "%s.str_buf = malloc(%s.str_max);\n", param->p_name, param->p_name); emit(ctx, "if (!%s.str_buf) {\n", param->p_name); emit_indent(ctx); emit_string_destructor_list(ctx, b_queue_prev(entry)); emit(ctx, "return KERN_NO_MEMORY;\n"); emit_unindent(ctx); emit(ctx, "}\n", param->p_name); break; default: break; } entry = b_queue_next(entry); } emit(ctx, "status = %s_%s_recv(\n", iface->if_name, msg->msg_name); emit_indent(ctx); emit(ctx, "channel,\nid"); entry = b_queue_first(&msg->msg_params); while (entry) { struct msg_parameter *param = b_unbox(struct msg_parameter, entry, p_entry); emit(ctx, ",\n&%s", param->p_name); entry = b_queue_next(entry); } emit(ctx, ");\n"); emit_unindent(ctx); emit(ctx, "if (status != KERN_OK) {\n"); emit_indent(ctx); emit_string_destructor_list(ctx, b_queue_last(&msg->msg_params)); emit(ctx, "msg_reply_generic(channel, id, msg, status);\n"); emit(ctx, "return status;\n"); emit_unindent(ctx); emit(ctx, "}\n"); emit_indent_zero(ctx); emit(ctx, "#endif\n"); emit_unindent(ctx); emit(ctx, "status = vtable->%s(\n", msg->msg_name); emit_indent(ctx); size_t i = 0; entry = b_queue_first(&msg->msg_params); while (entry) { struct msg_parameter *param = b_unbox(struct msg_parameter, entry, p_entry); if (i > 0) { emit(ctx, ",\n"); } switch (param->p_type->ty_id) { case TYPE_INT: emit(ctx, "%s", param->p_name); break; case TYPE_STRING: emit(ctx, "%s.str_buf", param->p_name); break; default: break; } i++; entry = b_queue_next(entry); } entry = b_queue_first(&msg->msg_results); while (entry) { struct msg_parameter *param = b_unbox(struct msg_parameter, entry, p_entry); if (i > 0) { emit(ctx, ",\n"); } emit(ctx, "&%s", param->p_name); i++; entry = b_queue_next(entry); } emit(ctx, ");\n"); emit_unindent(ctx); emit_indent_zero(ctx); emit(ctx, "#if !defined(MSG_NO_MALLOC)\n"); emit_unindent(ctx); emit_string_destructor_list(ctx, b_queue_last(&msg->msg_params)); emit_indent_zero(ctx); emit(ctx, "#endif\n"); emit_unindent(ctx); emit(ctx, "if (status != KERN_OK) {\n"); emit_indent(ctx); emit(ctx, "msg_reply_generic(channel, id, msg, status);\n"); emit(ctx, "return status;\n"); emit_unindent(ctx); emit(ctx, "}\n\n"); emit(ctx, "status = %s_%s_reply(\n", iface->if_name, msg->msg_name); emit_indent(ctx); emit(ctx, "channel,\nid"); entry = b_queue_first(&msg->msg_results); while (entry) { struct msg_parameter *param = b_unbox(struct msg_parameter, entry, p_entry); switch (param->p_type->ty_id) { case TYPE_INT: emit(ctx, ",\n%s", param->p_name); break; case TYPE_STRING: emit(ctx, ",\n%s.str_buf", param->p_name); break; default: break; } entry = b_queue_next(entry); } emit(ctx, ");\n"); emit_unindent(ctx); emit(ctx, "if (status != KERN_OK) return status;\n"); return 0; } static int emit_interface_dispatcher_impl( struct emit_ctx *ctx, const struct interface_definition *iface) { b_string *iface_ucase = b_string_create_from_cstr(iface->if_name); b_string_toupper(iface_ucase); b_string *msg_ucase = b_string_create(); emit(ctx, "if (msg->hdr_protocol != PROTOCOL_%s) return " "KERN_INVALID_ARGUMENT;\n\n", b_string_ptr(iface_ucase)); emit(ctx, "kern_status_t status = KERN_OK;\n"); emit(ctx, "switch (msg->hdr_func) {\n"); b_queue_entry *entry = b_queue_first(&iface->if_msg); while (entry) { const struct msg_definition *msg = b_unbox(struct msg_definition, entry, msg_entry); b_string_clear(msg_ucase); b_string_append_cstr(msg_ucase, msg->msg_name); b_string_toupper(msg_ucase); emit(ctx, "case MSG_%s_%s: {\n", b_string_ptr(iface_ucase), b_string_ptr(msg_ucase)); emit_indent(ctx); emit_interface_dispatcher_impl_msg(ctx, iface, msg); emit(ctx, "break;\n"); emit_unindent(ctx); emit(ctx, "}\n"); entry = b_queue_next(entry); } b_string_unref(iface_ucase); emit(ctx, "default:\n"); emit_indent(ctx); emit(ctx, "status = KERN_INVALID_ARGUMENT;\nbreak;\n"); emit_unindent(ctx); emit(ctx, "}\n\nreturn status;\n"); return 0; } static int emit_interface_dispatcher( struct emit_ctx *ctx, const struct interface_definition *iface, bool prototype_only) { if (prototype_only) { emit(ctx, "extern "); } emit(ctx, "kern_status_t %s_dispatch(\n", iface->if_name); emit_indent(ctx); emit(ctx, "kern_handle_t channel,\n"); emit(ctx, "const struct %s_vtable *vtable,\n", iface->if_name); emit(ctx, "msgid_t id,\n"); emit(ctx, "const struct msg_header *msg)", iface->if_name); emit_unindent(ctx); if (prototype_only) { emit(ctx, ";\n"); return 0; } emit(ctx, "\n{\n"); emit_indent(ctx); emit_interface_dispatcher_impl(ctx, iface); emit_unindent(ctx); emit(ctx, "}\n\n"); return 0; } static int emit_interface_functions( struct emit_ctx *ctx, const struct interface_definition *iface, bool prototype_only) { b_queue_entry *entry = b_queue_first(&iface->if_msg); while (entry) { const struct msg_definition *msg = b_unbox(struct msg_definition, entry, msg_entry); emit_interface_msg_functions(ctx, iface, msg, prototype_only); entry = b_queue_next(entry); } emit_interface_dispatcher(ctx, iface, prototype_only); return 0; } static int emit_interface_vtable_entry( struct emit_ctx *ctx, const struct interface_definition *iface, const struct msg_definition *msg) { emit(ctx, "kern_status_t(*%s)(\n", msg->msg_name); emit_indent(ctx); size_t i = 0; b_queue_entry *entry = b_queue_first(&msg->msg_params); while (entry) { struct msg_parameter *param = b_unbox(struct msg_parameter, entry, p_entry); if (i > 0) { emit(ctx, ",\n"); } switch (param->p_type->ty_id) { case TYPE_INT: emit(ctx, "int %s", param->p_name); break; case TYPE_STRING: emit(ctx, "const char *%s", param->p_name); break; default: break; } i++; entry = b_queue_next(entry); } entry = b_queue_first(&msg->msg_results); while (entry) { struct msg_parameter *param = b_unbox(struct msg_parameter, entry, p_entry); if (i > 0) { emit(ctx, ",\n"); } switch (param->p_type->ty_id) { case TYPE_INT: emit(ctx, "int *out_%s", param->p_name); break; case TYPE_STRING: emit(ctx, "struct msg_string *out_%s", param->p_name); break; default: break; } i++; entry = b_queue_next(entry); } emit(ctx, ");\n", msg->msg_name); emit_unindent(ctx); return 0; } static int emit_interface_vtable( struct emit_ctx *ctx, const struct interface_definition *iface) { emit(ctx, "struct %s_vtable {\n", iface->if_name); emit_indent(ctx); b_queue_entry *entry = b_queue_first(&iface->if_msg); while (entry) { const struct msg_definition *msg = b_unbox(struct msg_definition, entry, msg_entry); emit_interface_vtable_entry(ctx, iface, msg); entry = b_queue_next(entry); } emit_unindent(ctx); emit(ctx, "};\n\n"); return 0; } static int emit_header( struct emit_ctx *ctx, const struct interface_definition *iface) { emit_header_guard_start(ctx, iface); emit_include(ctx, "stdint.h", true); emit_include(ctx, "stddef.h", true); emit_include(ctx, "string.h", true); emit(ctx, "#if !defined(MSG_NO_MALLOC)\n"); emit_include(ctx, "stdlib.h", true); emit(ctx, "#endif\n"); emit_include(ctx, "mango/msg.h", true); emit(ctx, "\n"); emit_header_lib_definitions(ctx); emit_interface_id_macros(ctx, iface); emit_interface_vtable(ctx, iface); emit_interface_msg_structs(ctx, iface); emit_interface_functions(ctx, iface, true); emit(ctx, "\n#if defined(MSG_IMPLEMENTATION)\n"); emit_interface_functions(ctx, iface, false); emit(ctx, "#endif\n"); emit_header_guard_end(ctx, iface); return 0; } static int emit_interface(const struct interface_definition *iface) { char path[4096]; b_file *file = NULL; snprintf(path, sizeof path, "%s.h", iface->if_name); b_result result = b_file_open( NULL, B_RV_PATH(path), B_FILE_WRITE_ONLY | B_FILE_CREATE, &file); if (b_result_is_error(result)) { b_throw(result); fprintf(stderr, "cannot create C header file %s\n", path); return -1; } struct emit_ctx ctx = { .ctx_out = file, }; int err = emit_header(&ctx, iface); b_file_unref(file); return err; } static struct backend backend = { .b_name = "c-mpc", .b_emit = emit_interface, }; const struct backend *c_mpc_backend(void) { return &backend; }