// Copyright (C) 2012-2013 ChaosForge / Kornel Kisielewicz // http://chaosforge.org/ // // This file is part of NV Libraries. // For conditions of distribution and use, see copyright notice in nv.hh #include "nv/lua/lua_state.hh" #include "nv/lua/lua_raw.hh" #include "nv/logging.hh" #include "nv/string.hh" #include "nv/root.hh" #include "nv/types.hh" using namespace nv; lua::stack_guard::stack_guard( lua::state* aL ) : L(aL), m_level( lua_gettop(aL->L) ) { } lua::stack_guard::stack_guard( lua::state& aL ) : L(&aL), m_level( lua_gettop(aL.L) ) { } lua::stack_guard::~stack_guard() { lua_settop( L->L, m_level ); } lua::state::state( bool load_libs /*= false*/ ) { load_lua_library(); m_owner = true; L = luaL_newstate( ); lua_pushcfunction(L, luaopen_base); lua_pushliteral(L, LUA_TABLIBNAME); lua_call(L, 1, 0); if ( load_libs ) { stack_guard guard( this ); static const luaL_Reg lualibs[] = { { "string", luaopen_string }, { "table", luaopen_table }, { "math", luaopen_math }, { NULL, NULL} }; const luaL_Reg *lib = lualibs; for(; lib->func != NULL; lib++) { lib->func( L ); } } NV_LOG( nv::LOG_TRACE, "Lua state created" ); } int lua::state::load_string( const std::string& code, const std::string& name ) { NV_LOG( nv::LOG_TRACE, "Loading Lua string '" << name << "'"); return luaL_loadbuffer( L, code.c_str(), code.length(), name.c_str() ); } int lua::state::load_stream( std::istream& stream, const std::string& name ) { NV_LOG( nv::LOG_NOTICE, "Loading Lua stream '" << name << "'"); return load_string( std::string( (std::istreambuf_iterator(stream)), std::istreambuf_iterator()), name ); } int lua::state::load_file( const std::string& filename ) { NV_LOG( nv::LOG_NOTICE, "Loading Lua file '" << filename << "'"); return luaL_loadfile( L, filename.c_str() ); } bool lua::state::do_string( const std::string& code, const std::string& name, int rvalues ) { lua::stack_guard( this ); int result = load_string(code,name); if (result) { NV_LOG( nv::LOG_WARNING, "Failed to load string " << name << ": " << lua_tostring(L, -1)); return false; } return do_current( name, rvalues ) == 0; } bool lua::state::do_stream( std::istream& stream, const std::string& name ) { lua::stack_guard( this ); int result = load_stream(stream,name); if (result) { NV_LOG( nv::LOG_WARNING, "Failed to open stream " << name << ": " << lua_tostring(L, -1)); return false; } return do_current( name ) == 0; } bool lua::state::do_file( const std::string& filename ) { lua::stack_guard( this ); int result = load_file(filename); if (result) { NV_LOG( nv::LOG_WARNING, "Failed to open file " << filename << ": " << lua_tostring(L, -1)); return false; } return do_current( filename ) == 0; } int lua::state::do_current( const std::string& name, int rvalues ) { int result = lua_pcall(L, 0, rvalues, 0); if (result) { NV_LOG( nv::LOG_WARNING, "Failed to run script " << name << ": " << lua_tostring(L, -1)); lua_pop( L, 1 ); } return result; } lua::state::~state() { if (m_owner) { lua_close( L ); } } bool lua::state::push( const std::string& path, bool global ) { size_t point = path.find('.'); if (point == std::string::npos) { if (global) { lua_getglobal( L, path.c_str() ); } else { lua_getfield( L, -1, path.c_str() ); } return !lua_isnil( L, -1 ); } size_t idx = 0; size_t start = 0; while( point != std::string::npos ) { if (idx == 0) { if (global) { lua_getglobal( L, path.substr(start,point-start).c_str() ); } else { lua_getfield( L, -1, path.substr(start,point-start).c_str() ); } } else { if ( lua_istable( L, -1 ) ) { lua_pushstring( L, path.substr(start,point-start).c_str() ); lua_gettable( L, -2 ); lua_insert( L, -2 ); lua_pop( L, 1 ); } else { lua_pop(L, 1); lua_pushnil(L); return false; } } start = point+1; point = path.find( '.', start ); } return true; } int lua::state::get_stack_size() { return lua_gettop( L ); } lua::table_guard::table_guard( lua::state* lstate, const std::string& table, bool global ) : L(lstate), m_guard(lstate) { L->push( table, global ); } lua::table_guard::table_guard( lua::state* lstate, const std::string& table, int index, bool global ) : L(lstate), m_guard(lstate) { L->push( table, global ); lua_rawgeti( L->L, -1, index ); } lua::table_guard::table_guard( lua::state* lstate, const std::string& table, const std::string& index, bool global /*= true */ ) : L(lstate), m_guard(lstate) { L->push( table, global ); lua_pushstring( L->L, index.c_str() ); lua_rawget( L->L, -2 ); } lua::table_guard::table_guard( const table_guard& parent, const std::string& index ) : L( parent.L ), m_guard( parent.L ) { lua_getfield( L->L, -1, index.c_str() ); } lua::table_guard::table_guard( const table_guard& parent, int index ) : L( parent.L ), m_guard( parent.L ) { lua_rawgeti( L->L, -1, index ); } bool lua::table_guard::has_field( const string& element ) { lua_getfield( L->L, -1, element.c_str() ); bool result = lua_isnil( L->L, -1 ); lua_pop( L->L, 1 ); return result; } string lua::table_guard::get_string( const string& element, const string& defval /*= "" */ ) { lua_getfield( L->L, -1, element.c_str() ); string result( ( lua_type( L->L, -1 ) == LUA_TSTRING ) ? lua_tostring( L->L, -1 ) : defval ); lua_pop( L->L, 1 ); return result; } char lua::table_guard::get_char( const string& element, char defval /*= "" */ ) { lua_getfield( L->L, -1, element.c_str() ); char result = ( lua_type( L->L, -1 ) == LUA_TSTRING && lua_rawlen( L->L, -1 ) > 0 ) ? lua_tostring( L->L, -1 )[0] : defval; lua_pop( L->L, 1 ); return result; } int lua::table_guard::get_integer( const string& element, int defval /*= "" */ ) { lua_getfield( L->L, -1, element.c_str() ); int result = lua_type( L->L, -1 ) == LUA_TNUMBER ? lua_tointeger( L->L, -1 ) : defval; lua_pop( L->L, 1 ); return result; } double lua::table_guard::get_double( const string& element, double defval /*= "" */ ) { lua_getfield( L->L, -1, element.c_str() ); double result = lua_type( L->L, -1 ) == LUA_TNUMBER ? lua_tonumber( L->L, -1 ) : defval; lua_pop( L->L, 1 ); return result; } bool lua::table_guard::get_boolean( const string& element, bool defval /*= "" */ ) { lua_getfield( L->L, -1, element.c_str() ); bool result = lua_type( L->L, -1 ) == LUA_TBOOLEAN ? lua_toboolean( L->L, -1 ) != 0 : defval; lua_pop( L->L, 1 ); return result; } void lua::state::log_stack() { int top = lua_gettop(L); NV_LOG( LOG_DEBUG, "Stack dump (" << top << ")"); for ( int i = 0; i < top; ++i ) { NV_LOG( LOG_DEBUG, "#" << i+1 << " - " << lua_typename(L, lua_type(L, i+1) ) << " = " << nlua_typecontent(L, i+1) ); } } lua_State* lua::state::get_raw() { return L; } lua::reference lua::state::register_object( object * o ) { if (!o) return ref_none; type_database *db = o->get_root()->get_type_database(); if (!db) return ref_none; type_entry* t = db->get_type(typeid(o)); if (!t) return ref_none; stack_guard guard( this ); lua_getglobal( L, t->name.c_str() ); if ( lua_isnil( L, -1 ) ) { NV_THROW( runtime_error, std::string( t->name ) + " type not registered!" ); } deep_pointer_copy( -1, o ); return luaL_ref( L, LUA_REGISTRYINDEX ); } void lua::state::unregister_object( object * o ) { if (!o) return; stack_guard guard( this ); lua_rawgeti( L, LUA_REGISTRYINDEX, o->get_lua_index() ); lua_pushstring( L, "__ptr" ); lua_pushboolean( L, false ); lua_rawset( L, -3 ); lua_pop( L, 1 ); luaL_unref( L, LUA_REGISTRYINDEX, o->get_lua_index() ); } void lua::state::deep_pointer_copy( int index, void* obj ) { index = lua_absindex( L, index ); lua_newtable( L ); lua_pushnil( L ); bool has_functions = false; bool has_metatable = false; while ( lua_next( L, index ) != 0 ) { if ( lua_isfunction( L, -1 ) ) has_functions = true; else if ( lua_istable( L, -1 ) ) { deep_pointer_copy( -1, obj ); lua_insert( L, -2 ); lua_pop( L, 1 ); } lua_pushvalue( L, -2 ); lua_insert( L, -2 ); lua_settable( L, -4 ); } if ( lua_getmetatable( L, -2 ) ) { lua_setmetatable( L, -2 ); has_metatable = true; } if ( has_functions || has_metatable ) { lua_pushstring( L, "__ptr" ); lua_pushlightuserdata( L, obj ); lua_rawset( L, -3 ); } }