#include "LuaVM.hpp" #include "StringUtils.hpp" #ifdef _WIN32 #define snprintf _snprintf #endif #define __STDC_FORMAT_MACROS #include "util/IntTypes.hpp" #include #include #include #include #include using namespace std; LuaVM::LuaVM( ) : m_lua( 0 ) { initialize( ); } LuaVM::~LuaVM( ) { lua_close( m_lua ); } lua_State *LuaVM::handle( ) { return m_lua; } void LuaVM::initialize( ) { m_lua = luaL_newstate( ); luaL_openlibs( m_lua ); } void LuaVM::fullGarbageCollect( ) { lua_gc( m_lua, LUA_GCCOLLECT, 0 ); } void LuaVM::loadSource( const char *sourceFilename ) { int res; m_sourceFilename.assign( sourceFilename ); res = luaL_loadfile( m_lua, m_sourceFilename.c_str( ) ); if( res != 0 ) { ostringstream ss; ss << "Can't read Lua source file from file '" << m_sourceFilename << "': " << lua_tostring( m_lua, -1 ); lua_pop( m_lua, 1 ); throw std::runtime_error( ss.str( ) ); } } void LuaVM::executeMain( ) { int res = lua_pcall( m_lua, 0, 0, 0 ); if( res != 0 ) { ostringstream ss; ss << "Can't execute main body of Lua source file '" << m_sourceFilename << "': " << lua_tostring( m_lua, -1 ); lua_pop( m_lua, 1 ); throw std::runtime_error( ss.str( ) ); } } void LuaVM::executeFunction( const string &f ) { //int top = lua_gettop( m_lua ); lua_getglobal( m_lua, f.c_str( ) ); int res = lua_pcall( m_lua, 0, LUA_MULTRET, 0 ); if( res != 0 ) { ostringstream ss; ss << "Unable to call Lua function '" << f << "': " << lua_tostring( m_lua, -1 ); lua_pop( m_lua, 1 ); throw std::runtime_error( ss.str( ) ); } //int nresults = lua_gettop( m_lua ) - top; // TODO: return results } void LuaVM::dumpGlobals( ) { lua_rawgeti( m_lua, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS ); lua_pushnil( m_lua ); while( lua_next( m_lua, -2 ) ) { if( lua_type( m_lua, -2 ) == LUA_TSTRING ) { cout << lua_tostring( m_lua, -2 ) << endl; } lua_pop( m_lua, 1 ); } } void LuaVM::dumpStackElement( lua_State *l, int i, int indent ) { int type = lua_type( l, i ); if( indent == 0 ) { cout << setfill( '0' ) << setw( 2 ) << i << ": "; } switch( type ) { case LUA_TNIL: cout << "LUA_TNIL nil"; break; case LUA_TBOOLEAN: cout << "LUA_TBOOLEAN " << ( lua_toboolean( l, i ) ? "true" : "false" ); break; case LUA_TNUMBER: cout << "LUA_TNUMBER " << lua_tonumber( l, i ) << ""; break; case LUA_TSTRING: cout << "LUA_TSTRING '" << lua_tostring( l, i ) << "'"; break; case LUA_TTABLE: cout << "LUA_TTABLE { "; lua_pushvalue( l, i ); lua_pushnil( l ); while( lua_next( l, -2 ) ) { lua_pushvalue( l, -2 ); if( lua_isstring( l, -1 ) ) { cout << lua_tostring( l, -1 ); } else { cout << ""; } cout << " = "; dumpStackElement( l, -2, indent + 1 ); cout << ", "; lua_pop( l, 2 ); } lua_pop( l, 1 ); cout << " }"; break; case LUA_TFUNCTION: { lua_CFunction f = lua_tocfunction( l, i ); char buf[33]; snprintf( buf, 32, "function[%016" PRIxPTR "]", (uintptr_t)f ); // TODO: int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar); cout << "LUA_TFUNCTION " << buf; break; } case LUA_TLIGHTUSERDATA: { void *p = lua_touserdata( l, i ); char buf[33]; snprintf( buf, 32, "void *[%016" PRIxPTR "]", (uintptr_t)p ); cout << "LUA_TLIGHTUSERDATA " << buf; break; } case LUA_TUSERDATA: { void *p = lua_touserdata( l, i ); char buf[33]; snprintf( buf, 32, "void *[%016" PRIxPTR "]", (uintptr_t)p ); // TODO: is there a tostring entry in the metatable? cout << "LUA_TUSERDATA " << buf; break; } case LUA_TTHREAD: { //lua_State *thread = lua_tothread( l, i ); // TODO: more information like status of the thread? cout << "LUA_TTHREAD "; break; } default: cout << lua_typename( l, type ) << " "; break; } if( indent == 0 ) { cout << endl; } } void LuaVM::dumpStack( ) { cout << "-- stack --" << endl; int top = lua_gettop( m_lua ); for( int i = 1; i <= top; i++ ) { dumpStackElement( m_lua, i ); } cout << "-- end of stack --" << endl; } void LuaVM::dumpState( ) { cout << "globals:" << endl; dumpGlobals( ); cout << "stack:" << endl; dumpStack( ); } int LuaVM::findValue( const string &key ) { vector parts = split( key, "." ); if( parts.size( ) == 1 ) { lua_getglobal( m_lua, parts[0].c_str( ) ); if( lua_isnil( m_lua, -1 ) ) { lua_pop( m_lua, 1 ); ostringstream ss; ss << "key '" << key << "' refers to an empty element"; throw runtime_error( ss.str( ) ); } return 1; } lua_getglobal( m_lua, parts[0].c_str( ) ); for( int i = 1; i <= (int)parts.size( ) - 1; i++ ) { if( !lua_istable( m_lua, -1 ) ) { lua_pop( m_lua, i ); ostringstream ss; ss << "table expected in '" << key << "' when derefencing field '" << parts[i] << "'"; throw runtime_error( ss.str( ) ); } lua_getfield( m_lua, -1, parts[i].c_str( ) ); } if( lua_isnil( m_lua, -1 ) ) { lua_pop( m_lua, (int)parts.size( ) ); ostringstream ss; ss << "key '" << key << "' refers to an empty element"; throw runtime_error( ss.str( ) ); } return parts.size( ); } string LuaVM::getString( const string &key ) { int n = findValue( key ); string res = lua_tostring( m_lua, -1 ); lua_pop( m_lua, n ); //dumpStack( ); return res; } int LuaVM::getInt( const std::string &key ) { int n = findValue( key ); int res = lua_tointeger( m_lua, -1 ); lua_pop( m_lua, n ); //dumpStack( ); return res; } bool LuaVM::getBoolean( const string &key ) { int n = findValue( key ); bool res = lua_toboolean( m_lua, -1 ); lua_pop( m_lua, n ); //dumpStack( ); return res; } vector LuaVM::getStringArray( const string &key ) { vector res; int n = findValue( key ); if( !lua_istable( m_lua, -1 ) ) { lua_pop( m_lua, n ); ostringstream ss; ss << "key '" << key << "' refers not to a table"; throw runtime_error( ss.str( ) ); } //dumpStack( ); lua_pushvalue( m_lua, -1 ); lua_pushnil( m_lua ); while( lua_next( m_lua, -2 ) ) { res.push_back( lua_tostring( m_lua, -1 ) ); lua_pop( m_lua, 1 ); } lua_pop( m_lua, 1 ); lua_pop( m_lua, n ); return res; }