/*
* 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));
}
}