@ -10,17 +10,127 @@
/** @file squirrel.cpp the implementation of the Squirrel class. It handles all Squirrel-stuff and gives a nice API back to work with. */
# include <stdarg.h>
# include <map>
# include "../stdafx.h"
# include "../debug.h"
# include "squirrel_std.hpp"
# include "../fileio_func.h"
# include "../string_func.h"
# include "script_fatalerror.hpp"
# include "../settings_type.h"
# include <sqstdaux.h>
# include <../squirrel/sqpcheader.h>
# include <../squirrel/sqvm.h>
# include "../core/alloc_func.hpp"
# include "../safeguards.h"
/*
* If changing the call paths into the scripting engine , define this symbol to enable full debugging of allocations .
* This lets you track whether the allocator context is being switched correctly in all call paths .
# define SCRIPT_DEBUG_ALLOCATIONS
*/
struct ScriptAllocator {
size_t allocated_size ; ///< Sum of allocated data size
size_t allocation_limit ; ///< Maximum this allocator may use before allocations fail
static const size_t SAFE_LIMIT = 0x8000000 ; ///< 128 MiB, a safe choice for almost any situation
# ifdef SCRIPT_DEBUG_ALLOCATIONS
std : : map < void * , size_t > allocations ;
# endif
void CheckLimit ( ) const
{
if ( this - > allocated_size > this - > allocation_limit ) throw Script_FatalError ( " Maximum memory allocation exceeded " ) ;
}
void * Malloc ( SQUnsignedInteger size )
{
void * p = MallocT < char > ( size ) ;
this - > allocated_size + = size ;
# ifdef SCRIPT_DEBUG_ALLOCATIONS
assert ( p ! = nullptr ) ;
assert ( this - > allocations . find ( p ) = = this - > allocations . end ( ) ) ;
this - > allocations [ p ] = size ;
# endif
return p ;
}
void * Realloc ( void * p , SQUnsignedInteger oldsize , SQUnsignedInteger size )
{
if ( p = = nullptr ) {
return this - > Malloc ( size ) ;
}
if ( size = = 0 ) {
this - > Free ( p , oldsize ) ;
return nullptr ;
}
# ifdef SCRIPT_DEBUG_ALLOCATIONS
assert ( this - > allocations [ p ] = = oldsize ) ;
this - > allocations . erase ( p ) ;
# endif
void * new_p = ReallocT < char > ( static_cast < char * > ( p ) , size ) ;
this - > allocated_size - = oldsize ;
this - > allocated_size + = size ;
# ifdef SCRIPT_DEBUG_ALLOCATIONS
assert ( new_p ! = nullptr ) ;
assert ( this - > allocations . find ( p ) = = this - > allocations . end ( ) ) ;
this - > allocations [ new_p ] = size ;
# endif
return new_p ;
}
void Free ( void * p , SQUnsignedInteger size )
{
if ( p = = nullptr ) return ;
free ( p ) ;
# ifdef SCRIPT_DEBUG_ALLOCATIONS
assert ( this - > allocations . at ( p ) = = size ) ;
this - > allocations . erase ( p ) ;
# endif
}
ScriptAllocator ( )
{
this - > allocated_size = 0 ;
this - > allocation_limit = static_cast < size_t > ( _settings_game . script . script_max_memory_megabytes ) < < 20 ;
if ( this - > allocation_limit = = 0 ) this - > allocation_limit = SAFE_LIMIT ; // in case the setting is somehow zero
}
~ ScriptAllocator ( )
{
# ifdef SCRIPT_DEBUG_ALLOCATIONS
assert ( this - > allocations . size ( ) = = 0 ) ;
# endif
}
} ;
ScriptAllocator * _squirrel_allocator = nullptr ;
/* See 3rdparty/squirrel/squirrel/sqmem.cpp for the default allocator implementation, which this overrides */
# ifndef SQUIRREL_DEFAULT_ALLOCATOR
void * sq_vm_malloc ( SQUnsignedInteger size ) { return _squirrel_allocator - > Malloc ( size ) ; }
void * sq_vm_realloc ( void * p , SQUnsignedInteger oldsize , SQUnsignedInteger size ) { return _squirrel_allocator - > Realloc ( p , oldsize , size ) ; }
void sq_vm_free ( void * p , SQUnsignedInteger size ) { _squirrel_allocator - > Free ( p , size ) ; }
# endif
size_t Squirrel : : GetAllocatedMemory ( ) const noexcept
{
assert ( this - > allocator ! = nullptr ) ;
return this - > allocator - > allocated_size ;
}
void Squirrel : : CompileError ( HSQUIRRELVM vm , const SQChar * desc , const SQChar * source , SQInteger line , SQInteger column )
{
SQChar buf [ 1024 ] ;
@ -115,6 +225,8 @@ void Squirrel::PrintFunc(HSQUIRRELVM vm, const SQChar *s, ...)
void Squirrel : : AddMethod ( const char * method_name , SQFUNCTION proc , uint nparam , const char * params , void * userdata , int size )
{
ScriptAllocatorScope alloc_scope ( this ) ;
sq_pushstring ( this - > vm , method_name , - 1 ) ;
if ( size ! = 0 ) {
@ -130,6 +242,8 @@ void Squirrel::AddMethod(const char *method_name, SQFUNCTION proc, uint nparam,
void Squirrel : : AddConst ( const char * var_name , int value )
{
ScriptAllocatorScope alloc_scope ( this ) ;
sq_pushstring ( this - > vm , var_name , - 1 ) ;
sq_pushinteger ( this - > vm , value ) ;
sq_newslot ( this - > vm , - 3 , SQTrue ) ;
@ -137,6 +251,8 @@ void Squirrel::AddConst(const char *var_name, int value)
void Squirrel : : AddConst ( const char * var_name , bool value )
{
ScriptAllocatorScope alloc_scope ( this ) ;
sq_pushstring ( this - > vm , var_name , - 1 ) ;
sq_pushbool ( this - > vm , value ) ;
sq_newslot ( this - > vm , - 3 , SQTrue ) ;
@ -144,6 +260,8 @@ void Squirrel::AddConst(const char *var_name, bool value)
void Squirrel : : AddClassBegin ( const char * class_name )
{
ScriptAllocatorScope alloc_scope ( this ) ;
sq_pushroottable ( this - > vm ) ;
sq_pushstring ( this - > vm , class_name , - 1 ) ;
sq_newclass ( this - > vm , SQFalse ) ;
@ -151,6 +269,8 @@ void Squirrel::AddClassBegin(const char *class_name)
void Squirrel : : AddClassBegin ( const char * class_name , const char * parent_class )
{
ScriptAllocatorScope alloc_scope ( this ) ;
sq_pushroottable ( this - > vm ) ;
sq_pushstring ( this - > vm , class_name , - 1 ) ;
sq_pushstring ( this - > vm , parent_class , - 1 ) ;
@ -164,6 +284,8 @@ void Squirrel::AddClassBegin(const char *class_name, const char *parent_class)
void Squirrel : : AddClassEnd ( )
{
ScriptAllocatorScope alloc_scope ( this ) ;
sq_newslot ( vm , - 3 , SQFalse ) ;
sq_pop ( vm , 1 ) ;
}
@ -171,6 +293,8 @@ void Squirrel::AddClassEnd()
bool Squirrel : : MethodExists ( HSQOBJECT instance , const char * method_name )
{
assert ( ! this - > crashed ) ;
ScriptAllocatorScope alloc_scope ( this ) ;
int top = sq_gettop ( this - > vm ) ;
/* Go to the instance-root */
sq_pushobject ( this - > vm , instance ) ;
@ -187,6 +311,8 @@ bool Squirrel::MethodExists(HSQOBJECT instance, const char *method_name)
bool Squirrel : : Resume ( int suspend )
{
assert ( ! this - > crashed ) ;
ScriptAllocatorScope alloc_scope ( this ) ;
/* Did we use more operations than we should have in the
* previous tick ? If so , subtract that from the current run . */
if ( this - > overdrawn_ops > 0 & & suspend > 0 ) {
@ -200,23 +326,29 @@ bool Squirrel::Resume(int suspend)
this - > crashed = ! sq_resumecatch ( this - > vm , suspend ) ;
this - > overdrawn_ops = - this - > vm - > _ops_till_suspend ;
this - > allocator - > CheckLimit ( ) ;
return this - > vm - > _suspended ! = 0 ;
}
void Squirrel : : ResumeError ( )
{
assert ( ! this - > crashed ) ;
ScriptAllocatorScope alloc_scope ( this ) ;
sq_resumeerror ( this - > vm ) ;
}
void Squirrel : : CollectGarbage ( )
{
ScriptAllocatorScope alloc_scope ( this ) ;
sq_collectgarbage ( this - > vm ) ;
}
bool Squirrel : : CallMethod ( HSQOBJECT instance , const char * method_name , HSQOBJECT * ret , int suspend )
{
assert ( ! this - > crashed ) ;
ScriptAllocatorScope alloc_scope ( this ) ;
this - > allocator - > CheckLimit ( ) ;
/* Store the stack-location for the return value. We need to
* restore this after saving or the stack will be corrupted
* if we ' re in the middle of a DoCommand . */
@ -325,17 +457,20 @@ bool Squirrel::CallBoolMethod(HSQOBJECT instance, const char *method_name, bool
bool Squirrel : : CreateClassInstance ( const char * class_name , void * real_instance , HSQOBJECT * instance )
{
ScriptAllocatorScope alloc_scope ( this ) ;
return Squirrel : : CreateClassInstanceVM ( this - > vm , class_name , real_instance , instance , nullptr ) ;
}
Squirrel : : Squirrel ( const char * APIName ) :
APIName ( APIName )
APIName ( APIName ) , allocator ( new ScriptAllocator ( ) )
{
this - > Initialize ( ) ;
}
void Squirrel : : Initialize ( )
{
ScriptAllocatorScope alloc_scope ( this ) ;
this - > global_pointer = nullptr ;
this - > print_func = nullptr ;
this - > crashed = false ;
@ -432,6 +567,8 @@ static SQInteger _io_file_read(SQUserPointer file, SQUserPointer buf, SQInteger
SQRESULT Squirrel : : LoadFile ( HSQUIRRELVM vm , const char * filename , SQBool printerror )
{
ScriptAllocatorScope alloc_scope ( this ) ;
FILE * file ;
size_t size ;
if ( strncmp ( this - > GetAPIName ( ) , " AI " , 2 ) = = 0 ) {
@ -517,6 +654,8 @@ SQRESULT Squirrel::LoadFile(HSQUIRRELVM vm, const char *filename, SQBool printer
bool Squirrel : : LoadScript ( HSQUIRRELVM vm , const char * script , bool in_root )
{
ScriptAllocatorScope alloc_scope ( this ) ;
/* Make sure we are always in the root-table */
if ( in_root ) sq_pushroottable ( vm ) ;
@ -549,6 +688,8 @@ Squirrel::~Squirrel()
void Squirrel : : Uninitialize ( )
{
ScriptAllocatorScope alloc_scope ( this ) ;
/* Clean up the stuff */
sq_pop ( this - > vm , 1 ) ;
sq_close ( this - > vm ) ;
@ -562,6 +703,8 @@ void Squirrel::Reset()
void Squirrel : : InsertResult ( bool result )
{
ScriptAllocatorScope alloc_scope ( this ) ;
sq_pushbool ( this - > vm , result ) ;
if ( this - > IsSuspended ( ) ) { // Called before resuming a suspended script?
vm - > GetAt ( vm - > _stackbase + vm - > _suspended_target ) = vm - > GetUp ( - 1 ) ;
@ -571,6 +714,8 @@ void Squirrel::InsertResult(bool result)
void Squirrel : : InsertResult ( int result )
{
ScriptAllocatorScope alloc_scope ( this ) ;
sq_pushinteger ( this - > vm , result ) ;
if ( this - > IsSuspended ( ) ) { // Called before resuming a suspended script?
vm - > GetAt ( vm - > _stackbase + vm - > _suspended_target ) = vm - > GetUp ( - 1 ) ;
@ -600,6 +745,7 @@ void Squirrel::CrashOccurred()
bool Squirrel : : CanSuspend ( )
{
ScriptAllocatorScope alloc_scope ( this ) ;
return sq_can_suspend ( this - > vm ) ;
}