@ -58,9 +58,18 @@ distribution.
# include <lauxlib.h>
# include <lualib.h>
# include <lstate.h>
using namespace DFHack ;
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 ( ) ) ;
}
void DFHack : : Lua : : PushDFObject ( lua_State * state , type_identity * type , void * ptr )
{
push_object_internal ( state , type , ptr , false ) ;
@ -71,6 +80,36 @@ void *DFHack::Lua::GetDFObject(lua_State *state, type_identity *type, int val_in
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 ;
}
static int DFHACK_OSTREAM_TOKEN = 0 ;
color_ostream * DFHack : : Lua : : GetOutput ( lua_State * L )
@ -418,6 +457,8 @@ static int lua_dfhack_safecall (lua_State *L)
bool DFHack : : Lua : : SafeCall ( color_ostream & out , lua_State * L , int nargs , int nres , bool perr )
{
AssertCoreSuspend ( L ) ;
int base = lua_gettop ( L ) - nargs ;
color_ostream * cur_out = Lua : : GetOutput ( L ) ;
@ -440,13 +481,52 @@ bool DFHack::Lua::SafeCall(color_ostream &out, lua_State *L, int nargs, int nres
return ok ;
}
bool DFHack : : Lua : : Require ( color_ostream & out , lua_State * state ,
const std : : string & module , bool setglobal )
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_pushstring ( state , module . c_str ( ) ) ;
lua_pushstring ( state , module ) ;
return Lua : : SafeCall ( out , state , 1 , 1 ) ;
}
if ( ! 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_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 ;
if ( setglobal )
@ -471,6 +551,8 @@ bool DFHack::Lua::SafeCallString(color_ostream &out, lua_State *state, const std
int nargs , int nres , bool perr ,
const char * debug_tag , int env_idx )
{
AssertCoreSuspend ( state ) ;
if ( ! debug_tag )
debug_tag = code . c_str ( ) ;
if ( env_idx )
@ -507,6 +589,8 @@ bool DFHack::Lua::SafeCallString(color_ostream &out, lua_State *state, const std
bool DFHack : : Lua : : InterpreterLoop ( color_ostream & out , lua_State * state ,
const char * prompt , int env , const char * hfile )
{
AssertCoreSuspend ( state ) ;
if ( ! out . is_console ( ) )
return false ;
if ( ! lua_checkstack ( state , 20 ) )
@ -761,6 +845,15 @@ static int dfhack_open_plugin(lua_State *L)
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 [ ] = {
{ " print " , lua_dfhack_print } ,
{ " println " , lua_dfhack_println } ,
@ -777,6 +870,132 @@ static const luaL_Reg dfhack_funcs[] = {
{ 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 : : CreateEvent ( 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 )
{
CreateEvent ( state , this ) ;
if ( handler )
{
PushFunctionWrapper ( state , 0 , name , handler ) ;
lua_rawsetp ( state , - 2 , NULL ) ;
}
this - > state = state ;
this - > key = this ;
}
/************************
* Main Open function *
* * * * * * * * * * * * * * * * * * * * * * * */
@ -798,6 +1017,9 @@ lua_State *DFHack::Lua::Open(color_ostream &out, lua_State *state)
// Create the dfhack global
lua_newtable ( state ) ;
lua_pushboolean ( state , IsCoreContext ( state ) ) ;
lua_setfield ( state , - 2 , " is_core_context " ) ;
// Create the metatable for exceptions
lua_newtable ( state ) ;
lua_pushcfunction ( state , dfhack_exception_tostring ) ;
@ -806,6 +1028,15 @@ lua_State *DFHack::Lua::Open(color_ostream &out, lua_State *state)
lua_rawsetp ( state , LUA_REGISTRYINDEX , & DFHACK_EXCEPTION_META_TOKEN ) ;
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
luaL_setfuncs ( state , dfhack_funcs , 0 ) ;
@ -813,9 +1044,40 @@ lua_State *DFHack::Lua::Open(color_ostream &out, lua_State *state)
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 ) ;
// load dfhack.lua
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 ;
}
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 ) ;
}
}