|
|
@ -58,9 +58,22 @@ distribution.
|
|
|
|
#include <lauxlib.h>
|
|
|
|
#include <lauxlib.h>
|
|
|
|
#include <lualib.h>
|
|
|
|
#include <lualib.h>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include <lstate.h>
|
|
|
|
|
|
|
|
|
|
|
|
using namespace DFHack;
|
|
|
|
using namespace DFHack;
|
|
|
|
using namespace DFHack::LuaWrapper;
|
|
|
|
using namespace DFHack::LuaWrapper;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lua_State *DFHack::Lua::Core::State = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
inline void AssertCoreSuspend(lua_State *state)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
assert(!Lua::IsCoreContext(state) || DFHack::Core::getInstance().isSuspended());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
* Public DF object reference handling API
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
void DFHack::Lua::PushDFObject(lua_State *state, type_identity *type, void *ptr)
|
|
|
|
void DFHack::Lua::PushDFObject(lua_State *state, type_identity *type, void *ptr)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
push_object_internal(state, type, ptr, false);
|
|
|
|
push_object_internal(state, type, ptr, false);
|
|
|
@ -71,6 +84,40 @@ void *DFHack::Lua::GetDFObject(lua_State *state, type_identity *type, int val_in
|
|
|
|
return get_object_internal(state, type, val_index, exact_type, false);
|
|
|
|
return get_object_internal(state, type, val_index, exact_type, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void *DFHack::Lua::CheckDFObject(lua_State *state, type_identity *type, int val_index, bool exact_type)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (lua_type(state, val_index) == LUA_TNONE)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (val_index > 0)
|
|
|
|
|
|
|
|
luaL_argerror(state, val_index, "pointer expected");
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
luaL_error(state, "at index %d: pointer expected", val_index);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (lua_isnil(state, val_index))
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void *rv = get_object_internal(state, type, val_index, exact_type, false);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!rv)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
std::string error = "invalid pointer type";
|
|
|
|
|
|
|
|
if (type)
|
|
|
|
|
|
|
|
error += "; expected: " + type->getFullName();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (val_index > 0)
|
|
|
|
|
|
|
|
luaL_argerror(state, val_index, error.c_str());
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
luaL_error(state, "at index %d: %s", val_index, error.c_str());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
* Console I/O wrappers
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
static int DFHACK_OSTREAM_TOKEN = 0;
|
|
|
|
static int DFHACK_OSTREAM_TOKEN = 0;
|
|
|
|
|
|
|
|
|
|
|
|
color_ostream *DFHack::Lua::GetOutput(lua_State *L)
|
|
|
|
color_ostream *DFHack::Lua::GetOutput(lua_State *L)
|
|
|
@ -193,15 +240,14 @@ static int lua_dfhack_is_interactive(lua_State *S)
|
|
|
|
return 1;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int lua_dfhack_lineedit(lua_State *S)
|
|
|
|
static int dfhack_lineedit_sync(lua_State *S, Console *pstream)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
const char *prompt = luaL_optstring(S, 1, ">> ");
|
|
|
|
|
|
|
|
const char *hfile = luaL_optstring(S, 2, NULL);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Console *pstream = get_console(S);
|
|
|
|
|
|
|
|
if (!pstream)
|
|
|
|
if (!pstream)
|
|
|
|
return 2;
|
|
|
|
return 2;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const char *prompt = luaL_optstring(S, 1, ">> ");
|
|
|
|
|
|
|
|
const char *hfile = luaL_optstring(S, 2, NULL);
|
|
|
|
|
|
|
|
|
|
|
|
DFHack::CommandHistory hist;
|
|
|
|
DFHack::CommandHistory hist;
|
|
|
|
if (hfile)
|
|
|
|
if (hfile)
|
|
|
|
hist.load(hfile);
|
|
|
|
hist.load(hfile);
|
|
|
@ -224,6 +270,51 @@ static int lua_dfhack_lineedit(lua_State *S)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int DFHACK_QUERY_COROTABLE_TOKEN = 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int yield_helper(lua_State *S)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return lua_yield(S, lua_gettop(S));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
int dfhack_lineedit_cont(lua_State *L, int status, int)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (Lua::IsSuccess(status))
|
|
|
|
|
|
|
|
return lua_gettop(L) - 2;
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
return dfhack_lineedit_sync(L, get_console(L));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int dfhack_lineedit(lua_State *S)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
lua_settop(S, 2);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Console *pstream = get_console(S);
|
|
|
|
|
|
|
|
if (!pstream)
|
|
|
|
|
|
|
|
return 2;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lua_rawgetp(S, LUA_REGISTRYINDEX, &DFHACK_QUERY_COROTABLE_TOKEN);
|
|
|
|
|
|
|
|
lua_rawgetp(S, -1, S);
|
|
|
|
|
|
|
|
bool in_coroutine = !lua_isnil(S, -1);
|
|
|
|
|
|
|
|
lua_settop(S, 2);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (in_coroutine)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
lua_pushcfunction(S, yield_helper);
|
|
|
|
|
|
|
|
lua_pushvalue(S, 1);
|
|
|
|
|
|
|
|
lua_pushvalue(S, 2);
|
|
|
|
|
|
|
|
return Lua::TailPCallK<dfhack_lineedit_cont>(S, 2, LUA_MULTRET, 0, 0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return dfhack_lineedit_sync(S, pstream);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
* Exception handling
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
static int DFHACK_EXCEPTION_META_TOKEN = 0;
|
|
|
|
static int DFHACK_EXCEPTION_META_TOKEN = 0;
|
|
|
|
|
|
|
|
|
|
|
|
static void error_tostring(lua_State *L, bool keep_old = false)
|
|
|
|
static void error_tostring(lua_State *L, bool keep_old = false)
|
|
|
@ -265,8 +356,21 @@ static void report_error(lua_State *L, color_ostream *out = NULL)
|
|
|
|
lua_pop(L, 1);
|
|
|
|
lua_pop(L, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static bool convert_to_exception(lua_State *L)
|
|
|
|
static bool convert_to_exception(lua_State *L, int slevel, lua_State *thread = NULL)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
|
|
|
|
if (!thread)
|
|
|
|
|
|
|
|
thread = L;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (thread == L)
|
|
|
|
|
|
|
|
lua_pushthread(L);
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
lua_pushthread(thread);
|
|
|
|
|
|
|
|
lua_xmove(thread, L, 1);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lua_swap(L);
|
|
|
|
|
|
|
|
|
|
|
|
int base = lua_gettop(L);
|
|
|
|
int base = lua_gettop(L);
|
|
|
|
|
|
|
|
|
|
|
|
bool force_unknown = false;
|
|
|
|
bool force_unknown = false;
|
|
|
@ -277,13 +381,33 @@ static bool convert_to_exception(lua_State *L)
|
|
|
|
bool is_exception = lua_rawequal(L, -1, -2);
|
|
|
|
bool is_exception = lua_rawequal(L, -1, -2);
|
|
|
|
lua_settop(L, base);
|
|
|
|
lua_settop(L, base);
|
|
|
|
|
|
|
|
|
|
|
|
// If it is an exception, return as is
|
|
|
|
|
|
|
|
if (is_exception)
|
|
|
|
if (is_exception)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// If it is an exception from the same thread, return as is
|
|
|
|
|
|
|
|
lua_getfield(L, base, "thread");
|
|
|
|
|
|
|
|
bool same_thread = lua_rawequal(L, -1, base-1);
|
|
|
|
|
|
|
|
lua_settop(L, base);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (same_thread)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
lua_remove(L, base-1);
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Create a new exception for this thread
|
|
|
|
|
|
|
|
lua_newtable(L);
|
|
|
|
|
|
|
|
luaL_where(L, 1);
|
|
|
|
|
|
|
|
lua_pushstring(L, "coroutine resume failed");
|
|
|
|
|
|
|
|
lua_concat(L, 2);
|
|
|
|
|
|
|
|
lua_setfield(L, -2, "message");
|
|
|
|
|
|
|
|
lua_swap(L);
|
|
|
|
|
|
|
|
lua_setfield(L, -2, "cause");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
force_unknown = true;
|
|
|
|
force_unknown = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Promote non-table to table, and do some sanity checks
|
|
|
|
if (!lua_istable(L, base) || force_unknown)
|
|
|
|
if (!lua_istable(L, base) || force_unknown)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
lua_newtable(L);
|
|
|
|
lua_newtable(L);
|
|
|
@ -313,6 +437,10 @@ static bool convert_to_exception(lua_State *L)
|
|
|
|
|
|
|
|
|
|
|
|
lua_rawgetp(L, LUA_REGISTRYINDEX, &DFHACK_EXCEPTION_META_TOKEN);
|
|
|
|
lua_rawgetp(L, LUA_REGISTRYINDEX, &DFHACK_EXCEPTION_META_TOKEN);
|
|
|
|
lua_setmetatable(L, base);
|
|
|
|
lua_setmetatable(L, base);
|
|
|
|
|
|
|
|
lua_swap(L);
|
|
|
|
|
|
|
|
lua_setfield(L, -2, "thread");
|
|
|
|
|
|
|
|
luaL_traceback(L, thread, NULL, slevel);
|
|
|
|
|
|
|
|
lua_setfield(L, -2, "stacktrace");
|
|
|
|
return true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -321,13 +449,7 @@ static int dfhack_onerror(lua_State *L)
|
|
|
|
luaL_checkany(L, 1);
|
|
|
|
luaL_checkany(L, 1);
|
|
|
|
lua_settop(L, 1);
|
|
|
|
lua_settop(L, 1);
|
|
|
|
|
|
|
|
|
|
|
|
bool changed = convert_to_exception(L);
|
|
|
|
convert_to_exception(L, 1);
|
|
|
|
if (!changed)
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
luaL_traceback(L, L, NULL, 1);
|
|
|
|
|
|
|
|
lua_setfield(L, 1, "stacktrace");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -364,14 +486,8 @@ static void push_simple_error(lua_State *L, const char *str)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
lua_pushstring(L, str);
|
|
|
|
lua_pushstring(L, str);
|
|
|
|
|
|
|
|
|
|
|
|
if (lua_checkstack(L, 5))
|
|
|
|
|
|
|
|
convert_to_exception(L);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (lua_checkstack(L, LUA_MINSTACK))
|
|
|
|
if (lua_checkstack(L, LUA_MINSTACK))
|
|
|
|
{
|
|
|
|
convert_to_exception(L, 0);
|
|
|
|
luaL_traceback(L, L, NULL, 1);
|
|
|
|
|
|
|
|
lua_setfield(L, -2, "stacktrace");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static bool do_finish_pcall(lua_State *L, bool success, int base = 1, int space = 2)
|
|
|
|
static bool do_finish_pcall(lua_State *L, bool success, int base = 1, int space = 2)
|
|
|
@ -387,37 +503,34 @@ static bool do_finish_pcall(lua_State *L, bool success, int base = 1, int space
|
|
|
|
{
|
|
|
|
{
|
|
|
|
lua_pushboolean(L, success);
|
|
|
|
lua_pushboolean(L, success);
|
|
|
|
lua_replace(L, base); /* put first result in first slot */
|
|
|
|
lua_replace(L, base); /* put first result in first slot */
|
|
|
|
return true;
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int finish_dfhack_safecall (lua_State *L, bool success)
|
|
|
|
namespace {
|
|
|
|
{
|
|
|
|
int safecall_cont(lua_State *L, int status, int)
|
|
|
|
success = do_finish_pcall(L, success);
|
|
|
|
{
|
|
|
|
|
|
|
|
bool success = do_finish_pcall(L, Lua::IsSuccess(status));
|
|
|
|
|
|
|
|
|
|
|
|
if (!success)
|
|
|
|
if (!success)
|
|
|
|
report_error(L);
|
|
|
|
report_error(L);
|
|
|
|
|
|
|
|
|
|
|
|
return lua_gettop(L);
|
|
|
|
return lua_gettop(L);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int safecall_cont (lua_State *L)
|
|
|
|
static int dfhack_safecall (lua_State *L)
|
|
|
|
{
|
|
|
|
|
|
|
|
int status = lua_getctx(L, NULL);
|
|
|
|
|
|
|
|
return finish_dfhack_safecall(L, (status == LUA_YIELD));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int lua_dfhack_safecall (lua_State *L)
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
luaL_checkany(L, 1);
|
|
|
|
luaL_checkany(L, 1);
|
|
|
|
lua_pushcfunction(L, dfhack_onerror);
|
|
|
|
lua_pushcfunction(L, dfhack_onerror);
|
|
|
|
lua_insert(L, 1);
|
|
|
|
lua_insert(L, 1);
|
|
|
|
int status = lua_pcallk(L, lua_gettop(L) - 2, LUA_MULTRET, 1, 0, safecall_cont);
|
|
|
|
return Lua::TailPCallK<safecall_cont>(L, lua_gettop(L) - 2, LUA_MULTRET, 1, 0);
|
|
|
|
return finish_dfhack_safecall(L, (status == LUA_OK));
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool DFHack::Lua::SafeCall(color_ostream &out, lua_State *L, int nargs, int nres, bool perr)
|
|
|
|
bool DFHack::Lua::SafeCall(color_ostream &out, lua_State *L, int nargs, int nres, bool perr)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
|
|
|
|
AssertCoreSuspend(L);
|
|
|
|
|
|
|
|
|
|
|
|
int base = lua_gettop(L) - nargs;
|
|
|
|
int base = lua_gettop(L) - nargs;
|
|
|
|
|
|
|
|
|
|
|
|
color_ostream *cur_out = Lua::GetOutput(L);
|
|
|
|
color_ostream *cur_out = Lua::GetOutput(L);
|
|
|
@ -440,13 +553,180 @@ bool DFHack::Lua::SafeCall(color_ostream &out, lua_State *L, int nargs, int nres
|
|
|
|
return ok;
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool DFHack::Lua::Require(color_ostream &out, lua_State *state,
|
|
|
|
// Copied from lcorolib.c, with error handling modifications
|
|
|
|
const std::string &module, bool setglobal)
|
|
|
|
static int resume_helper(lua_State *L, lua_State *co, int narg, int nres)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
|
|
|
|
if (!co) {
|
|
|
|
|
|
|
|
lua_pop(L, narg);
|
|
|
|
|
|
|
|
push_simple_error(L, "coroutine expected in resume");
|
|
|
|
|
|
|
|
return LUA_ERRRUN;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!lua_checkstack(co, narg)) {
|
|
|
|
|
|
|
|
lua_pop(L, narg);
|
|
|
|
|
|
|
|
push_simple_error(L, "too many arguments to resume");
|
|
|
|
|
|
|
|
return LUA_ERRRUN;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lua_status(co) == LUA_OK && lua_gettop(co) == 0) {
|
|
|
|
|
|
|
|
lua_pop(L, narg);
|
|
|
|
|
|
|
|
push_simple_error(L, "cannot resume dead coroutine");
|
|
|
|
|
|
|
|
return LUA_ERRRUN;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
lua_xmove(L, co, narg);
|
|
|
|
|
|
|
|
int status = lua_resume(co, L, narg);
|
|
|
|
|
|
|
|
if (Lua::IsSuccess(status))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
int nact = lua_gettop(co);
|
|
|
|
|
|
|
|
if (nres == LUA_MULTRET)
|
|
|
|
|
|
|
|
nres = nact;
|
|
|
|
|
|
|
|
else if (nres < nact)
|
|
|
|
|
|
|
|
lua_settop(co, nact = nres);
|
|
|
|
|
|
|
|
if (!lua_checkstack(L, nres + 1)) {
|
|
|
|
|
|
|
|
lua_settop(co, 0);
|
|
|
|
|
|
|
|
push_simple_error(L, "too many results to resume");
|
|
|
|
|
|
|
|
return LUA_ERRRUN;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
int ttop = lua_gettop(L) + nres;
|
|
|
|
|
|
|
|
lua_xmove(co, L, nact);
|
|
|
|
|
|
|
|
lua_settop(L, ttop);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
lua_xmove(co, L, 1);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// A cross-thread version of dfhack_onerror
|
|
|
|
|
|
|
|
if (lua_checkstack(L, LUA_MINSTACK))
|
|
|
|
|
|
|
|
convert_to_exception(L, 0, co);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int dfhack_coresume (lua_State *L) {
|
|
|
|
|
|
|
|
lua_State *co = lua_tothread(L, 1);
|
|
|
|
|
|
|
|
luaL_argcheck(L, !!co, 1, "coroutine expected");
|
|
|
|
|
|
|
|
int r = resume_helper(L, co, lua_gettop(L) - 1, LUA_MULTRET);
|
|
|
|
|
|
|
|
bool ok = Lua::IsSuccess(r);
|
|
|
|
|
|
|
|
lua_pushboolean(L, ok);
|
|
|
|
|
|
|
|
lua_insert(L, 2);
|
|
|
|
|
|
|
|
return lua_gettop(L) - 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int dfhack_saferesume (lua_State *L) {
|
|
|
|
|
|
|
|
lua_State *co = lua_tothread(L, 1);
|
|
|
|
|
|
|
|
luaL_argcheck(L, !!co, 1, "coroutine expected");
|
|
|
|
|
|
|
|
int r = resume_helper(L, co, lua_gettop(L) - 1, LUA_MULTRET);
|
|
|
|
|
|
|
|
bool ok = Lua::IsSuccess(r);
|
|
|
|
|
|
|
|
lua_pushboolean(L, ok);
|
|
|
|
|
|
|
|
lua_insert(L, 2);
|
|
|
|
|
|
|
|
if (!ok)
|
|
|
|
|
|
|
|
report_error(L);
|
|
|
|
|
|
|
|
return lua_gettop(L) - 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int dfhack_coauxwrap (lua_State *L) {
|
|
|
|
|
|
|
|
lua_State *co = lua_tothread(L, lua_upvalueindex(1));
|
|
|
|
|
|
|
|
int r = resume_helper(L, co, lua_gettop(L), LUA_MULTRET);
|
|
|
|
|
|
|
|
if (Lua::IsSuccess(r))
|
|
|
|
|
|
|
|
return lua_gettop(L);
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
return lua_error(L);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int dfhack_cowrap (lua_State *L) {
|
|
|
|
|
|
|
|
luaL_checktype(L, 1, LUA_TFUNCTION);
|
|
|
|
|
|
|
|
lua_settop(L, 1);
|
|
|
|
|
|
|
|
Lua::NewCoroutine(L);
|
|
|
|
|
|
|
|
lua_pushcclosure(L, dfhack_coauxwrap, 1);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lua_State *DFHack::Lua::NewCoroutine(lua_State *L) {
|
|
|
|
|
|
|
|
lua_State *NL = lua_newthread(L);
|
|
|
|
|
|
|
|
lua_swap(L);
|
|
|
|
|
|
|
|
lua_xmove(L, NL, 1); /* move function from L to NL */
|
|
|
|
|
|
|
|
return NL;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int DFHack::Lua::SafeResume(color_ostream &out, lua_State *from, lua_State *thread, int nargs, int nres, bool perr)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
AssertCoreSuspend(from);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
color_ostream *cur_out = Lua::GetOutput(from);
|
|
|
|
|
|
|
|
set_dfhack_output(from, &out);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int rv = resume_helper(from, thread, nargs, nres);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!Lua::IsSuccess(rv) && perr)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
report_error(from, &out);
|
|
|
|
|
|
|
|
lua_pop(from, 1);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
set_dfhack_output(from, cur_out);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int DFHack::Lua::SafeResume(color_ostream &out, lua_State *from, int nargs, int nres, bool perr)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
int base = lua_gettop(from) - nargs;
|
|
|
|
|
|
|
|
lua_State *thread = lua_tothread(from, base);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int rv = SafeResume(out, from, thread, nargs, nres, perr);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lua_remove(from, base);
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
* Module loading
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int DFHACK_LOADED_TOKEN = 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool DFHack::Lua::PushModule(color_ostream &out, lua_State *state, const char *module)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
AssertCoreSuspend(state);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Check if it is already loaded
|
|
|
|
|
|
|
|
lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_LOADED_TOKEN);
|
|
|
|
|
|
|
|
lua_pushstring(state, module);
|
|
|
|
|
|
|
|
lua_rawget(state, -2);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (lua_toboolean(state, -1))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
lua_remove(state, -2);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lua_pop(state, 2);
|
|
|
|
lua_getglobal(state, "require");
|
|
|
|
lua_getglobal(state, "require");
|
|
|
|
lua_pushstring(state, module.c_str());
|
|
|
|
lua_pushstring(state, module);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return Lua::SafeCall(out, state, 1, 1);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool DFHack::Lua::PushModulePublic(color_ostream &out, lua_State *state,
|
|
|
|
|
|
|
|
const char *module, const char *name)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (!PushModule(out, state, module))
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
if (!Lua::SafeCall(out, state, 1, 1))
|
|
|
|
if (!lua_istable(state, -1))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
lua_pop(state, 1);
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lua_pushstring(state, name);
|
|
|
|
|
|
|
|
lua_rawget(state, -2);
|
|
|
|
|
|
|
|
lua_remove(state, -2);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool DFHack::Lua::Require(color_ostream &out, lua_State *state,
|
|
|
|
|
|
|
|
const std::string &module, bool setglobal)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (!PushModule(out, state, module.c_str()))
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
if (setglobal)
|
|
|
|
if (setglobal)
|
|
|
@ -471,6 +751,8 @@ bool DFHack::Lua::SafeCallString(color_ostream &out, lua_State *state, const std
|
|
|
|
int nargs, int nres, bool perr,
|
|
|
|
int nargs, int nres, bool perr,
|
|
|
|
const char *debug_tag, int env_idx)
|
|
|
|
const char *debug_tag, int env_idx)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
|
|
|
|
AssertCoreSuspend(state);
|
|
|
|
|
|
|
|
|
|
|
|
if (!debug_tag)
|
|
|
|
if (!debug_tag)
|
|
|
|
debug_tag = code.c_str();
|
|
|
|
debug_tag = code.c_str();
|
|
|
|
if (env_idx)
|
|
|
|
if (env_idx)
|
|
|
@ -504,129 +786,144 @@ bool DFHack::Lua::SafeCallString(color_ostream &out, lua_State *state, const std
|
|
|
|
return Lua::SafeCall(out, state, nargs, nres, perr);
|
|
|
|
return Lua::SafeCall(out, state, nargs, nres, perr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool DFHack::Lua::InterpreterLoop(color_ostream &out, lua_State *state,
|
|
|
|
/*
|
|
|
|
const char *prompt, int env, const char *hfile)
|
|
|
|
* Coroutine interactive query loop
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int resume_query_loop(color_ostream &out,
|
|
|
|
|
|
|
|
lua_State *thread, lua_State *state, int nargs,
|
|
|
|
|
|
|
|
std::string &prompt, std::string &histfile)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
int rv = Lua::SafeResume(out, state, thread, nargs, 2);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (Lua::IsSuccess(rv))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
prompt = ifnull(lua_tostring(state, -2), "");
|
|
|
|
|
|
|
|
histfile = ifnull(lua_tostring(state, -1), "");
|
|
|
|
|
|
|
|
lua_pop(state, 2);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool DFHack::Lua::RunCoreQueryLoop(color_ostream &out, lua_State *state,
|
|
|
|
|
|
|
|
bool (*init)(color_ostream&, lua_State*, void*),
|
|
|
|
|
|
|
|
void *arg)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (!out.is_console())
|
|
|
|
if (!out.is_console())
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
if (!lua_checkstack(state, 20))
|
|
|
|
if (!lua_checkstack(state, 20))
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
if (!hfile)
|
|
|
|
Console &con = static_cast<Console&>(out);
|
|
|
|
hfile = "lua.history";
|
|
|
|
|
|
|
|
if (!prompt)
|
|
|
|
|
|
|
|
prompt = "lua";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DFHack::CommandHistory hist;
|
|
|
|
|
|
|
|
hist.load(hfile);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
out.print("Type quit to exit interactive lua interpreter.\n");
|
|
|
|
lua_State *thread;
|
|
|
|
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
std::string prompt;
|
|
|
|
|
|
|
|
std::string histfile;
|
|
|
|
|
|
|
|
|
|
|
|
static bool print_banner = true;
|
|
|
|
DFHack::CommandHistory hist;
|
|
|
|
if (print_banner) {
|
|
|
|
std::string histname;
|
|
|
|
out.print("Shortcuts:\n"
|
|
|
|
|
|
|
|
" '= foo' => '_1,_2,... = foo'\n"
|
|
|
|
|
|
|
|
" '! foo' => 'print(foo)'\n"
|
|
|
|
|
|
|
|
"Both save the first result as '_'.\n");
|
|
|
|
|
|
|
|
print_banner = false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Console &con = static_cast<Console&>(out);
|
|
|
|
{
|
|
|
|
|
|
|
|
CoreSuspender suspend;
|
|
|
|
|
|
|
|
|
|
|
|
// Make a proxy global environment.
|
|
|
|
|
|
|
|
lua_newtable(state);
|
|
|
|
|
|
|
|
int base = lua_gettop(state);
|
|
|
|
int base = lua_gettop(state);
|
|
|
|
|
|
|
|
|
|
|
|
lua_newtable(state);
|
|
|
|
if (!init(out, state, arg))
|
|
|
|
if (env)
|
|
|
|
{
|
|
|
|
lua_pushvalue(state, env);
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
lua_rawgeti(state, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS);
|
|
|
|
|
|
|
|
lua_setfield(state, -2, "__index");
|
|
|
|
|
|
|
|
lua_setmetatable(state, -2);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Main interactive loop
|
|
|
|
|
|
|
|
int vcnt = 1;
|
|
|
|
|
|
|
|
string curline;
|
|
|
|
|
|
|
|
string prompt_str = "[" + string(prompt) + "]# ";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
|
|
|
|
lua_settop(state, base);
|
|
|
|
lua_settop(state, base);
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
con.lineedit(prompt_str,curline,hist);
|
|
|
|
lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_QUERY_COROTABLE_TOKEN);
|
|
|
|
|
|
|
|
lua_pushvalue(state, base+1);
|
|
|
|
|
|
|
|
lua_remove(state, base+1);
|
|
|
|
|
|
|
|
thread = Lua::NewCoroutine(state);
|
|
|
|
|
|
|
|
lua_rawsetp(state, -2, thread);
|
|
|
|
|
|
|
|
lua_pop(state, 1);
|
|
|
|
|
|
|
|
|
|
|
|
if (curline.empty())
|
|
|
|
rv = resume_query_loop(out, thread, state, lua_gettop(state)-base, prompt, histfile);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (curline == "quit")
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
hist.add(curline);
|
|
|
|
while (rv == LUA_YIELD)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (histfile != histname)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (!histname.empty())
|
|
|
|
|
|
|
|
hist.save(histname.c_str());
|
|
|
|
|
|
|
|
|
|
|
|
char pfix = curline[0];
|
|
|
|
hist.clear();
|
|
|
|
|
|
|
|
histname = histfile;
|
|
|
|
|
|
|
|
|
|
|
|
if (pfix == '=' || pfix == '!')
|
|
|
|
if (!histname.empty())
|
|
|
|
{
|
|
|
|
hist.load(histname.c_str());
|
|
|
|
curline = "return " + curline.substr(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!Lua::SafeCallString(out, state, curline, 0, LUA_MULTRET, true, "=(interactive)", base))
|
|
|
|
if (prompt.empty())
|
|
|
|
continue;
|
|
|
|
prompt = ">> ";
|
|
|
|
|
|
|
|
|
|
|
|
int numret = lua_gettop(state) - base;
|
|
|
|
std::string curline;
|
|
|
|
|
|
|
|
con.lineedit(prompt,curline,hist);
|
|
|
|
|
|
|
|
hist.add(curline);
|
|
|
|
|
|
|
|
|
|
|
|
if (numret >= 1)
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
lua_pushvalue(state, base+1);
|
|
|
|
CoreSuspender suspend;
|
|
|
|
lua_setfield(state, base, "_");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (pfix == '!')
|
|
|
|
lua_pushlstring(state, curline.data(), curline.size());
|
|
|
|
{
|
|
|
|
rv = resume_query_loop(out, thread, state, 1, prompt, histfile);
|
|
|
|
lua_pushcfunction(state, lua_dfhack_println);
|
|
|
|
|
|
|
|
lua_insert(state, base+1);
|
|
|
|
|
|
|
|
SafeCall(out, state, numret, 0);
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 1; i <= numret; i++)
|
|
|
|
if (!histname.empty())
|
|
|
|
{
|
|
|
|
hist.save(histname.c_str());
|
|
|
|
std::string name = stl_sprintf("_%d", vcnt++);
|
|
|
|
|
|
|
|
lua_pushvalue(state, base + i);
|
|
|
|
|
|
|
|
lua_setfield(state, base, name.c_str());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
out.print("%s = ", name.c_str());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lua_pushcfunction(state, lua_dfhack_println);
|
|
|
|
|
|
|
|
lua_pushvalue(state, base + i);
|
|
|
|
|
|
|
|
SafeCall(out, state, 1, 0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (!Lua::SafeCallString(out, state, curline, 0, LUA_MULTRET, true, "=(interactive)", base))
|
|
|
|
CoreSuspender suspend;
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_QUERY_COROTABLE_TOKEN);
|
|
|
|
|
|
|
|
lua_pushnil(state);
|
|
|
|
|
|
|
|
lua_rawsetp(state, -2, thread);
|
|
|
|
|
|
|
|
lua_pop(state, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
lua_settop(state, base-1);
|
|
|
|
return (rv == LUA_OK);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
hist.save(hfile);
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
struct InterpreterArgs {
|
|
|
|
|
|
|
|
const char *prompt;
|
|
|
|
|
|
|
|
const char *hfile;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static bool init_interpreter(color_ostream &out, lua_State *state, void *info)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
auto args = (InterpreterArgs*)info;
|
|
|
|
|
|
|
|
lua_getglobal(state, "dfhack");
|
|
|
|
|
|
|
|
lua_getfield(state, -1, "interpreter");
|
|
|
|
|
|
|
|
lua_remove(state, -2);
|
|
|
|
|
|
|
|
lua_pushstring(state, args->prompt);
|
|
|
|
|
|
|
|
lua_pushstring(state, args->hfile);
|
|
|
|
return true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int lua_dfhack_interpreter(lua_State *state)
|
|
|
|
bool DFHack::Lua::InterpreterLoop(color_ostream &out, lua_State *state,
|
|
|
|
|
|
|
|
const char *prompt, const char *hfile)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
Console *pstream = get_console(state);
|
|
|
|
if (!out.is_console())
|
|
|
|
if (!pstream)
|
|
|
|
return false;
|
|
|
|
return 2;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int argc = lua_gettop(state);
|
|
|
|
if (!hfile)
|
|
|
|
|
|
|
|
hfile = "lua.history";
|
|
|
|
|
|
|
|
if (!prompt)
|
|
|
|
|
|
|
|
prompt = "lua";
|
|
|
|
|
|
|
|
|
|
|
|
const char *prompt = (argc >= 1 ? lua_tostring(state, 1) : NULL);
|
|
|
|
InterpreterArgs args;
|
|
|
|
int env = (argc >= 2 && !lua_isnil(state,2) ? 2 : 0);
|
|
|
|
args.prompt = prompt;
|
|
|
|
const char *hfile = (argc >= 3 ? lua_tostring(state, 3) : NULL);
|
|
|
|
args.hfile = hfile;
|
|
|
|
|
|
|
|
|
|
|
|
lua_pushboolean(state, Lua::InterpreterLoop(*pstream, state, prompt, env, hfile));
|
|
|
|
return RunCoreQueryLoop(out, state, init_interpreter, &args);
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static bool do_invoke_cleanup(lua_State *L, int nargs, int errorfun, bool success)
|
|
|
|
static bool do_invoke_cleanup(lua_State *L, int nargs, int errorfun, bool success)
|
|
|
@ -648,8 +945,10 @@ static bool do_invoke_cleanup(lua_State *L, int nargs, int errorfun, bool succes
|
|
|
|
return success;
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int finish_dfhack_cleanup (lua_State *L, bool success)
|
|
|
|
int dfhack_cleanup_cont(lua_State *L, int status, int)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
|
|
|
|
bool success = Lua::IsSuccess(status);
|
|
|
|
|
|
|
|
|
|
|
|
int nargs = lua_tointeger(L, 1);
|
|
|
|
int nargs = lua_tointeger(L, 1);
|
|
|
|
bool always = lua_toboolean(L, 2);
|
|
|
|
bool always = lua_toboolean(L, 2);
|
|
|
|
int rvbase = 4+nargs;
|
|
|
|
int rvbase = 4+nargs;
|
|
|
@ -690,12 +989,6 @@ static int finish_dfhack_cleanup (lua_State *L, bool success)
|
|
|
|
return numret;
|
|
|
|
return numret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int dfhack_cleanup_cont (lua_State *L)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
int status = lua_getctx(L, NULL);
|
|
|
|
|
|
|
|
return finish_dfhack_cleanup(L, (status == LUA_YIELD));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int dfhack_call_with_finalizer (lua_State *L)
|
|
|
|
static int dfhack_call_with_finalizer (lua_State *L)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
int nargs = luaL_checkint(L, 1);
|
|
|
|
int nargs = luaL_checkint(L, 1);
|
|
|
@ -730,8 +1023,7 @@ static int dfhack_call_with_finalizer (lua_State *L)
|
|
|
|
// Actually invoke
|
|
|
|
// Actually invoke
|
|
|
|
|
|
|
|
|
|
|
|
// stack: [nargs] [always] [errorfun] [cleanup fun] [cleanup args...] |rvbase+1:| [fun] [args...]
|
|
|
|
// stack: [nargs] [always] [errorfun] [cleanup fun] [cleanup args...] |rvbase+1:| [fun] [args...]
|
|
|
|
int status = lua_pcallk(L, lua_gettop(L)-rvbase-1, LUA_MULTRET, 3, 0, dfhack_cleanup_cont);
|
|
|
|
return Lua::TailPCallK<dfhack_cleanup_cont>(L, lua_gettop(L)-rvbase-1, LUA_MULTRET, 3, 0);
|
|
|
|
return finish_dfhack_cleanup(L, (status == LUA_OK));
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int lua_dfhack_with_suspend(lua_State *L)
|
|
|
|
static int lua_dfhack_with_suspend(lua_State *L)
|
|
|
@ -761,15 +1053,24 @@ static int dfhack_open_plugin(lua_State *L)
|
|
|
|
return 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool Lua::IsCoreContext(lua_State *state)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// This uses a private field of the lua state to
|
|
|
|
|
|
|
|
// evaluate the condition without accessing the lua
|
|
|
|
|
|
|
|
// stack, and thus requiring a lock on the core state.
|
|
|
|
|
|
|
|
return state && Lua::Core::State &&
|
|
|
|
|
|
|
|
state->l_G == Lua::Core::State->l_G;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static const luaL_Reg dfhack_funcs[] = {
|
|
|
|
static const luaL_Reg dfhack_funcs[] = {
|
|
|
|
{ "print", lua_dfhack_print },
|
|
|
|
{ "print", lua_dfhack_print },
|
|
|
|
{ "println", lua_dfhack_println },
|
|
|
|
{ "println", lua_dfhack_println },
|
|
|
|
{ "printerr", lua_dfhack_printerr },
|
|
|
|
{ "printerr", lua_dfhack_printerr },
|
|
|
|
{ "color", lua_dfhack_color },
|
|
|
|
{ "color", lua_dfhack_color },
|
|
|
|
{ "is_interactive", lua_dfhack_is_interactive },
|
|
|
|
{ "is_interactive", lua_dfhack_is_interactive },
|
|
|
|
{ "lineedit", lua_dfhack_lineedit },
|
|
|
|
{ "lineedit", dfhack_lineedit },
|
|
|
|
{ "interpreter", lua_dfhack_interpreter },
|
|
|
|
{ "safecall", dfhack_safecall },
|
|
|
|
{ "safecall", lua_dfhack_safecall },
|
|
|
|
{ "saferesume", dfhack_saferesume },
|
|
|
|
{ "onerror", dfhack_onerror },
|
|
|
|
{ "onerror", dfhack_onerror },
|
|
|
|
{ "call_with_finalizer", dfhack_call_with_finalizer },
|
|
|
|
{ "call_with_finalizer", dfhack_call_with_finalizer },
|
|
|
|
{ "with_suspend", lua_dfhack_with_suspend },
|
|
|
|
{ "with_suspend", lua_dfhack_with_suspend },
|
|
|
@ -777,6 +1078,138 @@ static const luaL_Reg dfhack_funcs[] = {
|
|
|
|
{ NULL, NULL }
|
|
|
|
{ NULL, NULL }
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static const luaL_Reg dfhack_coro_funcs[] = {
|
|
|
|
|
|
|
|
{ "resume", dfhack_coresume },
|
|
|
|
|
|
|
|
{ "wrap", dfhack_cowrap },
|
|
|
|
|
|
|
|
{ NULL, NULL }
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/************
|
|
|
|
|
|
|
|
* Events *
|
|
|
|
|
|
|
|
************/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int DFHACK_EVENT_META_TOKEN = 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int DFHack::Lua::NewEvent(lua_State *state)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
lua_newtable(state);
|
|
|
|
|
|
|
|
lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_EVENT_META_TOKEN);
|
|
|
|
|
|
|
|
lua_setmetatable(state, -2);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void dfhack_event_invoke(lua_State *L, int base, bool from_c)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
int event = base+1;
|
|
|
|
|
|
|
|
int num_args = lua_gettop(L)-event;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int errorfun = base+2;
|
|
|
|
|
|
|
|
lua_pushcfunction(L, dfhack_onerror);
|
|
|
|
|
|
|
|
lua_insert(L, errorfun);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int argbase = base+3;
|
|
|
|
|
|
|
|
lua_pushnil(L);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// stack: |base| event errorfun (args) key cb (args)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
while (lua_next(L, event))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (from_c && lua_islightuserdata(L, -1) && !lua_touserdata(L, -1))
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < num_args; i++)
|
|
|
|
|
|
|
|
lua_pushvalue(L, argbase+i);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (lua_pcall(L, num_args, 0, errorfun) != LUA_OK)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
report_error(L);
|
|
|
|
|
|
|
|
lua_pop(L, 1);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lua_settop(L, base);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int dfhack_event_call(lua_State *state)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
luaL_checktype(state, 1, LUA_TTABLE);
|
|
|
|
|
|
|
|
luaL_checkstack(state, lua_gettop(state)+2, "stack overflow in event dispatch");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
dfhack_event_invoke(state, 0, false);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void DFHack::Lua::InvokeEvent(color_ostream &out, lua_State *state, void *key, int num_args)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
AssertCoreSuspend(state);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int base = lua_gettop(state) - num_args;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!lua_checkstack(state, num_args+4))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
out.printerr("Stack overflow in Lua::InvokeEvent");
|
|
|
|
|
|
|
|
lua_settop(state, base);
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lua_rawgetp(state, LUA_REGISTRYINDEX, key);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!lua_istable(state, -1))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (!lua_isnil(state, -1))
|
|
|
|
|
|
|
|
out.printerr("Invalid event object in Lua::InvokeEvent");
|
|
|
|
|
|
|
|
lua_settop(state, base);
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lua_insert(state, base+1);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
color_ostream *cur_out = Lua::GetOutput(state);
|
|
|
|
|
|
|
|
set_dfhack_output(state, &out);
|
|
|
|
|
|
|
|
dfhack_event_invoke(state, base, true);
|
|
|
|
|
|
|
|
set_dfhack_output(state, cur_out);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void DFHack::Lua::MakeEvent(lua_State *state, void *key)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
lua_rawgetp(state, LUA_REGISTRYINDEX, key);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (lua_isnil(state, -1))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
lua_pop(state, 1);
|
|
|
|
|
|
|
|
NewEvent(state);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lua_dup(state);
|
|
|
|
|
|
|
|
lua_rawsetp(state, LUA_REGISTRYINDEX, key);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void DFHack::Lua::Notification::invoke(color_ostream &out, int nargs)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
assert(state);
|
|
|
|
|
|
|
|
InvokeEvent(out, state, key, nargs);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void DFHack::Lua::Notification::bind(lua_State *state, void *key)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
this->state = state;
|
|
|
|
|
|
|
|
this->key = key;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void DFHack::Lua::Notification::bind(lua_State *state, const char *name)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
MakeEvent(state, this);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (handler)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
PushFunctionWrapper(state, 0, name, handler);
|
|
|
|
|
|
|
|
lua_rawsetp(state, -2, NULL);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this->state = state;
|
|
|
|
|
|
|
|
this->key = this;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/************************
|
|
|
|
/************************
|
|
|
|
* Main Open function *
|
|
|
|
* Main Open function *
|
|
|
|
************************/
|
|
|
|
************************/
|
|
|
@ -791,6 +1224,10 @@ lua_State *DFHack::Lua::Open(color_ostream &out, lua_State *state)
|
|
|
|
luaL_openlibs(state);
|
|
|
|
luaL_openlibs(state);
|
|
|
|
AttachDFGlobals(state);
|
|
|
|
AttachDFGlobals(state);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Table of query coroutines
|
|
|
|
|
|
|
|
lua_newtable(state);
|
|
|
|
|
|
|
|
lua_rawsetp(state, LUA_REGISTRYINDEX, &DFHACK_QUERY_COROTABLE_TOKEN);
|
|
|
|
|
|
|
|
|
|
|
|
// Replace the print function of the standard library
|
|
|
|
// Replace the print function of the standard library
|
|
|
|
lua_pushcfunction(state, lua_dfhack_println);
|
|
|
|
lua_pushcfunction(state, lua_dfhack_println);
|
|
|
|
lua_setglobal(state, "print");
|
|
|
|
lua_setglobal(state, "print");
|
|
|
@ -798,6 +1235,9 @@ lua_State *DFHack::Lua::Open(color_ostream &out, lua_State *state)
|
|
|
|
// Create the dfhack global
|
|
|
|
// Create the dfhack global
|
|
|
|
lua_newtable(state);
|
|
|
|
lua_newtable(state);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lua_pushboolean(state, IsCoreContext(state));
|
|
|
|
|
|
|
|
lua_setfield(state, -2, "is_core_context");
|
|
|
|
|
|
|
|
|
|
|
|
// Create the metatable for exceptions
|
|
|
|
// Create the metatable for exceptions
|
|
|
|
lua_newtable(state);
|
|
|
|
lua_newtable(state);
|
|
|
|
lua_pushcfunction(state, dfhack_exception_tostring);
|
|
|
|
lua_pushcfunction(state, dfhack_exception_tostring);
|
|
|
@ -806,6 +1246,15 @@ lua_State *DFHack::Lua::Open(color_ostream &out, lua_State *state)
|
|
|
|
lua_rawsetp(state, LUA_REGISTRYINDEX, &DFHACK_EXCEPTION_META_TOKEN);
|
|
|
|
lua_rawsetp(state, LUA_REGISTRYINDEX, &DFHACK_EXCEPTION_META_TOKEN);
|
|
|
|
lua_setfield(state, -2, "exception");
|
|
|
|
lua_setfield(state, -2, "exception");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lua_newtable(state);
|
|
|
|
|
|
|
|
lua_pushcfunction(state, dfhack_event_call);
|
|
|
|
|
|
|
|
lua_setfield(state, -2, "__call");
|
|
|
|
|
|
|
|
lua_pushcfunction(state, Lua::NewEvent);
|
|
|
|
|
|
|
|
lua_setfield(state, -2, "new");
|
|
|
|
|
|
|
|
lua_dup(state);
|
|
|
|
|
|
|
|
lua_rawsetp(state, LUA_REGISTRYINDEX, &DFHACK_EVENT_META_TOKEN);
|
|
|
|
|
|
|
|
lua_setfield(state, -2, "event");
|
|
|
|
|
|
|
|
|
|
|
|
// Initialize the dfhack global
|
|
|
|
// Initialize the dfhack global
|
|
|
|
luaL_setfuncs(state, dfhack_funcs, 0);
|
|
|
|
luaL_setfuncs(state, dfhack_funcs, 0);
|
|
|
|
|
|
|
|
|
|
|
@ -813,9 +1262,45 @@ lua_State *DFHack::Lua::Open(color_ostream &out, lua_State *state)
|
|
|
|
|
|
|
|
|
|
|
|
lua_setglobal(state, "dfhack");
|
|
|
|
lua_setglobal(state, "dfhack");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// stash the loaded module table into our own registry key
|
|
|
|
|
|
|
|
lua_getglobal(state, "package");
|
|
|
|
|
|
|
|
assert(lua_istable(state, -1));
|
|
|
|
|
|
|
|
lua_getfield(state, -1, "loaded");
|
|
|
|
|
|
|
|
assert(lua_istable(state, -1));
|
|
|
|
|
|
|
|
lua_rawsetp(state, LUA_REGISTRYINDEX, &DFHACK_LOADED_TOKEN);
|
|
|
|
|
|
|
|
lua_pop(state, 1);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// replace some coroutine functions
|
|
|
|
|
|
|
|
lua_getglobal(state, "coroutine");
|
|
|
|
|
|
|
|
luaL_setfuncs(state, dfhack_coro_funcs, 0);
|
|
|
|
|
|
|
|
lua_pop(state, 1);
|
|
|
|
|
|
|
|
|
|
|
|
// load dfhack.lua
|
|
|
|
// load dfhack.lua
|
|
|
|
Require(out, state, "dfhack");
|
|
|
|
Require(out, state, "dfhack");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lua_settop(state, 0);
|
|
|
|
|
|
|
|
if (!lua_checkstack(state, 64))
|
|
|
|
|
|
|
|
out.printerr("Could not extend initial lua stack size to 64 items.\n");
|
|
|
|
|
|
|
|
|
|
|
|
return state;
|
|
|
|
return state;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void DFHack::Lua::Core::Init(color_ostream &out)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (State)
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
State = luaL_newstate();
|
|
|
|
|
|
|
|
Lua::Open(out, State);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void DFHack::Lua::Core::Reset(color_ostream &out, const char *where)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
int top = lua_gettop(State);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (top != 0)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
out.printerr("Common lua context stack top left at %d after %s.\n", top, where);
|
|
|
|
|
|
|
|
lua_settop(State, 0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|