/* * Copyright (C) 2015 Patrick "P. J." McDermott * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program. If not, see * . */ #include #include #include #include #include "../logging.h" #include "../resources/script.h" #include "ffi.h" /* LUA_OK is defined in Lua 5.2 but not 5.1. */ #ifndef LUA_OK #define LUA_OK 0 #endif static struct script *ctx; static struct ffi_namespace root = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, }; static int call_arg_index; static void ffi_register_functions_in_namespace(lua_State *l, struct ffi_namespace *parent); static int ffi_handle_function(lua_State *l); struct ffi_namespace * ffi_add_namespace(struct ffi_namespace *parent, const char *name) { struct ffi_namespace *new_ns; if (parent == NULL) { parent = &root; } new_ns = malloc(sizeof(*new_ns)); if (new_ns == NULL) { err(1, "Failed to allocate FFI namespace \"%s\"", name); return NULL; } new_ns->name = strdup(name); new_ns->full_name = calloc( (parent->full_name != NULL ? strlen(parent->full_name) + 1 : 0) + strlen(name) + 1, sizeof(*new_ns->full_name)); if (new_ns->full_name == NULL) { free(new_ns); err(1, "Failed to allocate FFI namespace \"%s\"", name); return NULL; } sprintf(new_ns->full_name, "%s%s%s", (parent->full_name != NULL ? parent->full_name : ""), (parent->full_name != NULL ? ":" : ""), name); new_ns->ns_head = NULL; new_ns->ns_tail = NULL; new_ns->fn_head = NULL; new_ns->fn_tail = NULL; new_ns->next = NULL; if (parent->ns_head == NULL) { parent->ns_head = new_ns; } else { parent->ns_tail->next = new_ns; } parent->ns_tail = new_ns; return new_ns; } void ffi_add_function(struct ffi_namespace *parent, const char *name, void (*func)(struct ffi_function *)) { struct ffi_function *new_fn; new_fn = malloc(sizeof(*new_fn)); if (new_fn == NULL) { err(1, "Failed to allocate FFI function \"%s\"", name); return; } new_fn->name = strdup(name); new_fn->ns = parent; new_fn->func = func; new_fn->next = NULL; if (parent->fn_head == NULL) { parent->fn_head = new_fn; } else { parent->fn_tail->next = new_fn; } parent->fn_tail = new_fn; } void ffi_register_functions(struct script *script) { ffi_register_functions_in_namespace(script->lua_state, &root); } static void ffi_register_functions_in_namespace(lua_State *l, struct ffi_namespace *parent) { struct ffi_namespace *ns; struct ffi_function *fn; lua_checkstack(l, 2); for (ns = parent->ns_head; ns != NULL; ns = ns->next) { lua_newtable(l); ffi_register_functions_in_namespace(l, ns); if (parent == &root) { lua_setglobal(l, ns->name); } else { lua_setfield(l, -2, ns->name); } } for (fn = parent->fn_head; fn != NULL; fn = fn->next) { lua_pushlightuserdata(l, fn); lua_pushcclosure(l, &ffi_handle_function, 1); lua_setfield(l, -2, fn->name); } } static int ffi_handle_function(lua_State *l) { struct ffi_function *fn; call_arg_index = 0; fn = (struct ffi_function *) lua_touserdata(l, lua_upvalueindex(1)); fn->func(fn); return 0; } void ffi_context_switch(struct script *script) { ctx = script; } int ffi_stack_get_bool(struct ffi_function *fn) { if (lua_gettop(ctx->lua_state) < ++call_arg_index) { warn("Bad argument #%d to %s:%s() (no value)", call_arg_index, fn->ns->full_name, fn->name); return 0; } if (!lua_isboolean(ctx->lua_state, call_arg_index)) { warn("Bad argument #%d to %s:%s() (boolean expected)", call_arg_index, fn->ns->full_name, fn->name); return 0; } return lua_toboolean(ctx->lua_state, call_arg_index); } int ffi_stack_get_int(struct ffi_function *fn) { #if LUA_VERSION_NUM <= 501 if (lua_gettop(ctx->lua_state) < ++call_arg_index) { warn("Bad argument #%d to %s:%s() (no value)", call_arg_index, fn->ns->full_name, fn->name); return 0; } return lua_tointeger(ctx->lua_state, call_arg_index); #elif LUA_VERSION_NUM == 502 int v; int is_int; if (lua_gettop(ctx->lua_state) < ++call_arg_index) { warn("Bad argument #%d to %s:%s() (no value)", call_arg_index, fn->ns->full_name, fn->name); return 0; } v = lua_tointegerx(ctx->lua_state, call_arg_index, &is_int); if (!is_int) { warn("Bad argument #%d to %s:%s() (integer expected)", call_arg_index, fn->ns->full_name, fn->name); return 0; } return v; #elif LUA_VERSION_NUM >= 503 if (lua_gettop(ctx->lua_state) < ++call_arg_index) { warn("Bad argument #%d to %s:%s() (no value)", call_arg_index, fn->ns->full_name, fn->name); return 0; } if (!lua_isinteger(ctx->lua_state, call_arg_index)) { warn("Bad argument #%d to %s:%s() (integer expected)", call_arg_index, fn->ns->full_name, fn->name); return 0; } return lua_tointeger(ctx->lua_state, call_arg_index); #endif } double ffi_stack_get_float(struct ffi_function *fn) { if (lua_gettop(ctx->lua_state) < ++call_arg_index) { warn("Bad argument #%d to %s:%s() (no value)", call_arg_index, fn->ns->full_name, fn->name); return 0; } if (!lua_isnumber(ctx->lua_state, call_arg_index)) { warn("Bad argument #%d to %s:%s() (number expected)", call_arg_index, fn->ns->full_name, fn->name); return 0; } return lua_tonumber(ctx->lua_state, call_arg_index); } const char * ffi_stack_get_string(struct ffi_function *fn) { if (lua_gettop(ctx->lua_state) < ++call_arg_index) { warn("Bad argument #%d to %s:%s() (no value)", call_arg_index, fn->ns->full_name, fn->name); return 0; } if (!lua_isstring(ctx->lua_state, call_arg_index)) { warn("Bad argument #%d to %s:%s() (string expected)", call_arg_index, fn->ns->full_name, fn->name); return 0; } return lua_tostring(ctx->lua_state, call_arg_index); } void ffi_prepare_call(const char *func) { lua_getglobal(ctx->lua_state, func); call_arg_index = 0; } void ffi_stack_set_bool(int v) { lua_pushboolean(ctx->lua_state, v); } void ffi_stack_set_int(int v) { lua_pushinteger(ctx->lua_state, v); } void ffi_stack_set_float(double v) { lua_pushnumber(ctx->lua_state, v); } void ffi_stack_set_string(const char *v) { lua_pushstring(ctx->lua_state, v); } void ffi_call(void) { if (lua_pcall(ctx->lua_state, call_arg_index, 0, 0) != LUA_OK) { err(1, "Error calling function: %s\n", lua_tostring(ctx->lua_state, -1)); } }