ifc can be used to compile .if files into self-contained header-only C libraries, which can be used to send/receive messages that conform to the described interface.
1458 lines
32 KiB
C
1458 lines
32 KiB
C
#include "../../backend.h"
|
|
|
|
#include "../../interface.h"
|
|
#include "../../msg.h"
|
|
#include "../../type.h"
|
|
|
|
#include <blue/core/hash.h>
|
|
#include <blue/io/file.h>
|
|
#include <blue/io/path.h>
|
|
#include <stdarg.h>
|
|
#include <stddef.h>
|
|
|
|
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"
|
|
"struct msg kmsg = MSG(&iov, 1, NULL, 0);\n"
|
|
"kern_status_t status = msg_recv(channel, 0, out_id, &kmsg);\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"
|
|
"struct msg resp = MSG(&iov, 1, NULL, 0);\n"
|
|
"return msg_reply(channel, 0, id, &resp);\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, "struct msg req = MSG(req_iov, %zu, NULL, 0);\n", nr_req_iov);
|
|
emit(ctx,
|
|
"struct msg resp = MSG(resp_iov, %zu, NULL, 0);\n",
|
|
nr_resp_iov);
|
|
|
|
emit(ctx, "kern_status_t status = msg_send(port, 0, &req, &resp);\n");
|
|
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, "struct msg reply = MSG(iov, %zu, NULL, 0);\n", nr_iov);
|
|
emit(ctx, "return msg_reply(channel, 0, id, &reply);\n");
|
|
|
|
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;
|
|
}
|