@ -52,21 +52,24 @@ namespace LuaUtil
" tonumber " , " tostring " , " type " , " unpack " , " xpcall " , " rawequal " , " rawget " , " rawset " , " setmetatable " } ;
" tonumber " , " tostring " , " type " , " unpack " , " xpcall " , " rawequal " , " rawget " , " rawset " , " setmetatable " } ;
static const std : : string safePackages [ ] = { " coroutine " , " math " , " string " , " table " } ;
static const std : : string safePackages [ ] = { " coroutine " , " math " , " string " , " table " } ;
static constexpr int64_t countHookStep = 2000 ;
static constexpr int64_t countHookStep = 1000 ;
bool LuaState : : sProfilerEnabled = true ;
void LuaState : : countHook ( lua_State * L , lua_Debug * ar )
void LuaState : : countHook ( lua_State * L , lua_Debug * ar )
{
{
LuaState * THIS ;
LuaState * self ;
( void ) lua_getallocf ( L , reinterpret_cast < void * * > ( & THIS ) ) ;
( void ) lua_getallocf ( L , reinterpret_cast < void * * > ( & self ) ) ;
if ( ! THIS - > mActiveScriptId . mContainer )
if ( self - > mActiveScriptIdStack . empty ( ) )
return ;
return ;
THIS - > mActiveScriptId . mContainer - > addCPUusage ( THIS - > mActiveScriptId . mIndex , countHookStep ) ;
const ScriptId & activeScript = self - > mActiveScriptIdStack . back ( ) ;
THIS - > mCurrentCallInstructionCounter + = countHookStep ;
activeScript . mContainer - > addInstructionCount ( activeScript . mIndex , countHookStep ) ;
if ( THIS - > mSettings . mInstructionLimit > 0
self - > mWatchdogInstructionCounter + = countHookStep ;
& & THIS - > mCurrentCallInstructionCounter > THIS - > mSettings . mInstructionLimit )
if ( self - > mSettings . mInstructionLimit > 0
& & self - > mWatchdogInstructionCounter > self - > mSettings . mInstructionLimit )
{
{
lua_pushstring ( L ,
lua_pushstring ( L ,
" Lua CPU usage exceeded, probably an infinite loop in a script. "
" Lua instruction count exceeded, probably an infinite loop in a script. "
" To change the limit set \" [Lua] instruction limit per call \" in settings.cfg " ) ;
" To change the limit set \" [Lua] instruction limit per call \" in settings.cfg " ) ;
lua_error ( L ) ;
lua_error ( L ) ;
}
}
@ -74,9 +77,9 @@ namespace LuaUtil
void * LuaState : : trackingAllocator ( void * ud , void * ptr , size_t osize , size_t nsize )
void * LuaState : : trackingAllocator ( void * ud , void * ptr , size_t osize , size_t nsize )
{
{
LuaState * THIS = static_cast < LuaState * > ( ud ) ;
LuaState * self = static_cast < LuaState * > ( ud ) ;
const uint64_t smallAllocSize = THIS - > mSettings . mSmallAllocMaxSize ;
const uint64_t smallAllocSize = self - > mSettings . mSmallAllocMaxSize ;
const uint64_t memoryLimit = THIS - > mSettings . mMemoryLimit ;
const uint64_t memoryLimit = self - > mSettings . mMemoryLimit ;
if ( ! ptr )
if ( ! ptr )
osize = 0 ;
osize = 0 ;
@ -90,14 +93,14 @@ namespace LuaUtil
else
else
bigAllocDelta + = nsize ;
bigAllocDelta + = nsize ;
if ( bigAllocDelta > 0 & & memoryLimit > 0 & & THIS - > mTotalMemoryUsage + nsize - osize > memoryLimit )
if ( bigAllocDelta > 0 & & memoryLimit > 0 & & self - > mTotalMemoryUsage + nsize - osize > memoryLimit )
{
{
Log ( Debug : : Error ) < < " Lua realloc " < < osize < < " -> " < < nsize
Log ( Debug : : Error ) < < " Lua realloc " < < osize < < " -> " < < nsize
< < " is blocked because Lua memory limit (configurable in settings.cfg) is exceeded " ;
< < " is blocked because Lua memory limit (configurable in settings.cfg) is exceeded " ;
return nullptr ;
return nullptr ;
}
}
THIS - > mTotalMemoryUsage + = smallAllocDelta + bigAllocDelta ;
self - > mTotalMemoryUsage + = smallAllocDelta + bigAllocDelta ;
THIS - > mSmallAllocMemoryUsage + = smallAllocDelta ;
self - > mSmallAllocMemoryUsage + = smallAllocDelta ;
void * newPtr = nullptr ;
void * newPtr = nullptr ;
if ( nsize = = 0 )
if ( nsize = = 0 )
@ -107,59 +110,83 @@ namespace LuaUtil
if ( bigAllocDelta ! = 0 )
if ( bigAllocDelta ! = 0 )
{
{
auto it = osize > smallAllocSize ? THIS - > mBigAllocOwners . find ( ptr ) : THIS - > mBigAllocOwners . end ( ) ;
auto it = osize > smallAllocSize ? self - > mBigAllocOwners . find ( ptr ) : self - > mBigAllocOwners . end ( ) ;
ScriptId id ;
ScriptId id ;
if ( it ! = THIS - > mBigAllocOwners . end ( ) )
if ( it ! = self - > mBigAllocOwners . end ( ) )
{
{
if ( it - > second . mContainer )
if ( it - > second . mContainer )
id = ScriptId { * it - > second . mContainer , it - > second . mScriptIndex } ;
id = ScriptId { * it - > second . mContainer , it - > second . mScriptIndex } ;
if ( ptr ! = newPtr | | nsize < = smallAllocSize )
if ( ptr ! = newPtr | | nsize < = smallAllocSize )
THIS - > mBigAllocOwners . erase ( it ) ;
self - > mBigAllocOwners . erase ( it ) ;
}
}
else if ( bigAllocDelta > 0 )
else if ( bigAllocDelta > 0 )
{
{
id = THIS - > mActiveScriptId ;
if ( ! self - > mActiveScriptIdStack . empty ( ) )
id = self - > mActiveScriptIdStack . back ( ) ;
bigAllocDelta = nsize ;
bigAllocDelta = nsize ;
}
}
if ( id . mContainer )
if ( id . mContainer )
{
{
if ( static_cast < size_t > ( id . mIndex ) > = THIS - > mMemoryUsage . size ( ) )
if ( static_cast < size_t > ( id . mIndex ) > = self - > mMemoryUsage . size ( ) )
THIS - > mMemoryUsage . resize ( id . mIndex + 1 ) ;
self - > mMemoryUsage . resize ( id . mIndex + 1 ) ;
THIS - > mMemoryUsage [ id . mIndex ] + = bigAllocDelta ;
self - > mMemoryUsage [ id . mIndex ] + = bigAllocDelta ;
id . mContainer - > addMemoryUsage ( id . mIndex , bigAllocDelta ) ;
id . mContainer - > addMemoryUsage ( id . mIndex , bigAllocDelta ) ;
if ( newPtr & & nsize > smallAllocSize )
if ( newPtr & & nsize > smallAllocSize )
THIS - > mBigAllocOwners . emplace ( newPtr , AllocOwner { id . mContainer - > mThis , id . mIndex } ) ;
self - > mBigAllocOwners . emplace ( newPtr , AllocOwner { id . mContainer - > mThis , id . mIndex } ) ;
}
}
}
}
return newPtr ;
return newPtr ;
}
}
lua_State * LuaState : : createLuaRuntime ( LuaState * luaState )
{
if ( sProfilerEnabled )
{
Log ( Debug : : Info ) < < " Initializing LuaUtil::LuaState with profiler " ;
lua_State * L = lua_newstate ( & trackingAllocator , luaState ) ;
if ( L )
return L ;
else
{
sProfilerEnabled = false ;
Log ( Debug : : Error )
< < " Failed to initialize LuaUtil::LuaState with custom allocator; disabling Lua profiler " ;
}
}
Log ( Debug : : Info ) < < " Initializing LuaUtil::LuaState without profiler " ;
lua_State * L = luaL_newstate ( ) ;
if ( ! L )
throw std : : runtime_error ( " Can't create Lua runtime " ) ;
return L ;
}
LuaState : : LuaState ( const VFS : : Manager * vfs , const ScriptsConfiguration * conf , const LuaStateSettings & settings )
LuaState : : LuaState ( const VFS : : Manager * vfs , const ScriptsConfiguration * conf , const LuaStateSettings & settings )
: mSettings ( settings )
: mSettings ( settings )
, mLua ( sol : : default_at_panic , & trackingAllocator , this )
, mLuaHolder ( createLuaRuntime ( this ) )
, mSol ( mLuaHolder . get ( ) )
, mConf ( conf )
, mConf ( conf )
, mVFS ( vfs )
, mVFS ( vfs )
{
{
lua_sethook ( mLua . lua_state ( ) , & countHook , LUA_MASKCOUNT , countHookStep ) ;
if ( sProfilerEnabled )
Log ( Debug : : Verbose ) < < " Initializing LuaUtil::LuaState " ;
lua_sethook ( mLuaHolder . get ( ) , & countHook , LUA_MASKCOUNT , countHookStep ) ;
mLua . open_libraries ( sol : : lib : : base , sol : : lib : : coroutine , sol : : lib : : math , sol : : lib : : bit32 , sol : : lib : : string ,
m Sol . open_libraries ( sol : : lib : : base , sol : : lib : : coroutine , sol : : lib : : math , sol : : lib : : bit32 , sol : : lib : : string ,
sol : : lib : : table , sol : : lib : : os , sol : : lib : : debug ) ;
sol : : lib : : table , sol : : lib : : os , sol : : lib : : debug ) ;
mLua [ " math " ] [ " randomseed " ] ( static_cast < unsigned > ( std : : time ( nullptr ) ) ) ;
m Sol [ " math " ] [ " randomseed " ] ( static_cast < unsigned > ( std : : time ( nullptr ) ) ) ;
mLua [ " math " ] [ " randomseed " ] = [ ] { } ;
m Sol [ " math " ] [ " randomseed " ] = [ ] { } ;
mLua [ " writeToLog " ] = [ ] ( std : : string_view s ) { Log ( Debug : : Level : : Info ) < < s ; } ;
m Sol [ " writeToLog " ] = [ ] ( std : : string_view s ) { Log ( Debug : : Level : : Info ) < < s ; } ;
// Some fixes for compatibility between different Lua versions
// Some fixes for compatibility between different Lua versions
if ( mLua [ " unpack " ] = = sol : : nil )
if ( m Sol [ " unpack " ] = = sol : : nil )
mLua [ " unpack " ] = mLua [ " table " ] [ " unpack " ] ;
m Sol[ " unpack " ] = mSol [ " table " ] [ " unpack " ] ;
else if ( mLua [ " table " ] [ " unpack " ] = = sol : : nil )
else if ( m Sol [ " table " ] [ " unpack " ] = = sol : : nil )
mLua [ " table " ] [ " unpack " ] = mLua [ " unpack " ] ;
m Sol [ " table " ] [ " unpack " ] = m Sol [ " unpack " ] ;
if ( LUA_VERSION_NUM < = 501 )
if ( LUA_VERSION_NUM < = 501 )
{
{
m Lua . script ( R " (
m Sol . script ( R " (
local _pairs = pairs
local _pairs = pairs
local _ipairs = ipairs
local _ipairs = ipairs
pairs = function ( v ) return ( rawget ( getmetatable ( v ) or { } , ' __pairs ' ) or _pairs ) ( v ) end
pairs = function ( v ) return ( rawget ( getmetatable ( v ) or { } , ' __pairs ' ) or _pairs ) ( v ) end
@ -167,7 +194,7 @@ namespace LuaUtil
) " );
) " );
}
}
m Lua . script ( R " (
m Sol . script ( R " (
local printToLog = function ( . . . )
local printToLog = function ( . . . )
local strs = { }
local strs = { }
for i = 1 , select ( ' # ' , . . . ) do
for i = 1 , select ( ' # ' , . . . ) do
@ -212,31 +239,24 @@ namespace LuaUtil
end
end
) " );
) " );
mSandboxEnv = sol : : table ( m Lua , sol : : create ) ;
mSandboxEnv = sol : : table ( m Sol , sol : : create ) ;
mSandboxEnv [ " _VERSION " ] = m Lua [ " _VERSION " ] ;
mSandboxEnv [ " _VERSION " ] = m Sol [ " _VERSION " ] ;
for ( const std : : string & s : safeFunctions )
for ( const std : : string & s : safeFunctions )
{
{
if ( m Lua [ s ] = = sol : : nil )
if ( m Sol [ s ] = = sol : : nil )
throw std : : logic_error ( " Lua function not found: " + s ) ;
throw std : : logic_error ( " Lua function not found: " + s ) ;
mSandboxEnv [ s ] = m Lua [ s ] ;
mSandboxEnv [ s ] = m Sol [ s ] ;
}
}
for ( const std : : string & s : safePackages )
for ( const std : : string & s : safePackages )
{
{
if ( m Lua [ s ] = = sol : : nil )
if ( m Sol [ s ] = = sol : : nil )
throw std : : logic_error ( " Lua package not found: " + s ) ;
throw std : : logic_error ( " Lua package not found: " + s ) ;
mCommonPackages [ s ] = mSandboxEnv [ s ] = makeReadOnly ( m Lua [ s ] ) ;
mCommonPackages [ s ] = mSandboxEnv [ s ] = makeReadOnly ( m Sol [ s ] ) ;
}
}
mSandboxEnv [ " getmetatable " ] = m Lua [ " getSafeMetatable " ] ;
mSandboxEnv [ " getmetatable " ] = m Sol [ " getSafeMetatable " ] ;
mCommonPackages [ " os " ] = mSandboxEnv [ " os " ]
mCommonPackages [ " os " ] = mSandboxEnv [ " os " ]
= makeReadOnly ( tableFromPairs < std : : string_view , sol : : function > ( { { " date " , mLua [ " os " ] [ " date " ] } ,
= makeReadOnly ( tableFromPairs < std : : string_view , sol : : function > ( { { " date " , mSol [ " os " ] [ " date " ] } ,
{ " difftime " , mLua [ " os " ] [ " difftime " ] } , { " time " , mLua [ " os " ] [ " time " ] } } ) ) ;
{ " difftime " , mSol [ " os " ] [ " difftime " ] } , { " time " , mSol [ " os " ] [ " time " ] } } ) ) ;
}
LuaState : : ~ LuaState ( )
{
// Should be cleaned before destructing mLua.
mCommonPackages . clear ( ) ;
mSandboxEnv = sol : : nil ;
}
}
sol : : table makeReadOnly ( const sol : : table & table , bool strictIndex )
sol : : table makeReadOnly ( const sol : : table & table , bool strictIndex )
@ -280,9 +300,9 @@ namespace LuaUtil
{
{
sol : : protected_function script = loadScriptAndCache ( path ) ;
sol : : protected_function script = loadScriptAndCache ( path ) ;
sol : : environment env ( m Lua , sol : : create , mSandboxEnv ) ;
sol : : environment env ( m Sol , sol : : create , mSandboxEnv ) ;
std : : string envName = namePrefix + " [ " + path + " ]: " ;
std : : string envName = namePrefix + " [ " + path + " ]: " ;
env [ " print " ] = m Lua [ " printGen " ] ( envName ) ;
env [ " print " ] = m Sol [ " printGen " ] ( envName ) ;
env [ " _G " ] = env ;
env [ " _G " ] = env ;
env [ sol : : metatable_key ] [ " __metatable " ] = false ;
env [ sol : : metatable_key ] [ " __metatable " ] = false ;
@ -298,18 +318,18 @@ namespace LuaUtil
else
else
return package ;
return package ;
} ;
} ;
sol : : table loaded ( m Lua , sol : : create ) ;
sol : : table loaded ( m Sol , sol : : create ) ;
for ( const auto & [ key , value ] : mCommonPackages )
for ( const auto & [ key , value ] : mCommonPackages )
loaded [ key ] = maybeRunLoader ( value ) ;
loaded [ key ] = maybeRunLoader ( value ) ;
for ( const auto & [ key , value ] : packages )
for ( const auto & [ key , value ] : packages )
loaded [ key ] = maybeRunLoader ( value ) ;
loaded [ key ] = maybeRunLoader ( value ) ;
env [ " require " ] = [ this , env , loaded , hiddenData , scriptId ]( std : : string_view packageName ) mutable {
env [ " require " ] = [ this , env , loaded , hiddenData ]( std : : string_view packageName ) mutable {
sol : : object package = loaded [ packageName ] ;
sol : : object package = loaded [ packageName ] ;
if ( package ! = sol : : nil )
if ( package ! = sol : : nil )
return package ;
return package ;
sol : : protected_function packageLoader = loadScriptAndCache ( packageNameToVfsPath ( packageName , mVFS ) ) ;
sol : : protected_function packageLoader = loadScriptAndCache ( packageNameToVfsPath ( packageName , mVFS ) ) ;
sol : : set_environment ( env , packageLoader ) ;
sol : : set_environment ( env , packageLoader ) ;
package = call ( scriptId, packageLoader, packageName ) ;
package = call ( packageLoader, packageName ) ;
loaded [ packageName ] = package ;
loaded [ packageName ] = package ;
return package ;
return package ;
} ;
} ;
@ -320,8 +340,8 @@ namespace LuaUtil
sol : : environment LuaState : : newInternalLibEnvironment ( )
sol : : environment LuaState : : newInternalLibEnvironment ( )
{
{
sol : : environment env ( m Lua , sol : : create , mSandboxEnv ) ;
sol : : environment env ( m Sol , sol : : create , mSandboxEnv ) ;
sol : : table loaded ( m Lua , sol : : create ) ;
sol : : table loaded ( m Sol , sol : : create ) ;
for ( const std : : string & s : safePackages )
for ( const std : : string & s : safePackages )
loaded [ s ] = static_cast < sol : : object > ( mSandboxEnv [ s ] ) ;
loaded [ s ] = static_cast < sol : : object > ( mSandboxEnv [ s ] ) ;
env [ " require " ] = [ this , loaded , env ] ( const std : : string & module ) mutable {
env [ " require " ] = [ this , loaded , env ] ( const std : : string & module ) mutable {
@ -347,7 +367,7 @@ namespace LuaUtil
{
{
auto iter = mCompiledScripts . find ( path ) ;
auto iter = mCompiledScripts . find ( path ) ;
if ( iter ! = mCompiledScripts . end ( ) )
if ( iter ! = mCompiledScripts . end ( ) )
return m Lua . load ( iter - > second . as_string_view ( ) , path , sol : : load_mode : : binary ) ;
return m Sol . load ( iter - > second . as_string_view ( ) , path , sol : : load_mode : : binary ) ;
sol : : function res = loadFromVFS ( path ) ;
sol : : function res = loadFromVFS ( path ) ;
mCompiledScripts [ path ] = res . dump ( ) ;
mCompiledScripts [ path ] = res . dump ( ) ;
return res ;
return res ;
@ -356,7 +376,7 @@ namespace LuaUtil
sol : : function LuaState : : loadFromVFS ( const std : : string & path )
sol : : function LuaState : : loadFromVFS ( const std : : string & path )
{
{
std : : string fileContent ( std : : istreambuf_iterator < char > ( * mVFS - > get ( path ) ) , { } ) ;
std : : string fileContent ( std : : istreambuf_iterator < char > ( * mVFS - > get ( path ) ) , { } ) ;
sol : : load_result res = m Lua . load ( fileContent , path , sol : : load_mode : : text ) ;
sol : : load_result res = m Sol . load ( fileContent , path , sol : : load_mode : : text ) ;
if ( ! res . valid ( ) )
if ( ! res . valid ( ) )
throw std : : runtime_error ( " Lua error: " + res . get < std : : string > ( ) ) ;
throw std : : runtime_error ( " Lua error: " + res . get < std : : string > ( ) ) ;
return res ;
return res ;
@ -365,7 +385,7 @@ namespace LuaUtil
sol : : function LuaState : : loadInternalLib ( std : : string_view libName )
sol : : function LuaState : : loadInternalLib ( std : : string_view libName )
{
{
const auto path = packageNameToPath ( libName , mLibSearchPaths ) ;
const auto path = packageNameToPath ( libName , mLibSearchPaths ) ;
sol : : load_result res = m Lua . load_file ( Files : : pathToUnicodeString ( path ) , sol : : load_mode : : text ) ;
sol : : load_result res = m Sol . load_file ( Files : : pathToUnicodeString ( path ) , sol : : load_mode : : text ) ;
if ( ! res . valid ( ) )
if ( ! res . valid ( ) )
throw std : : runtime_error ( " Lua error: " + res . get < std : : string > ( ) ) ;
throw std : : runtime_error ( " Lua error: " + res . get < std : : string > ( ) ) ;
return res ;
return res ;