WIP: port alien swarm and extend engine for asw

This commit is contained in:
nillerusr
2023-10-03 20:02:58 +03:00
parent 7d3c0d8b5a
commit 8322130890
617 changed files with 161164 additions and 220 deletions
@@ -0,0 +1,413 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#include "gmConfig.h"
#include "gmArrayLib.h"
#include "gmThread.h"
#include "gmMachine.h"
#include "gmHelpers.h"
// Must be last header
#include "memdbgon.h"
//
//
// Implementation of array binding
//
//
#if GM_ARRAY_LIB
// Statics and globals
gmType GM_ARRAY = GM_NULL;
gmVariable gmUserArray::m_null;
bool gmUserArray::Construct(gmMachine * a_machine, int a_size)
{
m_null.Nullify();
m_array = NULL;
m_size = 0;
return Resize(a_machine, a_size);
}
void gmUserArray::Destruct(gmMachine * a_machine)
{
if(m_array)
{
a_machine->Sys_Free(m_array);
m_array = NULL;
}
m_size = 0;
}
bool gmUserArray::Resize(gmMachine * a_machine, int a_size)
{
if(a_size < 0) a_size = 0;
int copysize = (a_size > m_size) ? m_size : a_size;
gmVariable * array = (gmVariable *) a_machine->Sys_Alloc(sizeof(gmVariable) * a_size);
// copy contents.
if(m_array)
{
memcpy(array, m_array, sizeof(gmVariable) * copysize);
if(a_size > copysize)
{
memset(array + copysize, 0, sizeof(gmVariable) * (a_size - copysize));
}
a_machine->Sys_Free(m_array);
}
else
{
memset(array, 0, sizeof(gmVariable) * a_size);
}
m_array = array;
m_size = a_size;
return true;
}
#if GM_USE_INCGC
bool gmUserArray::Trace(gmMachine * a_machine, gmGarbageCollector* a_gc, const int a_workLeftToGo, int& a_workDone)
{
int i;
for(i = 0; i < m_size; ++i)
{
if(m_array[i].IsReference())
{
gmObject * object = GM_MOBJECT(a_machine, m_array[i].m_value.m_ref);
a_gc->GetNextObject(object);
++a_workDone;
}
}
++a_workDone;
return true;
}
#else //GM_USE_INCGC
void gmUserArray::Mark(gmMachine * a_machine, gmuint32 a_mark)
{
int i;
for(i = 0; i < m_size; ++i)
{
if(m_array[i].IsReference())
{
gmObject * object = GM_MOBJECT(a_machine, m_array[i].m_value.m_ref);
if(object->NeedsMark(a_mark)) object->Mark(a_machine, a_mark);
}
}
}
#endif //GM_USE_INCGC
bool gmUserArray::Shift(int a_shift)
{
if(a_shift < 0) // shift left
{
a_shift = -a_shift;
if(a_shift >= m_size)
{
memset(m_array, 0, m_size);
}
else
{
int size = m_size - a_shift;
memmove(m_array, m_array + a_shift, sizeof(gmVariable) * size);
memset(m_array + size, 0, sizeof(gmVariable) * a_shift);
}
}
else if(a_shift > 0) // shift right
{
if(a_shift >= m_size)
{
memset(m_array, 0, m_size);
}
else
{
int size = m_size - a_shift;
memmove(m_array + a_shift, m_array, sizeof(gmVariable) * size);
memset(m_array, 0, sizeof(gmVariable) * a_shift);
}
}
return true;
}
int gmUserArray::Move(int a_dest, int a_src, int a_size)
{
int start = a_src;
int dest = a_dest;
int size = a_size;
if(start < 0)
{
size += start;
dest -= start;
start = 0;
}
if(dest < 0)
{
size += dest;
start -= dest;
dest = 0;
}
if(size <= 0) return 0;
if(start >= m_size) return 0;
if(dest >= m_size) return 0;
if((dest + size) < 0) return 0;
if(start + size > m_size)
{
size = m_size - start;
}
if(dest + size > m_size)
{
size = m_size - dest;
}
if(size <= 0) return 0;
GM_ASSERT(dest >= 0);
GM_ASSERT(start >= 0);
GM_ASSERT(start + size <= m_size);
GM_ASSERT(dest + size <= m_size);
memmove(m_array + dest, m_array + start, sizeof(gmVariable) * size);
return size;
}
gmUserArray* gmUserArray_Create(gmMachine* a_machine, int a_size)
{
gmUserArray * newArray = (gmUserArray *) a_machine->Sys_Alloc(sizeof(gmUserArray));
newArray->Construct(a_machine, a_size);
return newArray;
}
// functions
static int GM_CDECL gmfArray(gmThread * a_thread) // size
{
GM_INT_PARAM(size, 0, 0);
gmUserArray * array = (gmUserArray *) a_thread->GetMachine()->Sys_Alloc(sizeof(gmUserArray));
array->Construct(a_thread->GetMachine(), size);
a_thread->PushNewUser(array, GM_ARRAY);
return GM_OK;
}
static int GM_CDECL gmfArraySize(gmThread * a_thread) // return size
{
gmUserObject * arrayObject = a_thread->ThisUserObject();
GM_ASSERT(arrayObject->m_userType == GM_ARRAY);
if(arrayObject->m_user)
{
gmUserArray * array = (gmUserArray *) arrayObject->m_user;
a_thread->PushInt(array->Size());
}
return GM_OK;
}
static int GM_CDECL gmfArrayResize(gmThread * a_thread) // size
{
GM_INT_PARAM(size, 0, 0);
gmUserObject * arrayObject = a_thread->ThisUserObject();
GM_ASSERT(arrayObject->m_userType == GM_ARRAY);
if(arrayObject->m_user)
{
gmUserArray * array = (gmUserArray *) arrayObject->m_user;
array->Resize(a_thread->GetMachine(), size);
}
return GM_OK;
}
static int GM_CDECL gmfArrayShift(gmThread * a_thread) // shift
{
GM_CHECK_NUM_PARAMS(1);
GM_CHECK_INT_PARAM(shift, 0);
gmUserObject * arrayObject = a_thread->ThisUserObject();
GM_ASSERT(arrayObject->m_userType == GM_ARRAY);
if(arrayObject->m_user)
{
gmUserArray * array = (gmUserArray *) arrayObject->m_user;
array->Shift(shift);
}
return GM_OK;
}
static int GM_CDECL gmfArrayMove(gmThread * a_thread) // dst, src, size
{
GM_CHECK_NUM_PARAMS(3);
GM_CHECK_INT_PARAM(dst, 0);
GM_CHECK_INT_PARAM(src, 1);
GM_CHECK_INT_PARAM(size, 2);
gmUserObject * arrayObject = a_thread->ThisUserObject();
GM_ASSERT(arrayObject->m_userType == GM_ARRAY);
if(arrayObject->m_user)
{
gmUserArray * array = (gmUserArray *) arrayObject->m_user;
array->Move(dst, src, size);
}
return GM_OK;
}
static void GM_CDECL gmArrayGetInd(gmThread * a_thread, gmVariable * a_operands)
{
gmUserObject * arrayObject = (gmUserObject *) GM_OBJECT(a_operands->m_value.m_ref);
GM_ASSERT(arrayObject->m_userType == GM_ARRAY);
gmUserArray * array = (gmUserArray *) arrayObject->m_user;
if(a_operands[1].m_type == GM_INT)
{
int index = a_operands[1].m_value.m_int;
*a_operands = array->GetAt(index);
return;
}
a_operands->Nullify();
}
static void GM_CDECL gmArraySetInd(gmThread * a_thread, gmVariable * a_operands)
{
gmUserObject * arrayObject = (gmUserObject *) GM_OBJECT(a_operands->m_value.m_ref);
GM_ASSERT(arrayObject->m_userType == GM_ARRAY);
gmUserArray * array = (gmUserArray *) arrayObject->m_user;
if(a_operands[1].m_type == GM_INT)
{
int index = a_operands[1].m_value.m_int;
#if GM_USE_INCGC
//Apply write barrier
gmVariable oldVar = array->GetAt(index);
if(oldVar.IsReference())
{
a_thread->GetMachine()->GetGC()->WriteBarrier((gmObject*)oldVar.m_value.m_ref);
}
#endif //GM_USE_INCGC
array->SetAt(index, a_operands[2]);
}
}
#if GM_USE_INCGC
static void GM_CDECL gmGCDestructArrayUserType(gmMachine * a_machine, gmUserObject* a_object)
{
if(a_object->m_user)
{
gmUserArray * array = (gmUserArray *) a_object->m_user;
array->Destruct(a_machine);
a_machine->Sys_Free(array);
}
a_object->m_user = NULL;
}
static bool GM_CDECL gmGCTraceArrayUserType(gmMachine * a_machine, gmUserObject* a_object, gmGarbageCollector* a_gc, const int a_workLeftToGo, int& a_workDone)
{
if(a_object->m_user)
{
gmUserArray * array = (gmUserArray *) a_object->m_user;
return array->Trace(a_machine, a_gc, a_workLeftToGo, a_workDone);
}
return true;
}
#else //GM_USE_INCGC
static void GM_CDECL gmMarkArrayUserType(gmMachine * a_machine, gmUserObject * a_object, gmuint32 a_mark)
{
if(a_object->m_user)
{
gmUserArray * array = (gmUserArray *) a_object->m_user;
array->Mark(a_machine, a_mark);
}
}
static void GM_CDECL gmGCArrayUserType(gmMachine * a_machine, gmUserObject * a_object, gmuint32 a_mark)
{
if(a_object->m_user)
{
gmUserArray * array = (gmUserArray *) a_object->m_user;
array->Destruct(a_machine);
a_machine->Sys_Free(array);
}
a_object->m_user = NULL;
}
#endif //GM_USE_INCGC
// libs
static gmFunctionEntry s_arrayLib[] =
{
/*gm
\lib gm
*/
/*gm
\function array
\brief array will create a fixed size array object
\param int size optional (0)
\return array
*/
{"array", gmfArray},
};
static gmFunctionEntry s_arrayTypeLib[] =
{
/*gm
\lib array
*/
/*gm
\function Size
\brief Size will return the current size of the fixed array
\return int array size
*/
{"Size", gmfArraySize},
/*gm
\function Resize
\brief Resize will resize the array to a new size
\param int size optional (0)
\return null
*/
{"Resize", gmfArrayResize},
/*gm
\function Shift
\brief Shift will shift slide the array elements by a delta, nulls are shifted in
\param int delta
\return null
*/
{"Shift", gmfArrayShift},
/*gm
\function Move
\brief Move will perform a non destructive move on the array
\param int dst
\param int src
\param int size
\return null
*/
{"Move", gmfArrayMove},
};
void gmBindArrayLib(gmMachine * a_machine)
{
gmUserArray::m_null.Nullify(); //Init static null
a_machine->RegisterLibrary(s_arrayLib, sizeof(s_arrayLib) / sizeof(s_arrayLib[0]));
GM_ARRAY = a_machine->CreateUserType("array");
a_machine->RegisterTypeLibrary(GM_ARRAY, s_arrayTypeLib, sizeof(s_arrayTypeLib) / sizeof(s_arrayTypeLib[0]));
#if GM_USE_INCGC
a_machine->RegisterUserCallbacks(GM_ARRAY, gmGCTraceArrayUserType, gmGCDestructArrayUserType);
#else //GM_USE_INCGC
a_machine->RegisterUserCallbacks(GM_ARRAY, gmMarkArrayUserType, gmGCArrayUserType);
#endif //GM_USE_INCGC
a_machine->RegisterTypeOperator(GM_ARRAY, O_GETIND, NULL, gmArrayGetInd);
a_machine->RegisterTypeOperator(GM_ARRAY, O_SETIND, NULL, gmArraySetInd);
}
#endif // GM_ARRAY_LIB
@@ -0,0 +1,95 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#ifndef _GMARRAYLIB_H_
#define _GMARRAYLIB_H_
#include "gmConfig.h"
#include "gmVariable.h"
// Fwd decls
class gmMachine;
#define GM_ARRAY_LIB 1
#define GM_ARRAY_LIB_GROW_BY 16
#if GM_ARRAY_LIB
extern gmType GM_ARRAY;
void gmBindArrayLib(gmMachine * a_machine);
/*!
\class gmUserArray
*/
class gmUserArray
{
public:
/// \brief Construct()
bool Construct(gmMachine * a_machine, int a_size);
/// \brief Destruct()
void Destruct(gmMachine * a_machine);
#if GM_USE_INCGC
bool Trace(gmMachine * a_machine, gmGarbageCollector* a_gc, const int a_workLeftToGo, int& a_workDone);
#else //GM_USE_INCGC
/// \brief Mark()
void Mark(gmMachine * a_machine, gmuint32 a_mark);
#endif //GM_USE_INCGC
/// \brief GetAt()
GM_FORCEINLINE const gmVariable &GetAt(int a_index)
{
if(a_index >= 0 && a_index < m_size)
{
return m_array[a_index];
}
return m_null;
}
/// \brief SetAt()
GM_FORCEINLINE bool SetAt(int a_index, const gmVariable &a_variable)
{
if(a_index >= 0 && a_index < m_size)
{
m_array[a_index] = a_variable;
return true;
}
return false;
}
/// \brief Size()
GM_FORCEINLINE int Size() const { return m_size; }
/// \brief Resize()
bool Resize(gmMachine * a_machine, int a_size);
/// \brief Shift()
bool Shift(int a_shift);
/// \brief Move()
int Move(int a_dest, int a_src, int a_size);
// data
gmVariable * m_array;
int m_size;
static gmVariable m_null;
};
/// \brief Create a GM_ARRAY. This much be put into a user object.
gmUserArray* gmUserArray_Create(gmMachine* a_machine, int a_size = 0);
#endif // GM_ARRAY_LIB
#endif // _GMARRAYLIB_H_
+17
View File
@@ -0,0 +1,17 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#include "gmConfig.h"
#include "gmCall.h"
// Must be last header
#include "memdbgon.h"
+434
View File
@@ -0,0 +1,434 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#ifndef _GMCALL_H_
#define _GMCALL_H_
#include "gmConfig.h"
#include "gmThread.h"
#include "gmMachine.h"
#undef GetObject //Fix for Win32 where GetObject is #defined
/// \class gmCall
/// \brief A helper class to call script functions from C
/// Warning: Do not store any of the reference type return variables (eg. GM_SRING).
/// As the object may be garbage collected. Instead, copy immediately as necessary.
class gmCall
{
public:
/// \brief Constructor
gmCall()
{
#ifdef GM_DEBUG_BUILD
m_locked = false;
#endif //GM_DEBUG_BUILD
}
/// \brief Begin the call of a global function
/// \param a_machine Virtual machine instance
/// \param a_funcName Name of function
/// \param a_this The 'this' used by the function.
/// \param a_delayExecuteFlag Set true if you want function thread to not execute now.
/// \return true on sucess, false if function was not found.
GM_FORCEINLINE bool BeginGlobalFunction(gmMachine * a_machine, const char * a_funcName,
const gmVariable& a_this = gmVariable::s_null,
bool a_delayExecuteFlag = false)
{
GM_ASSERT(a_machine);
gmStringObject * funcNameStringObj = a_machine->AllocPermanantStringObject(a_funcName); // Slow
return BeginGlobalFunction(a_machine, funcNameStringObj, a_this, a_delayExecuteFlag);
}
/// \brief Begin the call of a global function
/// \param a_machine Virtual machine instance
/// \param a_funcNameStringObj A string object that was found or created earlier, much faster than creating from c string.
/// \param a_this The 'this' used by the function.
/// \param a_delayExecuteFlag Set true if you want function thread to not execute now.
/// \return true on sucess, false if function was not found.
GM_FORCEINLINE bool BeginGlobalFunction(gmMachine * a_machine, gmStringObject * a_funcNameStringObj,
const gmVariable& a_this = gmVariable::s_null,
bool a_delayExecuteFlag = false)
{
GM_ASSERT(a_machine);
GM_ASSERT(a_funcNameStringObj);
gmVariable lookUpVar;
gmVariable foundFunc;
lookUpVar.SetString(a_funcNameStringObj);
foundFunc = a_machine->GetGlobals()->Get(lookUpVar);
if( GM_FUNCTION == foundFunc.m_type ) // Check found variable is a function
{
gmFunctionObject * functionObj = (gmFunctionObject *)foundFunc.m_value.m_ref; //Func Obj from variable
return BeginFunction(a_machine, functionObj, a_this, a_delayExecuteFlag);
}
return false;
}
/// \brief Begin the call of a object function
/// \param a_machine Virtual machine instance
/// \param a_funcName Name of function.
/// \param a_tableObj The table on the object to look up the function.
/// \param a_this The 'this' used by the function.
/// \param a_delayExecuteFlag Set true if you want function thread to not execute now.
/// \return true on sucess, false if function was not found.
GM_FORCEINLINE bool BeginTableFunction(gmMachine * a_machine, const char * a_funcName,
gmTableObject * a_tableObj, const gmVariable& a_this = gmVariable::s_null,
bool a_delayExecuteFlag = false)
{
GM_ASSERT(a_machine);
GM_ASSERT(a_funcName);
gmStringObject * funcNameStringObj = a_machine->AllocPermanantStringObject(a_funcName); // Slow
return BeginTableFunction(a_machine, funcNameStringObj, a_tableObj, a_this, a_delayExecuteFlag);
}
/// \brief Begin the call of a object function
/// \param a_machine Virtual machine instance
/// \param a_funcNameStringObj A string object that was found or created earlier, much faster than creating from c string.
/// \param a_tableObj The table on the object to look up the function.
/// \param a_this The 'this' used by the function.
/// \param a_delayExecuteFlag Set true if you want function thread to not execute now.
/// \return true on sucess, false if function was not found.
GM_FORCEINLINE bool BeginTableFunction(gmMachine * a_machine, gmStringObject * a_funcNameStringObj,
gmTableObject * a_tableObj, const gmVariable& a_this = gmVariable::s_null,
bool a_delayExecuteFlag = false)
{
GM_ASSERT(a_machine);
//GM_ASSERT(a_tableObj);
GM_ASSERT(a_funcNameStringObj);
gmVariable lookUpVar;
gmVariable foundFunc;
if ( !a_tableObj )
{
a_tableObj = a_machine->GetGlobals();
}
lookUpVar.SetString(a_funcNameStringObj);
foundFunc = a_tableObj->Get(lookUpVar);
if( GM_FUNCTION == foundFunc.m_type ) // Check found variable is a function
{
gmFunctionObject * functionObj = (gmFunctionObject *)foundFunc.m_value.m_ref; //Func Obj from variable
return BeginFunction(a_machine, functionObj, a_this, a_delayExecuteFlag);
}
return false;
}
/// \brief Begin the call of a object function
/// \param a_funcObj A function object that was found or created earlier.
/// \param a_tableObj The table on the object to look up the function.
/// \param a_this The 'this' used by the function.
/// \param a_delayExecuteFlag Set true if you want function thread to not execute now.
/// \return true on sucess, false if function was not found.
GM_FORCEINLINE bool BeginFunction(gmMachine * a_machine, gmFunctionObject * a_funcObj,
const gmVariable &a_thisVar = gmVariable::s_null,
bool a_delayExecuteFlag = false)
{
GM_ASSERT(a_machine);
GM_ASSERT(a_funcObj);
#ifdef GM_DEBUG_BUILD
// YOU CANNOT NEST gmCall::Begin
GM_ASSERT(m_locked == false);
m_locked = true;
#endif //GM_DEBUG_BUILD
Reset(a_machine);
if( GM_FUNCTION == a_funcObj->GetType() ) // Check found variable is a function
{
m_thread = m_machine->CreateThread(); // Create thread for func to run on
m_thread->Push(a_thisVar); // this
m_thread->PushFunction(a_funcObj); // function
m_delayExecuteFlag = a_delayExecuteFlag;
return true;
}
#ifdef GM_DEBUG_BUILD
GM_ASSERT(m_locked == true);
m_locked = false;
#endif //GM_DEBUG_BUILD
return false;
}
/// \brief Add a parameter variable
GM_FORCEINLINE void AddParam(const gmVariable& a_var)
{
GM_ASSERT(m_machine);
GM_ASSERT(m_thread);
m_thread->Push(a_var);
++m_paramCount;
}
/// \brief Add a parameter that is null
GM_FORCEINLINE void AddParamNull()
{
GM_ASSERT(m_machine);
GM_ASSERT(m_thread);
m_thread->PushNull();
++m_paramCount;
}
/// \brief Add a parameter that is a integer
GM_FORCEINLINE void AddParamInt(const int a_value)
{
GM_ASSERT(m_machine);
GM_ASSERT(m_thread);
m_thread->PushInt(a_value);
++m_paramCount;
}
/// \brief Add a parameter that is a float
GM_FORCEINLINE void AddParamFloat(const float a_value)
{
GM_ASSERT(m_machine);
GM_ASSERT(m_thread);
m_thread->PushFloat(a_value);
++m_paramCount;
}
/// \brief Add a parameter that is a string
GM_FORCEINLINE void AddParamString(const char * a_value, int a_len = -1)
{
GM_ASSERT(m_machine);
GM_ASSERT(m_thread);
m_thread->PushNewString(a_value, a_len);
++m_paramCount;
}
/// \brief Add a parameter that is a string (faster version since c string does not need lookup)
GM_FORCEINLINE void AddParamString(gmStringObject * a_value)
{
GM_ASSERT(m_machine);
GM_ASSERT(m_thread);
m_thread->PushString(a_value);
++m_paramCount;
}
/// \brief Add a parameter that is a user object. Creates a new user object.
/// \param a_value Pointer to user object data
/// \param a_userType Type of user object beyond GM_USER
GM_FORCEINLINE void AddParamUser(void * a_value, int a_userType)
{
GM_ASSERT(m_machine);
GM_ASSERT(m_thread);
m_thread->PushNewUser(a_value, a_userType);
++m_paramCount;
}
/// \brief Add a parameter that is a user object.
/// \param a_userObj Pushes an existing user object without creating a new one.
GM_FORCEINLINE void AddParamUser(gmUserObject * a_userObj)
{
GM_ASSERT(m_machine);
GM_ASSERT(m_thread);
m_thread->PushUser(a_userObj);
++m_paramCount;
}
/// \brief Add a parameter that is a table object.
/// \param a_tableObj Pushes an existing table object without creating a new one.
GM_FORCEINLINE void AddParamTable(gmTableObject * a_tableObj)
{
GM_ASSERT(m_machine);
GM_ASSERT(m_thread);
m_thread->PushTable(a_tableObj);
++m_paramCount;
}
/// \brief Make the call. If a return value was expected, it will be set in here.
/// \param a_threadId Optional
GM_FORCEINLINE void End(int * a_threadId = NULL)
{
GM_ASSERT(m_machine);
GM_ASSERT(m_thread);
#ifdef GM_DEBUG_BUILD
// CAN ONLY CALL ::End() after a successful ::Begin
GM_ASSERT(m_locked == true);
m_locked = false;
#endif //GM_DEBUG_BUILD
int state = m_thread->PushStackFrame(m_paramCount);
if(state != gmThread::KILLED) // Can be killed immedialy if it was a C function
{
if(m_delayExecuteFlag)
{
state = m_thread->GetState();
}
else
{
state = m_thread->Sys_Execute(&m_returnVar);
}
}
else
{
// Was a C function call, grab return var off top of stack
m_returnVar = *(m_thread->GetTop() - 1);
m_machine->Sys_SwitchState(m_thread, gmThread::KILLED);
}
// If we requested a thread Id
if(a_threadId)
{
if(state != gmThread::KILLED)
{
*a_threadId = m_thread->GetId();
}
else
{
*a_threadId = GM_INVALID_THREAD;
}
}
if(state == gmThread::KILLED)
{
m_returnFlag = true; // Function always returns something, null if not explicit.
m_thread = NULL; // Thread has exited, no need to remember it.
}
}
/// \brief Accesss thread created for function call.
GM_FORCEINLINE gmThread * GetThread() { return m_thread; }
/// \brief Returns reference to 'return' variable. Never fails, but variable may be a 'null' varaible if none was returned.
const gmVariable& GetReturnedVariable() { return m_returnVar; }
/// \brief Returns true if function exited and returned a variable.
bool DidReturnVariable() { return m_returnFlag; }
/// \brief Did function return a null?
/// \return true if function returned null.
bool GetReturnedNull()
{
if(m_returnFlag && (m_returnVar.m_type == GM_NULL))
{
return true;
}
return false;
}
/// \brief Get returned int
/// \return true if function returned an int.
bool GetReturnedInt(int& a_value)
{
if(m_returnFlag && (m_returnVar.m_type == GM_INT))
{
a_value = m_returnVar.m_value.m_int;
return true;
}
return false;
}
/// \brief Get returned float
/// \return true if function returned an float.
bool GetReturnedFloat(float& a_value)
{
if(m_returnFlag && (m_returnVar.m_type == GM_FLOAT))
{
a_value = m_returnVar.m_value.m_float;
return true;
}
return false;
}
/// \brief Get returned string
/// \return true if function returned an string.
bool GetReturnedString(const char *& a_value)
{
if(m_returnFlag && (m_returnVar.m_type == GM_STRING))
{
a_value = ((gmStringObject *)m_machine->GetObject(m_returnVar.m_value.m_ref))->GetString();
return true;
}
return false;
}
/// \brief Get returned user
/// \return true if function returned an user.
bool GetReturnedUser(void *& a_value, int a_userType)
{
if(m_returnFlag && (m_returnVar.m_type == a_userType))
{
a_value = (void *)m_returnVar.m_value.m_ref;
return true;
}
return false;
}
/// \brief Get returned user or null
/// \return true if function returned an user or null.
bool GetReturnedUserOrNull(void *& a_value, int a_userType)
{
if(m_returnFlag)
{
if(m_returnVar.m_type == a_userType)
{
a_value = (void *)m_returnVar.m_value.m_ref;
return true;
}
else if(m_returnVar.m_type == GM_NULL)
{
a_value = (void *)NULL;
return true;
}
}
return false;
}
protected:
gmMachine * m_machine;
gmThread * m_thread;
gmVariable m_returnVar;
int m_paramCount;
bool m_returnFlag;
bool m_delayExecuteFlag;
#ifdef GM_DEBUG_BUILD
bool m_locked;
#endif //GM_DEBUG_BUILD
/// \brief Used internally to clear call information.
GM_FORCEINLINE void Reset(gmMachine * a_machine)
{
GM_ASSERT(a_machine);
m_machine = a_machine;
m_thread = NULL;
m_returnVar.Nullify();
m_returnFlag = false;
m_paramCount = 0;
m_delayExecuteFlag = false;
};
};
#endif // _GMCALL_H_
+215
View File
@@ -0,0 +1,215 @@
//
// gmGCRoot.cpp
//
#include "gmGCRoot.h"
// Must be last header
#include "memdbgon.h"
// Init statics and constants
gmGCRootManager* gmGCRootManager::s_staticInstance;
gmGCRootManager::gmGCRootManager()
{
m_machineHolderSet.Init(0, 4);
}
gmGCRootManager::~gmGCRootManager()
{
DestroyAllMachines();
}
void gmGCRootManager::Init()
{
GM_ASSERT( !s_staticInstance );
if( !s_staticInstance )
{
s_staticInstance = new gmGCRootManager;
}
}
void gmGCRootManager::Destroy()
{
if( s_staticInstance )
{
delete s_staticInstance;
s_staticInstance = NULL;
}
}
gmgcrHolder* gmGCRootManager::FindOrAdd(gmObject* a_object, gmMachine* a_machine)
{
GM_ASSERT(a_object);
GM_ASSERT(a_machine);
if( !a_object || !a_machine )
{
return NULL;
}
MachineHolders* machineSet = FindOrAddMachine(a_machine);
gmgcrHolder* holder = NULL;
if( !machineSet->m_mapPtrToHolder.GetAt(a_object, holder) )
{
// Add new
holder = m_memHolder.Alloc();
holder->Init(a_object, a_machine);
machineSet->m_mapPtrToHolder.SetAt(a_object, holder);
a_machine->AddCPPOwnedGMObject(a_object); // Add to GC roots
}
return holder;
}
void gmGCRootManager::RemoveObject(gmObject* a_object, gmMachine* a_machine)
{
if( a_object && a_machine )
{
a_machine->RemoveCPPOwnedGMObject(a_object); // Remove from GC roots
gmGCRootManager::MachineHolders* machineSet = FindOrAddMachine(a_machine);
machineSet->m_mapPtrToHolder.RemoveAt(a_object);
}
}
void gmGCRootManager::FreeHolder(const gmgcrHolder* a_holder)
{
if( a_holder )
{
m_memHolder.Free( const_cast<gmgcrHolder*>(a_holder) );
}
}
void gmGCRootManager::DestroyMachine(gmMachine* a_machine)
{
int foundIndex = -1;
// Find existing set
for(int mIndex=0; mIndex < m_machineHolderSet.GetSize(); ++mIndex)
{
if( m_machineHolderSet[mIndex].m_machine == a_machine )
{
foundIndex = mIndex;
break;
}
}
if( foundIndex >= 0 )
{
MachineHolders* destroySet = &m_machineHolderSet[foundIndex];
// Cleanup all internal pointers
gmgcrMap<gmObject*, gmgcrHolder*>::Iterator it;
// Iterate carefully as we are deleting while iterating
destroySet->m_mapPtrToHolder.GetFirst(it);
while( !destroySet->m_mapPtrToHolder.IsNull(it) )
{
gmgcrHolder* toDelete = NULL;
if( destroySet->m_mapPtrToHolder.GetValue(it, toDelete) )
{
// NOTE: Delete this found value after incrementing iterator
}
destroySet->m_mapPtrToHolder.GetNext(it);
if( toDelete )
{
toDelete->Destroy();
}
}
// Remove machine set
m_machineHolderSet.RemoveAt(foundIndex);
}
}
gmGCRootManager::MachineHolders* gmGCRootManager::FindOrAddMachine(gmMachine* a_machine)
{
// NOTE: Expect very small number of machines. If this is not so, accelerate search.
GM_ASSERT( a_machine );
// Find existing set
for(int mIndex=0; mIndex < m_machineHolderSet.GetSize(); ++mIndex)
{
if( m_machineHolderSet[mIndex].m_machine == a_machine )
{
return &m_machineHolderSet[mIndex];
}
}
// Create new set
int newIndex = m_machineHolderSet.AddEmpty();
MachineHolders* newSet = &m_machineHolderSet[newIndex];
newSet->m_machine = a_machine;
return newSet;
}
void gmGCRootManager::DestroyAllMachines()
{
while( m_machineHolderSet.GetSize() )
{
DestroyMachine( m_machineHolderSet[m_machineHolderSet.GetLastIndex()].m_machine );
}
}
#if 0
// Test the gmGCRoot system
void TestGMGCRoot()
{
gmGCRootManager::Init(); // Init system
{
gmMachine machine1;
gmGCRoot<gmStringObject> ptr1;
gmStringObject* stringObj1 = machine1.AllocStringObject("hello");
ptr1.Set(stringObj1, &machine1); // Initialize via func
gmStringObject* nat1 = ptr1; // Assign pointer
gmGCRoot<gmStringObject> ptr2;
ptr2 = ptr1;
ptr1 = NULL; // Assign to null
ptr2 = NULL;
//ptr2 = nat1; // ERROR
gmStringObject* stringObj2 = machine1.AllocStringObject("apple");
gmGCRoot<gmStringObject> ptr3(stringObj2, &machine1); // Initialize via constructor
gmGCRoot<gmStringObject> ptr4(stringObj2, &machine1); // Duplicate without copy
gmGCRoot<gmStringObject> ptr5;
{
gmMachine machine2;
gmStringObject* stringObj3 = machine2.AllocStringObject("bannana");
ptr5.Set(stringObj3, &machine2);
gmGCRootManager::Get()->DestroyMachine(&machine2); // Null associated pointers
}
if( ptr5 )
{
int i=1; // Won't get here
}
}
gmGCRootManager::Destroy();
}
#endif
+399
View File
@@ -0,0 +1,399 @@
#ifndef GMGCROOT_H
#define GMGCROOT_H
//
// gmGCRoot.h
//
#include "gmThread.h"
#include "gmGCRootUtil.h"
///////////////////////////////////////////////////////////////////////////////
//
// gmGCRoot
//
// Templated smart pointer style wrapper for gmObject*
// Automatically adds and removed CPP owned gmObject* from gmMachine roots
//
// It works by storing the gmObject* inside a shared reference counted holder
// object. When a gmObject* is first wrapped, a holder object is created to
// store it. Subsequent initializations will share the same holder object. It
// is much more efficient to assign the pointer from one gmGCRoot to another
// than create a new one from the raw gmObject*.
//
//
// To use, you must initialize and destroy the system with:
// gmGCRootManager::Init();
// gmGCRootManager::Destroy();
// When you destruct a gmMachine that was used by the system, call this first:
// gmGCRootManager::Get()->DestroyMachine(myMachine);
// Initialize a pointer by either:
// gmGCRoot<gmStringObject> ptr1(myObject, myMachine);
// gmGCRoot<gmStringObject> ptr2; ptr2.Set(myObject, myMachine);
// Copy and test pointers as you would a C style pointer eg:
// if( ptr1 ) { int type = ptr1->GetType(); }
// ptr2 = ptr1;
// ptr1 = NULL;
//
//
// Example usage:
// gmGCRootManager::Init(); // Call once first eg. App initialization
// gmGCRoot<gmStringObject> ptr1; // A null pointer
// gmGCRoot<gmStringObject> ptr2(myString, myMachine); // A pointer
// gmStringObject* nat1 = ptr2; // Cast to native
// gmGCRoot<gmStringObject> ptr3 = ptr2; // Assign from other (Fast copy of shared pointer)
// ptr2 = NULL; // Assign to null
// ptr2 = nat1; // COMPILE ERROR! can't assign without gmMachine
// gmGCRootManager::Get()->DestroyMachine(myMachine); // Call before destructing a gmMachine
// gmGCRootManager::Destroy(); // Call once before App exit
//
///////////////////////////////////////////////////////////////////////////////
// Fwd decls
class gmGCRootManager;
class gmgcrHolder;
/// Bass class for objects reference counted types
class gmgcrRefCount
{
public:
/// Construct, starts reference count at 0 (Must be wrapped in smart ptr to increment and later release)
gmgcrRefCount() { m_referenceCount = 0; }
/// Destructor
virtual ~gmgcrRefCount() { }
/// Get reference count
int GetReferenceCount() const { return m_referenceCount; }
/// Increment reference count
void AddReference() const { ++m_referenceCount; }
/// Decrement reference count
int ReleaseReference() const
{
GM_ASSERT( m_referenceCount > 0 );
if( --m_referenceCount == 0 )
{
DeleteThis();
return true;
}
return false;
}
/// Delete this object. May override if this is not desired behavior.
/// NOTE: This function is 'const' so override with const also!
virtual void DeleteThis() const { delete this; }
private:
mutable int m_referenceCount; ///< Reference counter
};
/// Manager for gm gc roots
class gmGCRootManager
{
public:
/// Access singleton
static gmGCRootManager* Get() { return s_staticInstance; }
/// Initialize gm gcroot manager
static void Init();
/// Destroy gm gcroot manager
static void Destroy();
~gmGCRootManager();
// Find or Add shard holder for gmObject
gmgcrHolder* FindOrAdd(gmObject* a_object, gmMachine* a_machine);
// Free holder memory only
void FreeHolder(const gmgcrHolder* a_holder);
// Dissassociate holder contents
void RemoveObject(gmObject* a_object, gmMachine* a_machine);
/// Call before destroying a gmMachine so that any associated objects can be disassociated and have pointers nullified.
void DestroyMachine(gmMachine* a_machine);
protected:
// Store gmObjects per machine
class MachineHolders
{
public:
gmMachine* m_machine;
//gmgcrMap m_mapPtrToHolder;
gmgcrMap<gmObject*, gmgcrHolder*> m_mapPtrToHolder;
};
static gmGCRootManager* s_staticInstance; // Singleton
gmgcrArray< MachineHolders > m_machineHolderSet; // Holders per machine
gmgcrMemFixed m_memHolder; // Holder memory
// Non public constructor to prevent multiple instances
gmGCRootManager();
// Find or add machine
MachineHolders* FindOrAddMachine(gmMachine* a_machine);
// Destroy all machine associations
void DestroyAllMachines();
};
/// Holder that is always valid and may contain the real pointer
class gmgcrHolder : public gmgcrRefCount
{
public:
/// Default contsructor for simple allocation, Init() should be called immediately.
gmgcrHolder()
{
m_object = NULL;
m_machine = NULL;
}
/// Initialize with real object
void Init(gmObject* a_object,
gmMachine* a_machine)
{
m_object = a_object;
m_machine = a_machine;
}
/// Destroy object pointer (May occur before this holder dies)
void Destroy() const
{
if( m_object )
{
GM_ASSERT( gmGCRootManager::Get() );
gmGCRootManager::Get()->RemoveObject(m_object, m_machine);
}
m_object = NULL;
m_machine = NULL;
}
/// Get the real pointer
void* GetPtr() { return m_object; }
private:
/// Called when reference count reaches zero
void DeleteThis() const
{
Destroy();
GM_ASSERT( gmGCRootManager::Get() );
gmGCRootManager::Get()->FreeHolder(this);
}
mutable gmObject* m_object; ///< The object
mutable gmMachine* m_machine; ///< The owning machine
};
/// This is the smart pointer to a gmObject
/// It adds the gmObject to the gmMachine GC roots so that the object is not collected while held
/// Initialize via constructor or Set(). eg. gmGCRoot<gmStringObject> ptr(myString, myMachine);
/// Fast cast to native type and fast copy to compatible pointer.
/// Slower FIRST construct and FINAL destruct as shared holder object performs map insert/removal.
/// If gmMachine is destructed, contents will be nullified. (As long as manager was called correctly.)
template< typename TYPE >
class gmGCRoot
{
public:
/// Empty Constructor. Equals NULL.
gmGCRoot() { m_ptrToHolder = NULL; }
// NOTE: Currently not very safe since holder and controller are not typed.
/// Construct from holder
explicit gmGCRoot(gmgcrHolder* a_holder)
{
m_ptrToHolder = a_holder;
if( m_ptrToHolder )
{
m_ptrToHolder->AddReference();
}
}
/// Construct from pointers
gmGCRoot(TYPE* a_object, gmMachine* a_machine)
{
m_ptrToHolder = NULL;
Set(a_object, a_machine);
}
/// Set pointer
void Set(TYPE* a_object, gmMachine* a_machine)
{
if( m_ptrToHolder )
{
m_ptrToHolder->ReleaseReference();
}
if( !a_object ) // Handle null assignment
{
m_ptrToHolder = NULL;
return;
}
GM_ASSERT( gmGCRootManager::Get() );
m_ptrToHolder = gmGCRootManager::Get()->FindOrAdd( (gmObject*)a_object, (gmMachine*) a_machine );
m_ptrToHolder->AddReference();
}
/// Copy Constructor
gmGCRoot(const gmGCRoot<TYPE>& a_ref)
{
m_ptrToHolder = a_ref.m_ptrToHolder;
if (m_ptrToHolder != NULL)
{
m_ptrToHolder->AddReference();
}
}
/// Destructor
~gmGCRoot()
{
if( m_ptrToHolder )
{
m_ptrToHolder->ReleaseReference();
}
}
/// Access the referenced type
operator TYPE* () { return (TYPE*)m_ptrToHolder->GetPtr(); }
operator const TYPE* () const { return (TYPE*)m_ptrToHolder->GetPtr(); }
/// Access the object as pointer
TYPE* operator -> () { return (TYPE*)m_ptrToHolder->GetPtr(); }
const TYPE* operator -> () const { return (TYPE*)m_ptrToHolder->GetPtr(); }
/// Access the object as reference
TYPE& operator * () { return *((TYPE*)m_ptrToHolder->GetPtr()); }
const TYPE& operator * () const { return *((TYPE*)m_ptrToHolder->GetPtr()); }
/// Access object with standard function instead of operator. Useful when compiler finds conversion ambigous.
TYPE* Resolve() { return (TYPE*)m_ptrToHolder->GetPtr(); }
const TYPE* Resolve() const { return (TYPE*)m_ptrToHolder->GetPtr(); }
/// Assign from other reference
gmGCRoot<TYPE>& operator = (const gmGCRoot<TYPE>& a_ref)
{
if( m_ptrToHolder != a_ref.GetHolder() )
{
if( m_ptrToHolder != NULL )
{
m_ptrToHolder->ReleaseReference();
}
m_ptrToHolder = a_ref.GetHolder();
if(m_ptrToHolder)
{
m_ptrToHolder->AddReference();
}
}
return *this;
}
/// Assign to NULL. Make shorter code path for assignment with zero (null in C++)
gmGCRoot<TYPE>& operator = (int a_null)
{
GM_ASSERT(a_null == 0);
if( m_ptrToHolder != NULL )
{
m_ptrToHolder->ReleaseReference();
}
m_ptrToHolder = NULL;
return *this;
}
/// Equality test
int operator == (const gmGCRoot<TYPE>& a_ref) const
{
if( m_ptrToHolder == a_ref.GetHolder() )
{
return true;
}
// Both holders cannot be NULL at this point, but one maybe.
if( (a_ref.GetHolder() == NULL) && (m_ptrToHolder->GetPtr() == NULL) )
{
return true;
}
if( m_ptrToHolder == NULL )
{
if( a_ref.GetHolder()->GetPtr() == NULL )
{
return true;
}
}
else
{
if( a_ref.GetHolder() != NULL )
{
if( a_ref.GetHolder()->GetPtr() == m_ptrToHolder->GetPtr() )
{
return true;
}
}
}
return false;
}
/// Non equality test
int operator != (const gmGCRoot<TYPE>& a_ref) const { return !operator==(a_ref); }
/// Less than comparison for use in Containers
int operator < (const gmGCRoot<TYPE>& a_ref) const
{
if( a_ref.GetHolder() == NULL )
{
return false;
}
if( m_ptrToHolder == NULL )
{
return true;
}
return ( m_ptrToHolder->GetPtr() < a_ref.GetHolder()->GetPtr() );
}
/// Greater than comparison for use in Containers
int operator > (const gmGCRoot<TYPE>& a_ref) const
{
if( m_ptrToHolder == NULL )
{
return false;
}
if( a_ref.GetHolder() == NULL )
{
return true;
}
return ( m_ptrToHolder->GetPtr() > a_ref.GetHolder()->GetPtr() );
}
/// Access the holder. Necessary since this is a templated class.
gmgcrHolder* GetHolder() const { return m_ptrToHolder; }
private:
mutable gmgcrHolder* m_ptrToHolder; ///< Pointer to holder of the real pointer
};
#endif //GMGCROOT_H
@@ -0,0 +1,33 @@
//
// gmGCRootUtil.cpp
//
#include "gmGCRoot.h"
#include "gmGCRootUtil.h"
// Must be last header
#include "memdbgon.h"
gmgcrMemFixed::gmgcrMemFixed()
: gmMemFixed( sizeof(gmgcrHolder) )
{
}
gmgcrHolder* gmgcrMemFixed::Alloc()
{
gmgcrHolder* ptr = (gmgcrHolder*)gmMemFixed::Alloc();
gmConstructElement<gmgcrHolder>(ptr);
return ptr;
}
void gmgcrMemFixed::Free(gmgcrHolder* a_ptr)
{
if( a_ptr )
{
gmDestructElement(a_ptr);
gmMemFixed::Free(a_ptr);
}
}
@@ -0,0 +1,171 @@
#ifndef GMGCROOTUTIL_H
#define GMGCROOTUTIL_H
//
// gmGCRootUtil.h
//
#include "gmThread.h"
//
// This file has containers and utilities for implementing gmGCRoot
// You may replace these implementations with your own containers with relative ease.
//
// Fwd decls
class gmGCRootManager;
class gmgcrHolder;
// STL version
#include <map>
#include <vector>
template<typename KEY, typename VALUE>
class gmgcrMap
{
typename std::map<KEY, VALUE> m_map;
public:
// Iterator, derive or contain native container iterator
class Iterator
{
public:
typename std::map<KEY, VALUE>::iterator m_it;
};
// Get Value at unique Key, return true if found
int GetAt(KEY a_key, VALUE& a_value)
{
typename std::map<KEY, VALUE>::iterator it = m_map.find(a_key);
if( it != m_map.end() )
{
a_value = (*it).second;
return true;
}
return false;
};
// Set Value and unique Key
void SetAt(KEY a_key, VALUE& a_value)
{
m_map[a_key] = a_value;
}
// Remove pair at unique Key
void RemoveAt(KEY a_key)
{
m_map.erase(a_key);
}
// Iteration Get first pair
void GetFirst(Iterator& a_it)
{
a_it.m_it = m_map.begin();
}
// Iteration Get next pair
void GetNext(Iterator& a_it)
{
if( a_it.m_it != m_map.end() )
{
++a_it.m_it;
}
}
// Iteration Is iterator valid. Return false if not.
int IsNull(Iterator& a_it)
{
if( a_it.m_it == m_map.end() )
{
return true;
}
return false;
}
// Iteration Get Value from iterator, return false if failed
int GetValue(Iterator& a_it, VALUE& a_value)
{
if( a_it.m_it != m_map.end() )
{
a_value = (*a_it.m_it).second;
return true;
}
return false;
}
};
// Array, derive or contain array style container
template<typename TYPE>
class gmgcrArray
{
typename std::vector<TYPE> m_array;
public:
// Set initial array size and any grow / presize for efficiency
void Init(int a_initialSize, int a_growSize)
{
SetSize(a_initialSize);
}
// Set array size, and grow size
void SetSize(int a_size)
{
m_array.resize(a_size);
}
// Return number of elements in array
int GetSize()
{
return m_array.size();
}
// Return last index eg. GetSize()-1
int GetLastIndex()
{
return GetSize() - 1;
}
// Remove element at index
void RemoveAt(int a_index)
{
m_array.erase(m_array.begin() + a_index);
}
// Access element at index (by reference)
TYPE& operator[](int a_index)
{
return m_array[a_index];
}
// Add (default) element to array, returning index of new element
int AddEmpty()
{
int oldCount = GetSize(); // old count becomes new index
SetSize(oldCount+1);
return oldCount;
}
};
// Allocator, derive or contain frame style allocator
class gmgcrMemFixed : public gmMemFixed
{
public:
gmgcrMemFixed();
// Allocate and construct
gmgcrHolder* Alloc();
// Destruct and free
void Free(gmgcrHolder* a_ptr);
};
#endif //GMGCROOTUTIL_H
@@ -0,0 +1,64 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#include "gmConfig.h"
#include "gmHelpers.h"
// Must be last header
#include "memdbgon.h"
/*!
\brief gmRandomInt() returns a random int b/n two values
\return number is >= min and < max (exclusive of max)
*/
int gmRandomInt(int a_min, int a_max)
{
if(a_min > a_max)
{
int temp = a_max;
a_max = a_min;
a_min = temp;
}
else if(a_min == a_max)
{
return a_min; // hmmm, not good.
}
int randVal = rand();
int val = (randVal % (a_max - a_min)) + a_min;
return val;
}
/*!
\brief gmRandomInt() returns a random int b/n two values
\return number is >= min and < max (exclusive of max)
*/
float gmRandomFloat(float a_min, float a_max)
{
if(a_min > a_max)
{
float temp = a_max;
a_max = a_min;
a_min = temp;
}
else if(a_min == a_max)
{
return a_min; // hmmm, not good.
}
int randVal = rand() % RAND_MAX;
float frandVal = (float)randVal / (float)RAND_MAX;
return a_min + (frandVal * (a_max - a_min));
}
+179
View File
@@ -0,0 +1,179 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#ifndef _GMHELPERS_H_
#define _GMHELPERS_H_
#include "gmConfig.h"
#include "gmThread.h"
#include <math.h>
//
// Helpers
//
#define GM_PI_VALUE 3.1415927f
// Clamp value between to range
template <class TYPE>
GM_FORCEINLINE TYPE gmClamp(const TYPE a_min, const TYPE a_value, const TYPE a_max)
{
if(a_value < a_min)
{
return a_min;
}
else if(a_value > a_max)
{
return a_max;
}
return a_value;
}
template <class TYPE>
GM_FORCEINLINE TYPE gmMin3(const TYPE a_x, const TYPE a_y, const TYPE a_z)
{
if(a_y < a_x)
{
if(a_z < a_y)
{
return a_z;
}
else
{
return a_y;
}
}
else
{
if (a_z < a_x)
{
return a_z;
}
else
{
return a_x;
}
}
}
template <class TYPE>
GM_FORCEINLINE TYPE gmMax3(const TYPE a_x, const TYPE a_y, const TYPE a_z)
{
if(a_y > a_x)
{
if(a_z > a_y)
{
return a_z;
}
else
{
return a_y;
}
}
else
{
if (a_z > a_x)
{
return a_z;
}
else
{
return a_x;
}
}
}
GM_FORCEINLINE float gmGetFloatOrIntParamAsFloat(gmThread * a_thread, int a_paramIndex)
{
if(a_thread->ParamType(a_paramIndex) == GM_INT)
{
return (float)a_thread->Param(a_paramIndex).m_value.m_int;
}
else
{
return a_thread->Param(a_paramIndex).m_value.m_float;
}
}
GM_FORCEINLINE bool gmGetFloatOrIntParamAsFloat(gmThread * a_thread, int a_paramIndex, float& a_retValue)
{
if(a_thread->ParamType(a_paramIndex) == GM_INT)
{
a_retValue = (float)a_thread->Param(a_paramIndex).m_value.m_int;
return true;
}
else if(a_thread->ParamType(a_paramIndex) == GM_FLOAT)
{
a_retValue = a_thread->Param(a_paramIndex).m_value.m_float;
return true;
}
else
{
return false;
}
}
GM_FORCEINLINE int gmGetFloatOrIntParamAsInt(gmThread * a_thread, int a_paramIndex)
{
if(a_thread->ParamType(a_paramIndex) == GM_INT)
{
return a_thread->Param(a_paramIndex).m_value.m_int;
}
else
{
return (int)a_thread->Param(a_paramIndex).m_value.m_float;
}
}
GM_FORCEINLINE bool gmGetFloatOrIntParamAsInt(gmThread * a_thread, int a_paramIndex, int& a_retValue)
{
if(a_thread->ParamType(a_paramIndex) == GM_INT)
{
a_retValue = a_thread->Param(a_paramIndex).m_value.m_int;
return true;
}
else if(a_thread->ParamType(a_paramIndex) == GM_FLOAT)
{
a_retValue = (int)a_thread->Param(a_paramIndex).m_value.m_float;
return true;
}
else
{
return false;
}
}
/*!
\brief gmRandomInt() returns a random int b/n two values
Beware of overflow since ints are only 32bit on Intel
\return number is >= min and < max (exclusive of max)
*/
int gmRandomInt(int a_min, int a_max);
/*!
\brief gmRandomInt() returns a random int b/n two values
Note this is a low precision random value since it is generated from an int.
\return number is >= min and < max (exclusive of max)
*/
float gmRandomFloat(float a_min, float a_max);
/*!
\brief Returns the sine and cosine of values
Note should make platform specific version
*/
GM_FORCEINLINE void gmSinCos(const float a_angle, float& a_sin, float& a_cos)
{
a_sin = (float)sinf(a_angle);
a_cos = (float)cosf(a_angle);
}
#endif // _GMHELPERS_H_
@@ -0,0 +1,860 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#include "gmConfig.h"
#include "gmMathLib.h"
#include "gmThread.h"
#include "gmMachine.h"
#include "gmHelpers.h"
#include "gmUtil.h"
#include <math.h>
// Must be last header
#include "memdbgon.h"
//
// Conversion
//
int GM_CDECL gmfToString(gmThread * a_thread)
{
const gmVariable * var = a_thread->GetThis();
if(GM_INT == var->m_type)
{
char numberAsStringBuffer[64];
sprintf(numberAsStringBuffer, "%d", var->m_value.m_int); // this won't be > 64 chars
a_thread->PushNewString(numberAsStringBuffer);
}
else if (GM_FLOAT == var->m_type)
{
char numberAsStringBuffer[64];
sprintf(numberAsStringBuffer, "%f", var->m_value.m_float); // this won't be > 64 chars
a_thread->PushNewString(numberAsStringBuffer);
}
else if (GM_STRING == var->m_type)
{
a_thread->PushString( (gmStringObject *) GM_OBJECT(var->m_value.m_ref) );
}
else
{
return GM_EXCEPTION;
}
return GM_OK;
}
int GM_CDECL gmfToFloat(gmThread * a_thread)
{
const gmVariable * var = a_thread->GetThis();
if(GM_INT == var->m_type)
{
a_thread->PushFloat((float)var->m_value.m_int);
}
else if (GM_FLOAT == var->m_type)
{
a_thread->PushFloat(var->m_value.m_float);
}
else if (GM_STRING == var->m_type)
{
gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref);
const char * cstr = * strObj;
a_thread->PushFloat( (float)atof(cstr) );
}
else
{
//a_thread->PushFloat( 0.0f );
return GM_EXCEPTION;
}
return GM_OK;
}
int GM_CDECL gmfToInt(gmThread * a_thread)
{
const gmVariable * var = a_thread->GetThis();
if(GM_INT == var->m_type)
{
a_thread->PushInt(var->m_value.m_int);
}
else if (GM_FLOAT == var->m_type)
{
a_thread->PushInt( (int)var->m_value.m_float);
}
else if (GM_STRING == var->m_type)
{
gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref);
const char * cstr = * strObj;
a_thread->PushInt( atoi(cstr) );
}
else
{
//a_thread->PushInt( 0 );
return GM_EXCEPTION;
}
return GM_OK;
}
//
// Math
//
// int randint(int a_min, int a_max);
// returned number is >= a_min and < a_max (exclusive of max)
static int GM_CDECL gmfRandInt(gmThread * a_thread)
{
GM_CHECK_NUM_PARAMS(2);
GM_CHECK_INT_PARAM(min, 0);
GM_CHECK_INT_PARAM(max, 1);
a_thread->PushInt( gmRandomInt(min, max) );
return GM_OK;
}
// float randfloat(float a_min, float a_max);
// returned number is >= a_min and < a_max (exclusive of max)
static int GM_CDECL gmfRandFloat(gmThread * a_thread)
{
GM_CHECK_NUM_PARAMS(2);
GM_CHECK_FLOAT_PARAM(min, 0);
GM_CHECK_FLOAT_PARAM(max, 1);
a_thread->PushFloat( gmRandomFloat(min, max) );
return GM_OK;
}
// void randseed(int a_seed);
static int GM_CDECL gmfRandSeed(gmThread * a_thread)
{
GM_CHECK_NUM_PARAMS(1);
GM_CHECK_INT_PARAM(seed, 0);
srand(seed);
return GM_OK;
}
static int GM_CDECL gmfAbs(gmThread * a_thread)
{
GM_CHECK_NUM_PARAMS(1);
if(a_thread->ParamType(0) == GM_INT)
{
int intValue = a_thread->Param(0).m_value.m_int;
a_thread->PushInt(abs(intValue));
return GM_OK;
}
else if(a_thread->ParamType(0) == GM_FLOAT)
{
float floatValue = a_thread->Param(0).m_value.m_float;
a_thread->PushFloat((float)fabsf(floatValue));
return GM_OK;
}
return GM_EXCEPTION;
}
static int GM_CDECL gmfSqrt(gmThread * a_thread)
{
GM_CHECK_NUM_PARAMS(1);
if(a_thread->ParamType(0) == GM_INT)
{
int intValue = a_thread->Param(0).m_value.m_int;
a_thread->PushInt((int)sqrtf((float)intValue));
return GM_OK;
}
else if(a_thread->ParamType(0) == GM_FLOAT)
{
float floatValue = a_thread->Param(0).m_value.m_float;
a_thread->PushFloat(sqrtf(floatValue));
return GM_OK;
}
return GM_EXCEPTION;
}
static int GM_CDECL gmfPower(gmThread * a_thread)
{
GM_CHECK_NUM_PARAMS(2);
int minType = gmMin<int>(a_thread->ParamType(0), a_thread->ParamType(1));
if(minType < GM_INT)
{
return GM_EXCEPTION;
}
int maxType = gmMax<int>(a_thread->ParamType(0), a_thread->ParamType(1));
if(maxType == GM_INT)
{
int valX = a_thread->Param(0).m_value.m_int;
int valY = a_thread->Param(1).m_value.m_int;
a_thread->PushInt((int)pow((float)valX, (float)valY));
return GM_OK;
}
else if(maxType == GM_FLOAT)
{
float valX = gmGetFloatOrIntParamAsFloat(a_thread, 0);
float valY = gmGetFloatOrIntParamAsFloat(a_thread, 1);
a_thread->PushFloat((float)pow(valX, valY));
return GM_OK;
}
else
{
return GM_EXCEPTION;
}
}
static int GM_CDECL gmfFloor(gmThread * a_thread)
{
GM_CHECK_NUM_PARAMS(1);
if(a_thread->ParamType(0) == GM_INT) //Do nothing if Int
{
int intValue = a_thread->Param(0).m_value.m_int;
a_thread->PushInt(intValue);
return GM_OK;
}
else if(a_thread->ParamType(0) == GM_FLOAT)
{
float floatValue = a_thread->Param(0).m_value.m_float;
a_thread->PushFloat(floorf(floatValue));
return GM_OK;
}
return GM_EXCEPTION;
}
static int GM_CDECL gmfCeil(gmThread * a_thread)
{
GM_CHECK_NUM_PARAMS(1);
if(a_thread->ParamType(0) == GM_INT) //Do nothing if Int
{
int intValue = a_thread->Param(0).m_value.m_int;
a_thread->PushInt(intValue);
return GM_OK;
}
else if(a_thread->ParamType(0) == GM_FLOAT)
{
float floatValue = a_thread->Param(0).m_value.m_float;
a_thread->PushFloat(ceilf(floatValue));
return GM_OK;
}
return GM_EXCEPTION;
}
static int GM_CDECL gmfRound(gmThread * a_thread)
{
GM_CHECK_NUM_PARAMS(1);
if(a_thread->ParamType(0) == GM_INT) //Do nothing if Int
{
int intValue = a_thread->Param(0).m_value.m_int;
a_thread->PushInt(intValue);
return GM_OK;
}
else if(a_thread->ParamType(0) == GM_FLOAT)
{
float floatValue = a_thread->Param(0).m_value.m_float;
a_thread->PushFloat(floorf(floatValue + 0.5f));
return GM_OK;
}
return GM_EXCEPTION;
}
static int GM_CDECL gmfDegToRad(gmThread * a_thread)
{
GM_CHECK_NUM_PARAMS(1);
float floatValue;
if(a_thread->ParamType(0) == GM_INT) { floatValue = (float) a_thread->Param(0).m_value.m_int; }
else if(a_thread->ParamType(0) == GM_FLOAT) { floatValue = a_thread->Param(0).m_value.m_float; }
else { return GM_EXCEPTION; }
a_thread->PushFloat( floatValue * (GM_PI_VALUE / 180.0f) );
return GM_OK;
}
static int GM_CDECL gmfRadToDeg(gmThread * a_thread)
{
GM_CHECK_NUM_PARAMS(1);
float floatValue;
if(a_thread->ParamType(0) == GM_INT) { floatValue = (float) a_thread->Param(0).m_value.m_int; }
else if(a_thread->ParamType(0) == GM_FLOAT) { floatValue = a_thread->Param(0).m_value.m_float; }
else { return GM_EXCEPTION; }
a_thread->PushFloat( floatValue * (180.0f / GM_PI_VALUE) );
return GM_OK;
}
static int GM_CDECL gmfSin(gmThread * a_thread)
{
GM_CHECK_NUM_PARAMS(1);
float floatValue;
if(a_thread->ParamType(0) == GM_INT) { floatValue = (float) a_thread->Param(0).m_value.m_int; }
else if(a_thread->ParamType(0) == GM_FLOAT) { floatValue = a_thread->Param(0).m_value.m_float; }
else { return GM_EXCEPTION; }
a_thread->PushFloat(sinf(floatValue));
return GM_OK;
}
static int GM_CDECL gmfASin(gmThread * a_thread)
{
GM_CHECK_NUM_PARAMS(1);
float floatValue;
if(a_thread->ParamType(0) == GM_INT) { floatValue = (float) a_thread->Param(0).m_value.m_int; }
else if(a_thread->ParamType(0) == GM_FLOAT) { floatValue = a_thread->Param(0).m_value.m_float; }
else { return GM_EXCEPTION; }
a_thread->PushFloat(asinf(floatValue));
return GM_OK;
}
static int GM_CDECL gmfCos(gmThread * a_thread)
{
GM_CHECK_NUM_PARAMS(1);
float floatValue;
if(a_thread->ParamType(0) == GM_INT) { floatValue = (float) a_thread->Param(0).m_value.m_int; }
else if(a_thread->ParamType(0) == GM_FLOAT) { floatValue = a_thread->Param(0).m_value.m_float; }
else { return GM_EXCEPTION; }
a_thread->PushFloat(cosf(floatValue));
return GM_OK;
}
static int GM_CDECL gmfACos(gmThread * a_thread)
{
GM_CHECK_NUM_PARAMS(1);
float floatValue;
if(a_thread->ParamType(0) == GM_INT) { floatValue = (float) a_thread->Param(0).m_value.m_int; }
else if(a_thread->ParamType(0) == GM_FLOAT) { floatValue = a_thread->Param(0).m_value.m_float; }
else { return GM_EXCEPTION; }
a_thread->PushFloat(acosf(floatValue));
return GM_OK;
}
static int GM_CDECL gmfTan(gmThread * a_thread)
{
GM_CHECK_NUM_PARAMS(1);
float floatValue;
if(a_thread->ParamType(0) == GM_INT) { floatValue = (float) a_thread->Param(0).m_value.m_int; }
else if(a_thread->ParamType(0) == GM_FLOAT) { floatValue = a_thread->Param(0).m_value.m_float; }
else { return GM_EXCEPTION; }
a_thread->PushFloat(tanf(floatValue));
return GM_OK;
}
static int GM_CDECL gmfATan(gmThread * a_thread)
{
GM_CHECK_NUM_PARAMS(1);
float floatValue;
if(a_thread->ParamType(0) == GM_INT) { floatValue = (float) a_thread->Param(0).m_value.m_int; }
else if(a_thread->ParamType(0) == GM_FLOAT) { floatValue = a_thread->Param(0).m_value.m_float; }
else { return GM_EXCEPTION; }
a_thread->PushFloat(atanf(floatValue));
return GM_OK;
}
//returns the arctangent of y/x
static int GM_CDECL gmfATan2(gmThread * a_thread)
{
GM_CHECK_NUM_PARAMS(2);
float floatValueY;
float floatValueX;
if(a_thread->ParamType(0) == GM_INT) {floatValueY = (float) a_thread->Param(0).m_value.m_int;}
else if(a_thread->ParamType(0) == GM_FLOAT) {floatValueY = a_thread->Param(0).m_value.m_float;}
else {return GM_EXCEPTION;}
if(a_thread->ParamType(1) == GM_INT) {floatValueX = (float) a_thread->Param(1).m_value.m_int;}
else if(a_thread->ParamType(1) == GM_FLOAT) {floatValueX = a_thread->Param(1).m_value.m_float;}
else {return GM_EXCEPTION;}
a_thread->PushFloat(atan2f(floatValueY, floatValueX));
return GM_OK;
}
static int GM_CDECL gmfLog(gmThread * a_thread)
{
int numParams = GM_THREAD_ARG->GetNumParams();
if(numParams == 1) //Natural log
{
if(a_thread->ParamType(0) == GM_INT)
{
float floatValue = (float) a_thread->Param(0).m_value.m_int;
a_thread->PushInt( (int) log(floatValue) );
return GM_OK;
}
else if(a_thread->ParamType(0) == GM_FLOAT)
{
float floatValue = (float) a_thread->Param(0).m_value.m_float;
a_thread->PushFloat( logf(floatValue) );
return GM_OK;
}
else {return GM_EXCEPTION;}
}
else if(numParams == 2) //Log to base params: base, value
{
int minType = gmMin<int>(a_thread->ParamType(0), a_thread->ParamType(1));
if(minType < GM_INT)
{
return GM_EXCEPTION;
}
int maxType = gmMax<int>(a_thread->ParamType(0), a_thread->ParamType(1));
if(maxType == GM_INT)
{
int base = a_thread->Param(0).m_value.m_int;
int value = a_thread->Param(1).m_value.m_int;
a_thread->PushInt( (int)( log10f((float)value) / log10f((float)base) ) );
return GM_OK;
}
else if(maxType == GM_FLOAT)
{
float base = gmGetFloatOrIntParamAsFloat(a_thread, 0);
float value = gmGetFloatOrIntParamAsFloat(a_thread, 1);
a_thread->PushFloat( (float)( log10(value) / log10(base) ) );
return GM_OK;
}
else
{
return GM_EXCEPTION;
}
}
else
{
return GM_EXCEPTION;
}
}
static int GM_CDECL gmfMin(gmThread * a_thread)
{
GM_CHECK_NUM_PARAMS(2);
int minType = gmMin<int>(a_thread->ParamType(0), a_thread->ParamType(1));
if(minType < GM_INT)
{
return GM_EXCEPTION;
}
int maxType = gmMax<int>(a_thread->ParamType(0), a_thread->ParamType(1));
if(maxType == GM_INT)
{
int valX = a_thread->Param(0).m_value.m_int;
int valY = a_thread->Param(1).m_value.m_int;
a_thread->PushInt( gmMin(valX, valY) );
return GM_OK;
}
else if(maxType == GM_FLOAT)
{
float valX = gmGetFloatOrIntParamAsFloat(a_thread, 0);
float valY = gmGetFloatOrIntParamAsFloat(a_thread, 1);
a_thread->PushFloat( gmMin(valX, valY) );
return GM_OK;
}
else
{
return GM_EXCEPTION;
}
}
static int GM_CDECL gmfMax(gmThread * a_thread)
{
GM_CHECK_NUM_PARAMS(2);
int minType = gmMin<int>(a_thread->ParamType(0), a_thread->ParamType(1));
if(minType < GM_INT)
{
return GM_EXCEPTION;
}
int maxType = gmMax<int>(a_thread->ParamType(0), a_thread->ParamType(1));
if(maxType == GM_INT)
{
int valX = a_thread->Param(0).m_value.m_int;
int valY = a_thread->Param(1).m_value.m_int;
a_thread->PushInt( gmMax(valX, valY) );
return GM_OK;
}
else if(maxType == GM_FLOAT)
{
float valX = gmGetFloatOrIntParamAsFloat(a_thread, 0);
float valY = gmGetFloatOrIntParamAsFloat(a_thread, 1);
a_thread->PushFloat( gmMax(valX, valY) );
return GM_OK;
}
else
{
return GM_EXCEPTION;
}
}
static int GM_CDECL gmfClamp(gmThread * a_thread)
{
GM_CHECK_NUM_PARAMS(3);
//params: min, value, max
int minType = gmMin3(a_thread->ParamType(0), a_thread->ParamType(1), a_thread->ParamType(2));
if(minType < GM_INT)
{
return GM_EXCEPTION;
}
int maxType = gmMax3(a_thread->ParamType(0), a_thread->ParamType(1), a_thread->ParamType(2));
if(maxType == GM_INT)
{
int limitMin = a_thread->Param(0).m_value.m_int;
int value = a_thread->Param(1).m_value.m_int;
int limitMax = a_thread->Param(2).m_value.m_int;
a_thread->PushInt( gmClamp(limitMin, value, limitMax) );
return GM_OK;
}
else if(maxType == GM_FLOAT)
{
float limitMin = gmGetFloatOrIntParamAsFloat(a_thread, 0);
float value = gmGetFloatOrIntParamAsFloat(a_thread, 1);
float limitMax = gmGetFloatOrIntParamAsFloat(a_thread, 2);
a_thread->PushFloat( gmClamp(limitMin, value, limitMax) );
return GM_OK;
}
else
{
return GM_EXCEPTION;
}
}
//
// Libs and bindings
//
static gmFunctionEntry s_mathLib[] =
{
/*gm
\lib math
*/
/*gm
\function abs
\brief abs will return the absolute value of the passed int \ float
\param int\float
\return int\float abs(param)
*/
{"abs", gmfAbs},
/*gm
\function sqrt
\brief sqrt will return the square root of the passed int \ float
\param int\float
\return int\float sqrt(param)
*/
{"sqrt", gmfSqrt},
/*gm
\function sqrt
\brief sqrt will return the square root of the passed int \ float
\param int\float A
\param int\float B
\return int\float A to the power of B
*/
{"power", gmfPower},
/*gm
\function floor
\brief floor
\param float A
\return float floor(A)
*/
{"floor", gmfFloor},
/*gm
\function ceil
\brief ceil
\param float A
\return float ceil(A)
*/
{"ceil", gmfCeil},
/*gm
\function round
\brief round
\param float A
\return float round(A)
*/
{"round", gmfRound},
/*gm
\function degtorad
\brief degtorad will convert degrees to radians
\param float\int deg
\return float
*/
{"degtorad", gmfDegToRad},
/*gm
\function radtodeg
\brief radtodeg will convert radians to degrees
\param float\int rad
\return float
*/
{"radtodeg", gmfRadToDeg},
/*gm
\function cos
\brief cos will return the radian cosine
\param float
\return float
*/
{"cos", gmfCos},
/*gm
\function sin
\brief sin will return the radian sine
\param float
\return float
*/
{"sin", gmfSin},
/*gm
\function tan
\brief tan will return the radian tan (sin/cos)
\param float
\return float
*/
{"tan", gmfTan},
/*gm
\function acos
\brief acos will return the radian arc cosine
\param float
\return float
*/
{"acos", gmfACos},
/*gm
\function asin
\brief asin will return the radian arc sine
\param float
\return float
*/
{"asin", gmfASin},
/*gm
\function atan
\brief atan will return the radian arc tangent
\param float
\return float
*/
{"atan", gmfATan},
/*gm
\function atan
\brief atan will return the radian arc tangent of x / y
\param float x
\param float y
\return float
*/
{"atan2", gmfATan2},
/*gm
\function log
\brief log will return the natural logarithm of 1 parameter, or (base, value) the logarithm to base
\param float natural \ base
\param float value (optional)
\return float
*/
{"log", gmfLog},
/*gm
\function min
\brief min will return the min of the 2 passed values
\param float\int A
\param float\int B
\return float \ int min(A, B)
*/
{"min", gmfMin},
/*gm
\function max
\brief max will return the max of the 2 passed values
\param float\int A
\param float\int B
\return float \ int max(A, B)
*/
{"max", gmfMax},
/*gm
\function clamp
\brief clamp will return the clamed value. clamp(min, val, max)
\param float\int MIN
\param float\int VALUE
\param float\int MAX
\return float\int value clamped to min, max
*/
{"clamp", gmfClamp},
/*gm
\function randint
\brief randint will return a random int from lower inclusive to upper.
\param int lower inclusive
\param int upper
\return int
*/
#if 0 // Using Source versions [2/13/2008 tom]
{"randint", gmfRandInt},
/*gm
\function randfloat
\brief randfloat will return a random float from lower inclusive to upper.
\param float lower inclusive
\param float upper
\return float
*/
{"randfloat", gmfRandFloat},
/*gm
\function randseed
\brief randseed will seed the random number generator
\param int seed
*/
{"randseed", gmfRandSeed},
#endif
};
static gmFunctionEntry s_intLib[] =
{
{"String", gmfToString},
{"Float", gmfToFloat},
{"Int", gmfToInt},
};
static gmFunctionEntry s_floatLib[] =
{
{"String", gmfToString},
{"Float", gmfToFloat},
{"Int", gmfToInt},
};
void gmBindMathLib(gmMachine * a_machine)
{
a_machine->RegisterLibrary(s_mathLib, sizeof(s_mathLib) / sizeof(s_mathLib[0]));
a_machine->RegisterTypeLibrary(GM_INT, s_intLib, sizeof(s_intLib) / sizeof(s_intLib[0]));
a_machine->RegisterTypeLibrary(GM_FLOAT, s_floatLib, sizeof(s_floatLib) / sizeof(s_floatLib[0]));
}
@@ -0,0 +1,21 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#ifndef _GMMATHLIB_H_
#define _GMMATHLIB_H_
#include "gmConfig.h"
class gmMachine;
void gmBindMathLib(gmMachine * a_machine);
#endif // _GMMATHLIB_H_
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,21 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#ifndef _GMSTRINGLIB_H_
#define _GMSTRINGLIB_H_
#include "gmConfig.h"
class gmMachine;
void gmBindStringLib(gmMachine * a_machine);
#endif // _GMSTRINGLIB_H_
@@ -0,0 +1,986 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#include "gmConfig.h"
#include "gmSystemLib.h"
#include "gmThread.h"
#include "gmMachine.h"
#include "gmHelpers.h"
#if GM_SYSTEM_LIB
#include <ctype.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include <process.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <io.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#undef GetObject
//
//
// system functions
//
//
extern void gmConcat(gmMachine * a_machine, char * &a_dst, int &a_len, int &a_size, const char * a_src, int a_growBy = 32);
static int GM_CDECL gmfSystem(gmThread * a_thread)
{
const int bufferSize = 256;
int len = 0, size = 0, i, ret = -1;
char * str = NULL, buffer[bufferSize];
// build the string
for(i = 0; i < a_thread->GetNumParams(); ++i)
{
gmConcat(a_thread->GetMachine(), str, len, size, a_thread->Param(i).AsString(a_thread->GetMachine(), buffer, bufferSize), 64);
if(str)
{
GM_ASSERT(len < size);
str[len++] = ' ';
str[len] = '\0';
}
}
// print the string
if(str)
{
ret = system(str);
a_thread->GetMachine()->Sys_Free(str);
}
a_thread->PushInt(ret);
return GM_OK;
}
static int GM_CDECL gmfDoFile(gmThread * a_thread) // filename, now (1), return thread id, null on error, exception on compile error.
{
GM_CHECK_NUM_PARAMS(1);
GM_CHECK_STRING_PARAM(filename, 0);
GM_INT_PARAM(now, 1, 1);
gmVariable paramThis = a_thread->Param(2, gmVariable::s_null); // 3rd param is 'this'
int id = GM_INVALID_THREAD;
if(filename)
{
char * string = NULL;
FILE * fp = fopen(filename, "rb");
if(fp)
{
fseek(fp, 0, SEEK_END);
int size = ftell(fp);
rewind(fp);
string = new char[size + 1];
fread(string, 1, size, fp);
string[size] = 0;
fclose(fp);
}
else
{
GM_EXCEPTION_MSG("failed to open file '%s'", filename);
return GM_EXCEPTION;
}
if(string == NULL) return GM_OK;
int errors = a_thread->GetMachine()->ExecuteString(string, &id, (now) ? true : false, filename, &paramThis);
delete[] string;
if(errors)
{
return GM_EXCEPTION;
}
else
{
a_thread->PushInt(id);
}
}
return GM_OK;
}
//
//
// Implementation of ansi file binding
//
//
static gmType s_gmFileType = GM_NULL;
static int GM_CDECL gmfFile(gmThread * a_thread)
{
a_thread->PushNewUser(NULL, s_gmFileType);
return GM_OK;
}
static int GM_CDECL gmfFileExists(gmThread * a_thread)
{
GM_CHECK_NUM_PARAMS(1);
GM_CHECK_STRING_PARAM(filename, 0);
FILE * fp = fopen(filename, "rb");
if(fp)
{
a_thread->PushInt(1);
fclose(fp);
return GM_OK;
}
a_thread->PushInt(0);
return GM_OK;
}
static int GM_CDECL gmfFileOpen(gmThread * a_thread) // path, readonly(true), return 1 on success.
{
GM_CHECK_NUM_PARAMS(1);
GM_CHECK_STRING_PARAM(filename, 0);
GM_INT_PARAM(readonly, 1, 1);
gmUserObject * fileObject = a_thread->ThisUserObject();
GM_ASSERT(fileObject->m_userType == s_gmFileType);
if(fileObject->m_user) fclose((FILE *) fileObject->m_user);
fileObject->m_user = (void *) fopen(filename, (readonly) ? "rb" : "wb");
if(fileObject->m_user) a_thread->PushInt(1);
else a_thread->PushInt(0);
return GM_OK;
}
static int GM_CDECL gmfFileOpenText(gmThread * a_thread) // path, readonly(true), return 1 on success.
{
GM_CHECK_NUM_PARAMS(1);
GM_CHECK_STRING_PARAM(filename, 0);
GM_INT_PARAM(readonly, 1, 1);
gmUserObject * fileObject = a_thread->ThisUserObject();
GM_ASSERT(fileObject->m_userType == s_gmFileType);
if(fileObject->m_user) fclose((FILE *) fileObject->m_user);
fileObject->m_user = (void *) fopen(filename, (readonly) ? "r" : "w");
if(fileObject->m_user) a_thread->PushInt(1);
else a_thread->PushInt(0);
return GM_OK;
}
static int GM_CDECL gmfFileClose(gmThread * a_thread)
{
gmUserObject * fileObject = a_thread->ThisUserObject();
GM_ASSERT(fileObject->m_userType == s_gmFileType);
if(fileObject->m_user) fclose((FILE *) fileObject->m_user);
fileObject->m_user = NULL;
return GM_OK;
}
static int GM_CDECL gmfFileIsOpen(gmThread * a_thread) // return 1 if open, else 0
{
gmUserObject * fileObject = a_thread->ThisUserObject();
GM_ASSERT(fileObject->m_userType == s_gmFileType);
a_thread->PushInt((fileObject->m_user) ? 1 : 0);
return GM_OK;
}
static void GM_CDECL gmFileOpGetDot(gmThread * a_thread, gmVariable * a_operands)
{
gmUserObject * user = (gmUserObject *) GM_OBJECT(a_operands->m_value.m_ref);
if(user && user->m_user)
{
gmStringObject * member = (gmStringObject *) GM_OBJECT(a_operands[1].m_value.m_ref);
GM_ASSERT(sizeof(gmptr) == sizeof(time_t));
if(strcmp(member->GetString(), "SEEK_CUR") == 0)
a_operands->SetInt(SEEK_CUR);
else if(strcmp(member->GetString(), "SEEK_END") == 0)
a_operands->SetInt(SEEK_END);
else if(strcmp(member->GetString(), "SEEK_SET") == 0)
a_operands->SetInt(SEEK_SET);
else
{
a_operands->Nullify();
return;
}
return;
}
a_operands->Nullify();
}
static int GM_CDECL gmfFileSeek(gmThread * a_thread) // return false on error
{
gmUserObject * fileObject = a_thread->ThisUserObject();
GM_ASSERT(fileObject->m_userType == s_gmFileType);
GM_CHECK_NUM_PARAMS(2);
GM_CHECK_INT_PARAM(offset, 0);
GM_CHECK_INT_PARAM(origin, 1);
if( origin != SEEK_CUR
&& origin != SEEK_END
&& origin != SEEK_SET )
{
return GM_EXCEPTION;
}
int result = fseek((FILE*)fileObject->m_user, offset, origin);
if(result != 0)
{
a_thread->PushInt(false);
}
a_thread->PushInt(true);
return GM_OK;
}
static int GM_CDECL gmfFileTell(gmThread * a_thread) // return -1 on error, else file pos.
{
gmUserObject * fileObject = a_thread->ThisUserObject();
GM_ASSERT(fileObject->m_userType == s_gmFileType);
long pos = -1;
if(fileObject->m_user) pos = ftell((FILE *) fileObject->m_user);
a_thread->PushInt(pos);
return GM_OK;
}
static int GM_CDECL gmfFileReadLine(gmThread * a_thread) // flag keep \n (0), return string, or null on eof
{
GM_INT_PARAM(keepLF, 0, 0);
const int len = GM_SYSTEM_LIB_MAX_LINE;
char buffer[len];
gmUserObject * fileObject = a_thread->ThisUserObject();
GM_ASSERT(fileObject->m_userType == s_gmFileType);
if(fileObject->m_user)
{
char * str = fgets(buffer, len, (FILE *) fileObject->m_user);
if(str)
{
int slen = strlen(str);
if(!keepLF)
{
if(!feof((FILE *) fileObject->m_user))
str[--slen] = '\0';
}
a_thread->PushNewString(str, slen);
}
}
return GM_OK;
}
static int GM_CDECL gmfFileReadChar(gmThread * a_thread) // return int, return NULL on eof, or on error
{
gmUserObject * fileObject = a_thread->ThisUserObject();
GM_ASSERT(fileObject->m_userType == s_gmFileType);
if(fileObject->m_user)
{
int c = fgetc((FILE*) fileObject->m_user);
if(c != EOF) a_thread->PushInt(c);
}
return GM_OK;
}
static int GM_CDECL gmfFileWriteChar(gmThread * a_thread) // int, return char written, or NULL on error
{
GM_CHECK_NUM_PARAMS(1);
GM_CHECK_INT_PARAM(c, 0);
gmUserObject * fileObject = a_thread->ThisUserObject();
GM_ASSERT(fileObject->m_userType == s_gmFileType);
if(fileObject->m_user)
{
int r = fputc(c, (FILE *) fileObject->m_user);
if(r != EOF) a_thread->PushInt(r);
}
return GM_OK;
}
static int GM_CDECL gmfFileWriteString(gmThread * a_thread) // string, return 1 on success, or NULL on error
{
GM_CHECK_NUM_PARAMS(1);
GM_CHECK_STRING_PARAM(s, 0);
gmUserObject * fileObject = a_thread->ThisUserObject();
GM_ASSERT(fileObject->m_userType == s_gmFileType);
if(fileObject->m_user)
{
if(fputs(s, (FILE *) fileObject->m_user) != EOF) a_thread->PushInt(1);
}
return GM_OK;
}
#if GM_USE_INCGC
static void GM_CDECL gmGCDestructFileUserType(gmMachine * a_machine, gmUserObject* a_object)
{
if(a_object->m_user) fclose((FILE *) a_object->m_user);
a_object->m_user = NULL;
}
#else //GM_USE_INCGC
static void GM_CDECL gmGCFileUserType(gmMachine * a_machine, gmUserObject * a_object, gmuint32 a_mark)
{
if(a_object->m_user) fclose((FILE *) a_object->m_user);
a_object->m_user = NULL;
}
#endif //GM_USE_INCGC
//
//
// File Find user type
//
//
static gmType s_gmFileFindType = GM_NULL;
struct gmFileFindUser
{
WIN32_FIND_DATA m_findData;
HANDLE m_iterator;
};
//
//
// File Info user type
//
//
static gmType s_gmFileInfoType = GM_NULL;
struct gmFileInfoUser
{
time_t m_accessedTime; // last access time
time_t m_creationTime; // creation time
time_t m_modifiedTime; // last modify time
unsigned int m_size; // size
};
//
//
// System lib
//
//
static int GM_CDECL gmfFindFirstFile(gmThread * a_thread)
{
GM_CHECK_NUM_PARAMS(1);
GM_CHECK_STRING_PARAM(filename, 0);
gmFileFindUser * fileFind = (gmFileFindUser *) a_thread->GetMachine()->Sys_Alloc(sizeof(gmFileFindUser));
fileFind->m_iterator = FindFirstFile(filename, &fileFind->m_findData);
if(fileFind->m_iterator == INVALID_HANDLE_VALUE)
{
a_thread->GetMachine()->Sys_Free(fileFind);
return GM_OK;
}
a_thread->PushNewUser(fileFind, s_gmFileFindType);
return GM_OK;
}
static int GM_CDECL gmfFindNextFile(gmThread * a_thread)
{
GM_CHECK_NUM_PARAMS(1);
if(a_thread->ParamType(0) == s_gmFileFindType)
{
gmFileFindUser * fileFind = (gmFileFindUser *) a_thread->ParamUser(0);
if(fileFind && fileFind->m_iterator != INVALID_HANDLE_VALUE)
{
if(FindNextFile(fileFind->m_iterator, &fileFind->m_findData))
{
a_thread->PushUser(a_thread->ParamUserObject(0));
}
}
}
return GM_OK;
}
static int GM_CDECL gmfFileInfo(gmThread * a_thread)
{
GM_CHECK_NUM_PARAMS(1);
GM_CHECK_STRING_PARAM(filename, 0);
struct _stat buf;
int fh, result;
if((fh = _open(filename, _O_RDONLY)) == -1) return GM_OK; // return null
result = _fstat(fh, &buf); // Get data associated with "fh"
if(result == 0) //function obtained data correctly (0 == success, -1 == fail)
{
// create and push a gmFileInfoUser object
gmFileInfoUser * fileInfo = (gmFileInfoUser *) a_thread->GetMachine()->Sys_Alloc(sizeof(gmFileInfoUser));
fileInfo->m_creationTime = buf.st_ctime;
fileInfo->m_accessedTime = buf.st_atime;
fileInfo->m_modifiedTime = buf.st_mtime;
fileInfo->m_size = buf.st_size;
a_thread->PushNewUser(fileInfo, s_gmFileInfoType);
}
_close( fh );
return GM_OK;
}
static int GM_CDECL gmfCreateFolder(gmThread * a_thread)
{
GM_CHECK_NUM_PARAMS(1);
GM_CHECK_STRING_PARAM(path, 0);
BOOL result = CreateDirectory(path, NULL);
if(result)
{
a_thread->PushInt(1);
}
else
{
WIN32_FIND_DATA findData;
HANDLE handle = FindFirstFile(path, &findData);
if(handle == INVALID_HANDLE_VALUE)
{
a_thread->PushInt(0);
}
else
{
if(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
a_thread->PushInt(2);
}
else
{
a_thread->PushInt(0);
}
FindClose(handle);
}
}
return GM_OK;
}
bool RecurseDeletePath(const char * a_path)
{
WIN32_FIND_DATA findData;
char path[MAX_PATH] = "";
strcpy(path,a_path);
// remove trailing '\' char
int last = strlen(path) - 1;
if(path[last] == '\\')
{
path[last] = '\0';
}
// is path valid
HANDLE h = FindFirstFile(path,&findData);
// path not could not be found OR path is a file, not a folder
if((h == INVALID_HANDLE_VALUE) || (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)))
{
return false;
}
FindClose(h);
h = NULL;
// push current working directory
char currDir[MAX_PATH + 1] = "";
GetCurrentDirectory(MAX_PATH,currDir);
SetCurrentDirectory(path);
// iterate over contents of folder
h = FindFirstFile("*",&findData);
if(h != INVALID_HANDLE_VALUE)
{
for(;;)
{
if(strcmp(findData.cFileName,".") != 0 && strcmp(findData.cFileName,"..") != 0)
{
if(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
RecurseDeletePath(findData.cFileName);
}
else
{
DWORD attrs = GetFileAttributes(findData.cFileName);
if(attrs & FILE_ATTRIBUTE_READONLY)
{
SetFileAttributes(findData.cFileName,attrs ^ FILE_ATTRIBUTE_READONLY);
}
if(!DeleteFile(findData.cFileName))
{
DWORD res = GetLastError();
printf("\nDeleteFile() returned '%d'..\n",(int)res);
}
}
}
if(!FindNextFile(h,&findData)) break;
}
}
// pop current working directory
SetCurrentDirectory(currDir);
FindClose(h);
h = NULL;
// remove this directory
DWORD attrs = GetFileAttributes(path);
if(attrs & FILE_ATTRIBUTE_READONLY)
{
SetFileAttributes(path,attrs ^ FILE_ATTRIBUTE_READONLY);
}
return RemoveDirectory(path) != 0;
}
static int GM_CDECL gmfDeleteFolder(gmThread * a_thread)
{
GM_CHECK_NUM_PARAMS(1);
GM_CHECK_STRING_PARAM(path, 0);
GM_INT_PARAM(removeSubFolders, 1, 0);
if(removeSubFolders)
{
a_thread->PushInt(RecurseDeletePath(path) ? 1 : 0);
}
else
{
a_thread->PushInt(RemoveDirectory(path) ? 1 : 0);
}
return GM_OK;
}
static int GM_CDECL gmfTime(gmThread * a_thread)
{
time_t t;
time(&t);
GM_ASSERT(sizeof(time_t) == sizeof(gmptr));
a_thread->PushInt((gmptr) t);
return GM_OK;
}
static int GM_CDECL gmfFormatTime(gmThread * a_thread)
{
GM_INT_PARAM(t, 0, -1);
GM_STRING_PARAM(format, 1, "%A %d %B %Y, %I:%M:%S %p");
char buffer[256];
if(t == -1)
{
time_t lt;
time(&lt);
t = (int) lt;
}
struct tm * ct = localtime((time_t *) &t);
strftime(buffer, 256, format, ct);
a_thread->PushNewString(buffer);
return GM_OK;
}
static int GM_CDECL gmfFileFindGetAttribute(gmThread * a_thread)
{
GM_CHECK_NUM_PARAMS(1);
GM_CHECK_INT_PARAM(attribute, 0);
GM_ASSERT(a_thread->GetThis()->m_type == s_gmFileFindType);
gmFileFindUser * fileFind = (gmFileFindUser *) a_thread->ThisUser();
DWORD attr = 0;
if(attribute == 'r')
{
attr = FILE_ATTRIBUTE_READONLY;
}
else if(attribute == 'a')
{
attr = FILE_ATTRIBUTE_ARCHIVE;
}
else if(attribute == 's')
{
attr = FILE_ATTRIBUTE_SYSTEM;
}
else if(attribute == 'h')
{
attr = FILE_ATTRIBUTE_HIDDEN;
}
else if(attribute == 'd')
{
attr = FILE_ATTRIBUTE_DIRECTORY;
}
else if(attribute == 'c')
{
attr = FILE_ATTRIBUTE_COMPRESSED;
}
a_thread->PushInt((fileFind->m_findData.dwFileAttributes & attr) ? 1 : 0);
return GM_OK;
}
static void GM_CDECL gmFileFindOpGetDot(gmThread * a_thread, gmVariable * a_operands)
{
gmUserObject * user = (gmUserObject *) GM_OBJECT(a_operands->m_value.m_ref);
if(user && user->m_user)
{
gmFileFindUser * fileFind = (gmFileFindUser *) user->m_user;
gmStringObject * member = (gmStringObject *) GM_OBJECT(a_operands[1].m_value.m_ref);
if(strcmp(member->GetString(), "filename") == 0)
{
a_operands->SetString(a_thread->GetMachine()->AllocStringObject(fileFind->m_findData.cFileName));
return;
}
else if(strcmp(member->GetString(), "size") == 0)
{
a_operands->SetInt(fileFind->m_findData.nFileSizeLow);
return;
}
}
a_operands->Nullify();
}
#if GM_USE_INCGC
static void GM_CDECL gmGCDestructFileFindUserType(gmMachine * a_machine, gmUserObject* a_object)
{
if(a_object->m_user)
{
gmFileFindUser * fileFind = (gmFileFindUser *) a_object->m_user;
if(fileFind->m_iterator != INVALID_HANDLE_VALUE)
{
FindClose(fileFind->m_iterator);
}
a_machine->Sys_Free(a_object->m_user);
}
a_object->m_user = NULL;
}
#else //GM_USE_INCGC
static void GM_CDECL gmGCFileFindUserType(gmMachine * a_machine, gmUserObject * a_object, gmuint32 a_mark)
{
if(a_object->m_user)
{
gmFileFindUser * fileFind = (gmFileFindUser *) a_object->m_user;
if(fileFind->m_iterator != INVALID_HANDLE_VALUE)
{
FindClose(fileFind->m_iterator);
}
a_machine->Sys_Free(a_object->m_user);
}
a_object->m_user = NULL;
}
#endif //GM_USE_INCGC
static void GM_CDECL gmFileInfoOpGetDot(gmThread * a_thread, gmVariable * a_operands)
{
gmUserObject * user = (gmUserObject *) GM_OBJECT(a_operands->m_value.m_ref);
if(user && user->m_user)
{
gmFileInfoUser * fileInfo = (gmFileInfoUser *) user->m_user;
gmStringObject * member = (gmStringObject *) GM_OBJECT(a_operands[1].m_value.m_ref);
GM_ASSERT(sizeof(gmptr) == sizeof(time_t));
if(strcmp(member->GetString(), "creationTime") == 0)
a_operands->SetInt((gmptr) fileInfo->m_creationTime);
else if(strcmp(member->GetString(), "accessedTime") == 0)
a_operands->SetInt((gmptr) fileInfo->m_accessedTime);
else if(strcmp(member->GetString(), "modifiedTime") == 0)
a_operands->SetInt((gmptr) fileInfo->m_modifiedTime);
else if(strcmp(member->GetString(), "size") == 0)
a_operands->SetInt((gmptr) fileInfo->m_size);
else
{
a_operands->Nullify();
return;
}
return;
}
a_operands->Nullify();
}
#if GM_USE_INCGC
static void GM_CDECL gmGCDestructFileInfoUserType(gmMachine * a_machine, gmUserObject* a_object)
{
if(a_object->m_user)
{
a_machine->Sys_Free(a_object->m_user);
}
a_object->m_user = NULL;
}
#else //GM_USE_INCGC
static void GM_CDECL gmGCFileInfoUserType(gmMachine * a_machine, gmUserObject * a_object, gmuint32 a_mark)
{
if(a_object->m_user)
{
a_machine->Sys_Free(a_object->m_user);
}
a_object->m_user = NULL;
}
#endif //GM_USE_INCGC
//
//
// binding
//
//
static gmFunctionEntry s_systemLib[] =
{
/*gm
\lib system
\brief system functions are bound in a "system" table.
*/
/*gm
\function Exec
\brief Exec will execute a system command
\param string params will be concatinated together with a single space to form the final system command string
\return integer value returned from system exec call, -1 on error
*/
{"Exec", gmfSystem},
/*gm
\function DoFile
\brief DoFile will execute the gm script in the named file
\param string filename
\param int optional (1) as 1 will execute string before returning, 0 will execute later.
\param ref optional (null) set 'this'
\return thread id of new thread created to execute file
*/
{"DoFile", gmfDoFile},
/*gm
\function File
\brief File will create a file object
\return file object
*/
{"File", gmfFile},
/*gm
\function FileExists
\brief FileExists will test to see if a file exists
\param string filename
\return 1 if the file exists, otherwise 0
*/
{"FileExists", gmfFileExists},
/*gm
\function FileFindFirst
\brief FileFindFirst will start a file search will test to see if a file exists
\param string filesearch (may contain wildcards, eg, `c:\temp\*.txt`)
\return fileFind object. fileFind object has .filename and .size member
\sa fileFind
*/
{"FileFindFirst", gmfFindFirstFile},
/*gm
\function FileFindNext
\brief FileFindNext will get the next file matching the file find
\param fileFind object returned by FileFindFirst call or FileFindNext call
\return fileFind object
\sa fileFind
*/
{"FileFindNext", gmfFindNextFile},
/*gm
\function FileInfo
\brief FileInfo will return a file info object that has readonly members for .creationDate
\param string path
\return fileInfo object, fileInfo object has a .creationTime, .accessedTime, .modifiedTime and a .size
*/
{"FileInfo", gmfFileInfo},
/*gm
\function CreateFolder
\brief CreateFolder will create a file path if it does not already exist
\param string path
\return int 0 on failure, 1 on successful create, 2 if folder already exists
*/
{"CreateFolder", gmfCreateFolder},
/*gm
\function DeleteFolder
\brief DeleteFolder will remove a file path
\param string path
\param int remove subfiles and folders optional (0)
\return int 1 on success, 0 con failure
*/
{"DeleteFolder", gmfDeleteFolder},
/*gm
\function Time
\brief Time will return a unix style time_t as an int
\return the current time
*/
{"Time", gmfTime},
/*gm
\function FormatTime
\brief FormatTime will take a int (time_t) value and format according to the passed format string.
\param int time (-1) is a (time_t) to be converted to a string, passing -1 gets current time
\param string format ("%A %d %B %Y, %I:%M:%S %p") is the format string to use.<BR>
%a : Abbreviated weekday name<BR>
%A : Full weekday name<BR>
%b : Abbreviated month name<BR>
%B : Full month name<BR>
%c : Date and time representation appropriate for locale<BR>
%d : Day of month as decimal number (01 31)<BR>
%H : Hour in 24-hour format (00 23)<BR>
%I : Hour in 12-hour format (01 12)<BR>
%j : Day of year as decimal number (001 366)<BR>
%m : Month as decimal number (01 12)<BR>
%M : Minute as decimal number (00 59)<BR>
%p : Current locales A.M./P.M. indicator for 12-hour clock<BR>
%S : Second as decimal number (00 59)<BR>
%U : Week of year as decimal number, with Sunday as first day of week (00 53)<BR>
%w : Weekday as decimal number (0 6; Sunday is 0)<BR>
%W : Week of year as decimal number, with Monday as first day of week (00 53)<BR>
%x : Date representation for current locale<BR>
%X : Time representation for current locale<BR>
%y : Year without century, as decimal number (00 99)<BR>
%Y : Year with century, as decimal number<BR>
%z, %Z : Time-zone name or abbreviation; no characters if time zone is unknown<BR>
%% : Percent sign<BR>
\return the time as a string.
*/
{"FormatTime", gmfFormatTime},
};
static gmFunctionEntry s_fileFindLib[] =
{
/*gm
\lib fileFind
\brief fileFind object has a "filename" and "size" member
*/
/*gm
\function GetAttribute
\brief GetAttribute will test a file attribute.
\param int char attribute 'r' readonly, 'a' archive, 's' system, 'h' hidden, 'c' compressed, 'd' directory
\return 1 if the attribute is set, 0 otherwise
*/
{"GetAttribute", gmfFileFindGetAttribute},
};
static gmFunctionEntry s_fileLib[] =
{
/*gm
\lib file
*/
/*gm
\function Open
\brief Open will open a file in binary mode
\param string filename
\param int readonly optional (1)
\return 1 if the open was successful, 0 otherwise
*/
{"Open", gmfFileOpen},
/*gm
\function OpenText
\brief OpenText will open a file in text mode
\param string filename
\param int readonly optional (1)
\return 1 if the open was successful, 0 otherwise
*/
{"OpenText", gmfFileOpenText},
/*gm
\function Close
\brief Close will close a file
*/
{"Close", gmfFileClose},
/*gm
\function IsOpen
\brief IsOpen will test to see if a file is open
\return 1 if the file is open, 0 otherwise
*/
{"IsOpen", gmfFileIsOpen},
/*gm
\function Seek
\brief Move to position within file
\param int offset positional offset relative to origin
\param int origin eg. myFile.SEEK_CUR, myFile.SEEK_END, myFile.SEEK_SET for current, end, start origins.
\return int 1 if operation succeeded, 0 if failed.
*/
{"Seek", gmfFileSeek},
/*gm
\function Tell
\brief Tell will return the current cursor position of the file
\return int the current cursor position, -1 on error
*/
{"Tell", gmfFileTell},
/*gm
\function ReadLine
\brief ReadLine will read a line of text from the file.
\param int keep optional (0) as 1 will keep the "\n" char on the line, otherwise it is removed
\return string, or null on eof
*/
{"ReadLine", gmfFileReadLine},
/*gm
\function ReadChar
\brief ReadChar will read a char from the file
\return int char or null on eof
*/
{"ReadChar", gmfFileReadChar},
/*gm
\function WriteString
\brief WriteString will write a string to the file
\param string to write to file
\return 1 on success, null on error
*/
{"WriteString", gmfFileWriteString},
/*gm
\function WriteChar
\brief WriteChar will write a char to the file
\param int char to write to file
\return 1 on success, null on error
*/
{"WriteChar", gmfFileWriteChar},
};
void gmBindSystemLib(gmMachine * a_machine)
{
// system
a_machine->RegisterLibrary(s_systemLib, sizeof(s_systemLib) / sizeof(s_systemLib[0]), "system");
// file
s_gmFileType = a_machine->CreateUserType("file");
#if GM_USE_INCGC
a_machine->RegisterUserCallbacks(s_gmFileType, NULL, gmGCDestructFileUserType);
#else //GM_USE_INCGC
a_machine->RegisterUserCallbacks(s_gmFileType, NULL, gmGCFileUserType);
#endif //GM_USE_INCGC
a_machine->RegisterTypeLibrary(s_gmFileType, s_fileLib, sizeof(s_fileLib) / sizeof(s_fileLib[0]));
a_machine->RegisterTypeOperator(s_gmFileType, O_GETDOT, NULL, gmFileOpGetDot);
// fileFind
s_gmFileFindType = a_machine->CreateUserType("fileFind");
a_machine->RegisterTypeLibrary(s_gmFileFindType, s_fileFindLib, sizeof(s_fileFindLib) / sizeof(s_fileFindLib[0]));
#if GM_USE_INCGC
a_machine->RegisterUserCallbacks(s_gmFileFindType, NULL, gmGCDestructFileFindUserType);
#else //GM_USE_INCGC
a_machine->RegisterUserCallbacks(s_gmFileFindType, NULL, gmGCFileFindUserType);
#endif //GM_USE_INCGC
a_machine->RegisterTypeOperator(s_gmFileFindType, O_GETDOT, NULL, gmFileFindOpGetDot);
// fileInfo
s_gmFileInfoType = a_machine->CreateUserType("fileInfo");
#if GM_USE_INCGC
a_machine->RegisterUserCallbacks(s_gmFileInfoType, NULL, gmGCDestructFileInfoUserType);
#else //GM_USE_INCGC
a_machine->RegisterUserCallbacks(s_gmFileInfoType, NULL, gmGCFileInfoUserType);
#endif //GM_USE_INCGC
a_machine->RegisterTypeOperator(s_gmFileInfoType, O_GETDOT, NULL, gmFileInfoOpGetDot);
}
#endif // GM_SYSTEM_LIB
@@ -0,0 +1,28 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#ifndef _GMSYSTEMLIB_H_
#define _GMSYSTEMLIB_H_
#include "gmConfig.h"
class gmMachine;
#define GM_SYSTEM_LIB 1
#define GM_SYSTEM_LIB_MAX_LINE 1024 // maximum file line length
#if GM_SYSTEM_LIB
void gmBindSystemLib(gmMachine * a_machine);
#endif // GM_SYSTEM_LIB
#endif // _GMSYSTEMLIB_H_
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,37 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#ifndef _GMVECTOR3LIB_H_
#define _GMVECTOR3LIB_H_
#include "gmConfig.h"
#include "gmVariable.h"
class gmMachine;
class gmThread;
class gmUserObject;
// Bind the Vector3 Library.
void gmBindVector3Lib(gmMachine * a_machine);
// Push a Vector3 onto the stack
void gmVector3_Push(gmThread* a_thread, const float* a_vec);
// Create a Vector3 user object and fill it
gmUserObject* gmVector3_Create(gmMachine* a_machine, const float* a_vec);
// The Vector3 type Id.
extern gmType GM_VECTOR3;
// Example of getting Vector3 from parameter
// GM_CHECK_USER_PARAM(float*, GM_VECTOR3, vec1, 0);
#endif // _GMVECTOR3LIB_H_
@@ -0,0 +1,473 @@
#include <windows.h>
#include <mmsystem.h> // multimedia timer (may need winmm.lib)
#include "App.h"
#include "ScriptSys.h"
#include "GameObj.h"
#include "ScriptObj.h"
#include "gmCall.h"
#include "InputKBWin32.h"
App* App::s_instancePtr = NULL;
App::App()
{
s_instancePtr = this;
}
App::~App()
{
s_instancePtr = NULL;
}
bool App::Init()
{
InputKBWin32::Get().Init();
// Init console
InitConsole();
// Init script system for game objects
ScriptSys::Init();
// Register app bindings
RegisterScriptBindings();
// Init timer
m_deltaTime = 0;
m_lastTime = timeGetTime();
// Compile and run script
ScriptSys::Get()->ExecuteFile("TestGameObj.gm");
//TEST REMOVE
// ClearScreen();
// SetColor(COLOR_YELLOW, COLOR_RED);
// SetCursor(10,10);
// Print("Hello");
// PrintAt(10,10,"Hello");
return true;
}
void App::Destroy()
{
ScriptSys::Destroy();
}
bool App::Update()
{
int curTime = timeGetTime();
m_deltaTime = curTime - m_lastTime;
m_lastTime = curTime;
// Update input
InputKBWin32::Get().Update();
// Execute some script
ScriptSys::Get()->Execute(m_deltaTime);
if(InputKBWin32::Get().IsKeyPressed('A'))
{
TestA();
}
else if(InputKBWin32::Get().IsKeyPressed('B'))
{
TestB();
}
else if(InputKBWin32::Get().IsKeyPressed('C'))
{
TestC();
}
if(InputKBWin32::Get().IsKeyPressed(VK_ESCAPE))
{
return false;
}
return true;
}
void App::InitConsole()
{
CONSOLE_SCREEN_BUFFER_INFO csbi;
m_console = GetStdHandle(STD_OUTPUT_HANDLE);
GetConsoleScreenBufferInfo(m_console, &csbi);
DWORD dwConSize = csbi.dwSize.X * csbi.dwSize.Y;
m_screenSizeX = csbi.dwSize.X;
m_screenSizeY = csbi.dwSize.Y;
}
void App::TestA()
{
// Test create a game object and set member in script
GameObj* newObj = new GameObj;
printf("new GameObj = %x\n", newObj);
printf("GameObj.m_scriptObj = %x\n", newObj->GetScriptObj());
newObj->GetScriptObj()->SetMemberString("m_name", "MangoBoy");
newObj->GetScriptObj()->ExecuteGlobalFunctionOnThis("WhatsMyName");
delete newObj;
gmCall call;
if(call.BeginGlobalFunction(ScriptSys::Get()->GetMachine(), "RunGlobalObject"))
{
call.End();
}
}
void App::TestB()
{
// Test call script function that keeps running for a bit
gmCall call;
if(call.BeginGlobalFunction(ScriptSys::Get()->GetMachine(), "ThreadYieldTest"))
{
call.End();
}
}
void App::TestC()
{
// Quick test of collect garbage and cpp owned objects
GameObj* newObj = new GameObj;
printf("new GameObj = %x\n", newObj);
printf("GameObj.m_scriptObj = %x\n", newObj->GetScriptObj());
newObj->GetScriptObj()->SetMemberString("m_name", "PotatoHead");
//newObj->GetScriptObj()->ExecuteGlobalFunctionOnThis("WhatsMyName");
ScriptSys::Get()->GetMachine()->CollectGarbage(true);
delete newObj;
}
void App::SetCursor(int a_x, int a_y)
{
ClipScreenCoordsi(a_x, a_y);
COORD point;
point.X = (short) a_x;
point.Y = (short) a_y;
SetConsoleCursorPosition(m_console, point);
}
void App::ClearScreen()
{
COORD coordScreen = { 0, 0 };
DWORD cCharsWritten;
CONSOLE_SCREEN_BUFFER_INFO csbi;
DWORD dwConSize;
GetConsoleScreenBufferInfo(m_console, &csbi);
dwConSize = csbi.dwSize.X * csbi.dwSize.Y;
FillConsoleOutputCharacter(m_console, TEXT(' '), dwConSize, coordScreen, &cCharsWritten);
GetConsoleScreenBufferInfo(m_console, &csbi);
FillConsoleOutputAttribute(m_console, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten);
SetConsoleCursorPosition(m_console, coordScreen);
}
void App::Print(const char* a_string)
{
printf("%s", a_string);
}
void App::PrintAt(int a_x, int a_y, const char* a_string)
{
SetCursor(a_x, a_y);
Print(a_string);
}
int App::GetAttribFromColIndex(int a_colorIndex, bool a_isForeground)
{
// WARNING These struct must match the color enums
static int foreColors[COLOR_MAX]=
{
0, //COLOR_BLACK
FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE, //COLOR_WHITE,
FOREGROUND_RED, //COLOR_RED
FOREGROUND_GREEN, //COLOR_GREEN,
FOREGROUND_BLUE, //COLOR_BLUE,
FOREGROUND_RED | FOREGROUND_BLUE, //COLOR_MAGENTA,
FOREGROUND_GREEN | FOREGROUND_BLUE, //COLOR_CYAN,
FOREGROUND_RED | FOREGROUND_GREEN, //COLOR_YELLOW,
};
static int backColors[COLOR_MAX]=
{
0, //COLOR_BLACK
BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE, //COLOR_WHITE,
BACKGROUND_RED, //COLOR_RED
BACKGROUND_GREEN, //COLOR_GREEN,
BACKGROUND_BLUE, //COLOR_BLUE,
BACKGROUND_RED | BACKGROUND_BLUE, //COLOR_MAGENTA,
BACKGROUND_GREEN | BACKGROUND_BLUE, //COLOR_CYAN,
BACKGROUND_RED | BACKGROUND_GREEN, //COLOR_YELLOW,
};
if( (a_colorIndex < COLOR_MIN) && (a_colorIndex >= COLOR_MAX) )
{
if(a_isForeground)
{
return foreColors[COLOR_WHITE];
}
else
{
return backColors[COLOR_WHITE];
}
}
if(a_isForeground)
{
return foreColors[a_colorIndex];
}
else
{
return backColors[a_colorIndex];
}
}
void App::SetColor(int a_foreColorIndex, int a_backColorIndex)
{
int foreCol = GetAttribFromColIndex(a_foreColorIndex, true);
int backCol = GetAttribFromColIndex(a_backColorIndex, false);
int param = foreCol | backCol;
SetConsoleTextAttribute(m_console, (short) param);
}
bool App::ClipScreenCoordsf(float& a_posX, float& a_posY)
{
bool wasClipped = false;
if(a_posX < 0.0f)
{
a_posX = 0.0f;
wasClipped = true;
}
else if(a_posX >= (float)App::Get()->GetScreenSizeX())
{
a_posX = (float)(App::Get()->GetScreenSizeX() - 1);
wasClipped = true;
}
if(a_posY < 0.0f)
{
a_posY = 0.0f;
wasClipped = true;
}
else if(a_posY >= (float)App::Get()->GetScreenSizeY())
{
a_posY = (float)(App::Get()->GetScreenSizeY() - 1);
wasClipped = true;
}
return wasClipped;
}
bool App::ClipScreenCoordsi(int& a_posX, int& a_posY)
{
bool wasClipped = false;
if(a_posX < 0)
{
a_posX = 0;
wasClipped = true;
}
else if(a_posX >= App::Get()->GetScreenSizeX())
{
a_posX = App::Get()->GetScreenSizeX() - 1;
wasClipped = true;
}
if(a_posY < 0)
{
a_posY = 0;
wasClipped = true;
}
else if(a_posY >= App::Get()->GetScreenSizeY())
{
a_posY = App::Get()->GetScreenSizeY() - 1;
wasClipped = true;
}
return wasClipped;
}
//////////////////////////////////////////////////
// Script bindings
//////////////////////////////////////////////////
int GM_CDECL App::Console_Print(gmThread* a_thread)
{
GM_CHECK_NUM_PARAMS(1);
GM_CHECK_STRING_PARAM(a_string, 0);
App::Get()->Print(a_string);
return GM_OK;
}
int GM_CDECL App::Console_SetCursor(gmThread* a_thread)
{
GM_CHECK_NUM_PARAMS(2);
GM_CHECK_INT_PARAM(a_curX, 0);
GM_CHECK_INT_PARAM(a_curY, 1);
App::Get()->ClipScreenCoordsi(a_curX, a_curY);
App::Get()->SetCursor(a_curX, a_curY);
return GM_OK;
}
int GM_CDECL App::Console_SetColor(gmThread* a_thread)
{
GM_CHECK_NUM_PARAMS(2);
GM_CHECK_INT_PARAM(a_foreCol, 0);
GM_CHECK_INT_PARAM(a_backCol, 1);
App::Get()->SetColor(a_foreCol, a_backCol);
return GM_OK;
}
int GM_CDECL App::Input_KeyPressed(gmThread* a_thread)
{
GM_CHECK_NUM_PARAMS(1);
GM_CHECK_INT_PARAM(a_vKey, 0);
if(InputKBWin32::Get().IsKeyPressed(a_vKey))
{
a_thread->PushInt(1);
}
else
{
a_thread->PushInt(0);
}
return GM_OK;
}
int GM_CDECL App::Input_KeyDown(gmThread* a_thread)
{
GM_CHECK_NUM_PARAMS(1);
GM_CHECK_INT_PARAM(a_vKey, 0);
if(InputKBWin32::Get().IsKeyDown(a_vKey))
{
a_thread->PushInt(1);
}
else
{
a_thread->PushInt(0);
}
return GM_OK;
}
void App::RegisterScriptBindings()
{
static gmFunctionEntry ConsoleLib[] =
{
/*gm
\lib Console
\brief Console Library
*/
/*gm
\function Print
\brief Print string at current cursor position
\param string a_string
*/
{"Print", Console_Print},
/*gm
\function SetCursor
\brief Set cursor position
\param int a_curX Cursor x position
\param int a_curY Cursor y position
*/
{"SetCursor", Console_SetCursor},
/*gm
\function SetColor
\brief Set text color
\param int a_foreCol Foreground color
\param int a_backCol Background color
*/
{"SetColor", Console_SetColor},
};
static gmFunctionEntry InputLib[] =
{
/*gm
\lib Console
\brief Console Library
*/
/*gm
\function KeyPressed
\brief Was this key pressed
\param int a_vKey windows virtual key code (Most match ascii uppercase)
\return true if key was pressed this frame
*/
{"KeyPressed", Input_KeyPressed},
/*gm
\function KeyDown
\brief Is this key down
\param int a_vKey windows virtual key code (Most match ascii uppercase)
\return true if is down
*/
{"KeyDown", Input_KeyDown},
};
gmMachine* machine = ScriptSys::Get()->GetMachine();
machine->RegisterLibrary(ConsoleLib, sizeof(ConsoleLib) / sizeof(ConsoleLib[0]), "Console");
machine->RegisterLibrary(InputLib, sizeof(InputLib) / sizeof(InputLib[0]), "Input");
// Make some global constants
machine->GetGlobals()->Set(machine, "COLOR_BLACK", gmVariable(GM_INT, COLOR_BLACK));
machine->GetGlobals()->Set(machine, "COLOR_WHITE", gmVariable(GM_INT, COLOR_WHITE));
machine->GetGlobals()->Set(machine, "COLOR_RED", gmVariable(GM_INT, COLOR_RED));
machine->GetGlobals()->Set(machine, "COLOR_GREEN", gmVariable(GM_INT, COLOR_GREEN));
machine->GetGlobals()->Set(machine, "COLOR_BLUE", gmVariable(GM_INT, COLOR_BLUE));
machine->GetGlobals()->Set(machine, "COLOR_MAGENTA", gmVariable(GM_INT, COLOR_MAGENTA));
machine->GetGlobals()->Set(machine, "COLOR_CYAN", gmVariable(GM_INT, COLOR_CYAN));
machine->GetGlobals()->Set(machine, "COLOR_YELLOW", gmVariable(GM_INT, COLOR_YELLOW));
}
@@ -0,0 +1,72 @@
//
// Example application
//
#include <Windows.h>
#include "gmThread.h"
class App
{
public:
enum
{
COLOR_MIN = 0,
COLOR_BLACK = COLOR_MIN,
COLOR_WHITE,
COLOR_RED,
COLOR_GREEN,
COLOR_BLUE,
COLOR_MAGENTA,
COLOR_CYAN,
COLOR_YELLOW,
COLOR_MAX
};
static App* Get() { return s_instancePtr; }
App();
~App();
bool Init();
void Destroy();
bool Update();
void SetCursor(int a_x, int a_y);
void ClearScreen();
void Print(const char* a_string);
void PrintAt(int a_x, int a_y, const char* a_string);
void SetColor(int a_foreColorIndex, int a_backColorIndex);
int GetScreenSizeX() { return m_screenSizeX; }
int GetScreenSizeY() { return m_screenSizeY; }
bool ClipScreenCoordsf(float& a_posX, float& a_posY);
bool ClipScreenCoordsi(int& a_posX, int& a_posY);
protected:
int GetAttribFromColIndex(int a_colorIndex, bool a_isForeground);
void InitConsole();
void RegisterScriptBindings();
static int GM_CDECL Console_Print(gmThread* a_thread);
static int GM_CDECL Console_SetCursor(gmThread* a_thread);
static int GM_CDECL Console_SetColor(gmThread* a_thread);
static int GM_CDECL Input_KeyPressed(gmThread* a_thread);
static int GM_CDECL Input_KeyDown(gmThread* a_thread);
void TestA();
void TestB();
void TestC();
int m_deltaTime;
int m_lastTime;
int m_screenSizeX;
int m_screenSizeY;
HANDLE m_console;
static App* s_instancePtr; ///< Ptr to instance of this class when created
};
@@ -0,0 +1,317 @@
//
// GameObj.cpp
//
#include <math.h>
#include "GameObj.h"
#include "App.h"
#include "ScriptObj.h"
#include "ScriptSys.h"
GameObj::GameObj()
{
m_scriptObj = NULL;
m_posX = -1;
m_posY = -1;
m_destX = m_posX;
m_destY = m_posY;
m_speed = 1.0f;
m_colorIndex = App::COLOR_WHITE;
m_scriptObj = new ScriptObj(this);
}
GameObj::~GameObj()
{
if(m_scriptObj)
{
delete m_scriptObj;
}
}
float GameObj::GetPosX() const
{
return m_posX;
}
float GameObj::GetPosY() const
{
return m_posY;
}
void GameObj::SetPos(const float a_x, const float a_y)
{
m_posX = a_x;
m_posY = a_y;
App::Get()->ClipScreenCoordsf(m_posX, m_posY);
m_destX = m_posX;
m_destY = m_posY;
}
void GameObj::MoveTo(const float a_x, const float a_y)
{
m_destX = a_x;
m_destY = a_y;
App::Get()->ClipScreenCoordsf(m_destX, m_destY);
}
void GameObj::SetSpeed(const float a_speed)
{
m_speed = a_speed;
}
float GameObj::GetSpeed()
{
return m_speed;
}
void GameObj::SetColor(int a_colorIndex)
{
if(a_colorIndex >= App::COLOR_MIN && a_colorIndex < App::COLOR_MAX)
{
m_colorIndex = a_colorIndex;
}
}
void GameObj::Update(float a_deltaTime)
{
// NOTE: A real game would probably not update each object each frame
// but instead only update an object in a particular way when required.
// This example will do all updating in one place, and do so each frame.
// Update movement
{
float dx = m_destX - m_posX;
float dy = m_destY - m_posY;
float len2 = dx*dx + dy*dy;
if(len2 > 0.0f)
{
float moveThisFrame = a_deltaTime * m_speed;
float distToGo = sqrtf(len2);
// We can reach dest this frame
if(moveThisFrame > distToGo)
{
m_posX = m_destX;
m_posY = m_destY;
}
else
{
m_posX += dx * moveThisFrame;
m_posY += dy * moveThisFrame;
}
}
}
}
void GameObj::Render()
{
}
//////////////////////////////////////////////////
// Script
//////////////////////////////////////////////////
int GM_CDECL GameObj::GameObj_MoveTo(gmThread* a_thread)
{
GM_CHECK_NUM_PARAMS(2);
GM_CHECK_FLOAT_PARAM(destX, 0);
GM_CHECK_FLOAT_PARAM(destY, 1);
GameObj* thisPtr = GetThisGameObj(a_thread);
if(!thisPtr)
{
return GM_EXCEPTION;
}
thisPtr->MoveTo(destX, destY);
return GM_OK;
}
int GM_CDECL GameObj::GameObj_SetPos(gmThread* a_thread)
{
GM_CHECK_NUM_PARAMS(2);
GM_CHECK_FLOAT_PARAM(posX, 0);
GM_CHECK_FLOAT_PARAM(posY, 1);
GameObj* thisPtr = GetThisGameObj(a_thread);
if(!thisPtr)
{
return GM_EXCEPTION;
}
thisPtr->SetPos(posX, posY);
return GM_OK;
}
int GM_CDECL GameObj::GameObj_GetPosX(gmThread* a_thread)
{
GM_CHECK_NUM_PARAMS(0);
GameObj* thisPtr = GetThisGameObj(a_thread);
if(!thisPtr)
{
return GM_EXCEPTION;
}
a_thread->PushFloat(thisPtr->GetPosX());
return GM_OK;
}
int GM_CDECL GameObj::GameObj_GetPosY(gmThread* a_thread)
{
GM_CHECK_NUM_PARAMS(0);
GameObj* thisPtr = GetThisGameObj(a_thread);
if(!thisPtr)
{
return GM_EXCEPTION;
}
a_thread->PushFloat(thisPtr->GetPosY());
return GM_OK;
}
int GM_CDECL GameObj::GameObj_IsValid(gmThread* a_thread)
{
GM_CHECK_NUM_PARAMS(0);
GameObj* thisPtr = GetThisGameObj(a_thread);
if(!thisPtr)
{
a_thread->PushInt(false);
}
else
{
a_thread->PushInt(true);
}
return GM_OK;
}
int GM_CDECL GameObj::GameObj_SetSpeed(gmThread* a_thread)
{
GM_CHECK_NUM_PARAMS(1);
GM_CHECK_FLOAT_PARAM(speed, 0);
GameObj* thisPtr = GetThisGameObj(a_thread);
if(!thisPtr)
{
return GM_EXCEPTION;
}
thisPtr->SetSpeed(speed);
return GM_OK;
}
int GM_CDECL GameObj::GameObj_GetSpeed(gmThread* a_thread)
{
GM_CHECK_NUM_PARAMS(0);
GameObj* thisPtr = GetThisGameObj(a_thread);
if(!thisPtr)
{
return GM_EXCEPTION;
}
a_thread->PushFloat(thisPtr->GetSpeed());
return GM_OK;
}
int GM_CDECL GameObj::GameObj_SetColor(gmThread* a_thread)
{
GM_CHECK_NUM_PARAMS(1);
GM_CHECK_INT_PARAM(colorIndex, 0);
GameObj* thisPtr = GetThisGameObj(a_thread);
if(!thisPtr)
{
return GM_EXCEPTION;
}
thisPtr->SetColor(colorIndex);
return GM_OK;
}
void GameObj::RegisterScriptBindings()
{
static gmFunctionEntry gameObjTypeLib[] =
{
/*gm
\lib GameObj
\brief Game Object class
*/
/*gm
\function SetPos
\brief Set position
\param float a_posX New position X component
\param float a_posY New position Y component
*/
{"SetPos", GameObj_SetPos},
/*gm
\function GetPosX
\brief Get position X
\return float Get position X component.
*/
{"GetPosX", GameObj_GetPosX},
/*gm
\function GetPosY
\brief Get position Y
\return float Get position Y component.
*/
{"GetPosY", GameObj_GetPosY},
/*gm
\function IsValid
\brief Is this a valid object, or has it been deleted or such
\return int true if valid
*/
{"IsValid", GameObj_IsValid},
/*gm
\function SetSpeed
\brief Set speed
\param a_speed New speed
*/
{"SetSpeed", GameObj_SetSpeed},
/*gm
\function GetSpeed
\brief Get speed
\return float Current speed
*/
{"GetSpeed", GameObj_GetSpeed},
/*gm
\function SetColor
\brief Set color
\param a_color New color
*/
{"SetColor", GameObj_SetColor},
};
ScriptSys::Get()->GetMachine()->RegisterTypeLibrary(ScriptObj::GMTYPE_GAMEOBJ, gameObjTypeLib, sizeof(gameObjTypeLib) / sizeof(gameObjTypeLib[0]));
}
@@ -0,0 +1,61 @@
#ifndef GAMEOBJ_H
#define GAMEOBJ_H
#include "gmThread.h"
//
// GameObj.h
//
// Example game object that uses the script interface component
//
// Fwd decls
class ScriptObj;
class GameObj
{
public:
GameObj();
virtual ~GameObj();
ScriptObj* GetScriptObj() { return m_scriptObj; }
float GetPosX() const;
float GetPosY() const;
void SetPos(const float a_x, const float a_y);
void MoveTo(const float a_x, const float a_y);
void SetSpeed(const float a_speed);
float GetSpeed();
void SetColor(int a_colorIndex);
void Update(float a_deltaTime);
void Render();
static void RegisterScriptBindings();
private:
static int GM_CDECL GameObj_SetPos(gmThread* a_thread);
static int GM_CDECL GameObj_GetPosX(gmThread* a_thread);
static int GM_CDECL GameObj_GetPosY(gmThread* a_thread);
static int GM_CDECL GameObj_IsValid(gmThread* a_thread);
static int GM_CDECL GameObj_MoveTo(gmThread* a_thread);
static int GM_CDECL GameObj_SetSpeed(gmThread* a_thread);
static int GM_CDECL GameObj_GetSpeed(gmThread* a_thread);
static int GM_CDECL GameObj_SetColor(gmThread* a_thread);
ScriptObj* m_scriptObj;
float m_posX;
float m_posY;
float m_destX;
float m_destY;
float m_speed;
int m_colorIndex;
};
#endif //GAMEOBJ_H
@@ -0,0 +1,470 @@
# Microsoft Developer Studio Project File - Name="GameObject" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Console Application" 0x0103
CFG=GameObject - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE
!MESSAGE NMAKE /f "GameObject.mak".
!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
!MESSAGE NMAKE /f "GameObject.mak" CFG="GameObject - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "GameObject - Win32 Release" (based on "Win32 (x86) Console Application")
!MESSAGE "GameObject - Win32 Debug" (based on "Win32 (x86) Console Application")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName "Perforce Project"
# PROP Scc_LocalPath "..\.."
CPP=cl.exe
RSC=rc.exe
!IF "$(CFG)" == "GameObject - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Release"
# PROP BASE Intermediate_Dir "Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Release"
# PROP Intermediate_Dir "Release"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\gm" /I "..\..\platform\win32msvc" /I "..\..\binds" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD BASE RSC /l 0xc09 /d "NDEBUG"
# ADD RSC /l 0xc09 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
# ADD LINK32 winmm.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Ws2_32.lib /nologo /subsystem:console /machine:I386
!ELSEIF "$(CFG)" == "GameObject - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "Debug"
# PROP BASE Intermediate_Dir "Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "Debug"
# PROP Intermediate_Dir "Debug"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\gm" /I "..\..\platform\win32msvc" /I "..\..\binds" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
# ADD BASE RSC /l 0xc09 /d "_DEBUG"
# ADD RSC /l 0xc09 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
# ADD LINK32 winmm.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Ws2_32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
!ENDIF
# Begin Target
# Name "GameObject - Win32 Release"
# Name "GameObject - Win32 Debug"
# Begin Group "Source Files"
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat;h;hpp;hxx;hm;inl"
# Begin Source File
SOURCE=.\App.cpp
# End Source File
# Begin Source File
SOURCE=.\App.h
# End Source File
# Begin Source File
SOURCE=.\GameObj.cpp
# End Source File
# Begin Source File
SOURCE=.\GameObj.h
# End Source File
# Begin Source File
SOURCE=.\InputKBWin32.cpp
# End Source File
# Begin Source File
SOURCE=.\InputKBWin32.h
# End Source File
# Begin Source File
SOURCE=.\main.cpp
# End Source File
# Begin Source File
SOURCE=.\NetClient.cpp
# End Source File
# Begin Source File
SOURCE=.\NetClient.h
# End Source File
# Begin Source File
SOURCE=.\ReadMe.txt
# End Source File
# Begin Source File
SOURCE=.\ScriptObj.cpp
# End Source File
# Begin Source File
SOURCE=.\ScriptObj.h
# End Source File
# Begin Source File
SOURCE=.\ScriptSys.cpp
# End Source File
# Begin Source File
SOURCE=.\ScriptSys.h
# End Source File
# Begin Source File
SOURCE=.\StdStuff.cpp
# End Source File
# Begin Source File
SOURCE=.\StdStuff.h
# End Source File
# Begin Source File
SOURCE=.\TestGameObj.gm
# End Source File
# End Group
# Begin Group "gm"
# PROP Default_Filter ""
# Begin Source File
SOURCE=..\..\gm\gmArraySimple.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmArraySimple.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmByteCode.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmByteCode.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmByteCodeGen.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmByteCodeGen.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmCodeGen.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmCodeGen.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmCodeGenHooks.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmCodeGenHooks.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmCodeTree.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmCodeTree.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmConfig.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmCrc.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmCrc.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmDebug.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmDebug.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmFunctionObject.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmFunctionObject.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmHash.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmHash.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmIncGC.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmIncGC.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmIterator.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmLibHooks.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmLibHooks.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmListDouble.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmListDouble.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmLog.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmLog.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmMachine.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmMachine.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmMachineLib.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmMachineLib.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmMem.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmMem.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmMemChain.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmMemChain.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmMemFixed.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmMemFixed.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmMemFixedSet.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmMemFixedSet.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmOperators.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmOperators.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmParser.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmParser.cpp.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmScanner.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmScanner.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmStream.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmStream.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmStreamBuffer.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmStreamBuffer.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmStringObject.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmStringObject.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmTableObject.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmTableObject.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmThread.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmThread.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmUserObject.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmUserObject.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmUtil.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmUtil.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmVariable.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmVariable.h
# End Source File
# End Group
# Begin Group "gmBinds"
# PROP Default_Filter ""
# Begin Source File
SOURCE=..\..\binds\gmCall.cpp
# End Source File
# Begin Source File
SOURCE=..\..\binds\gmCall.h
# End Source File
# Begin Source File
SOURCE=..\..\binds\gmHelpers.cpp
# End Source File
# Begin Source File
SOURCE=..\..\binds\gmHelpers.h
# End Source File
# Begin Source File
SOURCE=..\..\binds\gmMathLib.cpp
# End Source File
# Begin Source File
SOURCE=..\..\binds\gmMathLib.h
# End Source File
# Begin Source File
SOURCE=..\..\binds\gmStringLib.cpp
# End Source File
# Begin Source File
SOURCE=..\..\binds\gmStringLib.h
# End Source File
# Begin Source File
SOURCE=..\..\binds\gmSystemLib.cpp
# End Source File
# Begin Source File
SOURCE=..\..\binds\gmSystemLib.h
# End Source File
# End Group
# Begin Group "gmConfig"
# PROP Default_Filter ""
# Begin Source File
SOURCE=..\..\platform\win32msvc\gmConfig_p.h
# End Source File
# End Group
# End Target
# End Project
@@ -0,0 +1,33 @@
Microsoft Developer Studio Workspace File, Format Version 6.00
# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
###############################################################################
Project: "GameObject"=.\GameObject.dsp - Package Owner=<4>
Package=<5>
{{{
begin source code control
Perforce Project
..\..
end source code control
}}}
Package=<4>
{{{
}}}
###############################################################################
Global:
Package=<5>
{{{
}}}
Package=<3>
{{{
}}}
###############################################################################
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,102 @@
//
// InputKBWin32.cpp
//
#include <windows.h>
#include "InputKBWin32.h"
// Init statics and constants
InputKBWin32 InputKBWin32::s_staticInstance;
InputKBWin32::InputKBWin32()
{
m_keyDownBufferIndex = 0;
Init();
}
InputKBWin32::~InputKBWin32()
{
}
void InputKBWin32::Init()
{
for(int kIndex=0; kIndex<MAX_KEYS; ++kIndex)
{
m_keyDownBuffer[0][kIndex] = 0;
m_keyDownBuffer[1][kIndex] = 0;
m_keyStatus[kIndex] = KEY_STATUS_UP;
}
}
void InputKBWin32::Update()
{
//#define HAS_WINDOW_UPDATE // Define this if we have a window and a message pump
int lastBuffIndex = !m_keyDownBufferIndex;
int curBuffIndex = m_keyDownBufferIndex;
m_keyDownBufferIndex = lastBuffIndex; // Flip buffers
char* bufferCurrent = &m_keyDownBuffer[curBuffIndex][0];
char* bufferLast = &m_keyDownBuffer[lastBuffIndex][0];
// Get the button states from Win32
#ifdef HAS_WINDOW_UPDATE
// We have a window and message pump
BYTE win32KeyBuffer[256];
GetKeyboardState(win32KeyBuffer);
#else // HAS_WINDOW_UPDATE
short win32KeyBuffer[256];
for(int vkIndex=0; vkIndex < 256; ++vkIndex)
{
win32KeyBuffer[vkIndex] = GetAsyncKeyState(vkIndex);
}
#endif //HAS_WINDOW_UPDATE
// Find state changes
for(int kIndex=0; kIndex < MAX_KEYS; ++kIndex)
{
int status;
// Convert win32 keystate to true / false
#ifdef HAS_WINDOW_UPDATE
if(win32KeyBuffer[kIndex] & (1<<7))
#else // HAS_WINDOW_UPDATE
if(win32KeyBuffer[kIndex] & (1<<15))
#endif // HAS_WINDOW_UPDATE
{
bufferCurrent[kIndex] = true;
}
else
{
bufferCurrent[kIndex] = false;
}
status = 0;
if(bufferCurrent[kIndex])
{
status |= KEY_STATUS_DOWN;
if(!bufferLast[kIndex])
{
status |= KEY_STATUS_PRESSED;
}
}
else
{
status |= KEY_STATUS_UP;
if(bufferLast[kIndex])
{
status |= KEY_STATUS_RELEASED;
}
}
m_keyStatus[kIndex] = status;
}
}
@@ -0,0 +1,90 @@
#ifndef INPUTKBWIN32_H
#define INPUTKBWIN32_H
//
// InputKBWin32.h
//
#include "gmThread.h" // For some basic types
/// A simple keyboard input class for Win32.
class InputKBWin32
{
public:
enum
{
MAX_KEYS = 256, ///< Max keys on keyboard (for buffer size etc.)
};
enum
{
KEY_STATUS_UNKNOWN = 0, ///< Invalid status
KEY_STATUS_UP = (1<<0), ///< Button is up
KEY_STATUS_RELEASED = (1<<1), ///< Button released this frame
KEY_STATUS_DOWN = (1<<2), ///< Button is down
KEY_STATUS_PRESSED = (1<<3), ///< Button pressed this frame
};
/// Access single instance of this class
static InputKBWin32& Get() { return s_staticInstance; }
/// Destructor
virtual ~InputKBWin32();
/// Initialize. Call before use.
void Init();
/// Call each frame.
void Update();
/// What is the status of a key. Returns one of the KEY_STATUS_* enums.
inline int GetKeyStatus(int a_keyIndex)
{
GM_ASSERT((a_keyIndex >=0) && (a_keyIndex < MAX_KEYS));
return m_keyStatus[a_keyIndex];
}
/// Was key pressed this frame? (Non-zero if true)
inline int IsKeyPressed(int a_keyIndex)
{
GM_ASSERT((a_keyIndex >= 0) && (a_keyIndex < MAX_KEYS));
return (m_keyStatus[a_keyIndex] & KEY_STATUS_PRESSED);
}
/// Is key down this frame? (Non-zero if true)
inline int IsKeyDown(int a_keyIndex)
{
GM_ASSERT((a_keyIndex >= 0) && (a_keyIndex < MAX_KEYS));
return (m_keyStatus[a_keyIndex] & KEY_STATUS_DOWN);
}
/// Was key released this frame? (Non-zero if true)
inline int IsKeyRelesed(int a_keyIndex)
{
GM_ASSERT((a_keyIndex >= 0) && (a_keyIndex < MAX_KEYS));
return (m_keyStatus[a_keyIndex] & KEY_STATUS_RELEASED);
}
/// Is key up this frame? (Non-zero if true)
inline int IsKeyUp(int a_keyIndex)
{
GM_ASSERT((a_keyIndex >= 0) && (a_keyIndex < MAX_KEYS));
return (m_keyStatus[a_keyIndex] & KEY_STATUS_UP);
}
private:
/// Constructor, non-public to prevent multiple instances
InputKBWin32();
char m_keyDownBuffer[2][MAX_KEYS]; ///< Store current and last frame snapshot
int m_keyDownBufferIndex; ///< Index to swap buffers for current and last frame
int m_keyStatus[MAX_KEYS]; ///< Status of keys for this frame, persists until updated.
static InputKBWin32 s_staticInstance; ///< Single instance of this class
};
#endif //INPUTKBWIN32_H
@@ -0,0 +1,356 @@
// See Copyright Notice in gmMachine.h
#include "NetClient.h"
#include <windows.h>
#include <process.h> // Requires Multi threaded library for _beginthread and _endthread
#include <stddef.h>
#include <stdlib.h>
#include <conio.h>
#include <winsock.h> // Requires Ws2_32.lib
#include <math.h>
#undef SendMessage // stupid windows
// These two are for MSVS 2005 security consciousness until safe std lib funcs are available
#pragma warning(disable : 4996) // Deprecated functions
#define _CRT_SECURE_NO_DEPRECATE // Allow old unsecure standard library functions, Disable some 'warning C4996 - function was deprecated'
struct nPacket
{
int id; // id == 0x4fe27d9a
int len;
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// QUEUE
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
struct nQueueNode
{
char * m_buffer;
int m_len;
nQueueNode * m_next;
};
class nQueue
{
public:
nQueue()
{
m_queue = NULL;
m_lastDeQueue = NULL;
m_mutex = CreateMutex(NULL, FALSE, NULL);
}
~nQueue()
{
WaitForSingleObject(m_mutex, INFINITE);
int a;
while(DeQueue(a));
DeQueue(a);
ReleaseMutex(m_mutex);
// destroy mutex... todo
}
bool EnQueue(const char * a_buffer, int a_len)
{
WaitForSingleObject(m_mutex, INFINITE);
nQueueNode * node = new nQueueNode;
node->m_len = a_len;
node->m_buffer = new char[a_len];
memcpy(node->m_buffer, a_buffer, a_len);
node->m_next = NULL;
// add to end of list
nQueueNode ** n = &m_queue;
while(*n) n = &(*n)->m_next;
*n = node;
ReleaseMutex(m_mutex);
return true;
}
const char * DeQueue(int &a_len)
{
const char * ret = NULL;
WaitForSingleObject(m_mutex, INFINITE);
if(m_lastDeQueue)
{
delete[] m_lastDeQueue->m_buffer;
delete m_lastDeQueue;
m_lastDeQueue = NULL;
}
if(m_queue)
{
m_lastDeQueue = m_queue;
m_queue = m_queue->m_next;
a_len = m_lastDeQueue->m_len;
ret = m_lastDeQueue->m_buffer;
}
ReleaseMutex(m_mutex);
return ret;
}
bool IsEmpty() { return (m_queue == NULL); }
private:
HANDLE m_mutex;
nQueueNode * m_lastDeQueue;
nQueueNode * m_queue;
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// CLIENT
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
struct nClientData
{
SOCKET client;
nQueue messages;
CRITICAL_SECTION criticalSection;
bool threadAlive;
};
nClient::nClient()
{
nClientData * cd = new nClientData;
cd->threadAlive = false;
cd->client = INVALID_SOCKET;
InitializeCriticalSection(&cd->criticalSection);
m_data = cd;
}
nClient::~nClient()
{
Close();
nClientData * cd = (nClientData *) m_data;
DeleteCriticalSection(&cd->criticalSection);
delete cd;
}
void nClient::Close()
{
nClientData * cd = (nClientData *) m_data;
EnterCriticalSection(&cd->criticalSection);
if(cd->client != INVALID_SOCKET)
{
closesocket(cd->client);
cd->client = INVALID_SOCKET;
}
LeaveCriticalSection(&cd->criticalSection);
// wait for the thread to die.
while(cd->threadAlive)
{
_sleep(0);
}
WSACleanup();
}
bool nClient::IsConnected()
{
bool result = false;
nClientData * cd = (nClientData *) m_data;
EnterCriticalSection(&cd->criticalSection);
if(cd->client != INVALID_SOCKET)
{
result = true;
}
LeaveCriticalSection(&cd->criticalSection);
return result;
}
bool nClient::SendMessage(const char * a_buffer, int a_len)
{
bool res = false;
nClientData * cd = (nClientData *) m_data;
nPacket packet;
packet.id = 0x4fe27d9a;
packet.len = a_len;
EnterCriticalSection(&cd->criticalSection);
if(cd->client != INVALID_SOCKET)
{
send(cd->client, (const char *) &packet, sizeof(nPacket), 0);
send(cd->client, (const char *) a_buffer, a_len, 0);
res = true;
}
LeaveCriticalSection(&cd->criticalSection);
return res;
}
const char * nClient::PumpMessage(int &a_len)
{
nClientData * cd = (nClientData *) m_data;
const char * buffer = cd->messages.DeQueue(a_len);
return buffer;
}
void nClientThread(void * param)
{
nClientData * cd = (nClientData *) param;
EnterCriticalSection(&cd->criticalSection);
cd->threadAlive = true;
SOCKET client = cd->client;
LeaveCriticalSection(&cd->criticalSection);
char * dbuffer = NULL;
char buffer[4096];
char * sbp;
int state = 0; // 0 searching for packet, 1 getting message
int need = sizeof(nPacket);
// packet header
nPacket packet;
char * dbp = (char *) &packet;
// packet data
int dbufferSize = 0, n;
// read loop
for(;;)
{
// read
n = recv(client, buffer, 4096, 0);
if(n == SOCKET_ERROR || n == 0) break;
sbp = buffer;
// consume
while(n > 0)
{
int have = (n > need) ? need : n;
need -= have;
n -= have;
memcpy(dbp, sbp, have);
sbp += have;
dbp += have;
// can we change state?
if(need == 0)
{
if(state == 0)
{
if(packet.id != 0x4fe27d9a) goto terror;
state = 1;
need = packet.len;
// allocate the dbuffer
if(need > dbufferSize)
{
if(dbuffer) { delete[] dbuffer; }
dbufferSize = need + 512;
dbuffer = new char[dbufferSize];
}
dbp = dbuffer;
}
else if(state == 1)
{
cd->messages.EnQueue(dbuffer, packet.len);
dbp = (char *) &packet;
need = sizeof(nPacket);
state = 0;
}
}
}
}
terror:
if(dbuffer) { delete[] dbuffer; }
EnterCriticalSection(&cd->criticalSection);
cd->threadAlive = false;
LeaveCriticalSection(&cd->criticalSection);
_endthread();
}
bool nClient::Connect(const char * a_server, short a_port)
{
WSADATA wsaData;
struct hostent *hp;
unsigned int addr;
struct sockaddr_in server;
int wsaret=WSAStartup(0x101,&wsaData);
if(wsaret)
return false;
SOCKET conn;
conn = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(conn==INVALID_SOCKET)
return false;
addr=inet_addr(a_server);
hp=gethostbyaddr((char*)&addr,sizeof(addr),AF_INET);
if(hp==NULL)
{
closesocket(conn);
return false;
}
server.sin_addr.s_addr=*((unsigned long*)hp->h_addr);
server.sin_family=AF_INET;
server.sin_port=htons((u_short) a_port);
if(connect(conn,(struct sockaddr*)&server,sizeof(server)))
{
closesocket(conn);
return false;
}
nClientData * cd = (nClientData *) m_data;
EnterCriticalSection(&cd->criticalSection);
cd->client = conn;
LeaveCriticalSection(&cd->criticalSection);
_beginthread(nClientThread, 0, cd);
_sleep(0);
return true;
}
@@ -0,0 +1,30 @@
#ifndef _NETCLIENT_H_
#define _NETCLIENT_H_
// See Copyright Notice in gmMachine.h
#undef SendMessage // windows clash
//
// nClient
//
class nClient
{
public:
nClient();
~nClient();
bool Connect(const char * a_server, short a_port);
void Close();
bool IsConnected();
bool SendMessage(const char * a_buffer, int a_len);
const char * PumpMessage(int &a_len);
private:
void * m_data;
};
#endif //_NETCLIENT_H_
@@ -0,0 +1,10 @@
This example is simply one way to implement 'Game Objects'. The example is a work in
progress, and is included because some of the code may provide useful to look at.
TODO
o make 'triggers' and 'units'
o make IsUnitInSquare function to clip movement
o use states for units to be 'player' 'following' 'mental'
o make triggers that change unit color and triggers that create mental state if not player
o highlight player by color and allow player to cycle through units to control
@@ -0,0 +1,326 @@
//
// ScriptObj.cpp
//
#include "gmCall.h"
#include "ScriptObj.h"
#include "ScriptSys.h"
#include "GameObj.h"
// Init statics and constants
gmType ScriptObj::GMTYPE_GAMEOBJ = -1;
ScriptObj::ScriptObj(GameObj* a_gameObj)
{
GM_ASSERT(ScriptSys::Get());
m_userObject = NULL; // A user object will be created when it is first used, and shared by all script variables.
m_gameObj = a_gameObj;
m_tableObject = ScriptSys::Get()->GetMachine()->AllocTableObject();
ScriptSys::Get()->GetMachine()->AddCPPOwnedGMObject(m_tableObject);
}
ScriptObj::~ScriptObj()
{
// Stop related threads
KillThreads();
if(m_userObject)
{
// Nullify script link to C object
m_userObject->m_user = NULL;
}
// Destruct the gmObjects
#if GM_USE_INCGC
// Do nothing, it will be collected later, just nullify all reference to it
#else
if(m_userObject)
{
m_userObject->Destruct(ScriptSys::Get()->GetMachine());
}
m_tableObject->Destruct(ScriptSys::Get()->GetMachine());
#endif
// Remove object from the list of all objects
ScriptSys::Get()->GetMachine()->RemoveCPPOwnedGMObject(m_tableObject);
if( m_userObject )
{
ScriptSys::Get()->GetMachine()->RemoveCPPOwnedGMObject(m_userObject);
}
}
gmUserObject* ScriptObj::GetUserObject()
{
if(!m_userObject)
{
m_userObject = ScriptSys::Get()->GetMachine()->AllocUserObject(this, GMTYPE_GAMEOBJ);
ScriptSys::Get()->GetMachine()->AddCPPOwnedGMObject(m_userObject);
}
return m_userObject;
}
void ScriptObj::KillThreads()
{
for(unsigned int tIndex=0; tIndex<m_threads.GetSize(); ++tIndex)
{
ScriptSys::Get()->RemoveThreadIdButDontTouchGameObj(m_threads[tIndex]);
ScriptSys::Get()->GetMachine()->KillThread(m_threads[tIndex]);
}
m_threads.Reset();
}
void ScriptObj::ExecuteStringOnThis(const char* a_string)
{
gmMachine* machine = ScriptSys::Get()->GetMachine();
gmVariable thisVar;
thisVar.SetUser(GetUserObject());
int threadId = GM_INVALID_THREAD;
int errors = machine->ExecuteString(a_string, &threadId, true, NULL, &thisVar);
if(errors)
{
bool first = true;
const char * message;
while((message = machine->GetLog().GetEntry(first)))
{
ScriptSys::Get()->LogError("%s\n", message);
}
machine->GetLog().Reset();
}
else
{
ScriptSys::Get()->AssociateThreadIdWithGameObj(threadId, *GetGameObj());
}
}
bool ScriptObj::ExecuteGlobalFunctionOnThis(const char* a_functionName)
{
gmVariable thisVar;
thisVar.SetUser(GetUserObject());
gmCall call;
if(call.BeginGlobalFunction(ScriptSys::Get()->GetMachine(), a_functionName, thisVar, false))
{
call.End();
return true;
}
return false;
}
void ScriptObj::SetMemberInt(const char* a_memberName, int a_int)
{
ScriptSys::Get()->SetTableInt(a_memberName, a_int, m_tableObject);
}
void ScriptObj::SetMemberFloat(const char* a_memberName, float a_float)
{
ScriptSys::Get()->SetTableFloat(a_memberName, a_float, m_tableObject);
}
void ScriptObj::SetMemberString(const char* a_memberName, const char* a_string, int a_strLength)
{
ScriptSys::Get()->SetTableString(a_memberName, a_string, a_strLength, m_tableObject);
}
void ScriptObj::SetMemberGameObj(const char* a_memberName, GameObj* a_gameObj)
{
ScriptSys::Get()->SetTableGameObj(a_memberName, a_gameObj, m_tableObject);
}
gmTableObject* ScriptObj::SetMemberTable(const char* a_memberName)
{
return ScriptSys::Get()->SetTableTable(a_memberName, m_tableObject);
}
bool ScriptObj::GetMemberInt(const char* a_memberName, int& a_int)
{
return ScriptSys::Get()->GetTableInt(a_memberName, a_int, m_tableObject);
}
bool ScriptObj::GetMemberFloat(const char* a_memberName, float& a_float)
{
return ScriptSys::Get()->GetTableFloat(a_memberName, a_float, m_tableObject);
}
bool ScriptObj::GetMemberString(const char* a_memberName, String& a_string)
{
return ScriptSys::Get()->GetTableString(a_memberName, a_string, m_tableObject);
}
bool ScriptObj::GetMemberGameObj(const char* a_memberName, GameObj*& a_gameObj)
{
return ScriptSys::Get()->GetTableGameObj(a_memberName, a_gameObj, m_tableObject);
}
bool ScriptObj::GetMemberTable(const char* a_memberName, gmTableObject*& a_retTable)
{
return ScriptSys::Get()->GetTableTable(a_memberName, a_retTable, m_tableObject);
}
void GM_CDECL ScriptObj::GameObjCallback_AsString(gmUserObject * a_object, char* a_buffer, int a_bufferLen)
{
char mixBuffer[128];
ScriptObj* scriptObj = (ScriptObj*)a_object->m_user;
GameObj* gameObjPtr = NULL;
if(scriptObj)
{
gameObjPtr = scriptObj->GetGameObj();
}
sprintf(mixBuffer,"CPtr: %x", gameObjPtr);
int mixLength = strlen(mixBuffer);
int useLength = gmMin(mixLength, a_bufferLen-1);
GM_ASSERT(useLength > 0);
strncpy(a_buffer, mixBuffer, useLength);
a_buffer[useLength] = 0;
}
#if GM_USE_INCGC
bool GM_CDECL ScriptObj::GameObjCallback_GCTrace(gmMachine * a_machine, gmUserObject* a_object, gmGarbageCollector* a_gc, const int a_workRemaining, int& a_workDone)
{
GM_ASSERT(a_object->m_userType == GMTYPE_GAMEOBJ);
ScriptObj* scriptObj = (ScriptObj*)a_object->m_user;
if(scriptObj)
{
a_gc->GetNextObject(scriptObj->GetTableObject());
}
a_workDone +=2;
return true;
}
void GM_CDECL ScriptObj::GameObjCallback_GCDestruct(gmMachine * a_machine, gmUserObject * a_object)
{
GM_ASSERT(a_object->m_userType == GMTYPE_GAMEOBJ);
ScriptObj* scriptObj = (ScriptObj*)a_object->m_user;
if(scriptObj)
{
scriptObj->m_userObject = NULL;
}
}
#else //GM_USE_INCGC
void GM_CDECL ScriptObj::GameObjCallback_GCMark(gmMachine * a_machine, gmUserObject * a_object, gmuint32 a_mark)
{
GM_ASSERT(a_object->m_userType == GMTYPE_GAMEOBJ);
ScriptObj* scriptObj = (ScriptObj*)a_object->m_user;
if(scriptObj)
{
if(scriptObj->GetTableObject()->NeedsMark(a_mark))
{
scriptObj->GetTableObject()->Mark(a_machine, a_mark);
}
}
}
void GM_CDECL ScriptObj::GameObjCallback_GCCollect(gmMachine * a_machine, gmUserObject * a_object, gmuint32 a_mark)
{
GM_ASSERT(a_object->m_userType == GMTYPE_GAMEOBJ);
ScriptObj* scriptObj = (ScriptObj*)a_object->m_user;
if(scriptObj)
{
scriptObj->m_userObject = NULL;
}
}
#endif //GM_USE_INCGC
// NOTE: If you wanted to enable other dot operator behavior
// here is the place to do it, in the GetDot and SetDot operators.
// This example merely uses the gmTable embedded in the GameObj
// to allow script functions and data to be members of this object type.
// GameObj also registers 'type' functions that are accessed via the
// dot operator.
void GM_CDECL ScriptObj::GameObj_GetDot(gmThread * a_thread, gmVariable * a_operands)
{
//O_GETDOT = 0, // object, "member" (tos is a_operands + 2)
GM_ASSERT(a_operands[0].m_type == GMTYPE_GAMEOBJ);
gmUserObject* userObj = (gmUserObject*) GM_OBJECT(a_operands[0].m_value.m_ref);
ScriptObj* scriptObj = (ScriptObj*)userObj->m_user;
if(!scriptObj)
{
a_operands[0].Nullify();
return;
}
a_operands[0] = scriptObj->GetTableObject()->Get(a_operands[1]);
}
void GM_CDECL ScriptObj::GameObj_SetDot(gmThread * a_thread, gmVariable * a_operands)
{
//O_SETDOT, // object, value, "member" (tos is a_operands + 3)
GM_ASSERT(a_operands[0].m_type == GMTYPE_GAMEOBJ);
gmUserObject* userObj = (gmUserObject*) GM_OBJECT(a_operands[0].m_value.m_ref);
ScriptObj* scriptObj = (ScriptObj*)userObj->m_user;
if(scriptObj)
{
scriptObj->GetTableObject()->Set(a_thread->GetMachine(), a_operands[2], a_operands[1]);
}
}
void ScriptObj::RegisterScriptBindings()
{
gmMachine* machine = ScriptSys::Get()->GetMachine();
GM_ASSERT(machine);
// Register new user type
GMTYPE_GAMEOBJ = machine->CreateUserType("GameObj");
// Register garbage collection for our new type
#if GM_USE_INCGC
machine->RegisterUserCallbacks(GMTYPE_GAMEOBJ, GameObjCallback_GCTrace, GameObjCallback_GCDestruct, GameObjCallback_AsString);
#else //GM_USE_INCGC
machine->RegisterUserCallbacks(GMTYPE_GAMEOBJ, GameObjCallback_GCMark, GameObjCallback_GCCollect, GameObjCallback_AsString);
#endif //GM_USE_INCGC
// Bind Get dot operator for our type
machine->RegisterTypeOperator(GMTYPE_GAMEOBJ, O_GETDOT, NULL, GameObj_GetDot);
// Bind Set dot operator for our type
machine->RegisterTypeOperator(GMTYPE_GAMEOBJ, O_SETDOT, NULL, GameObj_SetDot);
// Bind functions
// machine->RegisterLibrary(regFuncList, sizeof(regFuncList) / sizeof(regFuncList[0]));
// Bind type functions
// machine->RegisterTypeLibrary(GM_GOB, regTypeFuncList, sizeof(regTypeFuncList) / sizeof(regTypeFuncList[0]));
}
@@ -0,0 +1,165 @@
#ifndef SCRIPTOBJ_H
#define SCRIPTOBJ_H
//
// ScriptObj.h
//
// Example script interface component for game object
//
#include "gmThread.h"
#include "StdStuff.h"
// Fwd decls
class GameObj;
// NOTE: In this implementation, the 'gmUserObject' only exists when the cpp object is used or needed by script.
// This implementation also shares that single user object amongst all referencing variables in script.
// Because of this, the cpp code does not need to handle the user object as if it were owned by cpp.
// The cpp object does however always contain a gmTableObject, and this is owned by cpp as it may not
// exist (be referenced) within the script. For this reason, the gmTableObject must be handled as a
// cpp owned object to allow correct GC handling.
//
// An alternate method, would be to always have a gmUserObject and let cpp code own this. This
// user object would be the root of its own child objects like the gmTableObject. This method may
// be simpler.
//
// Script interface for game objects
class ScriptObj
{
public:
static gmType GMTYPE_GAMEOBJ; ///< The user type of a game object
static void RegisterScriptBindings(); ///< Register game object script bindings
ScriptObj(GameObj* a_gameObj);
virtual ~ScriptObj();
GameObj* GetGameObj() { return m_gameObj; }
gmTableObject* GetTableObject() { return m_tableObject; }
gmUserObject* GetUserObject();
void AddThreadId(int a_threadId)
{
m_threads.InsertLast(a_threadId);
}
void RemoveThreadId(int a_threadId)
{
for(unsigned int tIndex=0; tIndex < m_threads.GetSize(); ++tIndex)
{
if(m_threads[tIndex] == a_threadId)
{
m_threads.RemoveSwapLast(tIndex);
}
}
}
/// Kill all threads running on this object
void KillThreads();
void ExecuteStringOnThis(const char* a_string);
bool ExecuteGlobalFunctionOnThis(const char* a_functionName);
void SetMemberInt(const char* a_memberName, int a_int);
void SetMemberFloat(const char* a_memberName, float a_float);
void SetMemberString(const char* a_memberName, const char* a_string, int a_strLength = -1);
void SetMemberGameObj(const char* a_memberName, GameObj* a_gameObj);
gmTableObject* SetMemberTable(const char* a_memberName);
bool GetMemberInt(const char* a_memberName, int& a_int);
bool GetMemberFloat(const char* a_memberName, float& a_float);
bool GetMemberString(const char* a_memberName, String& a_string);
bool GetMemberGameObj(const char* a_memberName, GameObj*& a_gameObj);
bool GetMemberTable(const char* a_memberName, gmTableObject*& a_retTable);
protected:
static void GM_CDECL GameObjCallback_AsString(gmUserObject * a_object, char* a_buffer, int a_bufferLen);
#if GM_USE_INCGC
static bool GM_CDECL GameObjCallback_GCTrace(gmMachine * a_machine, gmUserObject* a_object, gmGarbageCollector* a_gc, const int a_workRemaining, int& a_workDone);
static void GM_CDECL GameObjCallback_GCDestruct(gmMachine * a_machine, gmUserObject * a_object);
#else //GM_USE_INCGC
static void GM_CDECL GameObjCallback_GCMark(gmMachine * a_machine, gmUserObject * a_object, gmuint32 a_mark);
static void GM_CDECL GameObjCallback_GCCollect(gmMachine * a_machine, gmUserObject * a_object, gmuint32 a_mark);
#endif //GM_USE_INCGC
static void GM_CDECL GameObj_GetDot(gmThread * a_thread, gmVariable * a_operands);
static void GM_CDECL GameObj_SetDot(gmThread * a_thread, gmVariable * a_operands);
GameObj* m_gameObj; ///< The game object owner of this interface
gmUserObject* m_userObject; ///< The script object
gmTableObject* m_tableObject; ///< Table functionality for script object members
gmArraySimple<int> m_threads; ///< Threads associated with this game object
};
/*
/// \brief Get 'this' as GameObj of TYPE
/// Eg. Soldier* obj = GetThisGameObj<Soldier>(a_thread);
template<class TYPE>
TYPE* GetThisGameObj(gmThread* a_thread)
{
GM_ASSERT(a_thread->GetThis()->m_type == ScriptObj::GMTYPE_GAMEOBJ); //Paranoid check for type function
ScriptObj* scriptObj = (ScriptObj*)a_thread->ThisUser();
CHECK(scriptObj); //Check for null GameObj ptr
// You can check for valid derived type here
return static_cast<TYPE*>(scriptObj->GetGameObj());
}
/// \brief Get param as GameObj of TYPE
/// Eg. Soldier* obj = GetGameObjParam<Soldier>(a_thread, 0);
template<class TYPE>
TYPE* GetGameObjParam(gmThread* a_thread, int a_paramIndex)
{
ScriptObj* scriptObj = (ScriptObj*)a_thread->ParamUserCheckType(a_paramIndex, ScriptObj::GMTYPE_GAMEOBJ);
CHECK(scriptObj); //Check for null GameObj ptr
// You can check for valid derived type here
return static_cast<TYPE*>(scriptObj->GetGameObj());
}
*/
/// \brief Get 'this' as GameObj of TYPE
inline GameObj* GetThisGameObj(gmThread* a_thread)
{
GM_ASSERT(a_thread->GetThis()->m_type == ScriptObj::GMTYPE_GAMEOBJ); //Paranoid check for type function
ScriptObj* scriptObj = (ScriptObj*)a_thread->ThisUser();
if(!scriptObj)
{
return NULL;
}
return scriptObj->GetGameObj();
}
/// \brief Get param as GameObj of TYPE
inline GameObj* GetGameObjParam(gmThread* a_thread, int a_paramIndex)
{
ScriptObj* scriptObj = (ScriptObj*)a_thread->ParamUserCheckType(a_paramIndex, ScriptObj::GMTYPE_GAMEOBJ);
if(!scriptObj)
{
return NULL;
}
return scriptObj->GetGameObj();
}
#endif //SCRIPTOBJ_H
@@ -0,0 +1,432 @@
//
// ScriptSys.cpp
//
#include "gmCall.h"
#include "ScriptSys.h"
#include "ScriptObj.h"
#include "GameObj.h"
// Init statics and constants
ScriptSys* ScriptSys::s_instance = NULL;
const int ScriptSys::DEBUGGER_DEFAULT_PORT = 49001;
const char* ScriptSys::DEBUGGER_DEFAULT_IP = "127.0.0.1"; // localhost
void ScriptSys::Init()
{
GM_ASSERT( !s_instance ); // Just have one instance for this example
s_instance = new ScriptSys;
// Register Game Object type and bindings
ScriptObj::RegisterScriptBindings();
GameObj::RegisterScriptBindings();
}
void ScriptSys::Destroy()
{
delete s_instance;
s_instance = NULL;
}
void ScriptSys::DebuggerSendMessage(gmDebugSession * a_session, const void * a_command, int a_len)
{
nClient * client = (nClient *) a_session->m_user;
client->SendMessage((const char *) a_command, a_len);
}
const void* ScriptSys::DebuggerPumpMessage(gmDebugSession * a_session, int &a_len)
{
nClient * client = (nClient *) a_session->m_user;
return client->PumpMessage(a_len);
}
ScriptSys::ScriptSys()
{
m_machine = new gmMachine;
//Set machine callbacks
gmMachine::s_machineCallback = ScriptSysCallback_Machine;
gmMachine::s_printCallback = ScriptSysCallback_Print;
// Init debugger
gmBindDebugLib(m_machine); // Register debugging library
m_debuggerIP = DEBUGGER_DEFAULT_IP;
m_debuggerPort = DEBUGGER_DEFAULT_PORT;
m_debugSession.m_sendMessage = DebuggerSendMessage;
m_debugSession.m_pumpMessage = DebuggerPumpMessage;
m_debugSession.m_user = &m_debugClient;
if(m_debugClient.Connect(m_debuggerIP, ((short) m_debuggerPort)))
{
m_debugSession.Open(m_machine);
fprintf(stderr, "Debug session opened"GM_NL);
}
m_machine->SetDebugMode(true);
}
ScriptSys::~ScriptSys()
{
// End debugger session if any
m_debugSession.Close();
m_debugClient.Close();
// For debugging
_gmDumpLeaks();
delete m_machine;
}
void __cdecl ScriptSys::LogError(const char *a_str, ...)
{
// WARNING This is not safe for longer strings, should use non-ansi vsprintnf, string type, or similar.
const int MAX_CHARS = 512;
char buffer[MAX_CHARS];
va_list args;
va_start(args, a_str);
vsprintf(buffer, a_str, args);
va_end(args);
fprintf(stderr, "ERROR: %s", a_str);
}
GameObj* ScriptSys::GetGameObjFromThreadId(int a_threadId)
{
ScriptObj* scriptObj;
if(m_mapThreadGameObjs.GetAt(a_threadId, scriptObj))
{
return scriptObj->GetGameObj();
}
return NULL;
}
void ScriptSys::AssociateThreadIdWithGameObj(int a_threadId, GameObj& a_gameObj)
{
ScriptObj* scriptObj = a_gameObj.GetScriptObj();
scriptObj->AddThreadId(a_threadId);
m_mapThreadGameObjs.SetAt(a_threadId, scriptObj);
}
void ScriptSys::DisassociateThreadIdWithGameObj(int a_threadId)
{
ScriptObj* scriptObj;
if(m_mapThreadGameObjs.RemoveAt(a_threadId, scriptObj))
{
scriptObj->RemoveThreadId(a_threadId);
}
}
void ScriptSys::RemoveThreadIdButDontTouchGameObj(int a_threadId)
{
m_mapThreadGameObjs.RemoveAt(a_threadId);
}
void ScriptSys::SetTableNull(const char* a_memberName, gmTableObject* a_table)
{
GM_ASSERT(a_table);
gmTableObject* table = a_table;
gmVariable newVar;
newVar.Nullify();
table->Set(m_machine, a_memberName, newVar);
}
void ScriptSys::SetTableInt(const char* a_memberName, int a_int, gmTableObject* a_table)
{
GM_ASSERT(a_table);
gmTableObject* table = a_table;
gmVariable newVar;
newVar.SetInt(a_int);
table->Set(m_machine, a_memberName, newVar);
}
void ScriptSys::SetTableFloat(const char* a_memberName, float a_float, gmTableObject* a_table)
{
GM_ASSERT(a_table);
gmTableObject* table = a_table;
gmVariable newVar;
newVar.SetFloat(a_float);
table->Set(m_machine, a_memberName, newVar);
}
void ScriptSys::SetTableString(const char* a_memberName, const char* a_string, int a_strLength, gmTableObject* a_table)
{
GM_ASSERT(a_table);
gmTableObject* table = a_table;
gmVariable newVar;
newVar.SetString(m_machine->AllocStringObject(a_string, a_strLength));
table->Set(m_machine, a_memberName, newVar);
}
void ScriptSys::SetTableGameObj(const char* a_memberName, GameObj* a_gameObj, gmTableObject* a_table)
{
GM_ASSERT(a_table);
GM_ASSERT(a_gameObj);
gmTableObject* table = a_table;
gmVariable newVar;
newVar.SetUser(a_gameObj->GetScriptObj()->GetUserObject());
table->Set(m_machine, a_memberName, newVar);
}
gmTableObject* ScriptSys::SetTableTable(const char* a_memberName, gmTableObject* a_table)
{
GM_ASSERT(a_table);
gmTableObject* table = a_table;
gmMachine* machine = m_machine;
gmVariable newVar;
gmTableObject* newTable = machine->AllocTableObject();
newVar.SetTable(newTable);
table->Set(machine, a_memberName, newVar);
return newTable; //Return the table so we can potentially put things in it
}
bool ScriptSys::GetTableInt(const char* a_memberName, int& a_int, gmTableObject* a_table)
{
GM_ASSERT(a_table);
gmTableObject* table = a_table;
gmVariable stringName;
gmVariable retVar;
stringName.SetString(m_machine->AllocStringObject(a_memberName));
retVar = table->Get(stringName);
if(retVar.m_type == GM_INT)
{
a_int = retVar.m_value.m_int;
return true;
}
return false;
}
bool ScriptSys::GetTableFloat(const char* a_memberName, float& a_float, gmTableObject* a_table)
{
GM_ASSERT(a_table);
gmTableObject* table = a_table;
gmVariable stringName;
gmVariable retVar;
stringName.SetString(m_machine->AllocStringObject(a_memberName));
retVar = table->Get(stringName);
if(retVar.m_type == GM_FLOAT)
{
a_float = retVar.m_value.m_float;
return true;
}
return false;
}
bool ScriptSys::GetTableString(const char* a_memberName, String& a_string, gmTableObject* a_table)
{
GM_ASSERT(a_table);
gmTableObject* table = a_table;
gmVariable stringName;
gmVariable retVar;
stringName.SetString(m_machine->AllocStringObject(a_memberName));
retVar = table->Get(stringName);
if(retVar.m_type == GM_STRING)
{
gmStringObject* stringObj = (gmStringObject*)GM_MOBJECT(m_machine, retVar.m_value.m_ref);
a_string = stringObj->GetString();
return true;
}
return false;
}
bool ScriptSys::GetTableGameObj(const char* a_memberName, GameObj*& a_gameObj, gmTableObject* a_table)
{
GM_ASSERT(a_table);
gmTableObject* table = a_table;
gmVariable stringName;
gmVariable retVar;
stringName.SetString(m_machine->AllocStringObject(a_memberName));
retVar = table->Get(stringName);
if(retVar.m_type == ScriptObj::GMTYPE_GAMEOBJ)
{
gmUserObject* userObj = (gmUserObject*)GM_MOBJECT(m_machine, retVar.m_value.m_ref);
a_gameObj = ((ScriptObj*)userObj->m_user)->GetGameObj();
return true;
}
return false;
}
bool ScriptSys::GetTableTable(const char* a_memberName, gmTableObject*& a_retTable, gmTableObject* a_table)
{
GM_ASSERT(a_table);
gmTableObject* table = a_table;
gmVariable stringName;
gmVariable retVar;
stringName.SetString(m_machine->AllocStringObject(a_memberName));
retVar = table->Get(stringName);
if(retVar.m_type == GM_TABLE)
{
a_retTable = (gmTableObject*)GM_MOBJECT(m_machine, retVar.m_value.m_ref);
return true;
}
return false;
}
void GM_CDECL ScriptSys::ScriptSysCallback_Print(gmMachine* a_machine, const char* a_string)
{
printf("%s\n", a_string);
}
bool GM_CDECL ScriptSys::ScriptSysCallback_Machine(gmMachine* a_machine, gmMachineCommand a_command, const void* a_context)
{
switch(a_command)
{
case MC_THREAD_EXCEPTION:
{
ScriptSys::Get()->LogAnyMachineErrorMessages();
break;
}
case MC_COLLECT_GARBAGE:
{
/* // Old code
#if GM_USE_INCGC
gmGarbageCollector* gc = a_machine->GetGC();
for(unsigned int objIndex = 0; objIndex<ScriptSys::Get()->m_allScriptObjs.Count(); ++objIndex)
{
ScriptObj* scriptObj = ScriptSys::Get()->m_allScriptObjs[objIndex];
gc->GetNextObject(scriptObj->GetTableObject());
}
#else //GM_USE_INCGC
gmuint32 mark = *(gmuint32*)a_context;
for(unsigned int objIndex = 0; objIndex<ScriptSys::Get()->m_allScriptObjs.Count(); ++objIndex)
{
ScriptObj* scriptObj = ScriptSys::Get()->m_allScriptObjs[objIndex];
if(scriptObj->GetTableObject()->NeedsMark(mark))
{
scriptObj->GetTableObject()->Mark(a_machine, mark);
}
}
#endif //GM_USE_INCGC
*/
break;
}
case MC_THREAD_CREATE: // Called when a thread is created. a_context is the thread.
{
break;
}
case MC_THREAD_DESTROY: // Called when a thread is destroyed. a_context is the thread that is about to die
{
gmThread* thread = (gmThread*)a_context;
ScriptSys::Get()->DisassociateThreadIdWithGameObj(thread->GetId());
break;
}
}
return false;
}
bool ScriptSys::ExecuteFile(const char* a_fileName)
{
FILE* scriptFile = NULL;
char* fileString = NULL;
int fileSize = 0;
GM_ASSERT(m_machine);
if( !(scriptFile = fopen(a_fileName, "rb")) )
{
return false;
}
fseek(scriptFile, 0, SEEK_END);
fileSize = ftell(scriptFile);
fseek(scriptFile, 0, SEEK_SET);
fileString = new char [fileSize+1];
fread(fileString, fileSize, 1, scriptFile);
fileString[fileSize] = 0; // Terminating null
fclose(scriptFile);
int threadId = GM_INVALID_THREAD;
int errors = m_machine->ExecuteString(fileString, &threadId, true, a_fileName);
if(errors)
{
LogAnyMachineErrorMessages();
}
delete [] fileString;
return true;
}
bool ScriptSys::ExecuteString(const char* a_string)
{
GM_ASSERT(m_machine);
int threadId = GM_INVALID_THREAD;
int errors = m_machine->ExecuteString(a_string, &threadId, true);
if (errors)
{
LogAnyMachineErrorMessages();
}
return true;
}
int ScriptSys::Execute(unsigned int a_deltaTimeMS)
{
int numThreads = m_machine->Execute(a_deltaTimeMS);
if(m_debugClient.IsConnected())
{
m_debugSession.Update();
}
else
{
m_debugSession.Close();
}
return numThreads;
}
void ScriptSys::LogAnyMachineErrorMessages()
{
bool first = true;
const char * message;
while((message = m_machine->GetLog().GetEntry(first)))
{
LogError("%s"GM_NL, message);
}
m_machine->GetLog().Reset();
}
@@ -0,0 +1,107 @@
#ifndef SCRIPTSYS_H
#define SCRIPTSYS_H
//
// ScriptSys.h
//
// Example script system to support scriptable game objects
//
#include "gmThread.h"
#include "gmDebug.h"
#include "gmArraySimple.h"
#include "StdStuff.h"
#include "NetClient.h"
// Fwd decls
class GameObj;
class ScriptObj;
// Script system to support and control the virtual machine
class ScriptSys
{
public:
/// Access this system from anywhere once it has been initialized for convenience
static ScriptSys* Get() { return s_instance; }
ScriptSys();
virtual ~ScriptSys();
/// Get the GM machine
gmMachine* GetMachine() { return m_machine; }
/// Set bindings and Init constant strings
static void Init();
/// Clean out this structure
static void Destroy();
/// Log an error message
void __cdecl LogError(const char *a_str, ...);
/// Log any machine error messages that may be waiting
void LogAnyMachineErrorMessages();
/// Get GameObj that was associated with a thread Id.
GameObj* GetGameObjFromThreadId(int a_threadId);
/// Associate a threadId with a GameObj, logically as a primary thread.
void AssociateThreadIdWithGameObj(int a_threadId, GameObj& a_gameObj);
/// Disassociate a threadId with a GameObj.
void DisassociateThreadIdWithGameObj(int a_threadId);
/// Remove the thread Id association, but don't modify the GameObj.
/// This can be used internally by GameObj to perform iteration and removal.
void RemoveThreadIdButDontTouchGameObj(int a_threadId);
/// Run a script file
bool ExecuteFile(const char* a_fileName);
/// Executes a string.
bool ExecuteString(const char* a_str);
/// Update the virtual machine.
int Execute(unsigned int a_deltaTimeMS);
void SetTableNull(const char* a_memberName, gmTableObject* a_table);
void SetTableInt(const char* a_memberName, int a_int, gmTableObject* a_table);
void SetTableFloat(const char* a_memberName, float a_float, gmTableObject* a_table);
void SetTableString(const char* a_memberName, const char* a_string, int a_strLength, gmTableObject* a_table);
void SetTableGameObj(const char* a_memberName, GameObj* a_gameObj, gmTableObject* a_table);
gmTableObject* SetTableTable(const char* a_memberName, gmTableObject* a_table);
bool GetTableInt(const char* a_memberName, int& a_int, gmTableObject* a_table);
bool GetTableFloat(const char* a_memberName, float& a_float, gmTableObject* a_table);
bool GetTableString(const char* a_memberName, String& a_string, gmTableObject* a_table);
bool GetTableGameObj(const char* a_memberName, GameObj*& a_gameObj, gmTableObject* a_table);
bool GetTableTable(const char* a_memberName, gmTableObject*& a_retTable, gmTableObject* a_table);
protected:
/// Machine 'print' binding callback
static void GM_CDECL ScriptSysCallback_Print(gmMachine* a_machine, const char* a_string);
/// Machine general and exception callback
static bool GM_CDECL ScriptSysCallback_Machine(gmMachine* a_machine, gmMachineCommand a_command, const void* a_context);
/// Debugging support Send a message
static void DebuggerSendMessage(gmDebugSession * a_session, const void * a_command, int a_len);
/// Debugging support Pump a message
static const void* DebuggerPumpMessage(gmDebugSession * a_session, int &a_len);
gmMachine* m_machine; ///< GM machine instance
Map<int, ScriptObj*> m_mapThreadGameObjs; ///< Map script threadId to game object
/* // Old code
gmArraySimple<ScriptObj*> m_allScriptObjs; ///< All the script objects, for garbage collection handling
*/
nClient m_debugClient; ///< Debugger network client
gmDebugSession m_debugSession; ///< Debugger session
const char* m_debuggerIP; ///< Debugger IP
int m_debuggerPort; ///< Debugger port
static const int DEBUGGER_DEFAULT_PORT; ///< Debugger port number
static const char* DEBUGGER_DEFAULT_IP; ///< Debugger port number
static ScriptSys* s_instance; ///< Static instance for convenience
};
#endif //SCRIPTSYS_H
@@ -0,0 +1,5 @@
//
// StdStuff.cpp
//
#include "StdStuff.h"
@@ -0,0 +1,194 @@
#ifndef STDSTUFF_H
#define STDSTUFF_H
//
// StdStuff.h
//
// Merely some containers and standard things you would
// find in MFC, STL, or your favourite library/engine.
// These were implemented as quickly and minimally as possible
// rather than introduce more code or external libraries.
//
#include "gmHash.h"
// Quick n dirty Map using the available hash table
template<class KEY, class VALUE>
class Map
{
public:
Map()
: m_hashTable(1024) // Just an arbitrary number at present
{
}
virtual ~Map()
{
m_hashTable.RemoveAndDeleteAll();
}
/// \brief Insert a element associated with a key.
/// \param a_key Key to identify data.
/// \param a_value Data associated with Key.
void SetAt(const KEY& a_key, const VALUE& a_value)
{
HashNode* node = m_hashTable.Find(a_key);
if(node)
{
node->m_value = a_value;
}
else
{
node = new HashNode;
node->m_key = a_key;
node->m_value = a_value;
m_hashTable.Insert(node);
}
};
/// \brief Find a node in the map.
/// \param a_key Key to identiy element.
/// \param a_value Found element returned here.
/// \return TRUE if found, FALSE if not in map.
bool GetAt(const KEY& a_key, VALUE& a_value)
{
HashNode* node = m_hashTable.Find(a_key);
if(node)
{
a_value = node->m_value;
return true;
}
return false;
}
/// \brief Remove a node from the map.
/// \param a_key Key to identiy element.
/// \param a_removedData Found element returned here.
/// \return TRUE if found, FALSE if not in map.
bool RemoveAt(const KEY& a_key, VALUE& a_removedData)
{
HashNode* node = m_hashTable.Find(a_key);
if(node)
{
a_removedData = node->m_value;
m_hashTable.Remove(node);
delete node;
return true;
}
return false;
}
/// \brief Remove a node from the map.
/// \param a_key Key to identiy element.
/// \return TRUE if found, FALSE if not in map.
bool RemoveAt(const KEY& a_key)
{
HashNode* node = m_hashTable.Find(a_key);
if(node)
{
m_hashTable.Remove(node);
delete node;
return true;
}
return false;
}
protected:
struct HashNode : public gmHashNode<KEY, HashNode, HashNode>
{
VALUE m_value;
KEY m_key;
virtual const KEY& GetKey() const { return m_key; }
static inline gmuint Hash(const KEY& a_key)
{
return (unsigned int)a_key;
}
static inline int Compare(const KEY& a_keyA, const KEY& a_keyB)
{
if(a_keyA < a_keyB)
{ return -1; }
if(a_keyA > a_keyB)
{ return 1; }
return 0;
}
};
// Blocking
gmHash<KEY, HashNode, HashNode> m_hashTable;
};
// The most crap string implementation ever
class String
{
public:
String()
{
m_buffer = NULL;
SetBuffer("");
}
String(const char* a_newString)
{
m_buffer = NULL;
SetBuffer(a_newString);
}
String(const char* a_newString, const int a_newStringLength)
{
m_buffer = NULL;
SetBuffer(a_newString, a_newStringLength);
}
~String()
{
delete [] m_buffer;
}
operator const char* () const
{
return m_buffer;
}
const char* operator = (const char* a_newString)
{
SetBuffer(a_newString);
return m_buffer;
}
const char* operator = (const String& a_newString)
{
SetBuffer(a_newString);
return m_buffer;
}
private:
void SetBuffer(const char* a_newString, const int a_newStringLength)
{
delete [] m_buffer;
m_buffer = new char [a_newStringLength + 1];
memcpy(m_buffer, a_newString, a_newStringLength);
m_buffer[a_newStringLength] = 0;
}
void SetBuffer(const char* a_newString)
{
int newStringLength = strlen(a_newString);
SetBuffer(a_newString, newStringLength);
}
char * m_buffer;
};
#endif //STDSTUFF_H
@@ -0,0 +1,44 @@
// Just a bunch of tests that don't mean anything at present.
global WhatsMyName = function()
{
print("m_name = ", .m_name);
print("this = ", this);
if(.IsValid())
{
.SetPos(23.0f, 56.0f);
print("position(", .GetPosX(), ",", .GetPosY(), ")");
global g_globalObj = this;
}
};
global RunGlobalObject = function ()
{
g_globalObj:WhatsMyName();
};
global ThreadYieldTest = function()
{
count = 0;
while(count < 30)
{
sleep(0.5f);
count += 1;
print("count=",count);
yield();
}
};
Console.SetColor(COLOR_RED, COLOR_BLACK);
Console.Print("Red");
Console.SetColor(COLOR_GREEN, COLOR_BLACK);
Console.Print("Green");
Console.Print("\n");
Console.SetColor(COLOR_WHITE, COLOR_BLACK);
print("Script compiled and executed. \n");
@@ -0,0 +1,30 @@
#include <windows.h>
#include "StdStuff.h"
#include "App.h"
// Entry point for Win32 app
int main(int argc, char* argv[])
{
String test1("hello");
test1 = "world";
const char* huh = test1;
App app;
if(!app.Init())
{
fprintf(stderr,"Failed App::Init()");
return 1;
}
while(app.Update())
{
}
app.Destroy();
printf("App finished, press ENTER to exit.");
getchar(); // Wait for key press
return 0;
}
@@ -0,0 +1,362 @@
# Microsoft Developer Studio Project File - Name="Minimal" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Console Application" 0x0103
CFG=Minimal - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE
!MESSAGE NMAKE /f "Minimal.mak".
!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
!MESSAGE NMAKE /f "Minimal.mak" CFG="Minimal - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "Minimal - Win32 Release" (based on "Win32 (x86) Console Application")
!MESSAGE "Minimal - Win32 Debug" (based on "Win32 (x86) Console Application")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName "Perforce Project"
# PROP Scc_LocalPath "..\.."
CPP=cl.exe
RSC=rc.exe
!IF "$(CFG)" == "Minimal - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Release"
# PROP BASE Intermediate_Dir "Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Release"
# PROP Intermediate_Dir "Release"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD CPP /nologo /W3 /GX /O2 /I "..\..\gm" /I "..\..\platform\win32msvc" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD BASE RSC /l 0xc09 /d "NDEBUG"
# ADD RSC /l 0xc09 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
# ADD LINK32 winmm.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
!ELSEIF "$(CFG)" == "Minimal - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "Debug"
# PROP BASE Intermediate_Dir "Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "Debug"
# PROP Intermediate_Dir "Debug"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\gm" /I "..\..\platform\win32msvc" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
# ADD BASE RSC /l 0xc09 /d "_DEBUG"
# ADD RSC /l 0xc09 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
# ADD LINK32 winmm.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
!ENDIF
# Begin Target
# Name "Minimal - Win32 Release"
# Name "Minimal - Win32 Debug"
# Begin Group "Source Files"
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat;h;hpp;hxx;hm;inl"
# Begin Source File
SOURCE=.\main.cpp
# End Source File
# End Group
# Begin Group "gm"
# PROP Default_Filter ""
# Begin Source File
SOURCE=..\..\gm\gmArraySimple.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmArraySimple.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmByteCode.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmByteCode.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmByteCodeGen.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmByteCodeGen.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmCodeGen.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmCodeGen.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmCodeGenHooks.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmCodeGenHooks.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmCodeTree.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmCodeTree.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmConfig.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmCrc.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmCrc.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmDebug.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmDebug.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmFunctionObject.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmFunctionObject.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmHash.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmHash.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmIncGC.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmIncGC.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmIterator.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmLibHooks.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmLibHooks.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmListDouble.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmListDouble.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmLog.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmLog.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmMachine.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmMachine.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmMachineLib.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmMachineLib.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmMem.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmMem.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmMemChain.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmMemChain.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmMemFixed.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmMemFixed.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmMemFixedSet.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmMemFixedSet.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmOperators.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmOperators.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmParser.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmParser.cpp.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmScanner.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmScanner.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmStream.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmStream.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmStreamBuffer.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmStreamBuffer.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmStringObject.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmStringObject.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmTableObject.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmTableObject.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmThread.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmThread.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmUserObject.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmUserObject.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmUtil.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmUtil.h
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmVariable.cpp
# End Source File
# Begin Source File
SOURCE=..\..\gm\gmVariable.h
# End Source File
# End Group
# Begin Group "win32"
# PROP Default_Filter ""
# Begin Source File
SOURCE=..\..\platform\win32msvc\gmConfig_p.h
# End Source File
# End Group
# End Target
# End Project
@@ -0,0 +1,33 @@
Microsoft Developer Studio Workspace File, Format Version 6.00
# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
###############################################################################
Project: "Minimal"=.\Minimal.dsp - Package Owner=<4>
Package=<5>
{{{
begin source code control
Perforce Project
..\..
end source code control
}}}
Package=<4>
{{{
}}}
###############################################################################
Global:
Package=<5>
{{{
}}}
Package=<3>
{{{
}}}
###############################################################################
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,78 @@
#if 0 // This is trully the smallest app
#include "gmThread.h" // game monkey script
int main(int argc, char* argv[])
{
gmMachine machine;
machine.ExecuteString("print(`Hello world`);");
getchar(); // Keypress before exit
return 0;
}
#else // This is a tiny app
#include <windows.h>
#include <mmsystem.h> // multimedia timer (may need winmm.lib)
#include "gmThread.h" // game monkey script
int main(int argc, char* argv[])
{
// Create virtual machine
gmMachine* machine = new gmMachine;
// Get a script from stdin. Some examples:
// print("Hello world");
// for( i = 0; i < 10; i=i+1 ) { print("i=",i); sleep(1.0); }
fprintf(stdout,"Please enter one line of script\n>");
const int MAX_SCRIPT_SIZE = 4096;
char script[MAX_SCRIPT_SIZE];
fgets(script, MAX_SCRIPT_SIZE-1, stdin);
// Compile the script, but don't run it for now
int errors = machine->ExecuteString(script, NULL, false, NULL);
// Dump compile time errors to output
if(errors)
{
bool first = true;
const char * message;
while((message = machine->GetLog().GetEntry(first)))
{
fprintf(stderr, "%s"GM_NL, message);
}
machine->GetLog().Reset();
}
else
{
int deltaTime = 0;
int lastTime = timeGetTime();
// Keep executing script while threads persist
while(machine->Execute(deltaTime))
{
// Update delta time
int curTime = timeGetTime();
deltaTime = curTime - lastTime;
lastTime = curTime;
// Dump run time errors to output
bool first = true;
const char * message;
while((message = machine->GetLog().GetEntry(first)))
{
fprintf(stderr, "%s"GM_NL, message);
}
machine->GetLog().Reset();
}
}
delete machine; // Finished with VM
fprintf(stdout,"Script complete. Press a key to exit.");
getchar(); // Keypress before exit
return 0;
}
#endif // Minimal build type
+334
View File
@@ -0,0 +1,334 @@
extern int timeclock;
int yyerror; /* Yyerror and yycost are set by guards. */
int yycost; /* If yyerror is set to a nonzero value by a */
/* guard, the reduction with which the guard */
/* is associated is not performed, and the */
/* error recovery mechanism is invoked. */
/* Yycost indicates the cost of performing */
/* the reduction given the attributes of the */
/* symbols. */
/* YYMAXDEPTH indicates the size of the parser's state and value */
/* stacks. */
#ifndef YYMAXDEPTH
#define YYMAXDEPTH 500
#endif
/* YYMAXRULES must be at least as large as the number of rules that */
/* could be placed in the rule queue. That number could be determined */
/* from the grammar and the size of the stack, but, as yet, it is not. */
#ifndef YYMAXRULES
#define YYMAXRULES 100
#endif
#ifndef YYMAXBACKUP
#define YYMAXBACKUP 100
#endif
short yyss[YYMAXDEPTH]; /* the state stack */
YYSTYPE yyvs[YYMAXDEPTH]; /* the semantic value stack */
YYLTYPE yyls[YYMAXDEPTH]; /* the location stack */
short yyrq[YYMAXRULES]; /* the rule queue */
int yychar; /* the lookahead symbol */
YYSTYPE yylval; /* the semantic value of the */
/* lookahead symbol */
YYSTYPE yytval; /* the semantic value for the state */
/* at the top of the state stack. */
YYSTYPE yyval; /* the variable used to return */
/* semantic values from the action */
/* routines */
YYLTYPE yylloc; /* location data for the lookahead */
/* symbol */
YYLTYPE yytloc; /* location data for the state at the */
/* top of the state stack */
int yynunlexed;
short yyunchar[YYMAXBACKUP];
YYSTYPE yyunval[YYMAXBACKUP];
YYLTYPE yyunloc[YYMAXBACKUP];
short *yygssp; /* a pointer to the top of the state */
/* stack; only set during error */
/* recovery. */
YYSTYPE *yygvsp; /* a pointer to the top of the value */
/* stack; only set during error */
/* recovery. */
YYLTYPE *yyglsp; /* a pointer to the top of the */
/* location stack; only set during */
/* error recovery. */
/* Yyget is an interface between the parser and the lexical analyzer. */
/* It is costly to provide such an interface, but it avoids requiring */
/* the lexical analyzer to be able to back up the scan. */
yyget()
{
if (yynunlexed > 0)
{
yynunlexed--;
yychar = yyunchar[yynunlexed];
yylval = yyunval[yynunlexed];
yylloc = yyunloc[yynunlexed];
}
else if (yychar <= 0)
yychar = 0;
else
{
yychar = yylex();
if (yychar < 0)
yychar = 0;
else yychar = YYTRANSLATE(yychar);
}
}
yyunlex(chr, val, loc)
int chr;
YYSTYPE val;
YYLTYPE loc;
{
yyunchar[yynunlexed] = chr;
yyunval[yynunlexed] = val;
yyunloc[yynunlexed] = loc;
yynunlexed++;
}
yyrestore(first, last)
register short *first;
register short *last;
{
register short *ssp;
register short *rp;
register int symbol;
register int state;
register int tvalsaved;
ssp = yygssp;
yyunlex(yychar, yylval, yylloc);
tvalsaved = 0;
while (first != last)
{
symbol = yystos[*ssp];
if (symbol < YYNTBASE)
{
yyunlex(symbol, yytval, yytloc);
tvalsaved = 1;
ssp--;
}
ssp--;
if (first == yyrq)
first = yyrq + YYMAXRULES;
first--;
for (rp = yyrhs + yyprhs[*first]; symbol = *rp; rp++)
{
if (symbol < YYNTBASE)
state = yytable[yypact[*ssp] + symbol];
else
{
state = yypgoto[symbol - YYNTBASE] + *ssp;
if (state >= 0 && state <= YYLAST && yycheck[state] == *ssp)
state = yytable[state];
else
state = yydefgoto[symbol - YYNTBASE];
}
*++ssp = state;
}
}
if ( ! tvalsaved && ssp > yyss)
{
yyunlex(yystos[*ssp], yytval, yytloc);
ssp--;
}
yygssp = ssp;
}
int
yyparse()
{
register int yystate;
register int yyn;
register short *yyssp;
register short *yyrq0;
register short *yyptr;
register YYSTYPE *yyvsp;
int yylen;
YYLTYPE *yylsp;
short *yyrq1;
short *yyrq2;
yystate = 0;
yyssp = yyss - 1;
yyvsp = yyvs - 1;
yylsp = yyls - 1;
yyrq0 = yyrq;
yyrq1 = yyrq0;
yyrq2 = yyrq0;
yychar = yylex();
if (yychar < 0)
yychar = 0;
else yychar = YYTRANSLATE(yychar);
yynewstate:
if (yyssp >= yyss + YYMAXDEPTH - 1)
{
yyabort("Parser Stack Overflow");
YYABORT;
}
*++yyssp = yystate;
yyresume:
yyn = yypact[yystate];
if (yyn == YYFLAG)
goto yydefault;
yyn += yychar;
if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar)
goto yydefault;
yyn = yytable[yyn];
if (yyn < 0)
{
yyn = -yyn;
goto yyreduce;
}
else if (yyn == 0)
goto yyerrlab;
yystate = yyn;
yyptr = yyrq2;
while (yyptr != yyrq1)
{
yyn = *yyptr++;
yylen = yyr2[yyn];
yyvsp -= yylen;
yylsp -= yylen;
yyguard(yyn, yyvsp, yylsp);
if (yyerror)
goto yysemerr;
yyaction(yyn, yyvsp, yylsp);
*++yyvsp = yyval;
yylsp++;
if (yylen == 0)
{
yylsp->timestamp = timeclock;
yylsp->first_line = yytloc.first_line;
yylsp->first_column = yytloc.first_column;
yylsp->last_line = (yylsp-1)->last_line;
yylsp->last_column = (yylsp-1)->last_column;
yylsp->text = 0;
}
else
{
yylsp->last_line = (yylsp+yylen-1)->last_line;
yylsp->last_column = (yylsp+yylen-1)->last_column;
}
if (yyptr == yyrq + YYMAXRULES)
yyptr = yyrq;
}
if (yystate == YYFINAL)
YYACCEPT;
yyrq2 = yyptr;
yyrq1 = yyrq0;
*++yyvsp = yytval;
*++yylsp = yytloc;
yytval = yylval;
yytloc = yylloc;
yyget();
goto yynewstate;
yydefault:
yyn = yydefact[yystate];
if (yyn == 0)
goto yyerrlab;
yyreduce:
*yyrq0++ = yyn;
if (yyrq0 == yyrq + YYMAXRULES)
yyrq0 = yyrq;
if (yyrq0 == yyrq2)
{
yyabort("Parser Rule Queue Overflow");
YYABORT;
}
yyssp -= yyr2[yyn];
yyn = yyr1[yyn];
yystate = yypgoto[yyn - YYNTBASE] + *yyssp;
if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp)
yystate = yytable[yystate];
else
yystate = yydefgoto[yyn - YYNTBASE];
goto yynewstate;
yysemerr:
*--yyptr = yyn;
yyrq2 = yyptr;
yyvsp += yyr2[yyn];
yyerrlab:
yygssp = yyssp;
yygvsp = yyvsp;
yyglsp = yylsp;
yyrestore(yyrq0, yyrq2);
yyrecover();
yystate = *yygssp;
yyssp = yygssp;
yyvsp = yygvsp;
yyrq0 = yyrq;
yyrq1 = yyrq0;
yyrq2 = yyrq0;
goto yyresume;
}
$
+688
View File
@@ -0,0 +1,688 @@
/* -*-C-*- Note some compilers choke on comments on `#line' lines. */
#line 3 "bison.simple"
/* Skeleton output parser for bison,
Copyright (C) 1984, 1989, 1990 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
/* As a special exception, when this file is copied by Bison into a
Bison output file, you may use that output file without restriction.
This special exception was added by the Free Software Foundation
in version 1.24 of Bison. */
#ifndef alloca
#ifdef __GNUC__
#define alloca __builtin_alloca
#else /* not GNU C. */
#if (!defined (__STDC__) && defined (sparc)) || defined (__sparc__) || defined (__sparc) || defined (__sgi)
#include <alloca.h>
#else /* not sparc */
#if defined (MSDOS) && !defined (__TURBOC__)
#include <malloc.h>
#else /* not MSDOS, or __TURBOC__ */
#if defined(_AIX)
#include <malloc.h>
#pragma alloca
#else /* not MSDOS, __TURBOC__, or _AIX */
#ifdef __hpux
#ifdef __cplusplus
extern "C" {
void *alloca (unsigned int);
};
#else /* not __cplusplus */
void *alloca ();
#endif /* not __cplusplus */
#endif /* __hpux */
#endif /* not _AIX */
#endif /* not MSDOS, or __TURBOC__ */
#endif /* not sparc. */
#endif /* not GNU C. */
#endif /* alloca not defined. */
/* This is the parser code that is written into each bison parser
when the %semantic_parser declaration is not specified in the grammar.
It was written by Richard Stallman by simplifying the hairy parser
used when %semantic_parser is specified. */
/* Note: there must be only one dollar sign in this file.
It is replaced by the list of actions, each action
as one case of the switch. */
#define yyerrok (yyerrstatus = 0)
#define yyclearin (yychar = YYEMPTY)
#define YYEMPTY -2
#define YYEOF 0
#define YYACCEPT return(0)
#define YYABORT return(1)
#define YYERROR goto yyerrlab1
/* Like YYERROR except do call yyerror.
This remains here temporarily to ease the
transition to the new meaning of YYERROR, for GCC.
Once GCC version 2 has supplanted version 1, this can go. */
#define YYFAIL goto yyerrlab
#define YYRECOVERING() (!!yyerrstatus)
#define YYBACKUP(token, value) \
do \
if (yychar == YYEMPTY && yylen == 1) \
{ yychar = (token), yylval = (value); \
yychar1 = YYTRANSLATE (yychar); \
YYPOPSTACK; \
goto yybackup; \
} \
else \
{ yyerror ("syntax error: cannot back up"); YYERROR; } \
while (0)
#define YYTERROR 1
#define YYERRCODE 256
#ifndef YYPURE
#define YYLEX yylex()
#endif
#ifdef YYPURE
#ifdef YYLSP_NEEDED
#ifdef YYLEX_PARAM
#define YYLEX yylex(&yylval, &yylloc, YYLEX_PARAM)
#else
#define YYLEX yylex(&yylval, &yylloc)
#endif
#else /* not YYLSP_NEEDED */
#ifdef YYLEX_PARAM
#define YYLEX yylex(&yylval, YYLEX_PARAM)
#else
#define YYLEX yylex(&yylval)
#endif
#endif /* not YYLSP_NEEDED */
#endif
/* If nonreentrant, generate the variables here */
#ifndef YYPURE
int yychar; /* the lookahead symbol */
YYSTYPE yylval; /* the semantic value of the */
/* lookahead symbol */
#ifdef YYLSP_NEEDED
YYLTYPE yylloc; /* location data for the lookahead */
/* symbol */
#endif
int yynerrs; /* number of parse errors so far */
#endif /* not YYPURE */
#if YYDEBUG != 0
int yydebug; /* nonzero means print parse trace */
/* Since this is uninitialized, it does not stop multiple parsers
from coexisting. */
#endif
/* YYINITDEPTH indicates the initial size of the parser's stacks */
#ifndef YYINITDEPTH
#define YYINITDEPTH 200
#endif
/* YYMAXDEPTH is the maximum size the stacks can grow to
(effective only if the built-in stack extension method is used). */
#if YYMAXDEPTH == 0
#undef YYMAXDEPTH
#endif
#ifndef YYMAXDEPTH
#define YYMAXDEPTH 10000
#endif
/* Prevent warning if -Wstrict-prototypes. */
#ifdef __GNUC__
int yyparse (void);
#endif
#if __GNUC__ > 1 /* GNU C and GNU C++ define this. */
#define __yy_memcpy(FROM,TO,COUNT) __builtin_memcpy(TO,FROM,COUNT)
#else /* not GNU C or C++ */
#ifndef __cplusplus
/* This is the most reliable way to avoid incompatibilities
in available built-in functions on various systems. */
static void
__yy_memcpy (from, to, count)
char *from;
char *to;
int count;
{
register char *f = from;
register char *t = to;
register int i = count;
while (i-- > 0)
*t++ = *f++;
}
#else /* __cplusplus */
/* This is the most reliable way to avoid incompatibilities
in available built-in functions on various systems. */
static void
__yy_memcpy (char *from, char *to, int count)
{
register char *f = from;
register char *t = to;
register int i = count;
while (i-- > 0)
*t++ = *f++;
}
#endif
#endif
#line 192 "bison.simple"
/* The user can define YYPARSE_PARAM as the name of an argument to be passed
into yyparse. The argument should have type void *.
It should actually point to an object.
Grammar actions can access the variable by casting it
to the proper pointer type. */
#ifdef YYPARSE_PARAM
#define YYPARSE_PARAM_DECL void *YYPARSE_PARAM;
#else
#define YYPARSE_PARAM
#define YYPARSE_PARAM_DECL
#endif
int
yyparse(YYPARSE_PARAM)
YYPARSE_PARAM_DECL
{
register int yystate;
register int yyn;
register short *yyssp;
register YYSTYPE *yyvsp;
int yyerrstatus; /* number of tokens to shift before error messages enabled */
int yychar1 = 0; /* lookahead token as an internal (translated) token number */
short yyssa[YYINITDEPTH]; /* the state stack */
YYSTYPE yyvsa[YYINITDEPTH]; /* the semantic value stack */
short *yyss = yyssa; /* refer to the stacks thru separate pointers */
YYSTYPE *yyvs = yyvsa; /* to allow yyoverflow to reallocate them elsewhere */
#ifdef YYLSP_NEEDED
YYLTYPE yylsa[YYINITDEPTH]; /* the location stack */
YYLTYPE *yyls = yylsa;
YYLTYPE *yylsp;
#define YYPOPSTACK (yyvsp--, yyssp--, yylsp--)
#else
#define YYPOPSTACK (yyvsp--, yyssp--)
#endif
int yystacksize = YYINITDEPTH;
#ifdef YYPURE
int yychar;
YYSTYPE yylval;
int yynerrs;
#ifdef YYLSP_NEEDED
YYLTYPE yylloc;
#endif
#endif
YYSTYPE yyval; /* the variable used to return */
/* semantic values from the action */
/* routines */
int yylen;
#if YYDEBUG != 0
if (yydebug)
fprintf(stderr, "Starting parse\n");
#endif
yystate = 0;
yyerrstatus = 0;
yynerrs = 0;
yychar = YYEMPTY; /* Cause a token to be read. */
/* Initialize stack pointers.
Waste one element of value and location stack
so that they stay on the same level as the state stack.
The wasted elements are never initialized. */
yyssp = yyss - 1;
yyvsp = yyvs;
#ifdef YYLSP_NEEDED
yylsp = yyls;
#endif
/* Push a new state, which is found in yystate . */
/* In all cases, when you get here, the value and location stacks
have just been pushed. so pushing a state here evens the stacks. */
yynewstate:
*++yyssp = (short) yystate;
if (yyssp >= yyss + yystacksize - 1)
{
/* Give user a chance to reallocate the stack */
/* Use copies of these so that the &'s don't force the real ones into memory. */
YYSTYPE *yyvs1 = yyvs;
short *yyss1 = yyss;
#ifdef YYLSP_NEEDED
YYLTYPE *yyls1 = yyls;
#endif
/* Get the current used size of the three stacks, in elements. */
int size = yyssp - yyss + 1;
#ifdef yyoverflow
/* Each stack pointer address is followed by the size of
the data in use in that stack, in bytes. */
#ifdef YYLSP_NEEDED
/* This used to be a conditional around just the two extra args,
but that might be undefined if yyoverflow is a macro. */
yyoverflow("parser stack overflow",
&yyss1, size * sizeof (*yyssp),
&yyvs1, size * sizeof (*yyvsp),
&yyls1, size * sizeof (*yylsp),
&yystacksize);
#else
yyoverflow("parser stack overflow",
&yyss1, size * sizeof (*yyssp),
&yyvs1, size * sizeof (*yyvsp),
&yystacksize);
#endif
yyss = yyss1; yyvs = yyvs1;
#ifdef YYLSP_NEEDED
yyls = yyls1;
#endif
#else /* no yyoverflow */
/* Extend the stack our own way. */
if (yystacksize >= YYMAXDEPTH)
{
yyerror("parser stack overflow");
return 2;
}
yystacksize *= 2;
if (yystacksize > YYMAXDEPTH)
yystacksize = YYMAXDEPTH;
yyss = (short *) alloca (yystacksize * sizeof (*yyssp));
__yy_memcpy ((char *)yyss1, (char *)yyss, size * sizeof (*yyssp));
yyvs = (YYSTYPE *) alloca (yystacksize * sizeof (*yyvsp));
__yy_memcpy ((char *)yyvs1, (char *)yyvs, size * sizeof (*yyvsp));
#ifdef YYLSP_NEEDED
yyls = (YYLTYPE *) alloca (yystacksize * sizeof (*yylsp));
__yy_memcpy ((char *)yyls1, (char *)yyls, size * sizeof (*yylsp));
#endif
#endif /* no yyoverflow */
yyssp = yyss + size - 1;
yyvsp = yyvs + size - 1;
#ifdef YYLSP_NEEDED
yylsp = yyls + size - 1;
#endif
#if YYDEBUG != 0
if (yydebug)
fprintf(stderr, "Stack size increased to %d\n", yystacksize);
#endif
if (yyssp >= yyss + yystacksize - 1)
YYABORT;
}
#if YYDEBUG != 0
if (yydebug)
fprintf(stderr, "Entering state %d\n", yystate);
#endif
goto yybackup;
yybackup:
/* Do appropriate processing given the current state. */
/* Read a lookahead token if we need one and don't already have one. */
/* yyresume: */
/* First try to decide what to do without reference to lookahead token. */
yyn = yypact[yystate];
if (yyn == YYFLAG)
goto yydefault;
/* Not known => get a lookahead token if don't already have one. */
/* yychar is either YYEMPTY or YYEOF
or a valid token in external form. */
if (yychar == YYEMPTY)
{
#if YYDEBUG != 0
if (yydebug)
fprintf(stderr, "Reading a token: ");
#endif
yychar = YYLEX;
}
/* Convert token to internal form (in yychar1) for indexing tables with */
if (yychar <= 0) /* This means end of input. */
{
yychar1 = 0;
yychar = YYEOF; /* Don't call YYLEX any more */
#if YYDEBUG != 0
if (yydebug)
fprintf(stderr, "Now at end of input.\n");
#endif
}
else
{
yychar1 = YYTRANSLATE(yychar);
#if YYDEBUG != 0
if (yydebug)
{
fprintf (stderr, "Next token is %d (%s", yychar, yytname[yychar1]);
/* Give the individual parser a way to print the precise meaning
of a token, for further debugging info. */
#ifdef YYPRINT
YYPRINT (stderr, yychar, yylval);
#endif
fprintf (stderr, ")\n");
}
#endif
}
yyn += yychar1;
if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar1)
goto yydefault;
yyn = yytable[yyn];
/* yyn is what to do for this token type in this state.
Negative => reduce, -yyn is rule number.
Positive => shift, yyn is new state.
New state is final state => don't bother to shift,
just return success.
0, or most negative number => error. */
if (yyn < 0)
{
if (yyn == YYFLAG)
goto yyerrlab;
yyn = -yyn;
goto yyreduce;
}
else if (yyn == 0)
goto yyerrlab;
if (yyn == YYFINAL)
YYACCEPT;
/* Shift the lookahead token. */
#if YYDEBUG != 0
if (yydebug)
fprintf(stderr, "Shifting token %d (%s), ", yychar, yytname[yychar1]);
#endif
/* Discard the token being shifted unless it is eof. */
if (yychar != YYEOF)
yychar = YYEMPTY;
*++yyvsp = yylval;
#ifdef YYLSP_NEEDED
*++yylsp = yylloc;
#endif
/* count tokens shifted since error; after three, turn off error status. */
if (yyerrstatus) yyerrstatus--;
yystate = yyn;
goto yynewstate;
/* Do the default action for the current state. */
yydefault:
yyn = yydefact[yystate];
if (yyn == 0)
goto yyerrlab;
/* Do a reduction. yyn is the number of a rule to reduce with. */
yyreduce:
yylen = yyr2[yyn];
if (yylen > 0)
yyval = yyvsp[1-yylen]; /* implement default value of the action */
#if YYDEBUG != 0
if (yydebug)
{
int i;
fprintf (stderr, "Reducing via rule %d (line %d), ",
yyn, yyrline[yyn]);
/* Print the symbols being reduced, and their result. */
for (i = yyprhs[yyn]; yyrhs[i] > 0; i++)
fprintf (stderr, "%s ", yytname[yyrhs[i]]);
fprintf (stderr, " -> %s\n", yytname[yyr1[yyn]]);
}
#endif
$ /* the action file gets copied in in place of this dollarsign */
#line 487 "bison.simple"
yyvsp -= yylen;
yyssp -= yylen;
#ifdef YYLSP_NEEDED
yylsp -= yylen;
#endif
#if YYDEBUG != 0
if (yydebug)
{
short *ssp1 = yyss - 1;
fprintf (stderr, "state stack now");
while (ssp1 != yyssp)
fprintf (stderr, " %d", *++ssp1);
fprintf (stderr, "\n");
}
#endif
*++yyvsp = yyval;
#ifdef YYLSP_NEEDED
yylsp++;
if (yylen == 0)
{
yylsp->first_line = yylloc.first_line;
yylsp->first_column = yylloc.first_column;
yylsp->last_line = (yylsp-1)->last_line;
yylsp->last_column = (yylsp-1)->last_column;
yylsp->text = 0;
}
else
{
yylsp->last_line = (yylsp+yylen-1)->last_line;
yylsp->last_column = (yylsp+yylen-1)->last_column;
}
#endif
/* Now "shift" the result of the reduction.
Determine what state that goes to,
based on the state we popped back to
and the rule number reduced by. */
yyn = yyr1[yyn];
yystate = yypgoto[yyn - YYNTBASE] + *yyssp;
if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp)
yystate = yytable[yystate];
else
yystate = yydefgoto[yyn - YYNTBASE];
goto yynewstate;
yyerrlab: /* here on detecting error */
if (! yyerrstatus)
/* If not already recovering from an error, report this error. */
{
++yynerrs;
#ifdef YYERROR_VERBOSE
yyn = yypact[yystate];
if (yyn > YYFLAG && yyn < YYLAST)
{
int size = 0;
char *msg;
int x, count;
count = 0;
/* Start X at -yyn if nec to avoid negative indexes in yycheck. */
for (x = (yyn < 0 ? -yyn : 0);
x < (int)(sizeof(yytname) / sizeof(char *)); x++) //_GD_
if (yycheck[x + yyn] == x)
size += strlen(yytname[x]) + 15, count++;
//_GD_ msg = (char *) malloc(size + 15);
msg = GM_NEW( char [size + 15] );
if (msg != 0)
{
strcpy(msg, "parse error");
if (count < 5)
{
count = 0;
for (x = (yyn < 0 ? -yyn : 0);
x < (sizeof(yytname) / sizeof(char *)); x++)
if (yycheck[x + yyn] == x)
{
strcat(msg, count == 0 ? ", expecting `" : " or `");
strcat(msg, yytname[x]);
strcat(msg, "'");
count++;
}
}
yyerror(msg);
//_GD_ free(msg);
delete [] msg;
}
else
yyerror ("parse error; also virtual memory exceeded");
}
else
#endif /* YYERROR_VERBOSE */
yyerror("parse error");
}
goto yyerrlab1;
yyerrlab1: /* here on error raised explicitly by an action */
if (yyerrstatus == 3)
{
/* if just tried and failed to reuse lookahead token after an error, discard it. */
/* return failure if at end of input */
if (yychar == YYEOF)
YYABORT;
#if YYDEBUG != 0
if (yydebug)
fprintf(stderr, "Discarding token %d (%s).\n", yychar, yytname[yychar1]);
#endif
yychar = YYEMPTY;
}
/* Else will try to reuse lookahead token
after shifting the error token. */
yyerrstatus = 3; /* Each real token shifted decrements this */
goto yyerrhandle;
yyerrdefault: /* current state does not do anything special for the error token. */
#if 0
/* This is wrong; only states that explicitly want error tokens
should shift them. */
yyn = yydefact[yystate]; /* If its default is to accept any token, ok. Otherwise pop it.*/
if (yyn) goto yydefault;
#endif
yyerrpop: /* pop the current state because it cannot handle the error token */
if (yyssp == yyss) YYABORT;
yyvsp--;
yystate = *--yyssp;
#ifdef YYLSP_NEEDED
yylsp--;
#endif
#if YYDEBUG != 0
if (yydebug)
{
short *ssp1 = yyss - 1;
fprintf (stderr, "Error: state stack now");
while (ssp1 != yyssp)
fprintf (stderr, " %d", *++ssp1);
fprintf (stderr, "\n");
}
#endif
yyerrhandle:
yyn = yypact[yystate];
if (yyn == YYFLAG)
goto yyerrdefault;
yyn += YYTERROR;
if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != YYTERROR)
goto yyerrdefault;
yyn = yytable[yyn];
if (yyn < 0)
{
if (yyn == YYFLAG)
goto yyerrpop;
yyn = -yyn;
goto yyreduce;
}
else if (yyn == 0)
goto yyerrpop;
if (yyn == YYFINAL)
YYACCEPT;
#if YYDEBUG != 0
if (yydebug)
fprintf(stderr, "Shifting error token, ");
#endif
*++yyvsp = yylval;
#ifdef YYLSP_NEEDED
*++yylsp = yylloc;
#endif
yystate = yyn;
goto yynewstate;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,18 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#include "gmConfig.h"
#include "gmArraySimple.h"
// Must be last header
#include "memdbgon.h"
+333
View File
@@ -0,0 +1,333 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#ifndef _GMARRAYSIMPLE_H_
#define _GMARRAYSIMPLE_H_
#include "gmConfig.h"
#include "gmUtil.h"
#define TMPL template <class T>
#define QUAL gmArraySimple<T>
/// \class gmArraySimple
/// \brief templated array class for simple types with power of 2 auto size option.
/// Elements may be moved around in memory using memcpy() during resize operations.
TMPL class gmArraySimple
{
public:
enum
{
NULL_INDEX = ~0
};
gmArraySimple(void);
gmArraySimple(const QUAL &a_array);
~gmArraySimple(void);
template <class J>
inline void InsertLast(const J &a_elem)
{
if(m_count >= m_size)
{
Resize(m_count + 1);
}
m_elem[m_count++] = a_elem;
}
/// \brief SetBlockSize() will set the hysteresis memory grow by in elements.
/// \param a_blockSize as 0 will set automatic power of 2.
inline void SetBlockSize(gmuint a_blockSize) { m_blockSize = a_blockSize; }
inline bool InsertLastIfUnique(const T &a_elem);
inline T& InsertLast(void);
inline void InsertBefore(gmuint a_index, const T &a_elem);
inline void Remove(gmuint a_index);
inline void RemoveSwapLast(gmuint a_index);
inline void RemoveLast(void);
inline T &operator[](gmuint a_index);
inline const T &operator[](gmuint a_index) const;
inline gmuint Count(void) const { return m_count; }
inline bool IsEmpty(void) const { return (m_count == 0); }
inline void Reset(void) { SetCount(0); }
inline void ResetAndFreeMemory(void);
inline void SetCount(gmuint a_count);
inline void SetCountAndFreeMemory(gmuint a_count);
inline void Touch(gmuint a_element);
inline T* GetData(void) { return m_elem; }
inline const T* GetData(void) const { return m_elem; }
inline gmuint GetSize(void) { return m_size; }
bool IsValid(const T* a_elem) const;
inline QUAL &operator=(const QUAL &a_array);
inline bool FindRemove(const T &a_elem);
template <class Q>
inline gmuint FindIndex(const Q &a_elem) const
{
// iterate backwards, better chance of finding a_elem, given InsertLast() is
// used commonly which presents possible element coherence
if(m_count == 0) return NULL_INDEX;
gmuint i = m_count - 1;
do
{
if (m_elem[i] == a_elem) // used commonly which presents possible element coherence
return i;
} while(i-- > 0);
return NULL_INDEX;
}
private:
T *m_elem;
gmuint m_count, m_size;
gmuint m_blockSize; //!< 0 and will be power of 2 sizing.
/// \brief Resize() will resize the array.
/// \param a_size is the required size.
void Resize(gmuint a_size, bool a_shrinkIfPossible = false);
};
//
// implementation
//
TMPL
inline QUAL::gmArraySimple(void)
{
m_elem = NULL;
m_count = 0;
m_size = 0;
m_blockSize = 0; // power of 2 auto
}
TMPL inline QUAL::gmArraySimple(const QUAL &a_array)
{
m_elem = NULL;
m_count = 0;
m_size = 0;
m_blockSize = 0; // power of 2 auto
operator=(a_array);
}
TMPL
inline QUAL::~gmArraySimple(void)
{
if(m_elem)
{
delete[] (char *) m_elem;
}
}
TMPL bool QUAL::InsertLastIfUnique(const T &a_elem)
{
if(FindIndex(a_elem) == NULL_INDEX)
{
InsertLast(a_elem);
return true;
}
return false;
}
TMPL inline T& QUAL::InsertLast(void)
{
if(m_count >= m_size)
{
Resize(m_count + 1);
}
return m_elem[m_count++];
}
TMPL
inline void QUAL::InsertBefore(gmuint a_index, const T &a_elem)
{
if(a_index >= m_count)
{
InsertLast(a_elem);
}
else
{
if(m_count >= m_size)
{
Resize(m_count + 1);
}
memmove(&m_elem[a_index+1], &m_elem[a_index], (m_count - a_index) * sizeof(T));
m_elem[a_index] = a_elem;
++m_count;
}
}
TMPL
inline void QUAL::Remove(gmuint a_index)
{
if(a_index >= m_count) return;
memmove(&m_elem[a_index], &m_elem[a_index+1], (m_count - (a_index + 1)) * sizeof(T));
--m_count;
}
TMPL
inline void QUAL::RemoveSwapLast(gmuint a_index)
{
if (a_index >= m_count) return;
if(--m_count != a_index)
{
m_elem[a_index] = m_elem[m_count];
}
}
TMPL
inline void QUAL::RemoveLast(void)
{
GM_ASSERT(m_count > 0);
--m_count;
}
TMPL
inline T &QUAL::operator[](gmuint a_index)
{
GM_ASSERT(a_index >= 0 && a_index < m_count);
return m_elem[a_index];
}
TMPL
inline const T &QUAL::operator[](gmuint a_index) const
{
GM_ASSERT(a_index >= 0 && a_index < m_count);
return m_elem[a_index];
}
TMPL
inline void QUAL::ResetAndFreeMemory(void)
{
if(m_elem)
{
delete[] (char *) m_elem;
m_elem = NULL;
}
m_count = m_size = 0;
}
TMPL
inline void QUAL::SetCount(gmuint a_count)
{
if(a_count > m_size)
{
Resize(a_count);
}
m_count = a_count;
}
TMPL
inline void QUAL::SetCountAndFreeMemory(gmuint a_count)
{
Resize(a_count, true);
m_count = a_count;
}
TMPL
inline void QUAL::Touch(gmuint a_element)
{
if(a_element >= m_count)
{
SetCount(a_element + 1);
}
}
TMPL bool QUAL::IsValid(const T* a_elem) const
{
gmuint index = (a_elem - m_elem);
return (index < m_count);
}
TMPL
inline QUAL &QUAL::operator=(const QUAL &a_array)
{
SetCount(a_array.m_count);
memcpy((char*)m_elem, (const char*)a_array.m_elem, m_count * sizeof(T));
return *this;
}
TMPL bool QUAL::FindRemove(const T &a_elem)
{
gmuint index = FindIndex(a_elem);
if(index != NULL_INDEX)
{
Remove(index);
return true;
}
return false;
}
TMPL
void QUAL::Resize(gmuint a_size, bool a_shrinkIfPossible)
{
if(m_size >= a_size)
{
// TODO: handle a_shrinkIfPossible.
return;
}
// we need to grow, figure out a new size.
gmuint size = 0;
if(m_blockSize > 0)
{
size = ((a_size / m_blockSize) + 1) * m_blockSize;
}
else
{
size = gmLog2ge(gmMax<gmuint>(4, a_size + 1));
}
// alloc, copy, free
{
T * t = (T*) GM_NEW(char[sizeof(T) * size]);
if(m_elem)
{
memcpy(t, m_elem, m_count * sizeof(T));
delete[] (char *) m_elem;
}
m_elem = t;
}
m_size = size;
}
#undef QUAL
#undef TMPL
#endif
+134
View File
@@ -0,0 +1,134 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#include "gmConfig.h"
#include "gmByteCode.h"
// Must be last header
#include "memdbgon.h"
#if GM_COMPILE_DEBUG
void gmByteCodePrint(FILE * a_fp, const void * a_byteCode, int a_byteCodeLength)
{
union
{
const gmuint32 * instruction32;
const gmuint8 * instruction;
};
instruction = (const gmuint8 *) a_byteCode;
const gmuint8 * end = instruction + a_byteCodeLength;
const gmuint8 * start = instruction;
const char * cp;
bool opiptr, opf32;
while(instruction < end)
{
opiptr = false;
opf32 = false;
int addr = instruction - start;
switch(*instruction)
{
case BC_NOP : cp = "nop"; break;
case BC_LINE : cp = "line"; break;
case BC_GETDOT : cp = "get dot"; opiptr = true; break;
case BC_SETDOT : cp = "set dot"; opiptr = true; break;
case BC_GETIND : cp = "get index"; break;
case BC_SETIND : cp = "set index"; break;
case BC_BRA : cp = "bra"; opiptr = true; break;
case BC_BRZ : cp = "brz"; opiptr = true; break;
case BC_BRNZ : cp = "brnz"; opiptr = true; break;
case BC_BRZK : cp = "brzk"; opiptr = true; break;
case BC_BRNZK : cp = "brnzk"; opiptr = true; break;
case BC_CALL : cp = "call"; opiptr = true; break;
case BC_RET : cp = "ret"; break;
case BC_RETV : cp = "retv"; break;
case BC_FOREACH : cp = "foreach"; opiptr = true; break;
case BC_POP : cp = "pop"; break;
case BC_POP2 : cp = "pop2"; break;
case BC_DUP : cp = "dup"; break;
case BC_DUP2 : cp = "dup2"; break;
case BC_SWAP : cp = "swap"; break;
case BC_PUSHNULL : cp = "push null"; break;
case BC_PUSHINT : cp = "push int"; opiptr = true; break;
case BC_PUSHINT0 : cp = "push int 0"; break;
case BC_PUSHINT1 : cp = "push int 1"; break;
case BC_PUSHFP : cp = "push fp"; opf32 = true; break;
case BC_PUSHSTR : cp = "push str"; opiptr = true; break;
case BC_PUSHTBL : cp = "push tbl"; break;
case BC_PUSHFN : cp = "push fn"; opiptr = true; break;
case BC_PUSHTHIS : cp = "push this"; break;
case BC_GETLOCAL : cp = "get local"; opiptr = true; break;
case BC_SETLOCAL : cp = "set local"; opiptr = true; break;
case BC_GETGLOBAL : cp = "get global"; opiptr = true; break;
case BC_SETGLOBAL : cp = "set global"; opiptr = true; break;
case BC_GETTHIS : cp = "get this"; opiptr = true; break;
case BC_SETTHIS : cp = "set this"; opiptr = true; break;
case BC_OP_ADD : cp = "add"; break;
case BC_OP_SUB : cp = "sub"; break;
case BC_OP_MUL : cp = "mul"; break;
case BC_OP_DIV : cp = "div"; break;
case BC_OP_REM : cp = "rem"; break;
case BC_BIT_OR : cp = "bor"; break;
case BC_BIT_XOR : cp = "bxor"; break;
case BC_BIT_AND : cp = "band"; break;
case BC_BIT_INV : cp = "binv"; break;
case BC_BIT_SHL : cp = "bshl"; break;
case BC_BIT_SHR : cp = "bshr"; break;
case BC_OP_NEG : cp = "neg"; break;
case BC_OP_POS : cp = "pos"; break;
case BC_OP_NOT : cp = "not"; break;
case BC_OP_LT : cp = "lt"; break;
case BC_OP_GT : cp = "gt"; break;
case BC_OP_LTE : cp = "lte"; break;
case BC_OP_GTE : cp = "gte"; break;
case BC_OP_EQ : cp = "eq"; break;
case BC_OP_NEQ : cp = "neq"; break;
default : cp = "ERROR"; break;
}
++instruction32;
if(opf32)
{
float fval = *((float *) instruction);
instruction += sizeof(gmint32);
fprintf(a_fp, " %04d %s %f"GM_NL, addr, cp, fval);
}
else if (opiptr)
{
gmptr ival = *((gmptr *) instruction);
instruction += sizeof(gmptr);
fprintf(a_fp, " %04d %s %d"GM_NL, addr, cp, ival);
}
else
{
fprintf(a_fp, " %04d %s"GM_NL, addr, cp);
}
}
}
#endif // GM_COMPILE_DEBUG
+102
View File
@@ -0,0 +1,102 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#ifndef _GMBYTECODE_H_
#define _GMBYTECODE_H_
#include "gmConfig.h"
/// \enum gmByteCode
/// \brief gmByteCode are the op codes for the game monkey scripting. The first byte codes MUST match the gmOperator
/// enum.
enum gmByteCode
{
// BC_GETDOT to BC_NOP MUST MATCH ENUM GMOPERATOR
BC_GETDOT = 0, // tos '.' opptr, push result
BC_SETDOT, // tos-1 '.' opptr = tos, tos -= 2
BC_GETIND, // tos-1 = tos-1 [tos], --tos
BC_SETIND, // tos-2 [tos-1] = tos, tos -= 3
// math
BC_OP_ADD,
BC_OP_SUB,
BC_OP_MUL,
BC_OP_DIV,
BC_OP_REM,
// bit
BC_BIT_OR,
BC_BIT_XOR,
BC_BIT_AND,
BC_BIT_SHL,
BC_BIT_SHR,
BC_BIT_INV,
// compare
BC_OP_LT,
BC_OP_GT,
BC_OP_LTE,
BC_OP_GTE,
BC_OP_EQ,
BC_OP_NEQ,
// unary
BC_OP_NEG,
BC_OP_POS,
BC_OP_NOT,
BC_NOP,
BC_LINE, // indicates instruction is on a new code line to the last executed instruction. used in debug mode
// branch
BC_BRA, // branch always
BC_BRZ, // branch tos equal to zero, --tos
BC_BRNZ, // branch tos not equal to zero, --tos
BC_BRZK, // branch tos equal to zero keep value on stack
BC_BRNZK, // branch tos not equal to zero keep value on stack
BC_CALL, // call op16 num parameters
BC_RET, // return null, ++tos
BC_RETV, // return tos
BC_FOREACH, // op16 op16, table, iterator, leave loop complete bool on stack.
// stack
BC_POP, // --tos
BC_POP2, // tos -=2
BC_DUP, // tos + 1 = tos, ++tos
BC_DUP2, // tos + 1 = tos -1, tos + 2 = tos, tos += 2
BC_SWAP, //
BC_PUSHNULL, // push null,
BC_PUSHINT, // push int opptr
BC_PUSHINT0, // push 0
BC_PUSHINT1, // push 1
BC_PUSHFP, // push floating point op32
BC_PUSHSTR, // push string opptr
BC_PUSHTBL, // push table
BC_PUSHFN, // push function opptr
BC_PUSHTHIS, // push this
// get set
BC_GETLOCAL, // get local op16 (stack offset) ++tos
BC_SETLOCAL, // set local op16 (stack offset) --tos
BC_GETGLOBAL, // get global opptr (symbol id) ++tos
BC_SETGLOBAL, // set global opptr (symbol id) --tos
BC_GETTHIS, // get this opptr (symbol id) ++tos
BC_SETTHIS, // set this opptr (symbol id) --tos
};
#if GM_COMPILE_DEBUG
void gmByteCodePrint(FILE * a_fp, const void * a_byteCode, int a_byteCodeLength);
#endif // GM_COMPILE_DEBUG
#endif
@@ -0,0 +1,158 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#include "gmConfig.h"
#include "gmByteCodeGen.h"
// Must be last header
#include "memdbgon.h"
gmByteCodeGen::gmByteCodeGen(void * a_context)
{
m_tos = 0;
m_maxTos = 0;
m_emitCallback = NULL;
m_context = a_context;
}
void gmByteCodeGen::Reset(void * a_context)
{
gmStreamBufferDynamic::Reset();
m_tos = 0;
m_maxTos = 0;
m_emitCallback = NULL;
m_context = a_context;
}
bool gmByteCodeGen::Emit(gmByteCode a_instruction)
{
if(m_emitCallback) m_emitCallback(Tell(), m_context);
AdjustStack(a_instruction);
*this << (gmuint32) a_instruction;
return true;
}
bool gmByteCodeGen::Emit(gmByteCode a_instruction, gmuint32 a_operand32)
{
if(m_emitCallback) m_emitCallback(Tell(), m_context);
AdjustStack(a_instruction);
*this << (gmuint32) a_instruction;
*this << a_operand32;
return true;
}
bool gmByteCodeGen::EmitPtr(gmByteCode a_instruction, gmptr a_operand)
{
if(m_emitCallback) m_emitCallback(Tell(), m_context);
AdjustStack(a_instruction);
*this << ((gmuint32) a_instruction);
*this << a_operand;
return true;
}
unsigned int gmByteCodeGen::Skip(unsigned int p_n, unsigned char p_value)
{
unsigned int oldPos = Tell();
if(p_n)
{
char * fill = (char *) alloca(p_n);
memset(fill, p_value, p_n);
Write(fill, p_n);
}
return oldPos;
}
void gmByteCodeGen::AdjustStack(gmByteCode a_instruction)
{
switch(a_instruction)
{
case BC_NOP : break;
case BC_LINE : break;
case BC_GETDOT : m_tos += 0; break;
case BC_SETDOT : m_tos -= 2; break;
case BC_GETIND : --m_tos; break;
case BC_SETIND : m_tos -= 3; break;
case BC_BRA : break;
case BC_BRZ : --m_tos; break;
case BC_BRNZ : --m_tos; break;
case BC_BRZK : break;
case BC_BRNZK : break;
case BC_CALL : break;
case BC_RET : break;
case BC_RETV : break;
case BC_FOREACH : ++m_tos; break;
case BC_POP : --m_tos; break;
case BC_POP2 : m_tos -= 2; break;
case BC_DUP : ++m_tos; break;
case BC_DUP2 : m_tos += 2; break;
case BC_SWAP : break;
case BC_PUSHNULL : ++m_tos; break;
case BC_PUSHINT : ++m_tos; break;
case BC_PUSHINT0 : ++m_tos; break;
case BC_PUSHINT1 : ++m_tos; break;
case BC_PUSHFP : ++m_tos; break;
case BC_PUSHSTR : ++m_tos; break;
case BC_PUSHTBL : ++m_tos; break;
case BC_PUSHFN : ++m_tos; break;
case BC_PUSHTHIS : ++m_tos; break;
case BC_GETLOCAL : ++m_tos; break;
case BC_SETLOCAL : --m_tos; break;
case BC_GETGLOBAL : ++m_tos; break;
case BC_SETGLOBAL : --m_tos; break;
case BC_GETTHIS : ++m_tos; break;
case BC_SETTHIS : --m_tos; break;
case BC_OP_ADD : --m_tos; break;
case BC_OP_SUB : --m_tos; break;
case BC_OP_MUL : --m_tos; break;
case BC_OP_DIV : --m_tos; break;
case BC_OP_REM : --m_tos; break;
case BC_BIT_OR : --m_tos; break;
case BC_BIT_XOR : --m_tos; break;
case BC_BIT_AND : --m_tos; break;
case BC_BIT_INV : --m_tos; break;
case BC_BIT_SHL : --m_tos; break;
case BC_BIT_SHR : --m_tos; break;
case BC_OP_NEG : break;
case BC_OP_POS : break;
case BC_OP_NOT : break;
case BC_OP_LT : --m_tos; break;
case BC_OP_GT : --m_tos; break;
case BC_OP_LTE : --m_tos; break;
case BC_OP_GTE : --m_tos; break;
case BC_OP_EQ : --m_tos; break;
case BC_OP_NEQ : --m_tos; break;
}
if(m_tos > m_maxTos) m_maxTos = m_tos;
}
@@ -0,0 +1,49 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#ifndef _GMBYTECODEGEN_H_
#define _GMBYTECODEGEN_H_
#include "gmConfig.h"
#include "gmStreamBuffer.h"
#include "gmByteCode.h"
/// \class gmByteCodeBuffer
class gmByteCodeGen : public gmStreamBufferDynamic
{
public:
gmByteCodeGen(void * a_context = NULL);
virtual ~gmByteCodeGen() {}
void Reset(void * a_context = NULL);
bool Emit(gmByteCode a_instruction);
bool Emit(gmByteCode a_instruction, gmuint32 a_operand32);
bool EmitPtr(gmByteCode a_instruction, gmptr a_operand);
unsigned int Skip(unsigned int p_n, unsigned char p_value = 0);
/// \brief m_emitCallback will be called whenever code is emitted
void (GM_CDECL *m_emitCallback)(int a_address, void * a_context);
inline int GetMaxTos() const { return m_maxTos; }
inline int GetTos() const { return m_tos; }
inline void SetTos(int a_tos) { m_tos = a_tos; }
protected:
void AdjustStack(gmByteCode a_instruction);
int m_tos;
int m_maxTos;
void * m_context;
};
#endif // _GMBYTECODEGEN_H_
File diff suppressed because it is too large Load Diff
+49
View File
@@ -0,0 +1,49 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#ifndef _GMCODEGEN_H_
#define _GMCODEGEN_H_
#include "gmConfig.h"
#include "gmLog.h"
#include "gmCodeGenHooks.h"
// fwd decl
struct gmCodeTreeNode;
/// \class gmCodeGen
/// \brief gmCodeGen will create byte code for a given code tree. after parsing script into a code tree using gmCodeTree,
/// turn it into byte code using this class. After the code gen has been run, the gmCodeTree may be unlocked.
/// Note that the code tree is parsed into a set of functions authored using a gmCodeGenHooks implementation.
class gmCodeGen
{
public:
/// \brief Get() will return the singleton code generator.
static gmCodeGen& Get();
/// \brief FreeMemory() will free all memory allocated by the code tree. must be unlocked
virtual void FreeMemory() = 0;
/// \brief Lock() will create the byte code for the given gode tree.
/// \param a_codeTree is the code tree.
/// \param a_hooks is the byte code authoring object.
/// \param a_debug is true if debug info is required.
/// \param a_log is the compile log.
/// \return the number of errors encounted
virtual int Lock(const gmCodeTreeNode * a_codeTree, gmCodeGenHooks * a_hooks, bool a_debug, gmLog * a_log) = 0;
/// \brief Unlock() will reset the code generator.
virtual int Unlock() = 0;
};
#endif // _GMCODEGEN_H_
@@ -0,0 +1,17 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#include "gmConfig.h"
#include "gmCodeGenHooks.h"
// Must be last header
#include "memdbgon.h"
@@ -0,0 +1,101 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#ifndef _GMCODEGENHOOKS_H_
#define _GMCODEGENHOOKS_H_
#include "gmConfig.h"
/// \struct gmLineInfo
/// \brief gmLineInfo describes the debug info required for line number debugging
struct gmLineInfo
{
int m_address; //!< byte code address
int m_lineNumber; //!< code line number
};
/// \struct gmFunctionInfo
/// \brief gmFunctionInfo
struct gmFunctionInfo
{
gmptr m_id; //!< unique id of the function (as used in BC_PUSHFN)
bool m_root; //!< is this function the root function '__main'
const void * m_byteCode; //!< byte code
int m_byteCodeLength; //!< byte code length in bytes
int m_numParams; //!< parameter count
int m_numLocals; //!< local variable count (includes registers)
int m_maxStackSize; //!< required temporary storage
const char * m_debugName; //!< name of variable function was assigned to... may be NULL
const char ** m_symbols; //!< param and local variable names, sizeof m_numParams + m_numLocals; (indexed by stack offset)
int m_lineInfoCount; //!< number of entries in the line info array
const gmLineInfo * m_lineInfo; //!< line - instruction address mapping for debugging purposes.
};
/// \class gmCodeGenHooks
/// \brief gmCodeGenHooks is an interface that is fed to the compiler. basically the code gen hooks class allows you
/// to compile script directly into the runtime vm, or into a libary.
class gmCodeGenHooks
{
public:
gmCodeGenHooks() {}
virtual ~gmCodeGenHooks() {}
/// \brief Begin() will be called by gmCodeGen at the start of compilation.
/// \param a_debug is true if this is a debug build.
virtual bool Begin(bool a_debug) = 0;
/// \brief AddFunction() is called each time the byte code for a function has been created. The memory passed in
/// the info structure is not valid after AddFunction returns.
/// \return true on success
virtual bool AddFunction(gmFunctionInfo &a_functionInfo) = 0;
/// \brief End() is called by gmCodeGen at the end of a compilation.
/// \param a_errors is the number of compilation errors.
virtual bool End(int a_errors) = 0;
/// \brief GetFunctionId() is called for the creation of unique function ids.
/// \return a unique id.
virtual gmptr GetFunctionId() = 0;
/// \brief GetSymbolId() is called by the compiler to get a unique symbol id. this sybol id is a machine size int
/// id written into the byte code.
/// \return a unique id for each unique a_symbol.
virtual gmptr GetSymbolId(const char * a_symbol) = 0;
/// \brief GetStringId() is called by the compiler to get a constant string id. the returned value is written into
/// the byte code for string lookups.
/// \return a unique id for each unique string.
virtual gmptr GetStringId(const char * a_string) = 0;
/// \brief SwapEndian() returns true if the byte code is being compiled for a machine of differing endian
virtual bool SwapEndian() const { return false; }
};
/// \class gmCodeGenHooksNull
/// \brief used for syntax checking etc.
class gmCodeGenHooksNull : public gmCodeGenHooks
{
public:
gmCodeGenHooksNull() {}
virtual ~gmCodeGenHooksNull() {}
virtual bool Begin(bool a_debug) { return true; }
virtual bool AddFunction(gmFunctionInfo &a_functionInfo) { return true; }
virtual bool End(int a_errors) { return true; }
virtual gmptr GetFunctionId() { return 0; }
virtual gmptr GetSymbolId(const char * a_symbol) { return 0; }
virtual gmptr GetStringId(const char * a_string) { return 0; }
};
#endif // _GMCODEGENHOOKS_H_
+555
View File
@@ -0,0 +1,555 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#include "gmConfig.h"
#include "gmCodeTree.h"
// Must be last header
#include "memdbgon.h"
gmCodeTreeNode * g_codeTree = NULL;
gmCodeTree::gmCodeTree() :
m_mem(1, GMCODETREE_CHAINSIZE)
{
g_codeTree = NULL;
m_locked = false;
m_errors = 0;
m_log = 0;
}
gmCodeTree::~gmCodeTree()
{
Unlock();
}
gmCodeTree &gmCodeTree::Get()
{
static gmCodeTree codeTree;
return codeTree;
}
void gmCodeTree::FreeMemory()
{
if(m_locked == false)
{
m_mem.ResetAndFreeMemory();
}
}
int gmCodeTree::Lock(const char * a_script, gmLog * a_log)
{
if(m_locked == true) return 1;
m_errors = 0;
m_locked = true;
m_log = a_log;
g_codeTree = NULL;
//gmdebug = 1;
gmlineno = 1;
// create a scan buffer
YY_BUFFER_STATE buffer = gm_scan_string(a_script);
if(buffer)
{
m_errors = gmparse();
gm_delete_buffer(buffer);
}
return m_errors;
}
int gmCodeTree::Unlock()
{
m_mem.Reset();
g_codeTree = NULL;
m_locked = false;
m_errors = 0;
m_log = NULL;
return 0;
}
const gmCodeTreeNode * gmCodeTree::GetCodeTree() const
{
return g_codeTree;
}
void * gmCodeTree::Alloc(int a_size, int a_align)
{
return m_mem.AllocBytes(a_size, a_align);
}
#if GM_COMPILE_DEBUG
const char * gmGetOperatorTypeName(gmCodeTreeNodeOperationType a_type)
{
switch(a_type)
{
case CTNOT_INVALID : return "CTNOT_INVALID";
case CTNOT_DOT : return "CTNOT_DOT";
case CTNOT_UNARY_PLUS : return "CTNOT_UNARY_PLUS";
case CTNOT_UNARY_MINUS : return "CTNOT_UNARY_MINUS";
case CTNOT_UNARY_NOT : return "CTNOT_UNARY_NOT";
case CTNOT_UNARY_COMPLEMENT : return "CTNOT_UNARY_COMPLEMENT";
case CTNOT_ARRAY_INDEX : return "CTNOT_ARRAY_INDEX";
case CTNOT_TIMES : return "CTNOT_TIMES";
case CTNOT_DIVIDE : return "CTNOT_DIVIDE";
case CTNOT_REM : return "CTNOT_REM";
case CTNOT_ADD : return "CTNOT_ADD";
case CTNOT_MINUS : return "CTNOT_MINUS";
case CTNOT_LT : return "CTNOT_LT";
case CTNOT_GT : return "CTNOT_GT";
case CTNOT_LTE : return "CTNOT_LTE";
case CTNOT_GTE : return "CTNOT_GTE";
case CTNOT_EQ : return "CTNOT_EQ";
case CTNOT_NEQ : return "CTNOT_NEQ";
case CTNOT_AND : return "CTNOT_AND";
case CTNOT_OR : return "CTNOT_OR";
case CTNOT_BIT_OR : return "CTNOT_BIT_OR";
case CTNOT_BIT_XOR : return "CTNOT_BIT_XOR";
case CTNOT_BIT_AND : return "CTNOT_BIT_AND";
case CTNOT_SHIFT_LEFT : return "CTNOT_SHIFT_LEFT";
case CTNOT_SHIFT_RIGHT : return "CTNOT_SHIFT_RIGHT";
case CTNOT_ASSIGN : return "CTNOT_ASSIGN";
case CTNOT_ASSIGN_FIELD : return "CTNOT_ASSIGN_FIELD";
default : break;
}
return "UNKNOWN OPERATOR TYPE";
};
static void PrintRecursive(const gmCodeTreeNode * a_node, FILE * a_fp, bool a_firstCall)
{
if(a_node)
{
static int indent;
int i;
if(a_firstCall)
{
indent = 0;
}
indent += 2;
while(a_node != NULL)
{
for(i = 0; i < indent; ++i)
fprintf(a_fp, " ");
if(a_node->m_type == CTNT_DECLARATION)
{
//
// DECLARATIONS
//
switch(a_node->m_subType)
{
case CTNDT_PARAMETER : fprintf(a_fp, "CTNDT_PARAMETER:%04d"GM_NL, a_node->m_lineNumber); break;
case CTNDT_VARIABLE : fprintf(a_fp, "CTNDT_VARIABLE:%04d, type %d"GM_NL, a_node->m_lineNumber, a_node->m_subTypeType); break;
default : fprintf(a_fp, "UNKNOWN DECLARATION:"GM_NL); break;
}
}
else if(a_node->m_type == CTNT_STATEMENT)
{
//
// STATEMENTS
//
switch(a_node->m_subType)
{
case CTNST_RETURN : fprintf(a_fp, "CTNST_RETURN:%04d"GM_NL, a_node->m_lineNumber); break;
case CTNST_BREAK : fprintf(a_fp, "CTNST_BREAK:%04d"GM_NL, a_node->m_lineNumber); break;
case CTNST_CONTINUE : fprintf(a_fp, "CTNST_CONTINUE:%04d"GM_NL, a_node->m_lineNumber); break;
case CTNST_FOR : fprintf(a_fp, "CTNST_FOR:%04d"GM_NL, a_node->m_lineNumber); break;
case CTNST_FOREACH : fprintf(a_fp, "CTNST_FOREACH:%04d"GM_NL, a_node->m_lineNumber); break;
case CTNST_WHILE : fprintf(a_fp, "CTNST_WHILE:%04d"GM_NL, a_node->m_lineNumber); break;
case CTNST_DOWHILE : fprintf(a_fp, "CTNST_DOWHILE:%04d"GM_NL, a_node->m_lineNumber); break;
case CTNST_IF : fprintf(a_fp, "CTNST_IF:%04d"GM_NL, a_node->m_lineNumber); break;
case CTNST_COMPOUND : fprintf(a_fp, "CTNST_COMPOUND:%04d"GM_NL, a_node->m_lineNumber); break;
default : fprintf(a_fp, "UNKNOWN STATEMENT:"GM_NL); break;
}
}
else if(a_node->m_type == CTNT_EXPRESSION)
{
//
// EXPRESSIONS
//
switch(a_node->m_subType)
{
case CTNET_OPERATION :
{
if(a_node->m_subTypeType < CTNOT_MAX)
{
fprintf(a_fp, "CTNET_OPERATION:%04d : %s"GM_NL, a_node->m_lineNumber, gmGetOperatorTypeName((gmCodeTreeNodeOperationType) a_node->m_subTypeType));
}
else
{
fprintf(a_fp, "UNKNOWN CTNET_OPERATION"GM_NL);
}
break;
}
case CTNET_CONSTANT :
{
switch(a_node->m_subTypeType)
{
case CTNCT_INT : fprintf(a_fp, "CTNCT_INT:%04d : %d"GM_NL, a_node->m_lineNumber, a_node->m_data.m_iValue); break;
case CTNCT_FLOAT : fprintf(a_fp, "CTNCT_FLOAT:%04d : %f"GM_NL, a_node->m_lineNumber, a_node->m_data.m_fValue); break;
case CTNCT_STRING : fprintf(a_fp, "CTNCT_STRING:%04d : %s"GM_NL, a_node->m_lineNumber, a_node->m_data.m_string); break;
case CTNCT_NULL : fprintf(a_fp, "CTNCT_NULL:%04d"GM_NL, a_node->m_lineNumber); break;
default: fprintf(a_fp, "UNKNOWN CTNET_CONSTANT"GM_NL);
}
break;
}
case CTNET_IDENTIFIER : fprintf(a_fp, "CTNET_IDENTIFIER:%04d : %s"GM_NL, a_node->m_lineNumber, a_node->m_data.m_string); break;
case CTNET_THIS : fprintf(a_fp, "CTNET_THIS:%04d"GM_NL, a_node->m_lineNumber); break;
case CTNET_CALL : fprintf(a_fp, "CTNET_CALL:%04d"GM_NL, a_node->m_lineNumber); break;
case CTNET_FUNCTION : fprintf(a_fp, "CTNET_FUNCTION:%04d"GM_NL, a_node->m_lineNumber); break;
case CTNET_TABLE : fprintf(a_fp, "CTNET_TABLE:%04d"GM_NL, a_node->m_lineNumber); break;
default : fprintf(a_fp, "UNKNOWN EXPRESSION:"GM_NL); break;
}
}
else
{
fprintf(a_fp, "UNKNOWN NODE TYPE"GM_NL);
}
// print the child nodes
for(i = 0; i < GMCODETREE_NUMCHILDREN; ++i)
{
if(a_node->m_children[i])
{
PrintRecursive(a_node->m_children[i], a_fp, false);
}
}
a_node = a_node->m_sibling;
} // while(a_node != NULL)
indent -= 2;
}
}
void gmCodeTree::Print(FILE * a_fp)
{
if(m_locked)
{
PrintRecursive(g_codeTree, a_fp, true);
}
}
#endif // GM_COMPILE_DEBUG
gmCodeTreeNode * gmCodeTreeNode::Create(gmCodeTreeNodeType a_type, int a_subType, int a_lineNumber, int a_subTypeType)
{
gmCodeTreeNode * node = (gmCodeTreeNode *) gmCodeTree::Get().Alloc(sizeof(gmCodeTreeNode), GM_DEFAULT_ALLOC_ALIGNMENT);
GM_ASSERT(node != NULL);
memset(node, 0, sizeof(gmCodeTreeNode));
node->m_type = a_type;
node->m_subType = a_subType;
node->m_lineNumber = a_lineNumber;
node->m_subTypeType = a_subTypeType;
node->m_flags = 0;
return node;
}
void gmCodeTreeNode::SetChild(int a_index, gmCodeTreeNode * a_node)
{
GM_ASSERT(a_index >= 0 && a_index < GMCODETREE_NUMCHILDREN);
m_children[a_index] = a_node;
if(a_node != NULL)
{
a_node->m_parent = this;
}
}
static bool gmFold(float &a_r, float a_a, int a_op)
{
switch(a_op)
{
case CTNOT_UNARY_PLUS : a_r = a_a; break;
case CTNOT_UNARY_MINUS : a_r = -a_a; break;
default: return false;
}
return true;
}
static bool gmFold(int &a_r, int a_a, int a_op)
{
switch(a_op)
{
case CTNOT_UNARY_PLUS : a_r = a_a; break;
case CTNOT_UNARY_MINUS : a_r = -a_a; break;
case CTNOT_UNARY_NOT : a_r = !a_a; break;
case CTNOT_UNARY_COMPLEMENT : a_r = ~a_a; break;
default: return false;
}
return true;
}
#include <math.h>
static bool gmFold(float &a_r, float a_a, float a_b, int a_op)
{
switch(a_op)
{
case CTNOT_TIMES : a_r = a_a * a_b; break;
case CTNOT_DIVIDE : if(a_b == 0) return false; a_r = a_a / a_b; break;
case CTNOT_REM : a_r = fmodf(a_a, a_b); break;
case CTNOT_ADD : a_r = a_a + a_b; break;
case CTNOT_MINUS : a_r = a_a - a_b; break;
default: return false;
}
return true;
}
static bool gmFold(int &a_r, int a_a, int a_b, int a_op)
{
switch(a_op)
{
case CTNOT_TIMES : a_r = a_a * a_b; break;
case CTNOT_DIVIDE : if(a_b == 0) return false; a_r = a_a / a_b; break;
case CTNOT_REM : a_r = a_a % a_b; break;
case CTNOT_ADD : a_r = a_a + a_b; break;
case CTNOT_MINUS : a_r = a_a - a_b; break;
case CTNOT_BIT_OR : a_r = a_a | a_b; break;
case CTNOT_BIT_XOR : a_r = a_a ^ a_b; break;
case CTNOT_BIT_AND : a_r = a_a & a_b; break;
case CTNOT_SHIFT_LEFT : a_r = a_a << a_b; break;
case CTNOT_SHIFT_RIGHT : a_r = a_a >> a_b; break;
default: return false;
}
return true;
}
bool gmCodeTreeNode::ConstantFold()
{
if(m_type == CTNT_EXPRESSION && m_subType == CTNET_OPERATION)
{
bool possibleUnaryFold = false;
bool possibleFold = false;
bool intOnly = false;
switch(m_subTypeType)
{
case CTNOT_UNARY_PLUS :
case CTNOT_UNARY_MINUS :
possibleUnaryFold = true;
break;
case CTNOT_UNARY_NOT :
case CTNOT_UNARY_COMPLEMENT :
possibleUnaryFold = true;
intOnly = true;
break;
case CTNOT_TIMES :
case CTNOT_DIVIDE :
case CTNOT_REM :
case CTNOT_ADD :
case CTNOT_MINUS :
possibleFold = true;
break;
case CTNOT_BIT_OR :
case CTNOT_BIT_XOR :
case CTNOT_BIT_AND :
case CTNOT_SHIFT_LEFT :
case CTNOT_SHIFT_RIGHT :
possibleFold = true;
intOnly = true;
break;
default:
break;
}
if(possibleUnaryFold)
{
gmCodeTreeNode * l = m_children[0];
if(l && l->m_type == CTNT_EXPRESSION && l->m_subType == CTNET_CONSTANT)
{
if(l->m_subTypeType == CTNCT_INT || (l->m_subTypeType == CTNCT_FLOAT && !intOnly))
{
// we can fold....
m_children[0] = NULL;
m_subType = CTNET_CONSTANT;
if(l->m_subTypeType == CTNCT_INT)
{
gmFold(m_data.m_iValue, l->m_data.m_iValue, m_subTypeType);
m_subTypeType = CTNCT_INT;
}
else if(l->m_subTypeType == CTNCT_FLOAT)
{
gmFold(m_data.m_fValue, l->m_data.m_fValue, m_subTypeType);
m_subTypeType = CTNCT_FLOAT;
}
return true;
}
}
}
else if(possibleFold)
{
gmCodeTreeNode * l = m_children[0], * r = m_children[1];
if((l && l->m_type == CTNT_EXPRESSION && l->m_subType == CTNET_CONSTANT) &&
(r && r->m_type == CTNT_EXPRESSION && r->m_subType == CTNET_CONSTANT))
{
if((l->m_subTypeType == CTNCT_INT || (l->m_subTypeType == CTNCT_FLOAT && !intOnly)) &&
(r->m_subTypeType == CTNCT_INT || (r->m_subTypeType == CTNCT_FLOAT && !intOnly)))
{
// we can fold....
m_children[0] = NULL; m_children[1] = NULL;
m_subType = CTNET_CONSTANT;
if(l->m_subTypeType == CTNCT_INT && r->m_subTypeType == CTNCT_INT)
{
gmFold(m_data.m_iValue, l->m_data.m_iValue, r->m_data.m_iValue, m_subTypeType);
m_subTypeType = CTNCT_INT;
}
else if(l->m_subTypeType == CTNCT_FLOAT && r->m_subTypeType == CTNCT_FLOAT)
{
gmFold(m_data.m_fValue, l->m_data.m_fValue, r->m_data.m_fValue, m_subTypeType);
m_subTypeType = CTNCT_FLOAT;
}
else if(l->m_subTypeType == CTNCT_INT && r->m_subTypeType == CTNCT_FLOAT)
{
gmFold(m_data.m_fValue, (float) l->m_data.m_iValue, r->m_data.m_fValue, m_subTypeType);
m_subTypeType = CTNCT_FLOAT;
}
else if(l->m_subTypeType == CTNCT_FLOAT && r->m_subTypeType == CTNCT_INT)
{
gmFold(m_data.m_fValue, l->m_data.m_fValue, (float) r->m_data.m_iValue, m_subTypeType);
m_subTypeType = CTNCT_FLOAT;
}
return true;
}
}
}
}
return false;
}
void gmProcessSingleQuoteString(char * a_string)
{
char * c = a_string;
char * r = a_string;
while(*c)
{
if(c[0] == '`' && c[1] == '`' && c[2])
{
*(r++) = *c;
c += 2;
continue;
}
else if(c[0] == '`')
{
++c;
continue;
}
*(r++) = *(c++);
}
*r = '\0';
}
void gmProcessDoubleQuoteString(char * a_string)
{
char * c = a_string;
char * r = a_string;
while(*c)
{
if(c[0] == '\"')
{
++c;
continue;
}
else if(c[0] == '\\')
{
switch(c[1])
{
case '0' : case '1' : case '2' : case '3' : case '4' : case '5' :
case '6' : case '7' : case '8' : case '9' :
{
char buffer[4]; int i = 0;
while(i < 3 && isdigit(c[i+1]))
{
buffer[i] = c[i+1];
++i;
}
buffer[i] = '\0';
*r = (char) (atoi(buffer) & 0xff);
c += (i - 1);
break;
}
case 'a' : *r = '\a'; break;
case 'b' : *r = '\b'; break;
case 'f' : *r = '\f'; break;
case 'n' : *r = '\n'; break;
case 'r' : *r = '\r'; break;
case 't' : *r = '\t'; break;
case 'v' : *r = '\v'; break;
case '\'' : *r = '\''; break;
case '\"' : *r = '\"'; break;
case '\\' : *r = '\\'; break;
default: *r = c[1];
}
++r;
c += 2;
continue;
}
*(r++) = *(c++);
}
*r = '\0';
}
int gmerror(char * a_message)
{
gmCodeTree & ct = gmCodeTree::Get();
if(ct.GetLog())
{
ct.GetLog()->LogEntry("error (%d) %s", gmlineno, a_message);
}
return 0;
}
+254
View File
@@ -0,0 +1,254 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#ifndef _GMCODETREE_H_
#define _GMCODETREE_H_
#include "gmConfig.h"
#include "gmMem.h"
#include "gmMemChain.h"
#include "gmLog.h"
#include "gmScanner.h"
#define GMCODETREE_NUMCHILDREN 4
// fwd decl
struct gmCodeTreeNode;
/// \class gmCodeTree
/// \brief gmCodeTree is a singleton class for creating code trees.
class gmCodeTree
{
protected:
gmCodeTree();
public:
~gmCodeTree();
/// \brief Get() will return the singlton parser.
static gmCodeTree &Get();
/// \brief FreeMemory() will free all memory allocated by the code tree. must be unlocked
void FreeMemory();
/// \brief Lock() will create a code tree for the passed script. Note that the code tree is valid until
/// Unlock() is called.
/// \param a_script is a null terminated script string.
/// \return the number of errors encounted when parsing.
/// \sa Unlock()
int Lock(const char * a_script, gmLog * a_log = NULL);
/// \brief Unlock() will unlock the singleton code tree such that it may be used again.
/// \return 0 on success
/// \sa Lock()
int Unlock();
/// \brief GetCodeTree() will return the code tree resulting from a Lock() operation.
/// \return NULL on failure
/// \sa Lock()
const gmCodeTreeNode * GetCodeTree() const;
inline gmLog * GetLog() const { return m_log; }
/// \brief Alloc() will return memory from the code tree memory pool. This method is used by the
/// parser when building the code tree.
/// \return NULL on failure
void * Alloc(int a_size, int a_align = GM_DEFAULT_ALLOC_ALIGNMENT);
#if GM_COMPILE_DEBUG
/// \brief Print() will write the tree to the given file. this is purely for debugging.
/// \param a_fp is an open file for writing.
void Print(FILE * a_fp);
#endif // GM_COMPILE_DEBUG
private:
bool m_locked;
int m_errors;
gmLog * m_log;
gmMemChain m_mem;
};
/// \enum gmCodeTreeNodeType
/// \brief gmCodeTreeNodeType indicates the type of a gmCodeTreeNode.
enum gmCodeTreeNodeType
{
CTNT_INVALID = 0,
CTNT_DECLARATION,
CTNT_STATEMENT,
CTNT_EXPRESSION,
};
/// \enum gmCodeTreeNodeDeclarationType
/// \brief if a tree node is of type CTNT_DECLARATION, gmCodeTreeNodeDeclarationType are the sub types
enum gmCodeTreeNodeDeclarationType
{
CTNDT_PARAMETER = 0,
CTNDT_VARIABLE,
};
/// \enum gmCodeTreeVariableType
/// \brief if a treenode is CTNT_DECLARATION, CTNDT_VARIABLE, this is the type of variable decl.
enum gmCodeTreeVariableType
{
CTVT_LOCAL = 0,
CTVT_GLOBAL,
CTVT_MEMBER,
};
/// \enum gmCodeTreeNodeStatementType
/// \brief if a tree node is of type CTNT_STATEMENT, gmCodeTreeNodeStatementType are the sub types
enum gmCodeTreeNodeStatementType
{
CTNST_INVALID = 0,
CTNST_RETURN,
CTNST_BREAK,
CTNST_CONTINUE,
CTNST_FOR,
CTNST_FOREACH,
CTNST_WHILE,
CTNST_DOWHILE,
CTNST_IF,
CTNST_COMPOUND,
};
/// \enum gmCodeTreeNodeExpressionType
/// \brief
enum gmCodeTreeNodeExpressionType
{
CTNET_INVALID = 0,
CTNET_OPERATION,
CTNET_CONSTANT,
CTNET_IDENTIFIER,
CTNET_THIS,
CTNET_CALL,
CTNET_FUNCTION,
CTNET_TABLE,
};
/// \enum gmCodeTreeNodeOperationType
/// \brief
enum gmCodeTreeNodeOperationType
{
CTNOT_INVALID = 0,
CTNOT_DOT,
CTNOT_UNARY_PLUS,
CTNOT_UNARY_MINUS,
CTNOT_UNARY_COMPLEMENT,
CTNOT_UNARY_NOT,
CTNOT_ARRAY_INDEX,
CTNOT_TIMES,
CTNOT_DIVIDE,
CTNOT_REM,
CTNOT_ADD,
CTNOT_MINUS,
CTNOT_LT,
CTNOT_GT,
CTNOT_LTE,
CTNOT_GTE,
CTNOT_EQ,
CTNOT_NEQ,
CTNOT_AND,
CTNOT_OR,
CTNOT_BIT_OR,
CTNOT_BIT_XOR,
CTNOT_BIT_AND,
CTNOT_SHIFT_LEFT,
CTNOT_SHIFT_RIGHT,
CTNOT_ASSIGN,
CTNOT_ASSIGN_FIELD,
CTNOT_MAX,
};
/// \enum gmCodeTreeNodeConstantType
enum gmCodeTreeNodeConstantType
{
CTNCT_INVALID = 0,
CTNCT_INT,
CTNCT_FLOAT,
CTNCT_STRING,
CTNCT_NULL,
};
/// \union gmCodeTreeNodeUnion
/// \brief
union gmCodeTreeNodeData
{
char * m_string;
int m_iValue;
float m_fValue;
};
/// \struct gmCodeTreeNode
/// \brief gmCodeTreeNode is the tree node structure used to represent the game monkey script syntax tree.
struct gmCodeTreeNode
{
// flags
enum
{
CTN_POP = (1 << 0),
CTN_MEMBER = (1 << 1),
};
/// \brief Create() will create a tree node. the singleton gmCodeTree must be locked.
/// \return a tree node
static gmCodeTreeNode * Create(gmCodeTreeNodeType a_type, int a_subType, int a_lineNumber, int a_subTypeType = 0);
/// \brief SetChild() will set the child at the given index.
/// \param a_node is the child node, whose parent pointer will be assigned to this.
void SetChild(int a_index, gmCodeTreeNode * a_node);
/// \brief ConstantFold() will pull child nodes into this node, and make this node a constant if possible
bool ConstantFold();
gmCodeTreeNodeType m_type;
int m_subType;
int m_subTypeType;
int m_flags;
gmCodeTreeNode * m_children[GMCODETREE_NUMCHILDREN];
gmCodeTreeNode * m_sibling;
gmCodeTreeNode * m_parent;
int m_lineNumber;
gmCodeTreeNodeData m_data;
};
//
// misc lexing and parsing functions.
//
void gmProcessSingleQuoteString(char * a_string);
void gmProcessDoubleQuoteString(char * a_string);
int gmerror(char * a_message);
int gmparse(void);
#endif // _GMCODETREE_H_
+106
View File
@@ -0,0 +1,106 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#ifndef _GMCONFIG_H_
#define _GMCONFIG_H_
// Include the platform config.
// All platform configuration exists in gmconfig_p.h
#include "gmConfig_p.h"
#include <stdlib.h> // atoi, strtoul (binds: rand, srand)
#include <stdio.h> // fprintf, sprintf, _snprintf, _vnsprintf
#include <string.h> // stricmp, strcmp, strcpy, strlen, strcat, memset, memcpy (binds: strlwr, wtrupr, strspn, strcspn, strchr, strstr)
#include <stdarg.h> // va_start, va_end
#include <ctype.h> // isdigit
#include <math.h> // floorf, fmodf
/// \enum gmEndian Endian byte order
enum gmEndian
{
GM_ENDIAN_BIG = 0, //!< MOTOROLA (MAC), NINTENDO GC
GM_ENDIAN_LITTLE = 1 //!< x86, XBOX, PS2
};
//
// Game Monkey Configuration
//
// COMPILE
#define GM_COMPILE_DEBUG 1 // define for compile debugging code, ie, printing code trees, byte code etc.
// COMPILE LOG
#define GMLOG_CHAINSIZE 2048 // memory chunk resolution for the compile log
// COMPILE PARSER
#define GMCODETREE_CHAINSIZE 4096 // memory chunk resolution for compiler code tree nodes.
// COMPILER CODE GENERATOR
#define GM_COMPILE_PASS_THIS_ALWAYS 0 // set to 1 to pass current this to each function call
// RUNTIME THREAD
#define GMTHREAD_INITIALBYTESIZE 512 // initial stack byte size for a single thread
#define GMTHREAD_MAXBYTESIZE 128000 //1024 // max stack byte size for a single thread (Sample scripts like it big)
// MACHINE
#define GMMACHINE_REMOVECOMPILER 0 // Remove compiler code, will only be able to run precompiled libs
#define GMMACHINE_GMCHECKDIVBYZERO 0 // Let GM operator check for divide by zero and possibly cause GM run time exception (rather than OS exception)
#define GMMACHINE_NULL_VAR_CTOR 0 // Nullify gmVariable in constructor. Not recommended for real-time / time critical applications.
#define GMMACHINE_USERTYPEGROWBY 16 // allocate user types in chunks of this size
#define GMMACHINE_OBJECTCHUNKSIZE 32 // default object chunk allocation size
#define GMMACHINE_TBLCHUNKSIZE 32 // table object chunk allocation size
#define GMMACHINE_STRINGCHUNKSIZE 128 // default object chunk allocation size
#define GMMACHINE_STACKFCHUNKSIZE 128 // stack frame chunk size
#define GMMACHINE_AUTOMEM true // automatically decide garbage collection limit
#define GMMACHINE_AUTOMEMMULTIPY 2.5f // after gc cycle, set limit = current * GMMACHINE_AUTOMEMMULTIPY (This is for atomic GC)
#define GMMACHINE_AUTOMEMALLOWSHRINK 0 // Allow memory liimits to shrink, otherwise memory will grow when needed only
#define GMMACHINE_INITIALGCHARDLIMIT 128*1024 // default gc hard memory limit.
#define GMMACHINE_INITIALGCSOFTLIMIT (GMMACHINE_INITIALGCHARDLIMIT * 9 / 10) // default gc soft memory limit
#define GMMACHINE_STRINGHASHSIZE 8192 // this will be dynamic... todo
#define GMMACHINE_MAXKILLEDTHREADS 16 // max size of the free thread list (don't make too large, ie, < 32)
#define GMMACHINE_GCEVERYALLOC 0 // define this to check garbage collection every allocate.
#define GMMACHINE_SUPERPARANOIDGC 0 // validate references (only for debugging purposes)
#define GMMACHINE_THREEPASSGC 0 // 1 for safe gc of persisting objects that reference other objects,
// ie, persisting tables. if you only have persisting simple objects, ie
// strings, set to 0 for faster garbage collection.
// Auto GC Calibration values
#define GMMACHINE_GC_HARD_MEM_INC_FRAC_OF_USED 1.5f // what to set hard limit to above used mem when growing hard limit
#define GMMACHINE_GC_HARD_MEM_DEC_FRAC_OF_USED 1.5f // what to set hard limit to above used mem when shinking hard limit
#define GMMACHINE_GC_SOFT_MEM_DEFAULT_FRAC_OF_HARD (9.0f/10.0f) // what to set soft limit as frac of hard limit by default
#define GMMACHINE_GC_HARD_MEM_SHRINK_THRESH 0.5f // threshold at which hard limit should shrink
#define GMMACHINE_GC_SOFT_MEM_MIN_FRAC 0.25f // minimum soft limit as frac of hard limit to shrink soft limit
#define GMMACHINE_GC_SOFT_MEM_DEC_FRAC 0.1f // amount to shrink soft limit as frac of soft/hard
#define GMMACHINE_GC_MIN_FRAMES_SINCE_RESTART 100 // if gc is restarting within this many frames/calls, it is probably configured bad
#define GM_GC_DEFAULT_WORK_INCREMENT 200 // Desired number of objects to trace per frame
#define GM_GC_DEFAULT_DESTRUCT_INCREMENT 200 // Desired number of old objects to free per frame
#define GMMACHINE_CPPOWNEDGMOBJHASHSIZE 1024 // default hash table size for objects owned by cpp code, necessary for GC.
// DEBUGGING
#define GMDEBUG_SUPPORT 1 // allow use with the gm debugger
// GARBAGE COLLECTOR
#define GM_USE_INCGC 1 // use incremental garbage collector
#define GM_BOOL_OP 1 // Spport for a bool operator on user types for use in if statements. For full effect, users will want to implement operators [bool, ==, !=, !]
#endif // _GMCONFIG_H_
+84
View File
@@ -0,0 +1,84 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#include "gmConfig.h"
#include "gmCrc.h"
// Must be last header
#include "memdbgon.h"
#define _gmUPDC32(A,C) (s_gmCrc32Table[((C) ^ ((gmuint8) A)) & 0xff] ^ ((C) >> 8))
// CRC polynomial 0xedb88320
static gmuint32 s_gmCrc32Table[] =
{
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};
gmuint32 gmCrc32String(const char *p_string)
{
register gmuint32 crc32;
crc32 = 0xffffffff;
for (; *p_string; ++p_string)
{
crc32 = _gmUPDC32(*p_string, crc32);
}
return ~crc32;
}
+19
View File
@@ -0,0 +1,19 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#ifndef _GMCRC_H_
#define _GMCRC_H_
#include "gmConfig.h"
gmuint32 gmCrc32String(const char *p_string);
#endif // _GMCRC_H_
+741
View File
@@ -0,0 +1,741 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#include "gmDebug.h"
#include "gmConfig.h"
#include "gmMachine.h"
#include "gmThread.h"
// Must be last header
#include "memdbgon.h"
#if GMDEBUG_SUPPORT
#define ID_mrun GM_MAKE_ID32('m','r','u','n')
#define ID_msin GM_MAKE_ID32('m','s','i','n')
#define ID_msou GM_MAKE_ID32('m','s','o','u')
#define ID_msov GM_MAKE_ID32('m','s','o','v')
#define ID_mgct GM_MAKE_ID32('m','g','c','t')
#define ID_mgsr GM_MAKE_ID32('m','g','s','r')
#define ID_mgsi GM_MAKE_ID32('m','g','s','i')
#define ID_mgti GM_MAKE_ID32('m','g','t','i')
#define ID_mgvi GM_MAKE_ID32('m','g','v','i')
#define ID_msbp GM_MAKE_ID32('m','s','b','p')
#define ID_mbrk GM_MAKE_ID32('m','b','r','k')
#define ID_mend GM_MAKE_ID32('m','e','n','d')
#define ID_dbrk GM_MAKE_ID32('d','b','r','k')
#define ID_dexc GM_MAKE_ID32('d','e','x','c')
#define ID_drun GM_MAKE_ID32('d','r','u','n')
#define ID_dstp GM_MAKE_ID32('d','s','t','p')
#define ID_dsrc GM_MAKE_ID32('d','s','r','c')
#define ID_dctx GM_MAKE_ID32('d','c','t','x')
#define ID_call GM_MAKE_ID32('c','a','l','l')
#define ID_vari GM_MAKE_ID32('v','a','r','i')
#define ID_done GM_MAKE_ID32('d','o','n','e')
#define ID_dsri GM_MAKE_ID32('d','s','r','i')
#define ID_srci GM_MAKE_ID32('s','r','c','i')
#define ID_done GM_MAKE_ID32('d','o','n','e')
#define ID_dthi GM_MAKE_ID32('d','t','h','i')
#define ID_thri GM_MAKE_ID32('t','h','r','i')
#define ID_done GM_MAKE_ID32('d','o','n','e')
#define ID_derr GM_MAKE_ID32('d','e','r','r')
#define ID_dmsg GM_MAKE_ID32('d','m','s','g')
#define ID_dack GM_MAKE_ID32('d','a','c','k')
#define ID_dend GM_MAKE_ID32('d','e','n','d')
//
// functions to handle incomming commands from a debugger
//
void gmMachineRun(gmDebugSession * a_session, int a_threadId);
void gmMachineStepInto(gmDebugSession * a_session, int a_threadId);
void gmMachineStepOver(gmDebugSession * a_session, int a_threadId);
void gmMachineStepOut(gmDebugSession * a_session, int a_threadId);
void gmMachineGetContext(gmDebugSession * a_session, int a_threadId, int a_callframe);
void gmMachineGetSource(gmDebugSession * a_session, int a_sourceId);
void gmMachineGetSourceInfo(gmDebugSession * a_session);
void gmMachineGetThreadInfo(gmDebugSession * a_session);
void gmMachineGetVariableInfo(gmDebugSession * a_session, int a_variableId);
void gmMachineSetBreakPoint(gmDebugSession * a_session, int a_responseId, int a_sourceId, int a_lineNumber, int a_threadId, int a_enabled);
void gmMachineBreak(gmDebugSession * a_session, int a_threadId);
void gmMachineQuit(gmDebugSession * a_session);
//
// functions to package outgoing messages to a debugger
//
void gmDebuggerBreak(gmDebugSession * a_session, int a_threadId, int a_sourceId, int a_lineNumber);
void gmDebuggerRun(gmDebugSession * a_session, int a_threadId);
void gmDebuggerStop(gmDebugSession * a_session, int a_threadId);
void gmDebuggerSource(gmDebugSession * a_session, int a_sourceId, const char * a_sourceName, const char * a_source);
void gmDebuggerException(gmDebugSession * a_session, int a_threadId);
void gmDebuggerBeginContext(gmDebugSession * a_session, int a_threadId, int a_callFrame);
void gmDebuggerContextCallFrame(gmDebugSession * a_session, int a_callFrame, const char * a_functionName, int a_sourceId, int a_lineNumber, const char * a_thisSymbol, const char * a_thisValue, int a_thisId);
void gmDebuggerContextVariable(gmDebugSession * a_session, const char * a_varSymbol, const char * a_varValue, int a_varId);
void gmDebuggerEndContext(gmDebugSession * a_session);
void gmDebuggerBeginSourceInfo(gmDebugSession * a_session);
void gmDebuggerSourceInfo(gmDebugSession * a_session, int a_sourceId, const char * a_sourceName);
void gmDebuggerEndSourceInfo(gmDebugSession * a_session);
void gmDebuggerBeginThreadInfo(gmDebugSession * a_session);
void gmDebuggerThreadInfo(gmDebugSession * a_session, int a_threadId, int a_threadState);
void gmDebuggerEndThreadInfo(gmDebugSession * a_session);
void gmDebuggerError(gmDebugSession * a_session, const char * a_error);
void gmDebuggerMessage(gmDebugSession * a_session, const char * a_message);
void gmDebuggerAck(gmDebugSession * a_session, int a_response, int a_posNeg);
void gmDebuggerQuit(gmDebugSession * a_session);
//
// debug machine callback
//
enum gmdThreadFlags
{
TF_STEPOVER = (1 << 0),
TF_STEPINTO = (1 << 1),
TF_STEPOUT = (1 << 2),
TF_BREAK = (1 << 3),
};
// the following callbacks return true if the thread is to yield after completion of the callback.
static bool LineCallback(gmThread * a_thread)
{
gmDebugSession * session = (gmDebugSession *) a_thread->GetMachine()->m_debugUser;
GM_ASSERT(session);
if(!(a_thread->m_debugFlags & TF_STEPOVER) ||
(a_thread->m_debugUser != ((a_thread->GetFrame()) ? a_thread->GetFrame()->m_returnBase : 0)))
{
int * bp = session->FindBreakPoint((void *) a_thread->GetInstruction());
if(bp == NULL)
return false;
if(*bp && *bp != a_thread->GetId())
return false;
}
a_thread->m_debugFlags = TF_BREAK;
const gmFunctionObject * fn = a_thread->GetFunctionObject();
gmDebuggerBreak(session, a_thread->GetId(), fn->GetSourceId(), fn->GetLine(a_thread->GetInstruction()));
return true;
}
static bool CallCallback(gmThread * a_thread)
{
gmDebugSession * session = (gmDebugSession *) a_thread->GetMachine()->m_debugUser;
GM_ASSERT(session);
if(a_thread->m_debugFlags & TF_STEPINTO)
{
a_thread->m_debugFlags = TF_BREAK;
const gmFunctionObject * fn = a_thread->GetFunctionObject();
gmDebuggerBreak(session, a_thread->GetId(), fn->GetSourceId(), fn->GetLine(a_thread->GetInstruction()));
return true;
}
return false;
}
static bool RetCallback(gmThread * a_thread)
{
gmDebugSession * session = (gmDebugSession *) a_thread->GetMachine()->m_debugUser;
GM_ASSERT(session);
if(((a_thread->m_debugFlags & TF_STEPOUT) && (a_thread->m_debugUser == a_thread->GetIntBase())) ||
((a_thread->m_debugFlags & TF_STEPOVER) && (a_thread->m_debugUser == a_thread->GetIntBase())))
{
a_thread->m_debugFlags = TF_BREAK;
const gmFunctionObject * fn = a_thread->GetFunctionObject();
gmDebuggerBreak(session, a_thread->GetId(), fn->GetSourceId(), fn->GetLine(a_thread->GetInstruction()));
return true;
}
return false;
}
static bool IsBrokenCallback(gmThread * a_thread)
{
return (a_thread->m_debugFlags & TF_BREAK) > 0;
}
static gmMachineCallback s_prevMachineCallback = NULL;
bool GM_CDECL gmdMachineCallback(gmMachine * a_machine, gmMachineCommand a_command, const void * a_context)
{
gmDebugSession * session = (gmDebugSession *) a_machine->m_debugUser;
const gmThread * thread = (const gmThread *) a_context;
// chain callback
if(s_prevMachineCallback) s_prevMachineCallback(a_machine, a_command, a_context);
// do we have a debug session?
if(session == NULL) return false;
// command
switch(a_command)
{
case MC_THREAD_EXCEPTION :
{
// send thread exception message
gmDebuggerException(session, thread->GetId());
a_machine->GetLog();
bool first = true;
const char * entry;
while((entry = a_machine->GetLog().GetEntry(first)))
{
gmDebuggerError(session, entry);
}
return true;
}
case MC_THREAD_CREATE :
{
gmDebuggerRun(session, thread->GetId());
break;
}
case MC_THREAD_DESTROY :
{
gmDebuggerStop(session, thread->GetId());
break;
}
default : break;
};
return false;
}
//
// debug session
//
gmDebugSession::gmDebugSession() :
m_breaks(32)
{
m_machine = NULL;
}
gmDebugSession::~gmDebugSession()
{
m_breaks.RemoveAndDeleteAll();
}
void gmDebugSession::Update()
{
for(;;)
{
int len;
const void * msg = m_pumpMessage(this, len);
if(msg == NULL)
break;
m_in.Open(msg, len);
// parse the message
int id, pa, pb, pc, pd;
Unpack(id);
switch(id)
{
case ID_mrun :
Unpack(id);
gmMachineRun(this, id);
break;
case ID_msin :
Unpack(id);
gmMachineStepInto(this, id);
break;
case ID_msou :
Unpack(id);
gmMachineStepOut(this, id);
break;
case ID_msov :
Unpack(id);
gmMachineStepOver(this, id);
break;
case ID_mgct :
Unpack(id).Unpack(pa);
gmMachineGetContext(this, id, pa);
break;
case ID_mgsr :
Unpack(id);
gmMachineGetSource(this, id);
break;
case ID_mgsi :
gmMachineGetSourceInfo(this);
break;
case ID_mgti :
gmMachineGetThreadInfo(this);
break;
case ID_mgvi :
Unpack(id);
gmMachineGetVariableInfo(this, id);
break;
case ID_msbp :
Unpack(pa).Unpack(pb).Unpack(pc).Unpack(id).Unpack(pd);
gmMachineSetBreakPoint(this, pa, pb, pc, id, pd);
break;
case ID_mbrk :
Unpack(id);
gmMachineBreak(this, id);
break;
case ID_mend :
gmMachineQuit(this);
break;
default:;
}
}
}
bool gmDebugSession::Open(gmMachine * a_machine)
{
Close();
m_machine = a_machine;
m_machine->m_debugUser = this;
m_machine->m_line = LineCallback;
m_machine->m_call = CallCallback;
m_machine->m_isBroken = IsBrokenCallback;
m_machine->m_return = RetCallback;
s_prevMachineCallback = a_machine->s_machineCallback;
a_machine->s_machineCallback = gmdMachineCallback;
return true;
}
static bool threadIterClose(gmThread * a_thread, void * a_context)
{
a_thread->m_debugFlags = 0;
a_thread->m_debugUser = 0;
return true;
}
bool gmDebugSession::Close()
{
if(m_machine && m_machine->m_debugUser == this)
{
gmDebuggerQuit(this);
m_machine->m_debugUser = NULL;
m_machine->s_machineCallback = s_prevMachineCallback;
m_machine->m_line = NULL;
m_machine->m_call = NULL;
m_machine->m_return = NULL;
m_machine->m_isBroken = NULL;
m_machine->KillExceptionThreads();
m_machine->ForEachThread(threadIterClose, NULL);
m_machine = NULL;
m_breaks.RemoveAndDeleteAll();
m_out.ResetAndFreeMemory();
return true;
}
m_breaks.RemoveAndDeleteAll();
m_out.ResetAndFreeMemory();
return false;
}
gmDebugSession &gmDebugSession::Pack(int a_val)
{
m_out << a_val;
return *this;
}
gmDebugSession &gmDebugSession::Pack(const char * a_val)
{
if(a_val)
m_out.Write(a_val, strlen(a_val) + 1);
else
m_out.Write("", 1);
return *this;
}
void gmDebugSession::Send()
{
m_sendMessage(this, m_out.GetData(), m_out.GetSize());
m_out.Reset();
}
gmDebugSession &gmDebugSession::Unpack(int &a_val)
{
if(m_in.Read(&a_val, 4) != 4) a_val = 0;
return *this;
}
gmDebugSession &gmDebugSession::Unpack(const char * &a_val)
{
// this is dangerous!!!
a_val = &m_in.GetData()[m_in.Tell()];
int len = strlen(a_val);
m_in.Seek(m_in.Tell() + len + 1);
return *this;
}
bool gmDebugSession::AddBreakPoint(const void * a_bp, int a_threadId)
{
BreakPoint * bp = m_breaks.Find((void *const&)a_bp);
if(bp) return false;
bp = GM_NEW( BreakPoint() );
bp->m_bp = a_bp;
bp->m_threadId = a_threadId;
m_breaks.Insert(bp);
return true;
}
int * gmDebugSession::FindBreakPoint(const void * a_bp)
{
BreakPoint * bp = m_breaks.Find((void *const&)a_bp);
if(bp)
{
return &bp->m_threadId;
}
return NULL;
}
bool gmDebugSession::RemoveBreakPoint(const void * a_bp)
{
BreakPoint * bp = m_breaks.Find((void *const&)a_bp);
if(bp)
{
m_breaks.Remove(bp);
delete bp;
return true;
}
return false;
}
//
// implementation
//
void gmMachineRun(gmDebugSession * a_session, int a_threadId)
{
gmThread * thread = a_session->GetMachine()->GetThread(a_threadId);
if(thread)
{
thread->m_debugFlags = 0;
}
}
void gmMachineStepInto(gmDebugSession * a_session, int a_threadId)
{
gmThread * thread = a_session->GetMachine()->GetThread(a_threadId);
if(thread)
{
thread->m_debugUser = (thread->GetFrame()) ? thread->GetFrame()->m_returnBase : 0;
thread->m_debugFlags = TF_STEPINTO | TF_STEPOVER;
}
}
void gmMachineStepOver(gmDebugSession * a_session, int a_threadId)
{
gmThread * thread = a_session->GetMachine()->GetThread(a_threadId);
if(thread)
{
thread->m_debugUser = (thread->GetFrame()) ? thread->GetFrame()->m_returnBase : 0;
thread->m_debugFlags = TF_STEPOVER;
}
}
void gmMachineStepOut(gmDebugSession * a_session, int a_threadId)
{
gmThread * thread = a_session->GetMachine()->GetThread(a_threadId);
if(thread)
{
thread->m_debugUser = (thread->GetFrame()) ? thread->GetFrame()->m_returnBase : 0;
thread->m_debugFlags = TF_STEPOUT;
}
}
void gmMachineGetContext(gmDebugSession * a_session, int a_threadId, int a_callframe)
{
const int buffSize = 256;
char buff[buffSize]; // buff is used for AsString
gmThread * thread = a_session->GetMachine()->GetThread(a_threadId);
if(thread)
{
// count the number of frames on the thread
int numFrames = 0;
const gmStackFrame * frame = thread->GetFrame();
while(frame)
{
++numFrames;
frame = frame->m_prev;
}
// if a valid frame was requested, fill out a context.
if(a_callframe >= 0 && a_callframe <= numFrames)
{
gmDebuggerBeginContext(a_session, a_threadId, a_callframe);
// pack frames
frame = thread->GetFrame();
numFrames = 0;
gmVariable * base = thread->GetBase();
const gmuint8 * ip = thread->GetInstruction();
while(frame)
{
// get the function object
gmVariable * fnVar = base - 1;
if(fnVar->m_type == GM_FUNCTION)
{
gmFunctionObject * fn = (gmFunctionObject *) GM_MOBJECT(thread->GetMachine(), fnVar->m_value.m_ref);
// this
base[-2].AsStringWithType(thread->GetMachine(), buff, buffSize);
gmDebuggerContextCallFrame(a_session, numFrames, fn->GetDebugName(), fn->GetSourceId(), fn->GetLine(ip), "this", buff, (base[-2].IsReference()) ? base[-2].m_value.m_ref : 0);
if(numFrames == a_callframe)
{
// this is the active frame, fill out the variables
int i;
for(i = 0; i < fn->GetNumParamsLocals(); ++i)
{
base[i].AsStringWithType(thread->GetMachine(), buff, buffSize);
gmDebuggerContextVariable(a_session, fn->GetSymbol(i), buff, (base[i].IsReference()) ? base[i].m_value.m_ref : 0);
}
}
}
else
{
base[-2].AsStringWithType(thread->GetMachine(), buff, buffSize);
gmDebuggerContextCallFrame(a_session, numFrames, "unknown", 0, 0, "this", buff, (base[-2].IsReference()) ? base[-2].m_value.m_ref : 0);
}
// next call frame
++numFrames;
base = thread->GetBottom() + frame->m_returnBase;
ip = frame->m_returnAddress;
frame = frame->m_prev;
}
gmDebuggerEndContext(a_session);
}
}
}
void gmMachineGetSource(gmDebugSession * a_session, int a_sourceId)
{
const char * source;
const char * filename;
if(a_session->GetMachine()->GetSourceCode(a_sourceId, source, filename))
{
gmDebuggerSource(a_session, a_sourceId, filename, source);
}
}
void gmMachineGetSourceInfo(gmDebugSession * a_session)
{
// todo
}
static bool threadIter(gmThread * a_thread, void * a_context)
{
gmDebugSession * session = (gmDebugSession *) a_context;
int state = 0; // 0 - running, 1 - blocked, 2 - sleeping, 3 - exception, 4 - debug
if(a_thread->m_debugFlags)
state = 4;
else if(a_thread->GetState() == gmThread::EXCEPTION)
state = 3;
else if(a_thread->GetState() == gmThread::RUNNING)
state = 0;
else if(a_thread->GetState() == gmThread::BLOCKED)
state = 1;
else if(a_thread->GetState() == gmThread::SLEEPING)
state = 2;
else
state = 3;
gmDebuggerThreadInfo(session, a_thread->GetId(), state);
return true;
}
void gmMachineGetThreadInfo(gmDebugSession * a_session)
{
gmDebuggerBeginThreadInfo(a_session);
a_session->GetMachine()->ForEachThread(threadIter, a_session);
gmDebuggerEndThreadInfo(a_session);
}
void gmMachineGetVariableInfo(gmDebugSession * a_session, int a_variableId)
{
// todo
}
void gmMachineSetBreakPoint(gmDebugSession * a_session, int a_responseId, int a_sourceId, int a_lineNumber, int a_threadId, int a_enabled)
{
bool sendAck = false;
// get break point
const void * bp = (const void *) a_session->GetMachine()->GetInstructionAtBreakPoint(a_sourceId, a_lineNumber);
if(bp)
{
// get to next instruction
bp = (const void *) (((const char *) bp) + 4);
int * id = a_session->FindBreakPoint(bp);
if(id)
{
if(!a_enabled)
{
a_session->RemoveBreakPoint(bp);
sendAck = true;
}
}
else
{
if(a_session->AddBreakPoint(bp, a_threadId))
{
sendAck = true;
}
}
}
if(sendAck)
gmDebuggerAck(a_session, a_responseId, 1);
else
gmDebuggerAck(a_session, a_responseId, 0);
}
void gmMachineBreak(gmDebugSession * a_session, int a_threadId)
{
gmThread * thread = a_session->GetMachine()->GetThread(a_threadId);
if(thread)
{
thread->m_debugUser = (thread->GetFrame()) ? thread->GetFrame()->m_returnBase : 0;
thread->m_debugFlags = TF_STEPINTO | TF_STEPOVER;
}
}
void gmMachineQuit(gmDebugSession * a_session)
{
a_session->Close();
}
void gmDebuggerBreak(gmDebugSession * a_session, int a_threadId, int a_sourceId, int a_lineNumber) {
a_session->Pack(ID_dbrk).Pack(a_threadId).Pack(a_sourceId).Pack(a_lineNumber).Send();
}
void gmDebuggerException(gmDebugSession * a_session, int a_threadId) {
a_session->Pack(ID_dexc).Pack(a_threadId).Send();
}
void gmDebuggerRun(gmDebugSession * a_session, int a_threadId) {
a_session->Pack(ID_drun).Pack(a_threadId).Send();
}
void gmDebuggerStop(gmDebugSession * a_session, int a_threadId) {
a_session->Pack(ID_dstp).Pack(a_threadId).Send();
}
void gmDebuggerSource(gmDebugSession * a_session, int a_sourceId, const char * a_sourceName, const char * a_source) {
a_session->Pack(ID_dsrc).Pack(a_sourceId).Pack(a_sourceName).Pack(a_source).Send();
}
void gmDebuggerBeginContext(gmDebugSession * a_session, int a_threadId, int a_callFrame) {
a_session->Pack(ID_dctx).Pack(a_threadId).Pack(a_callFrame);
}
void gmDebuggerContextCallFrame(gmDebugSession * a_session, int a_callFrame, const char * a_functionName, int a_sourceId, int a_lineNumber, const char * a_thisSymbol, const char * a_thisValue, int a_thisId) {
a_session->Pack(ID_call).Pack(a_callFrame).Pack(a_functionName).Pack(a_sourceId).Pack(a_lineNumber).Pack(a_thisSymbol).Pack(a_thisValue).Pack(a_thisId);
}
void gmDebuggerContextVariable(gmDebugSession * a_session, const char * a_varSymbol, const char * a_varValue, int a_varId) {
a_session->Pack(ID_vari).Pack(a_varSymbol).Pack(a_varValue).Pack(a_varId);
}
void gmDebuggerEndContext(gmDebugSession * a_session) {
a_session->Pack(ID_done).Send();
}
void gmDebuggerBeginSourceInfo(gmDebugSession * a_session) {
a_session->Pack(ID_dsri);
}
void gmDebuggerSourceInfo(gmDebugSession * a_session, int a_sourceId, const char * a_sourceName) {
a_session->Pack(ID_srci).Pack(a_sourceId).Pack(a_sourceName);
}
void gmDebuggerEndSourceInfo(gmDebugSession * a_session) {
a_session->Pack(ID_done).Send();
}
void gmDebuggerBeginThreadInfo(gmDebugSession * a_session) {
a_session->Pack(ID_dthi);
}
void gmDebuggerThreadInfo(gmDebugSession * a_session, int a_threadId, int a_threadState) {
a_session->Pack(ID_thri).Pack(a_threadId).Pack(a_threadState);
}
void gmDebuggerEndThreadInfo(gmDebugSession * a_session) {
a_session->Pack(ID_done).Send();
}
void gmDebuggerError(gmDebugSession * a_session, const char * a_error) {
a_session->Pack(ID_derr).Pack(a_error).Send();
}
void gmDebuggerMessage(gmDebugSession * a_session, const char * a_message) {
a_session->Pack(ID_dmsg).Pack(a_message).Send();
}
void gmDebuggerAck(gmDebugSession * a_session, int a_response, int a_posNeg) {
a_session->Pack(ID_dack).Pack(a_response).Pack(a_posNeg).Send();
}
void gmDebuggerQuit(gmDebugSession * a_session) {
a_session->Pack(ID_dend).Send();
}
//
// lib binding
//
int GM_CDECL gmdDebug(gmThread * a_thread)
{
// if the machine has a debug session, attach a debug hook to the thread
if(a_thread->GetMachine()->m_debugUser && a_thread->GetMachine()->GetDebugMode())
{
a_thread->m_debugUser = (a_thread->GetFrame()) ? a_thread->GetFrame()->m_returnBase : 0;
a_thread->m_debugFlags = TF_STEPINTO | TF_STEPOVER;
}
return GM_OK;
}
static gmFunctionEntry s_debugLib[] =
{
/*gm
\lib gm
\brief functions in the gm lib are all global scope
*/
/*gm
\function debug
\brief debug will cause a the debugger to break at this point while running.
*/
{"debug", gmdDebug},
};
void gmBindDebugLib(gmMachine * a_machine)
{
a_machine->RegisterLibrary(s_debugLib, sizeof(s_debugLib) / sizeof(s_debugLib[0]));
}
#endif
+88
View File
@@ -0,0 +1,88 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#ifndef _GMDEBUG_H_
#define _GMDEBUG_H_
#include "gmConfig.h"
#include "gmStreamBuffer.h"
#include "gmHash.h"
class gmMachine;
class gmDebugSession;
// bind debug lib
void gmBindDebugLib(gmMachine * a_machine);
// callbacks used to hook up comms
typedef void (GM_CDECL *gmSendDebuggerMessage)(gmDebugSession * a_session, const void * a_command, int a_len);
typedef const void * (GM_CDECL *gmPumpDebuggerMessage)(gmDebugSession * a_session, int &a_len);
#if GMDEBUG_SUPPORT
/// \class gmDebugSession
class gmDebugSession
{
public:
gmDebugSession();
~gmDebugSession();
/// \brief Update() must be called to pump messages
void Update();
/// \brief Open() will start debugging on a_machine
bool Open(gmMachine * a_machine);
/// \brief Close() will stop debugging
bool Close();
/// \brief GetMachine()
inline gmMachine * GetMachine() const { return m_machine; }
gmSendDebuggerMessage m_sendMessage;
gmPumpDebuggerMessage m_pumpMessage;
void * m_user;
// send message helpers
gmDebugSession &Pack(int a_val);
gmDebugSession &Pack(const char * a_val);
void Send();
// rcv message helpers
gmDebugSession &Unpack(int &a_val);
gmDebugSession &Unpack(const char * &a_val);
// helpers
bool AddBreakPoint(const void * a_bp, int a_threadId);
int * FindBreakPoint(const void * a_bp); // return thread id
bool RemoveBreakPoint(const void * a_bp);
private:
class BreakPoint : public gmHashNode<void *, BreakPoint>
{
public:
inline const void * GetKey() const { return m_bp; }
const void * m_bp;
int m_threadId;
};
gmMachine * m_machine;
gmHash<void *, BreakPoint> m_breaks;
gmStreamBufferDynamic m_out;
gmStreamBufferStatic m_in;
};
#endif
#endif // _GMDEBUG_H_
+321
View File
@@ -0,0 +1,321 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#include <string.h>
#include "gmDebugger.h"
//
// Please note that gmDebugger.c/.h are for implementing
// a debugger application and should not be included
// in an normal GM application build.
//
#ifndef GM_MAKE_ID32
#define GM_MAKE_ID32( a, b, c, d ) ( ((d)<<24) | ((c)<<16) | ((b)<<8) | (a))
#endif //GM_MAKE_ID32
#define ID_mrun GM_MAKE_ID32('m','r','u','n')
#define ID_msin GM_MAKE_ID32('m','s','i','n')
#define ID_msou GM_MAKE_ID32('m','s','o','u')
#define ID_msov GM_MAKE_ID32('m','s','o','v')
#define ID_mgct GM_MAKE_ID32('m','g','c','t')
#define ID_mgsr GM_MAKE_ID32('m','g','s','r')
#define ID_mgsi GM_MAKE_ID32('m','g','s','i')
#define ID_mgti GM_MAKE_ID32('m','g','t','i')
#define ID_mgvi GM_MAKE_ID32('m','g','v','i')
#define ID_msbp GM_MAKE_ID32('m','s','b','p')
#define ID_mbrk GM_MAKE_ID32('m','b','r','k')
#define ID_mend GM_MAKE_ID32('m','e','n','d')
#define ID_dbrk GM_MAKE_ID32('d','b','r','k')
#define ID_dexc GM_MAKE_ID32('d','e','x','c')
#define ID_drun GM_MAKE_ID32('d','r','u','n')
#define ID_dstp GM_MAKE_ID32('d','s','t','p')
#define ID_dsrc GM_MAKE_ID32('d','s','r','c')
#define ID_dctx GM_MAKE_ID32('d','c','t','x')
#define ID_call GM_MAKE_ID32('c','a','l','l')
#define ID_vari GM_MAKE_ID32('v','a','r','i')
#define ID_done GM_MAKE_ID32('d','o','n','e')
#define ID_dsri GM_MAKE_ID32('d','s','r','i')
#define ID_srci GM_MAKE_ID32('s','r','c','i')
#define ID_done GM_MAKE_ID32('d','o','n','e')
#define ID_dthi GM_MAKE_ID32('d','t','h','i')
#define ID_thri GM_MAKE_ID32('t','h','r','i')
#define ID_done GM_MAKE_ID32('d','o','n','e')
#define ID_derr GM_MAKE_ID32('d','e','r','r')
#define ID_dmsg GM_MAKE_ID32('d','m','s','g')
#define ID_dack GM_MAKE_ID32('d','a','c','k')
#define ID_dend GM_MAKE_ID32('d','e','n','d')
//
// Please note that gmDebugger.c/.h are for implementing
// a debugger application and should not be included
// in an normal GM application build.
//
gmDebuggerSession::gmDebuggerSession()
{
m_outSize = 256;
m_out = (void*) new char[m_outSize];
m_outCursor = 0;
m_in = NULL;
m_inCursor = m_inSize = NULL;
}
gmDebuggerSession::~gmDebuggerSession()
{
if(m_out)
{
delete [] (char*)m_out;
}
}
void gmDebuggerSession::Update()
{
for(;;)
{
m_in = m_pumpMessage(this, m_inSize);
if(m_in == NULL) break;
m_inCursor = 0;
int id, pa, pb, pc;
const char * sa, * sb, * sc;
Unpack(id);
switch(id)
{
case ID_dbrk :
Unpack(id).Unpack(pa).Unpack(pb);
gmDebuggerBreak(this, id, pa, pb);
break;
case ID_drun :
Unpack(id);
gmDebuggerRun(this, id);
break;
case ID_dstp :
Unpack(id);
gmDebuggerStop(this, id);
break;
case ID_dsrc :
Unpack(id).Unpack(sa).Unpack(sb);
gmDebuggerSource(this, id, sa, sb);
break;
case ID_dexc :
Unpack(id);
gmDebuggerException(this, id);
break;
case ID_dctx :
Unpack(id).Unpack(pa); // thread id, callframe
gmDebuggerBeginContext(this, id, pa);
for(;;)
{
Unpack(id);
if(id == ID_call)
{
Unpack(id).Unpack(sa).Unpack(pa).Unpack(pb).Unpack(sb).Unpack(sc).Unpack(pc);
gmDebuggerContextCallFrame(this, id, sa, pa, pb, sb, sc, pc);
}
else if(id == ID_vari)
{
Unpack(sa).Unpack(sb).Unpack(pa);
gmDebuggerContextVariable(this, sa, sb, pa);
}
else if(id == ID_done) break;
else break;
}
gmDebuggerEndContext(this);
break;
case ID_dsri :
// todo
break;
case ID_dthi :
gmDebuggerBeginThreadInfo(this);
for(;;)
{
Unpack(id);
if(id == ID_thri)
{
Unpack(pa).Unpack(pb);
gmDebuggerThreadInfo(this, pa, pb);
}
else if(id == ID_done) break;
else break;
}
gmDebuggerEndThreadInfo(this);
break;
case ID_derr :
Unpack(sa);
gmDebuggerError(this, sa);
break;
case ID_dmsg :
Unpack(sa);
gmDebuggerMessage(this, sa);
break;
case ID_dack :
Unpack(pa).Unpack(pb);
gmDebuggerAck(this, pa, pb);
break;
case ID_dend :
gmDebuggerQuit(this);
break;
default:;
}
}
}
bool gmDebuggerSession::Open()
{
m_outCursor = 0;
return true;
}
bool gmDebuggerSession::Close()
{
return true;
}
gmDebuggerSession &gmDebuggerSession::Pack(int a_val)
{
Need(4);
memcpy((char *) m_out + m_outCursor, &a_val, 4);
m_outCursor += 4;
return *this;
}
gmDebuggerSession &gmDebuggerSession::Pack(const char * a_val)
{
if(a_val)
{
int len = strlen(a_val) + 1;
Need(len);
memcpy((char *) m_out + m_outCursor, a_val, len);
m_outCursor += len;
}
else
{
Need(1);
memcpy((char *) m_out + m_outCursor, "", 1);
m_outCursor += 1;
}
return *this;
}
void gmDebuggerSession::Send()
{
m_sendMessage(this, m_out, m_outCursor);
m_outCursor = 0;
}
gmDebuggerSession &gmDebuggerSession::Unpack(int &a_val)
{
if(m_inCursor + 4 <= m_inSize)
{
memcpy(&a_val, (const char *) m_in + m_inCursor, 4);
m_inCursor += 4;
}
else
{
a_val = 0;
}
return *this;
}
gmDebuggerSession &gmDebuggerSession::Unpack(const char * &a_val)
{
a_val = (const char *) m_in + m_inCursor;
m_inCursor += strlen(a_val) + 1;
return *this;
}
void gmDebuggerSession::Need(int a_bytes)
{
if((m_outCursor + a_bytes) >= m_outSize)
{
int newSize = m_outSize + a_bytes + 256;
void * buffer = (void*)new char[newSize];
memcpy(buffer, m_out, m_outCursor);
delete [] (char*)m_out;
m_out = buffer;
m_outSize = newSize;
}
}
void gmMachineRun(gmDebuggerSession * a_session, int a_threadId)
{
a_session->Pack(ID_mrun).Pack(a_threadId).Send();
}
void gmMachineStepInto(gmDebuggerSession * a_session, int a_threadId)
{
a_session->Pack(ID_msin).Pack(a_threadId).Send();
}
void gmMachineStepOver(gmDebuggerSession * a_session, int a_threadId)
{
a_session->Pack(ID_msov).Pack(a_threadId).Send();
}
void gmMachineStepOut(gmDebuggerSession * a_session, int a_threadId)
{
a_session->Pack(ID_msou).Pack(a_threadId).Send();
}
void gmMachineGetContext(gmDebuggerSession * a_session, int a_threadId, int a_callframe)
{
a_session->Pack(ID_mgct).Pack(a_threadId).Pack(a_callframe).Send();
}
void gmMachineGetSource(gmDebuggerSession * a_session, int a_sourceId)
{
a_session->Pack(ID_mgsr).Pack(a_sourceId).Send();
}
void gmMachineGetSourceInfo(gmDebuggerSession * a_session)
{
a_session->Pack(ID_mgsi).Send();
}
void gmMachineGetThreadInfo(gmDebuggerSession * a_session)
{
a_session->Pack(ID_mgti).Send();
}
void gmMachineGetVariableInfo(gmDebuggerSession * a_session, int a_variableId)
{
a_session->Pack(ID_mgvi).Pack(a_variableId).Send();
}
void gmMachineSetBreakPoint(gmDebuggerSession * a_session, int a_responseId, int a_sourceId, int a_lineNumber, int a_threadId, int a_enabled)
{
a_session->Pack(ID_msbp).Pack(a_responseId).Pack(a_sourceId).Pack(a_lineNumber).Pack(a_threadId).Pack(a_enabled).Send();
}
void gmMachineBreak(gmDebuggerSession * a_session, int a_threadId)
{
a_session->Pack(ID_mbrk).Pack(a_threadId).Send();
}
void gmMachineQuit(gmDebuggerSession * a_session)
{
a_session->Pack(ID_mend).Send();
}
+114
View File
@@ -0,0 +1,114 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#ifndef _GMDEBUGGER_H_
#define _GMDEBUGGER_H_
//
// Please note that gmDebugger.c/.h are for implementing
// a debugger application and should not be included
// in an normal GM application build.
//
class gmDebuggerSession;
// callbacks used to hook up comms
typedef void (*gmSendMachineMessage)(gmDebuggerSession * a_session, const void * a_command, int a_len);
typedef const void * (*gmPumpMachineMessage)(gmDebuggerSession * a_session, int &a_len);
/// \class gmDebuggerSession
class gmDebuggerSession
{
public:
gmDebuggerSession();
~gmDebuggerSession();
/// \brief Update() must be called to pump messages
void Update();
/// \brief Open() will start debugging
bool Open();
/// \brief Close() will stop debugging
bool Close();
gmSendMachineMessage m_sendMessage;
gmPumpMachineMessage m_pumpMessage;
void * m_user; // hook to your debugger
// send message helpers
gmDebuggerSession &Pack(int a_val);
gmDebuggerSession &Pack(const char * a_val);
void Send();
// rcv message helpers
gmDebuggerSession &Unpack(int &a_val);
gmDebuggerSession &Unpack(const char * &a_val);
private:
void * m_out;
int m_outCursor, m_outSize;
void Need(int a_bytes);
const void * m_in;
int m_inCursor, m_inSize;
};
//
// the debugger must implement the following functions
//
extern void gmDebuggerBreak(gmDebuggerSession * a_session, int a_threadId, int a_sourceId, int a_lineNumber);
extern void gmDebuggerRun(gmDebuggerSession * a_session, int a_threadId);
extern void gmDebuggerStop(gmDebuggerSession * a_session, int a_threadId);
extern void gmDebuggerSource(gmDebuggerSession * a_session, int a_sourceId, const char * a_sourceName, const char * a_source);
extern void gmDebuggerException(gmDebuggerSession * a_session, int a_threadId);
extern void gmDebuggerBeginContext(gmDebuggerSession * a_session, int a_threadId, int a_callFrame);
extern void gmDebuggerContextCallFrame(gmDebuggerSession * a_session, int a_callFrame, const char * a_functionName, int a_sourceId, int a_lineNumber, const char * a_thisSymbol, const char * a_thisValue, int a_thisId);
extern void gmDebuggerContextVariable(gmDebuggerSession * a_session, const char * a_varSymbol, const char * a_varValue, int a_varId);
extern void gmDebuggerEndContext(gmDebuggerSession * a_session);
extern void gmDebuggerBeginSourceInfo(gmDebuggerSession * a_session);
extern void gmDebuggerSourceInfo(gmDebuggerSession * a_session, int a_sourceId, const char * a_sourceName);
extern void gmDebuggerEndSourceInfo(gmDebuggerSession * a_session);
extern void gmDebuggerBeginThreadInfo(gmDebuggerSession * a_session);
extern void gmDebuggerThreadInfo(gmDebuggerSession * a_session, int a_threadId, int a_threadState);
extern void gmDebuggerEndThreadInfo(gmDebuggerSession * a_session);
extern void gmDebuggerError(gmDebuggerSession * a_session, const char * a_error);
extern void gmDebuggerMessage(gmDebuggerSession * a_session, const char * a_message);
extern void gmDebuggerAck(gmDebuggerSession * a_session, int a_response, int a_posNeg);
extern void gmDebuggerQuit(gmDebuggerSession * a_session);
//
// the debugger can use the following functions to send messages to the machine
//
void gmMachineRun(gmDebuggerSession * a_session, int a_threadId);
void gmMachineStepInto(gmDebuggerSession * a_session, int a_threadId);
void gmMachineStepOver(gmDebuggerSession * a_session, int a_threadId);
void gmMachineStepOut(gmDebuggerSession * a_session, int a_threadId);
void gmMachineGetContext(gmDebuggerSession * a_session, int a_threadId, int a_callframe);
void gmMachineGetSource(gmDebuggerSession * a_session, int a_sourceId);
void gmMachineGetSourceInfo(gmDebuggerSession * a_session);
void gmMachineGetThreadInfo(gmDebuggerSession * a_session);
void gmMachineGetVariableInfo(gmDebuggerSession * a_session, int a_variableId);
void gmMachineSetBreakPoint(gmDebuggerSession * a_session, int a_responseId, int a_sourceId, int a_lineNumber, int a_threadId, int a_enabled);
void gmMachineBreak(gmDebuggerSession * a_session, int a_threadId);
void gmMachineQuit(gmDebuggerSession * a_session);
#endif
@@ -0,0 +1,286 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#include "gmConfig.h"
#include "gmFunctionObject.h"
#include "gmMachine.h"
// Must be last header
#include "memdbgon.h"
gmFunctionObject::gmFunctionObject()
{
m_cFunction = NULL;
m_cUserData = NULL;
m_debugInfo = NULL;
m_byteCode = NULL;
m_byteCodeLength = 0;
m_maxStackSize = 1; // return value
m_numLocals = 0;
m_numParams = 0;
m_numParamsLocals = 0;
m_numReferences = 0;
m_references = NULL;
}
void gmFunctionObject::Destruct(gmMachine * a_machine)
{
if(m_references)
{
a_machine->Sys_Free(m_references);
m_references = NULL;
}
if(m_byteCode)
{
a_machine->Sys_Free(m_byteCode);
m_byteCode = NULL;
}
if(m_debugInfo)
{
if(m_debugInfo->m_debugName) { a_machine->Sys_Free(m_debugInfo->m_debugName); }
if(m_debugInfo->m_lineInfo) { a_machine->Sys_Free(m_debugInfo->m_lineInfo); }
if(m_debugInfo->m_symbols)
{
int i;
for(i = 0; i < m_numParamsLocals; ++i)
{
a_machine->Sys_Free(m_debugInfo->m_symbols[i]);
}
a_machine->Sys_Free(m_debugInfo->m_symbols);
}
a_machine->Sys_Free(m_debugInfo);
m_debugInfo = NULL;
}
#if GM_USE_INCGC
a_machine->DestructDeleteObject(this);
#endif //GM_USE_INCGC
}
#if GM_USE_INCGC
bool gmFunctionObject::Trace(gmMachine * a_machine, gmGarbageCollector* a_gc, const int a_workLeftToGo, int& a_workDone)
{
int i;
for(i = 0; i < m_numReferences; ++i)
{
gmObject * object = a_machine->GetObject(m_references[i]);
a_gc->GetNextObject(object);
++a_workDone;
}
++a_workDone;
return true;
}
#else //GM_USE_INCGC
void gmFunctionObject::Mark(gmMachine * a_machine, gmuint32 a_mark)
{
if(m_mark != GM_MARK_PERSIST) m_mark = a_mark;
int i;
for(i = 0; i < m_numReferences; ++i)
{
gmObject * object = a_machine->GetObject(m_references[i]);
if(object->NeedsMark(a_mark)) object->Mark(a_machine, a_mark);
}
}
#endif //GM_USE_INCGC
bool gmFunctionObject::Init(gmMachine * a_machine, bool a_debug, gmFunctionInfo &a_info, gmuint32 a_sourceId)
{
// byte code
if(a_info.m_byteCodeLength)
{
m_byteCode = (gmuint8 *) a_machine->Sys_Alloc(a_info.m_byteCodeLength);
memcpy(m_byteCode, a_info.m_byteCode, a_info.m_byteCodeLength);
m_byteCodeLength = a_info.m_byteCodeLength;
}
else
{
m_byteCode = NULL;
m_byteCodeLength = 0;
}
// stack info
m_maxStackSize = a_info.m_maxStackSize;
m_numLocals = a_info.m_numLocals;
m_numParams = a_info.m_numParams;
m_numParamsLocals = a_info.m_numParams + a_info.m_numLocals;
// references
m_numReferences = 0;
m_references = NULL;
if(m_byteCode)
{
// find the objects this function references by iterating over the byte code and collecting them.
// we could perform this step in the compilation phase if we don't want to iterate over the byte code.
gmptr * references = (gmptr *) GM_NEW( char[a_info.m_byteCodeLength] );
union
{
const gmuint8 * instruction;
const gmuint32 * instruction32;
};
instruction = (const gmuint8 *) m_byteCode;
const gmuint8 * end = instruction + m_byteCodeLength;
for(;instruction < end;)
{
switch(*(instruction32++))
{
case BC_GETDOT :
case BC_SETDOT :
case BC_BRA :
case BC_BRZ :
case BC_BRNZ :
case BC_BRZK :
case BC_BRNZK :
case BC_FOREACH :
case BC_PUSHINT :
case BC_GETGLOBAL :
case BC_SETGLOBAL :
case BC_GETTHIS :
case BC_SETTHIS : instruction += sizeof(gmptr); break;
case BC_PUSHFP : instruction += sizeof(gmfloat); break;
case BC_CALL :
case BC_GETLOCAL :
case BC_SETLOCAL : instruction += sizeof(gmuint32); break;
case BC_PUSHSTR :
case BC_PUSHFN :
{
// if the reference does not already exist, add it.
gmptr reference = *((gmptr *) instruction);
instruction += sizeof(gmptr);
int i;
for(i = 0; i < m_numReferences; ++i)
{
if(references[i] == reference) break;
}
if(i == m_numReferences) references[m_numReferences++] = reference;
break;
}
default : break;
}
}
if(m_numReferences > 0)
{
m_references = (gmptr *) a_machine->Sys_Alloc(sizeof(gmptr) * m_numReferences);
memcpy(m_references, references, sizeof(gmptr) * m_numReferences);
}
delete [] (char*) references;
}
// debug info
m_debugInfo = NULL;
if(a_debug)
{
m_debugInfo = (gmFunctionObjectDebugInfo *) a_machine->Sys_Alloc(sizeof(gmFunctionObjectDebugInfo));
memset(m_debugInfo, 0, sizeof(gmFunctionObjectDebugInfo));
// source code id
m_debugInfo->m_sourceId = a_sourceId;
// debug name
if(a_info.m_debugName)
{
int len = strlen(a_info.m_debugName) + 1;
m_debugInfo->m_debugName = (char *) a_machine->Sys_Alloc(len);
memcpy(m_debugInfo->m_debugName, a_info.m_debugName, len);
}
// symbols
if(a_info.m_symbols)
{
m_debugInfo->m_symbols = (char **) a_machine->Sys_Alloc(sizeof(char *) * m_numParamsLocals);
int i;
for(i = 0; i < m_numParamsLocals; ++i)
{
int len = strlen(a_info.m_symbols[i]) + 1;
m_debugInfo->m_symbols[i] = (char *) a_machine->Sys_Alloc(len);
memcpy(m_debugInfo->m_symbols[i], a_info.m_symbols[i], len);
}
}
// line number debugging.
if(a_info.m_lineInfo)
{
// alloc and copy
m_debugInfo->m_lineInfo = (gmLineInfo *) a_machine->Sys_Alloc(sizeof(gmLineInfo) * a_info.m_lineInfoCount);
memcpy(m_debugInfo->m_lineInfo, a_info.m_lineInfo, sizeof(gmLineInfo) * a_info.m_lineInfoCount);
m_debugInfo->m_lineInfoCount = a_info.m_lineInfoCount;
}
}
return true;
}
int gmFunctionObject::GetLine(int a_address) const
{
if(m_debugInfo && m_debugInfo->m_lineInfo)
{
int i;
for(i = 0; i < m_debugInfo->m_lineInfoCount; ++i)
{
if(a_address < m_debugInfo->m_lineInfo[i].m_address)
{
// return entry before
if(i > 0) --i;
return m_debugInfo->m_lineInfo[i].m_lineNumber;
}
}
return m_debugInfo->m_lineInfo[i - 1].m_lineNumber;
}
return 0;
}
const void * gmFunctionObject::GetInstructionAtLine(int a_line) const
{
if(m_debugInfo && m_debugInfo->m_lineInfo && m_byteCode)
{
// serach for the first address using this line.
int i;
for(i = 0; i < m_debugInfo->m_lineInfoCount; ++i)
{
if(m_debugInfo->m_lineInfo[i].m_lineNumber == a_line)
{
return (void *) ((char *) m_byteCode + m_debugInfo->m_lineInfo[i].m_address);
}
}
}
return NULL;
}
gmuint32 gmFunctionObject::GetSourceId() const
{
if(m_debugInfo)
{
return m_debugInfo->m_sourceId;
}
return 0;
}
@@ -0,0 +1,163 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#ifndef _GMFUNCTIONOBJECT_H_
#define _GMFUNCTIONOBJECT_H_
#include "gmConfig.h"
#include "gmVariable.h"
#include "gmCodeGenHooks.h"
#include "gmMem.h"
// fwd decls
class gmThread;
enum gmCFunctionReturn
{
GM_OK = 0,
GM_EXCEPTION = -1,
GM_SYS_YIELD = -2, // system only
GM_SYS_BLOCK = -3, // system only
GM_SYS_SLEEP = -4, // system only
GM_SYS_KILL = -5, // system only
GM_SYS_STATE = -6, // system only
};
/*!
\brief gmCFunction is the function type for binding c functions to gm.
\return gmCFunctionReturn
*/
typedef int (GM_CDECL *gmCFunction)(gmThread *);
/*!
\class gmFunctionObject
\brief
*/
class gmFunctionObject : public gmObject
{
public:
virtual int GetType() const { return GM_FUNCTION; }
virtual void Destruct(gmMachine * a_machine);
#if GM_USE_INCGC
virtual bool Trace(gmMachine * a_machine, gmGarbageCollector* a_gc, const int a_workLeftToGo, int& a_workDone);
#else //GM_USE_INCGC
virtual void Mark(gmMachine * a_machine, gmuint32 a_mark);
#endif //GM_USE_INCGC
/*!
\brief Init() will initialise a function object.
\param a_debug is true if this is a debug build
\param a_info is a function info struct as built by gmCodeGenHooks.
\param a_sourceId is an unique id specifiying the source code of the function, is used for debugging.
\return true on success.
*/
bool Init(gmMachine * a_machine, bool a_debug, gmFunctionInfo &a_info, gmuint32 a_sourceId = 0);
/*!
\brief GetMaxStackSize
\return the maximum stack growth not including parameters or locals
*/
inline int GetMaxStackSize() const { return m_maxStackSize; }
/// \brief GetNumLocals
inline int GetNumLocals() const { return m_numLocals; }
/// \brief GetNumParams
inline int GetNumParams() const { return m_numParams; }
/// \brief GetNumParamsLocals()
inline int GetNumParamsLocals() const { return m_numParamsLocals; }
/// \brief GetByteCode()
inline const void * GetByteCode() const { return m_byteCode; }
/// \brief GetDebugName()
inline const char * GetDebugName() const;
/// \brief GetLine() will return the source line for the given address
int GetLine(int a_address) const;
int GetLine(const void * a_instruction) const { return GetLine((const char * ) a_instruction - (char *) m_byteCode); }
/// \brief GetInstructionAtLine() will return the instruction at the given line, or NULL of line was not within this function
const void * GetInstructionAtLine(int a_line) const;
/// \brief GetSourceId() will get the source code id when in debug mode, else 0
gmuint32 GetSourceId() const;
/// \brief GetSymbol() will return the symbol name at the given offset.
inline const char * GetSymbol(int a_offset) const;
// public data
gmCFunction m_cFunction;
const void* m_cUserData;
protected:
/// \brief Non-public constructor. Create via gmMachine.
gmFunctionObject();
friend class gmMachine;
private:
/*!
\brief gmFunctionObjectDebugInfo stores debugging info for a debug build
*/
struct gmFunctionObjectDebugInfo
{
char * m_debugName;
char ** m_symbols;
int m_lineInfoCount;
gmuint32 m_sourceId; // source code id.
gmLineInfo * m_lineInfo;
};
gmFunctionObjectDebugInfo * m_debugInfo;
void * m_byteCode;
int m_byteCodeLength;
int m_maxStackSize;
int m_numLocals;
int m_numParams;
int m_numParamsLocals; //!< m_numLocals + m_numParams
int m_numReferences; //!< number of references within the byte code.
gmptr * m_references; //!< references from the byte code
};
//
//
// INLINE IMPLEMENTATION
//
//
inline const char * gmFunctionObject::GetDebugName() const
{
if(m_debugInfo && m_debugInfo->m_debugName)
{
return m_debugInfo->m_debugName;
}
return "__unknown";
}
inline const char * gmFunctionObject::GetSymbol(int a_offset) const
{
if(m_debugInfo && m_debugInfo->m_symbols && (a_offset >= 0) && (a_offset < m_numParamsLocals))
{
return m_debugInfo->m_symbols[a_offset];
}
return "__unknown";
}
#endif // _GMFUNCTIONOBJECT_H_
+17
View File
@@ -0,0 +1,17 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#include "gmConfig.h"
#include "gmHash.h"
// Must be last header
#include "memdbgon.h"
+348
View File
@@ -0,0 +1,348 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#ifndef _GMHASH_H_
#define _GMHASH_H_
#include "gmConfig.h"
#include "gmIterator.h"
#define TMPL template<class KEY, class T, class HASHER>
#define QUAL gmHash<KEY, T, HASHER>
#define NQUAL gmHashNode<KEY, T, HASHER>
TMPL class gmHash;
class gmDefaultHasher;
/// \class gmHashNode
/// \brief inherit gmHashNode
template<class KEY, class T, class HASHER = gmDefaultHasher>
class gmHashNode
{
public:
inline gmHashNode() {}
/// \brief Return whatever is the 'key' for this type. Used by gmHash
//const KEY& GetKey() const = 0;
private:
T * m_next;
friend class QUAL;
};
/// \class gmHash
/// \brief templated intrusive hash class
/// HASHER must provide static gmuint ::Hash(const KEY &a_key) and int ::Compare(const KEY &a_key, const KEY &a_key)
template<class KEY, class T, class HASHER = gmDefaultHasher>
class gmHash
{
public:
/// \class Iterator
class Iterator
{
public:
GM_INCLUDE_ITERATOR_KERNEL(T)
inline Iterator()
{
m_hash = NULL;
m_elem = NULL;
m_slot = 0;
}
inline Iterator(const gmHash * a_hash)
{
m_hash = a_hash;
m_slot = 0;
m_elem = NULL;
Inc();
}
inline void Inc(void)
{
GM_ASSERT(m_hash);
if(m_elem)
{
m_elem = m_hash->GetNext(m_elem);
}
while(m_elem == NULL && m_slot < m_hash->m_size)
{
m_elem = m_hash->m_table[m_slot++];
}
}
inline void Dec(void) { GM_ASSERT(false); }
inline T * Resolve(void) { return m_elem; }
inline const T * Resolve(void) const { return m_elem; }
inline bool IsValid() const { return (m_elem != NULL); }
private:
const gmHash * m_hash;
T * m_elem;
unsigned int m_slot;
};
// members
gmHash(gmuint a_size);
~gmHash();
void RemoveAll();
void RemoveAndDeleteAll();
/// \brief Insert() will insert an item into the hash table.
/// \return non-null on failure, in which case the returned item is the duplicate existing in the hash
T * Insert(T * a_node);
/// \brief Remove() will remove an item from the hash table
/// \return the removed item
T * Remove(T * a_node);
/// \brief Remove() will remove an item from the hash table via an iterator
/// \return the removed item
T * Remove(Iterator & a_it);
/// \brief RemoveKey() will remove an item by key
/// \return the removed item
T * RemoveKey(const KEY &a_key);
/// \brief Find()
T * Find(const KEY &a_key);
inline gmuint Count() const { return m_count; }
inline Iterator First() const { return Iterator(this); }
private:
T * GetNext(T * a_elem) const { return a_elem->NQUAL::m_next; }
T ** m_table;
gmuint m_count;
gmuint m_size;
friend class Iterator;
};
/// \class gmDefaultHasher
/// \brief use the gmDefaultHasher as the HASHER template arg for the common hashing keys
class gmDefaultHasher
{
public:
static inline gmuint Hash(const char * a_key)
{
gmuint key = 0;
const char * cp = (const char *) a_key;
while(*cp != '\0')
{
key = (key + ((key << 5) + *cp));
++cp;
}
return key;
}
static inline int Compare(const char * a_keyA, const char * a_keyB)
{
return strcmp(a_keyA, a_keyB);
}
static inline gmuint Hash(int a_key)
{
return (gmuint) a_key;
}
static inline int Compare(int a_keyA, int a_keyB)
{
return (a_keyA - a_keyB);
}
static inline gmuint Hash(const void * a_key)
{
return (gmuint) (((gmuint) a_key) / sizeof(double));
}
static inline int Compare(const void * a_keyA, const void * a_keyB)
{
return (int) ((char *) a_keyA - (char *) a_keyB);
}
};
TMPL
QUAL::gmHash(gmuint a_size)
{
// make sure size is power of 2
GM_ASSERT((a_size & (a_size - 1)) == 0);
m_size = a_size;
m_table = GM_NEW(T * [a_size]);
int i = m_size;
while(i--)
{
m_table[i] = NULL;
}
m_count = 0;
}
TMPL
QUAL::~gmHash()
{
delete [] m_table;
}
TMPL
void QUAL::RemoveAll()
{
int i = m_size;
while(i--)
{
m_table[i] = NULL;
}
m_count = 0;
}
TMPL
void QUAL::RemoveAndDeleteAll()
{
// iterate over table and delete all
int i = m_size;
T * node, * next;
while(i--)
{
node = m_table[i];
while(node)
{
next = node->NQUAL::m_next;
delete node;
node = next;
}
m_table[i] = NULL;
}
m_count = 0;
}
TMPL
T * QUAL::Insert(T * a_node)
{
gmuint slot = HASHER::Hash(a_node->GetKey()) & (m_size - 1);
T ** node = &m_table[slot];
while(*node)
{
int compare = HASHER::Compare(a_node->GetKey(), (*node)->GetKey());
if(compare == 0) return (*node);
else if(compare < 0) break;
node = &((*node)->NQUAL::m_next);
}
a_node->NQUAL::m_next = *node;
*node = a_node;
++m_count;
return NULL;
}
TMPL
T * QUAL::Remove(T * a_node)
{
gmuint slot = HASHER::Hash(a_node->GetKey()) & (m_size - 1);
T ** node = &m_table[slot];
while(*node)
{
if(a_node == *node)
{
*node = a_node->NQUAL::m_next;
--m_count;
return a_node;
}
node = &((*node)->NQUAL::m_next);
}
return NULL;
}
TMPL
T * QUAL::Remove(Iterator & a_it)
{
T * node = a_it.Resolve();
if(node)
{
a_it.Inc();
return Remove(node);
}
return NULL;
}
TMPL
T * QUAL::RemoveKey(const KEY &a_key)
{
gmuint slot = HASHER::Hash(a_key) & (m_size - 1);
T ** node = &m_table[slot];
T * found;
while(*node)
{
int compare = HASHER::Compare((*node)->GetKey(), a_key);
if(compare == 0)
{
--m_count;
found = *node;
*node = found->NQUAL::m_next;
return (found);
}
else if(compare > 0)
{
return NULL;
}
node = &((*node)->NQUAL::m_next);
}
return NULL;
}
TMPL
T * QUAL::Find(const KEY &a_key)
{
gmuint slot = HASHER::Hash(a_key) & (m_size - 1);
T * node = m_table[slot];
while(node)
{
int compare = HASHER::Compare(static_cast<T*>(node)->GetKey(), a_key);
if(compare == 0)
{
return node;
}
else if(compare > 0)
{
return NULL;
}
node = node->NQUAL::m_next;
}
return NULL;
}
#undef TMPL
#undef QUAL
#undef NQUAL
#endif // _GMHASH_H_
+838
View File
@@ -0,0 +1,838 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#include "gmConfig.h"
#include "gmIncGC.h"
// Must be last header
#include "memdbgon.h"
// NOTES:
// o Q: What about when we turn GC off manually to prevent new objects from disappearing ?
// A: This can't happen with allocate black AS LONG AS the collect is not called during processing, only at yield.
// Still, would be nice to put this functionality in for non-game use.
//
// How to make new types compatible with GC.
//
// 1) Implement the following user type callbacks:
// typedef void (GM_CDECL *gmGCDestructCallback)(gmMachine * a_machine, gmUserObject* a_object);
// typedef bool (GM_CDECL *gmGCTraceCallback)(gmMachine * a_machine, gmUserObject* a_object, gmGarbageCollector* a_gc, const int a_workLeftToGo, int& a_workDone);
//
// o The Destruct function merely destructs and frees the user object. (Same as it used to.)
//
// o The Trace function calls gc->GetNextObject(obj) where obj is each gmObject* at that level.
// You should increment a_workDone for each call and once extra (so min value should be +1).
// The Trace has the potential to be re-called for multiple increments, but this functionality is not necessary and has not been tested.
// Always return true when finished tracing. Returning false signals that you are not finished and it should be re-called.
//
// 2) If you make a table or array type class that contains variables that could be gmObjects,
// call gc->WriteBarrier(obj) where obj is the old gmObject about to be overwritten in a SetInd or SetDot call etc.
//
// Note that permanant strings are stored in a separate list so they are ignored by the GC.
//////////////////////////////////////////////////
// gmGCColorSet
//////////////////////////////////////////////////
gmGCColorSet::gmGCColorSet()
{
Init(NULL);
}
gmGCColorSet::~gmGCColorSet()
{
}
void gmGCColorSet::Init(gmGarbageCollector* a_gc)
{
m_gc = a_gc;
// Note that only the Scan and Free markers actually move
//
// | GRAY BLACK FREE | WHITE |
// ^ ^ ^ ^ ^
// G S F W T
//
m_gray = &m_headObject;
m_white = &m_separatorObject;
m_free = m_white;
m_scan = m_free;
m_tail = &m_tailObject;
m_tailObject.SetPrev(&m_separatorObject);
m_tailObject.SetNext(NULL);
m_separatorObject.SetPrev(&m_headObject);
m_separatorObject.SetNext(&m_tailObject);
m_headObject.SetPrev(NULL);
m_headObject.SetNext(&m_separatorObject);
// Make persistList into a list node
m_persistList.SetNext(&m_persistList);
m_persistList.SetPrev(&m_persistList);
#if GM_GC_STATS
m_numAllocated = 0;
#endif //GM_GC_STATS
}
#if GM_GC_DEBUG
bool gmGCColorSet::VerifyIntegrity()
{
// Scan through list make sure all pointers are in valid positions and all objects are colored correctly
gmGCObjBase* curObj = m_gray->GetNext();
int curCol = GM_GC_DEBUG_COL_GRAY;
while(curObj != m_tail)
{
if(curObj == m_scan)
{
curCol = GM_GC_DEBUG_COL_BLACK;
}
if(curObj == m_free)
{
curCol = GM_GC_DEBUG_COL_FREE;
}
if(curObj == m_white)
{
curCol = GM_GC_DEBUG_COL_WHITE;
}
if(curObj != &m_separatorObject)
{
GM_ASSERT(curObj->m_curPosColor == curCol);
}
curObj = curObj->GetNext();
}
return true;
}
#endif //GM_GC_DEBUG
void gmGCColorSet::DestructPersistantObjects()
{
int count=0;
gmGCObjBase* curObj = m_persistList.GetNext();
while(curObj != &m_persistList)
{
gmGCObjBase* objToDestruct = curObj;
curObj = curObj->GetNext();
objToDestruct->Destruct(m_gc->GetVM());
++count;
}
// Reset persist list (Make persistList into a list node)
m_persistList.SetNext(&m_persistList);
m_persistList.SetPrev(&m_persistList);
}
int gmGCColorSet::FollowPointers(int a_maxBytesToTrace)
{
int workDone = 0;
if(m_gc->GetTraceState().m_object->Trace(m_gc->GetVM(), m_gc, a_maxBytesToTrace, workDone))
{
m_gc->GetTraceState().m_done = true;
}
return workDone;
}
bool gmGCColorSet::BlackenNextGray(int& a_workDone, int a_workLeftToGo)
{
if(m_gc->GetTraceState().m_done == true)
{
if(m_scan->GetPrev() == m_gray) // No grays to blacken.
{
a_workDone = 0;
return false;
}
else
{
m_scan = m_scan->GetPrev();
m_gc->GetTraceState().m_object = m_scan;
#if GM_GC_DEBUG
GM_ASSERT(m_scan->m_curPosColor == GM_GC_DEBUG_COL_GRAY);
m_scan->m_curPosColor = GM_GC_DEBUG_COL_BLACK;
#endif //GM_GC_DEBUG
a_workDone = FollowPointers(a_workLeftToGo);
return true; // Gray was blackened
}
}
else
{
// Resume previously interrupted scanning of an object
a_workDone = FollowPointers(a_workLeftToGo);
return true;
}
}
void gmGCColorSet::GrayThisObject(gmGCObjBase* a_obj)
{
gmGCObjBase* objPrev = a_obj->GetPrev();
gmGCObjBase* objNext = a_obj->GetNext();
// This routine should never get called with a shaded object
GM_ASSERT(!m_gc->IsShaded(a_obj));
#if GM_GC_DEBUG
GM_ASSERT(a_obj->m_curPosColor == GM_GC_DEBUG_COL_WHITE);
a_obj->m_curPosColor = GM_GC_DEBUG_COL_GRAY;
#endif //GM_GC_DEBUG
// Set object`s color to shaded first.
a_obj->SetColor(m_gc->GetCurShadeColor());
// The object must be shaded
GM_ASSERT(m_gc->IsShaded(a_obj));
// Splice the object out of the white list
// This can be done unconditionally as no set pointers can point to any object in this set.
objPrev->SetNext(objNext);
objNext->SetPrev(objPrev);
// Put the object into the correct place in the gray list
#if DEPTH_FIRST
// Put the gray object at the head of the gray list.
a_obj->SetPrev(m_scan->GetPrev());
a_obj->SetNext(m_scan);
m_scan->GetPrev()->SetNext(a_obj);
m_scan->SetPrev(a_obj);
#else // BREADTH_FIRST
// Put the gray object at the tail of the gray list.
a_obj->SetPrev(m_gray);
a_obj->SetNext(m_gray->GetNext());
m_gray->GetNext()->SetPrev(a_obj);
m_gray->SetNext(a_obj);
#endif // BREADTH_FIRST
#if GM_GC_DEBUG
// Slow, paranoid check
VerifyIntegrity();
#endif //GM_GC_DEBUG
}
void gmGCColorSet::Revive(gmGCObjBase* a_obj)
{
// NOTE: Once objects are in the free list, we can't trust the color mark,
// it may have been set either side of the flip.
// We should only revive 'free', but we can't simply tell where in the list the object is.
// Always mark black, this is only done for strings, and we are logically re-allocating the dead object
#if GM_GC_DEBUG
a_obj->m_curPosColor = GM_GC_DEBUG_COL_BLACK; // Blacken as if re-allocated
#endif //GM_GC_DEBUG
// Set object`s color to shaded first.
a_obj->SetColor(m_gc->GetCurShadeColor());
// Fix scan (NOTE: If scan == obj, obj must already be black, so could skip rest of this function)
if( m_scan == a_obj )
{
m_scan = m_scan->GetNext(); // Not Prev as scan should be the start of black inclusive
}
// We don't care if (m_gc->GetTraceState().m_object == a_obj ) as this is Done and write only, and can only be string, not potentially a resumable object.
#if GM_GC_DEBUG
if( m_gc->GetTraceState().m_object == a_obj )
{
GM_ASSERT( m_gc->GetTraceState().m_done );
}
#endif // GM_GC_DEBUG
// Fix sentinels
if( m_free == a_obj )
{
m_free = m_free->GetNext();
}
// Splice the object out of the free/white list (Or anywhere in this case)
gmGCObjBase* objPrev = a_obj->GetPrev();
gmGCObjBase* objNext = a_obj->GetNext();
objPrev->SetNext(objNext);
objNext->SetPrev(objPrev);
// Insert at the end of black list
a_obj->SetNext(m_free); // Next is first Free
a_obj->SetPrev(m_free->GetPrev()); // Prev is last Black
m_free->GetPrev()->SetNext(a_obj); // Last Black next is now this
m_free->SetPrev(a_obj); // Free prev is now this
// If there were no blacks, move scan forward to prevent scanning this new black
if( m_scan == m_free )
{
m_scan = a_obj;
}
}
void gmGCColorSet::ReclaimGarbage()
{
GM_ASSERT(m_scan->GetPrev() == m_gray);
#if GM_GC_DEBUG
{
// Traverse the newly found garbage objects just to make sure there
// aren't any live objects in there. Used for debugging only.
for(gmGCObjBase* temp = m_white->GetNext();
temp != m_tail;
temp = temp->GetNext())
{
GM_ASSERT(!m_gc->IsShaded(temp));
}
}
#endif
if (m_white->GetNext() != m_tail) // There are garbage objects
{
#if GM_GC_DEBUG
//flag white->tail as white?
for(gmGCObjBase* temp = m_white->GetNext();
temp != m_tail;
temp = temp->GetNext())
{
GM_ASSERT(temp->m_curPosColor == GM_GC_DEBUG_COL_WHITE);
temp->m_curPosColor = GM_GC_DEBUG_COL_FREE;
}
#endif //GM_GC_DEBUG
bool fixScan = false;
if( m_scan == m_free ) // There are no black objects
{
fixScan = true;
}
// Reclaim the garbage.
// Insert old White->Tail at start of Free (Free may == White)
gmGCObjBase* firstFree = m_white->GetNext();
firstFree->SetPrev(m_free->GetPrev());
m_free->GetPrev()->SetNext(firstFree);
m_tail->GetPrev()->SetNext(m_free);
m_free->SetPrev(m_tail->GetPrev());
m_free = firstFree;
if( fixScan )
{
m_scan = m_free;
}
m_white->SetNext(m_tail);
m_tail->SetPrev(m_white);
}
// Whiten the live objects.
if (m_scan != m_free) // There are live (black) objects
{
#if GM_GC_DEBUG
//flag scan->free as white?
for(gmGCObjBase* temp = m_scan;//m_scan->GetNext();
temp != m_free;
temp = temp->GetNext())
{
GM_ASSERT(temp->m_curPosColor == GM_GC_DEBUG_COL_BLACK);
temp->m_curPosColor = GM_GC_DEBUG_COL_WHITE;
}
#endif //GM_GC_DEBUG
GM_ASSERT(m_white->GetNext() == m_tail);
GM_ASSERT(m_tail->GetPrev() == m_white);
m_scan->GetPrev()->SetNext(m_free);
m_free->GetPrev()->SetNext(m_tail);
m_tail->SetPrev(m_free->GetPrev());
m_free->SetPrev(m_scan->GetPrev());
m_scan->SetPrev(m_white);
m_white->SetNext(m_scan);
m_scan = m_free;
}
GM_ASSERT(m_gray->GetNext() == m_scan);
#if GM_GC_DEBUG
{
int count = 0;
for(gmGCObjBase* temp = m_free; temp != m_white; temp = temp->GetNext())
{
++count;
}
}
#endif
}
int gmGCColorSet::DestructSomeFreeObjects(int a_maxToDestruct)
{
int numDestructed = 0;
// Go through the free list (perhaps over multiple installments in future) and call Destruct() on them.
if(m_free != m_white)
{
gmGCObjBase* beforeFree = m_free->GetPrev(); // Save previous node so we can relink after removing some
bool fixScan = false;
if(m_scan == m_free) // Will need to fix the scan ptr later if this is so.
{
fixScan = true;
}
while(m_free != m_white)
{
gmGCObjBase* objToRecycle = m_free;
m_free = m_free->GetNext();
#if GM_GC_DEBUG
//GM_ASSERT(objToRecycle->m_curPosColor == GM_GC_DEBUG_COL_WHITE);
GM_ASSERT(objToRecycle->m_curPosColor == GM_GC_DEBUG_COL_FREE);
objToRecycle->m_curPosColor = GM_GC_DEBUG_COL_INVALID;
#endif //GM_GC_DEBUG
#if GM_GC_KEEP_PERSISTANT_SEPARATE
GM_ASSERT(!objToRecycle->GetPersist());
#endif //GM_GC_KEEP_PERSISTANT_SEPARATE
#if GM_GC_STATS
--m_numAllocated;
#endif //GM_GC_STATS
objToRecycle->Destruct(m_gc->GetVM());
++numDestructed;
--a_maxToDestruct;
if(a_maxToDestruct <= 0)
{
// Relink since we removed elements
beforeFree->SetNext(m_free);
m_free->SetPrev(beforeFree);
if(fixScan)
{
m_scan = m_free;
}
return numDestructed; // Work is done for now.
}
}
// Relink since we removed elements
beforeFree->SetNext(m_free);
m_free->SetPrev(beforeFree);
if(fixScan)
{
m_scan = m_free;
}
}
return numDestructed;
}
void gmGCColorSet::Allocate(gmGCObjBase* a_obj)
{
#if GM_GC_STATS
++m_numAllocated;
#endif //GM_GC_STATS
a_obj->SetPersist(false);
a_obj->SetColor(m_gc->GetCurShadeColor());
#if GM_GC_DEBUG
GM_ASSERT(a_obj->m_curPosColor == GM_GC_DEBUG_COL_INVALID);
a_obj->m_curPosColor = GM_GC_DEBUG_COL_BLACK;
#endif //GM_GC_DEBUG
//Insert at the end of black list
a_obj->SetNext(m_free); //Next is first Free
a_obj->SetPrev(m_free->GetPrev()); //Prev is last Black
m_free->GetPrev()->SetNext(a_obj); //Last Black next is now this
m_free->SetPrev(a_obj); //Free prev is now this
//If there were no blacks, move scan forward to prevent scanning this new black
if(m_scan == m_free)
{
m_scan = a_obj;
}
}
void gmGCColorSet::DestructAll()
{
int count = 0;
DestructPersistantObjects();
// Black and Gray
gmGCObjBase* curGrayOrBlack = m_gray->GetNext();
while(curGrayOrBlack != m_free)
{
gmGCObjBase* objToRecycle = curGrayOrBlack;
curGrayOrBlack = curGrayOrBlack->GetNext();
#if GM_GC_KEEP_PERSISTANT_SEPARATE
GM_ASSERT(!objToRecycle->GetPersist());
#endif //GM_GC_KEEP_PERSISTANT_SEPARATE
objToRecycle->Destruct(m_gc->GetVM());
#if GM_GC_STATS
--m_numAllocated;
#endif //GM_GC_STATS
++count;
}
// Whites
gmGCObjBase* curWhite = m_white->GetNext();
while(curWhite != m_tail)
{
gmGCObjBase* objToRecycle = curWhite;
curWhite = curWhite->GetNext();
#if GM_GC_KEEP_PERSISTANT_SEPARATE
GM_ASSERT(!objToRecycle->GetPersist());
#endif //GM_GC_KEEP_PERSISTANT_SEPARATE
objToRecycle->Destruct(m_gc->GetVM());
#if GM_GC_STATS
--m_numAllocated;
#endif //GM_GC_STATS
++count;
}
// Free list
gmGCObjBase* curFree = m_free;
while(curFree != m_white)
{
gmGCObjBase* objToRecycle = curFree;
curFree = curFree->GetNext();
{
#if GM_GC_KEEP_PERSISTANT_SEPARATE
GM_ASSERT(!objToRecycle->GetPersist());
#endif //GM_GC_KEEP_PERSISTANT_SEPARATE
objToRecycle->Destruct(m_gc->GetVM());
#if GM_GC_STATS
--m_numAllocated;
#endif //GM_GC_STATS
++count;
}
}
Init(m_gc);
}
//////////////////////////////////////////////////
// gmGarbageCollector
//////////////////////////////////////////////////
gmGarbageCollector::gmGarbageCollector()
{
Init(NULL, NULL);
}
gmGarbageCollector::~gmGarbageCollector()
{
}
void gmGarbageCollector::Init(gmGCScanRootsCallBack a_scanRootsCallback, gmMachine* a_gmMachine)
{
m_curShadeColor = 0; // Another color is !0
m_workPerIncrement = GM_GC_DEFAULT_WORK_INCREMENT;
m_maxObjsToDestructPerIncrement = GM_GC_DEFAULT_DESTRUCT_INCREMENT;
m_workLeftToGo = 0;
m_fullThrottle = false;
m_gcTurnedOff = true; // Start in OFF state, machine will turn on when needed.
m_firstCollectionIncrement = true;
m_doneTracing = false;
m_colorSet.Init(this);
m_traceState.Reset();
m_flipCallback = NULL;
m_scanRootsCallback = a_scanRootsCallback;
m_gmMachine = a_gmMachine;
}
bool gmGarbageCollector::BlackenGrays()
{
int workDone;
while(m_colorSet.AnyGrays())
{
// gmGCColorSet::BlackenNextGray returns 1 if there was a gray to
// blacken (even if it couldn't finish blackening it), and a 0 otherwise.
workDone = m_workLeftToGo;
while(m_colorSet.BlackenNextGray(workDone, m_workLeftToGo))
{
m_workLeftToGo -= workDone;
if (m_workLeftToGo <= 0)
{
// Quit early
return true; // We have completed one increment of work
}
}
};
return false;
}
bool gmGarbageCollector::Collect()
{
if(m_fullThrottle)
{
m_workLeftToGo = GM_MAX_INT32;
}
else
{
m_workLeftToGo = m_workPerIncrement;
}
m_doneTracing = false;
if(m_firstCollectionIncrement)
{
// Scan each root object and gray it
GM_ASSERT(m_scanRootsCallback);
m_scanRootsCallback(m_gmMachine, this);
m_firstCollectionIncrement = false;
return false;
}
// If any grays exist, scan them first
if(m_colorSet.AnyGrays())
{
if(BlackenGrays()) // Returns 0 if no more grays, and 1 if done with an increment of collection.
{
return false; // Out of time, so exit function
}
}
m_doneTracing = true;
// Let the collect continue until garbage memory has been reclaimed
// This could be done as an external phase
if(ReclaimSomeFreeObjects())
{
return false;
}
#if GM_GC_TURN_OFF_ABLE
// Turn off gc until almost out of memory.
// Can only do when allocating black.
m_gcTurnedOff = true;
#else //GM_GC_TURN_OFF_ABLE
Flip();
#endif //GM_GC_TURN_OFF_ABLE
return true;
}
void gmGarbageCollector::Flip()
{
m_firstCollectionIncrement = true;
if(m_flipCallback)
{
m_flipCallback();
}
#if GM_GC_TURN_OFF_ABLE
// The garbage collector can only be turned off if we are allocating black.
m_gcTurnedOff = false; // Turn the garbage collector back on.
#endif //GM_GC_TURN_OFF_ABLE
m_colorSet.ReclaimGarbage();
ToggleCurShadeColor();
}
// This function is called when there are no free objects in the colorset.
// If the gc is turned off, it calls flip to reclaim any garbage objects
// that have been found by the garbage collector.
void gmGarbageCollector::ReclaimObjectsAndRestartCollection()
{
#if GM_GC_TURN_OFF_ABLE
// The garbage collector only gets turned off if we are allocating
// black. GC is turned off after finishing tracing and before
// doing a gc flip. So if there are no free objects left, first
// flip the GC and turn white objects into free. Hopefully, this
// will provide more free objects for allocation.
if(m_gcTurnedOff)
{
Flip();
}
#endif //GM_GC_TURN_OFF_ABLE
}
/// \brief Destruct all objects.
void gmGarbageCollector::DestructAll()
{
m_colorSet.DestructAll();
//Reset some of our members
m_curShadeColor = 0;
m_workPerIncrement = 100;
m_maxObjsToDestructPerIncrement = 100;
m_workLeftToGo = 0;
m_doneTracing = false;
m_fullThrottle = false;
m_firstCollectionIncrement = true;
m_traceState.Reset();
}
void gmGarbageCollector::FullCollect()
{
m_fullThrottle = true;
if(IsOff()) // If GC is off
{
ReclaimObjectsAndRestartCollection(); // Do flip and turn it back on
}
while(!Collect())
{
// Do the collect phase
}
ReclaimObjectsAndRestartCollection(); // Do flip and turn it back on
// Collect a second time to catch floating black objects
while(!Collect())
{
// Do the collect phase
}
ReclaimObjectsAndRestartCollection(); // Do flip and turn it back on
// NOTE: The GC is now restarted and in an 'On' state, meaning it will now collect again from the machine.
// This behavior may not be desirable, so this function really needs more analysis to determine the
// optimum sequence for a full collect with minimal redundancy.
// Free memory of garbage objects
while(ReclaimSomeFreeObjects())
{
// Reclaim all garbage
}
m_fullThrottle = false;
}
//////////////////////////////////////////////////
// Helper functions for VM and debugger
//////////////////////////////////////////////////
#include "gmVariable.h"
#include "gmFunctionObject.h"
const void* gmGCColorSet::GetInstructionAtBreakPoint(gmuint32 a_sourceId, int a_line)
{
gmGCObjBase* cur;
// Search Gray to Free
cur = m_gray->GetNext();
while(cur != m_free)
{
gmObject* object = (gmObject*)cur;
if(object->GetType() == GM_FUNCTION)
{
gmFunctionObject * function = (gmFunctionObject *) object;
if(function->GetSourceId() == a_sourceId)
{
const void * instr = function->GetInstructionAtLine(a_line);
if(instr)
{
return instr;
}
}
}
cur = cur->GetNext();
}
// Search White
cur = m_white->GetNext();
while(cur != m_tail)
{
gmObject* object = (gmObject*)cur;
if(object->GetType() == GM_FUNCTION)
{
gmFunctionObject * function = (gmFunctionObject *) object;
if(function->GetSourceId() == a_sourceId)
{
const void * instr = function->GetInstructionAtLine(a_line);
if(instr)
{
return instr;
}
}
}
cur = cur->GetNext();
}
return NULL;
}
gmObject* gmGCColorSet::CheckReference(gmptr a_ref)
{
gmGCObjBase* cur;
// Search Gray to Free
cur = m_gray->GetNext();
while(cur != m_free)
{
gmObject* object = (gmObject*)cur;
if((gmptr)object == a_ref)
{
return object;
}
cur = cur->GetNext();
}
// Search White
cur = m_white->GetNext();
while(cur != m_tail)
{
gmObject* object = (gmObject*)cur;
if((gmptr)object == a_ref)
{
return object;
}
cur = cur->GetNext();
}
// Search Persistant list
cur = m_persistList.GetNext();
while(cur != &m_persistList)
{
gmObject* object = (gmObject*)cur;
if((gmptr)object == a_ref)
{
return object;
}
cur = cur->GetNext();
}
return NULL;
}
+436
View File
@@ -0,0 +1,436 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#ifndef _GMINCGC_H_
#define _GMINCGC_H_
#include "gmConfig.h"
// Configuration options
#define GM_GC_TURN_OFF_ABLE 1 // Let GC turn off after completion, can be turned back on when memory low
#define GM_GC_KEEP_PERSISTANT_SEPARATE 1 // Keep persistant object in separate list for efficiency
#define DEPTH_FIRST 1 // Depth first or bredth first tracing
#ifdef GM_DEBUG_BUILD
#define GM_GC_STATS 1 // Some stats
#else //GM_DEBUG_BUILD
#define GM_GC_STATS 0
#endif //GM_DEBUG_BUILD
#define GM_GC_DEBUG 0 // GC Debugging paranoid check code. Only set to 1 when debugging the GC routines.
// Fwd decls
class gmgmGCObjBase;
class gmGarbageCollector;
class gmMachine;
class gmObject;
typedef void (GM_CDECL *GCFlipCallBack)();
typedef void (GM_CDECL *gmGCScanRootsCallBack)(gmMachine* a_machine, gmGarbageCollector* a_gc);
// Incremental garbage collection method:
//
// A tricolor marking scheme is used with new objects allocated Black.
// A write barrier is used to maintain integrity of the list.
// The list looks like:
//
// | GRAY BLACK FREE | WHITE |
// ^ ^ ^ ^ ^
// G S F W T
//
// Legend:
// G Gray (head) pointer
// S Scan pointer
// F Free pointer
// W White pointer
// T Tail pointer
// | List sentinels
//
// Note that only the Scan and Free markers actually move
// The objects are classified between the pointer pairs as follows:
//
// GRAY: Gray (exclusive) to Scan (exclusive)
// BLACK: Scan (inclusive) to Free (exclusive)
// FREE: Free (inclusive) to White (exclusive)
// WHITE: White (exclusive) to Tail (exclusive)
//
//////////////////////////////////////////////////
// gmgmGCObjBase
//////////////////////////////////////////////////
#if GM_GC_DEBUG
enum
{
GM_GC_DEBUG_COL_INVALID, //0
GM_GC_DEBUG_COL_GRAY, //1
GM_GC_DEBUG_COL_BLACK, //2
GM_GC_DEBUG_COL_WHITE, //3
GM_GC_DEBUG_COL_FREE, //4
};
#endif //GM_GC_DEBUG
/// \brief All GC objects are dervied from this class
class gmGCObjBase
{
public:
#if GM_GC_DEBUG
gmGCObjBase()
{
m_curPosColor = GM_GC_DEBUG_COL_INVALID;
}
int m_curPosColor;
#endif //GM_GC_DEBUG
inline void SetColor(int a_color) {m_color = (char)a_color;}
inline int GetColor() {return (int)m_color;}
inline gmGCObjBase* GetPrev() const {return m_prev;}
inline void SetPrev(gmGCObjBase* a_prev) {m_prev = a_prev;}
inline gmGCObjBase* GetNext() const {return m_next;}
inline void SetNext(gmGCObjBase* a_next) {m_next = a_next;}
inline char GetPersist() {return m_persist;}
inline void SetPersist(bool a_flag) {m_persist = a_flag;}
/// \brief Called when GC wants to free this memory
virtual void Destruct(gmMachine * a_machine) {}
/// \brief Trace pointers this object contains to other objects.
/// It must call gmGarbageCollector::GetNextObject() on each pointer,
/// until it has traced all pointers, or used up the a_workLeftToGo count.
/// \param a_workLeftToGo Number of pointers to trace.
/// \param a_workDone, The number of pointers traced.
/// \return true if finished tracing this object, false if not finished.
virtual bool Trace(gmMachine * a_machine, gmGarbageCollector* a_gc, const int a_workLeftToGo, int& a_workDone)
{
//NOTE: Use a_gc->GetTraceState().m_context to help resume incremental tracing.
a_workDone = 1;
return true;
}
private:
gmGCObjBase* m_prev; ///< Point to previous object in color set
gmGCObjBase* m_next; ///< Point to next object in color set
char m_color; ///< Is gray or black flag, really only need by 1 bit
char m_persist; ///< This object is persistant
char m_pad[2]; ///< Pad to dword
};
//////////////////////////////////////////////////
// gmGCColorSet
//////////////////////////////////////////////////
/// \brief Tri color managing class
class gmGCColorSet
{
public:
/// \brief Constructor
gmGCColorSet();
/// \brief Destructor
virtual ~gmGCColorSet();
/// \brief Initialize members
void Init(gmGarbageCollector* a_gc);
/// \brief Returns true if there are any gray objects in this size class, false otherwise.
inline bool AnyGrays(void)
{
return (m_gray->GetNext() != m_scan);
}
int FollowPointers(int a_maxBytesToTrace);
/// \brief Blacken the next object (at the scan pointer) in the gray set.
/// Object is blackened and its children are grayed.
/// Returns 0 when no grays are left.
bool BlackenNextGray(int& a_workDone, int a_workLeftToGo);
/// \brief Called by GCGetNextObject() that is called by user code while tracing over a object.
inline void GrayAWhite(gmGCObjBase* a_obj);
/// \brief Gray this object.
void GrayThisObject(gmGCObjBase* a_obj);
/// \brief Called on a new object being allocated.
void Allocate(gmGCObjBase* a_obj);
/// \brief This routine reclaims the garbage memory for the system.
void ReclaimGarbage();
/// \brief Destruct some free objects
/// \return The number of objects destructed
int DestructSomeFreeObjects(int a_maxToDestruct);
/// \brief Destruct all objects.
void DestructAll();
/// \brief Make an object persistant by moving it into the persistant list.
void MakePersistant(gmGCObjBase* a_obj)
{
// Fix scan (If scan == obj, obj must already be black, as we have just allocated or revived this string)
if( m_scan == a_obj )
{
m_scan = m_scan->GetNext(); // Not Prev as scan should be the start of black inclusive
}
// Fix sentinels
if( m_free == a_obj )
{
m_free = m_free->GetNext();
}
// Unlink from current list
a_obj->GetNext()->SetPrev(a_obj->GetPrev());
a_obj->GetPrev()->SetNext(a_obj->GetNext());
// Insert at start of persist list
a_obj->SetNext(m_persistList.GetNext());
a_obj->SetPrev(&m_persistList);
m_persistList.GetNext()->SetPrev(a_obj);
m_persistList.SetNext(a_obj);
}
/// \brief Destruct all objects in persistant list
void DestructPersistantObjects();
/// \brief Revive an object before it is finalized
void Revive(gmGCObjBase* a_obj);
/// \brief Check if reference is valid for VM
gmObject* CheckReference(gmptr a_ref);
/// \brief Get instruction at point for VM Debugger.
const void * GetInstructionAtBreakPoint(gmuint32 a_sourceId, int a_line);
#if GM_GC_DEBUG
bool VerifyIntegrity();
#endif //GM_GC_DEBUG
protected:
gmGCObjBase* m_gray;
gmGCObjBase* m_scan;
gmGCObjBase* m_free;
gmGCObjBase* m_white;
gmGCObjBase* m_tail;
gmGCObjBase m_tailObject;
gmGCObjBase m_headObject;
gmGCObjBase m_separatorObject;
gmGCObjBase m_persistList;
gmGarbageCollector* m_gc;
#if GM_GC_STATS
int m_numAllocated;
#endif //GM_GC_STATS
};
//////////////////////////////////////////////////
// gmGarbageCollector
//////////////////////////////////////////////////
struct gmGCTraceState
{
gmGCTraceState()
{
Reset();
}
void Reset()
{
m_done = true;
m_object = NULL;
m_context = NULL;
}
bool m_done;
gmGCObjBase* m_object;
void* m_context;
};
/// \brief Incremental garbage collection
/// Method: Tri-Color, Non-copying, Write barrier, Root Snapshot.
class gmGarbageCollector
{
public:
/// \brief Constructor
gmGarbageCollector();
/// \brief Destructor
virtual ~gmGarbageCollector();
/// \brief Initialize the garbage collection system
void Init(gmGCScanRootsCallBack a_scanRootsCallback, gmMachine* a_gmMachine);
/// \brief Perform write barrier operation on Left and/or Right side objects.
inline void WriteBarrier(gmGCObjBase* a_lObj /*, gmGCObjBase* a_rObj*/);
/// \brief Call to start collection
/// \return true if collection completed, false if more work to do.
bool Collect();
/// \brief Do a full collect and don't return until done.
void FullCollect();
/// \brief Called during trace by client code, and by scan roots callback.
/// This grays a white object.
inline void GetNextObject(gmGCObjBase* a_obj) {m_colorSet.GrayAWhite(a_obj);}
/// \brief Called on a new object being allocated
inline void AllocateObject(gmGCObjBase* a_obj) {m_colorSet.Allocate(a_obj);}
/// \brief Get the current shade color since it is flipped each cycle.
inline int GetCurShadeColor() {return (m_curShadeColor);}
/// \brief Is the object colored gray or black?
inline bool IsShaded(gmGCObjBase* a_obj) {return (a_obj->GetColor() == m_curShadeColor);}
/// \brief if GC is turned off, reclaim garbage memory
void ReclaimObjectsAndRestartCollection();
/// \brief Get the trace state to resume incremental collecting
inline gmGCTraceState& GetTraceState() {return m_traceState;}
/// \brief Set the amount of work to do per increment of collecting
inline void SetWorkPerIncrement(int a_workPerIncrement) {m_workPerIncrement = a_workPerIncrement;}
/// \brief Set the amount of objects to destruct per increment of collecting
inline void SetDestructPerIncrement(int a_destructPerIncrement) {m_maxObjsToDestructPerIncrement = a_destructPerIncrement;}
/// \brief Get the amount of work to do per increment of collecting
inline int GetWorkPerIncrement() {return m_workPerIncrement;}
/// \brief Get the amount of objects to destruct per increment of collecting
inline int GetDestructPerIncrement() {return m_maxObjsToDestructPerIncrement;}
/// \brief Set function to be called before flip when dead objects are reclaimed.
/// Optional, so pass NULL to disable.
inline void SetFlipCallback(GCFlipCallBack a_flipCallback) {m_flipCallback = a_flipCallback;}
/// \brief Is collector currently turned off?
inline bool IsOff() {return m_gcTurnedOff;}
/// \brief Destruct all objects.
void DestructAll();
/// \brief Reclaim some free objects.
int ReclaimSomeFreeObjects() {return m_colorSet.DestructSomeFreeObjects(m_maxObjsToDestructPerIncrement);}
/// \brief Make an object persistant by moving it into the persistant list.
void MakeObjectPersistant(gmGCObjBase* a_obj) {m_colorSet.MakePersistant(a_obj);}
/// \brief Get the virtual machine for language
inline gmMachine* GetVM() {return m_gmMachine;}
/// \brief Check if reference is valid for VM
gmObject* CheckReference(gmptr a_ref) {return m_colorSet.CheckReference(a_ref);}
/// \brief Get instruction at point for VM Debugger.
const void * GetInstructionAtBreakPoint(gmuint32 a_sourceId, int a_line) {return m_colorSet.GetInstructionAtBreakPoint(a_sourceId, a_line);}
/// \brief Revive a dead object (only used to re-live a shared string before it is finalized)
void Revive(gmGCObjBase* a_obj)
{
if( !a_obj->GetPersist() )
{
m_colorSet.Revive(a_obj);
}
}
protected:
/// \brief Has Collect() completed yet?
inline bool IsDoneTracing() {return m_doneTracing;}
/// \brief Called by Collect()
bool BlackenGrays();
/// \brief Flip system and reclaim garbage.
void Flip();
/// \brief Toggle bit used to represent 'colored'
inline void ToggleCurShadeColor() {m_curShadeColor = !m_curShadeColor;}
gmGCColorSet m_colorSet; ///< Tri color helper class
int m_curShadeColor; ///< Cur color used to shade this generation
int m_workPerIncrement; ///< How much work to do per increment
int m_workLeftToGo; ///< How much work left in this increment
int m_maxObjsToDestructPerIncrement; ///< How much destructing work to do this frame?
bool m_gcTurnedOff; ///< Is the GC currently turned off?
bool m_firstCollectionIncrement; ///< Using snapshot method, scan roots atomically first
bool m_fullThrottle; ///< Set to true when forcing a full collection
bool m_doneTracing; ///< Has Collect() completed yet?
gmGCTraceState m_traceState; ///< Allows trace to resume where it left off
GCFlipCallBack m_flipCallback; ///< Called before flip, when dead objects are reclaimed. Default is NULL.
gmGCScanRootsCallBack m_scanRootsCallback; ///< Called at start of Collect() to add gray all roots. MUST be implemented.
gmMachine* m_gmMachine; ///< Virtual machine to pass around
};
//////////////////////////////////////////////////
// inline functions
//////////////////////////////////////////////////
void gmGCColorSet::GrayAWhite(gmGCObjBase* a_obj)
{
#if GM_GC_KEEP_PERSISTANT_SEPARATE
if(a_obj->GetPersist()) // Don't do anything with persistant objects
{
return;
}
#endif //GM_GC_KEEP_PERSISTANT_SEPARATE
// If right object is not shaded, shade it
if(!m_gc->IsShaded(a_obj))
{
GrayThisObject(a_obj);
}
}
void gmGarbageCollector::WriteBarrier(gmGCObjBase* a_lObj/*, gmGCObjBase* a_rObj*/)
{
// If we are allocating black and the collector is off, do nothing
if(m_gcTurnedOff)
{
return;
}
// We don't need to use write barrier on root objects, so check for it if we can.
// if(IsRoot(a_lObj)) { return; }
// Note: Left side is logically the old right side
// This is a snapshot write barrier. There is not old pointer to overwrite, so do nothing.
if(!a_lObj)
{
return;
}
#if GM_GC_KEEP_PERSISTANT_SEPARATE
if(a_lObj->GetPersist()) // Don't do anything with persistant objects
{
return;
}
#endif //GM_GC_KEEP_PERSISTANT_SEPARATE
if(!IsShaded(a_lObj))
{
m_colorSet.GrayThisObject(a_lObj);
}
}
#endif //_GMINCGC_H_
+51
View File
@@ -0,0 +1,51 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#ifndef _GMITERATOR_H_
#define _GMITERATOR_H_
//
// Intrusive Container Iterator
//
//
// Iterator must declare the following functions
//
// void Inc()
// void Dec()
// T* Resolve()
// const T* Resolve() const
// bool IsValid() const
//
#define GM_INCLUDE_ITERATOR_KERNEL(T) \
inline void operator++() { Inc(); } \
inline void operator--() { Dec(); } \
inline void operator++(int) { Inc(); } \
inline void operator--(int) { Dec(); } \
inline T& operator*(void) { return *Resolve(); } \
inline const T& operator*(void) const { return *Resolve(); } \
inline T* operator->(void) { return Resolve(); } \
inline const T* operator->(void) const { return Resolve(); } \
\
inline operator bool(void) { return IsValid(); } \
inline operator bool(void) const { return IsValid(); } \
inline bool operator !(void) { return !IsValid(); } \
inline bool operator !(void) const { return !IsValid(); } \
\
private: \
inline operator unsigned int(void) { return 0xDEADBEEF; } \
inline operator int(void) { return 0xDEADBEEF; } \
inline operator unsigned int(void) const { return 0xDEADBEEF; } \
inline operator int(void) const { return 0xDEADBEEF; } \
public:
#endif // _GMITERATOR_H_
+447
View File
@@ -0,0 +1,447 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#include "gmConfig.h"
#include "gmLibHooks.h"
#include "gmMachine.h"
#include "gmFunctionObject.h"
#include "gmStringObject.h"
// Must be last header
#include "memdbgon.h"
#define ID_func GM_MAKE_ID32('f','u','n','c')
#define ID_gml0 GM_MAKE_ID32('g','m','l','0')
gmLibHooks::gmLibHooks(gmStream &a_stream, const char * a_source) :
m_allocator(1, GMCODETREE_CHAINSIZE)
{
m_stream = &a_stream;
m_source = a_source;
m_swapEndian = m_stream->GetSwapEndianOnWrite();
}
gmLibHooks::~gmLibHooks()
{
}
bool gmLibHooks::Begin(bool a_debug)
{
m_debug = a_debug;
m_symbolOffset = 0;
m_functionId = 0;
m_functionStream.Reset();
m_functionStream.SetSwapEndianOnWrite(m_swapEndian);
return true;
}
bool gmLibHooks::AddFunction(gmFunctionInfo &a_info)
{
// write the function into the stream
m_functionStream << (gmuint32) ID_func;
m_functionStream << (gmuint32) a_info.m_id;
m_functionStream << (gmuint32) ((a_info.m_root) ? 1 : 0);
m_functionStream << (gmuint32) a_info.m_numParams;
m_functionStream << (gmuint32) a_info.m_numLocals;
m_functionStream << (gmuint32) a_info.m_maxStackSize;
m_functionStream << (gmuint32) a_info.m_byteCodeLength;
m_functionStream.Write(a_info.m_byteCode, a_info.m_byteCodeLength);
if(m_debug)
{
int numSymbols = a_info.m_numLocals + a_info.m_numParams, i;
// debug name
m_functionStream << (gmuint32) GetSymbolId(a_info.m_debugName);
// line info
m_functionStream << (gmuint32) a_info.m_lineInfoCount;
for(i = 0; i < a_info.m_lineInfoCount; ++i)
{
m_functionStream << (gmuint32) a_info.m_lineInfo[i].m_address;
m_functionStream << (gmuint32) a_info.m_lineInfo[i].m_lineNumber;
}
// symbol info
for(i = 0; i < numSymbols; ++i)
{
if(a_info.m_symbols)
{
m_functionStream << (gmuint32) GetSymbolId(a_info.m_symbols[i]);
}
else
{
m_functionStream << (gmuint32) (~0);
}
}
}
return true;
}
bool gmLibHooks::End(int a_errors)
{
if(a_errors == 0)
{
gmuint32 offsets[3] = {0}, offsetPos;
gmuint32 t = ID_gml0, t1 = 0;
*m_stream << t;
t = (m_debug) ? 1 : 0;
*m_stream << t;
offsetPos = m_stream->Tell();
*m_stream << offsets[0] << offsets[1] << offsets[2];
// write the string table
offsets[0] = m_stream->Tell();
t = m_symbolOffset;
*m_stream << t;
USymbol * symbol = m_symbols.GetLast();
while(m_symbols.IsValid(symbol))
{
m_stream->Write(symbol->m_string, strlen(symbol->m_string) + 1);
symbol = m_symbols.GetPrev(symbol);
}
GM_ASSERT(m_stream->Tell() == m_symbolOffset + offsets[0] + sizeof(gmuint32));
// write the source code
if(m_debug && m_source)
{
offsets[1] = m_stream->Tell();
t = strlen(m_source) + 1;
t1 = 0;
*m_stream << t << t1;
m_stream->Write(m_source, t);
}
else
{
offsets[1] = 0;
}
// write the functions
offsets[2] = m_stream->Tell();
t = m_functionId;
*m_stream << t;
m_stream->Write(m_functionStream.GetData(), m_functionStream.GetSize());
m_functionStream.ResetAndFreeMemory();
// write the offsets
m_stream->Seek(offsetPos);
*m_stream << offsets[0] << offsets[1] << offsets[2];
return true;
}
return false;
}
gmptr gmLibHooks::GetFunctionId()
{
return m_functionId++;
}
gmptr gmLibHooks::GetSymbolId(const char * a_symbol)
{
if(a_symbol == NULL) a_symbol = "";
// see if we already have sybmol
USymbol * symbol = m_symbols.GetFirst();
while(m_symbols.IsValid(symbol))
{
if(strcmp(symbol->m_string, a_symbol) == 0)
{
return symbol->m_offset;
}
symbol = m_symbols.GetNext(symbol);
}
// add a new symbol
unsigned int len = strlen(a_symbol) + 1;
symbol = (USymbol *) m_allocator.AllocBytes(sizeof(USymbol), GM_DEFAULT_ALLOC_ALIGNMENT);
symbol->m_string = (char *) m_allocator.AllocBytes(len, GM_DEFAULT_ALLOC_ALIGNMENT);
memcpy(symbol->m_string, a_symbol, len);
symbol->m_offset = m_symbolOffset;
m_symbolOffset += len;
m_symbols.InsertFirst(symbol);
return symbol->m_offset;
}
gmptr gmLibHooks::GetStringId(const char * a_string)
{
return GetSymbolId(a_string);
}
//
//
// Library Loading
//
//
struct gmlHeader
{
gmuint32 m_id;
gmuint32 m_flags;
gmuint32 m_stOffset;
gmuint32 m_scOffset;
gmuint32 m_fnOffset;
};
struct gmlStrings
{
gmuint32 m_size;
};
struct gmlLineInfo
{
gmuint32 m_byteCodeAddress;
gmuint32 m_lineNumber;
};
struct gmlSource
{
gmuint32 m_size;
gmuint32 m_flags;
};
struct gmlFunction
{
gmuint32 m_func;
gmuint32 m_id;
gmuint32 m_flags;
gmuint32 m_numParams;
gmuint32 m_numLocals;
gmuint32 m_maxStackSize;
gmuint32 m_byteCodeLen;
};
gmFunctionObject * gmLibHooks::BindLib(gmMachine &a_machine, gmStream &a_stream, const char * a_filename)
{
gmlHeader header;
gmlStrings strings;
gmlSource source;
gmlFunction function;
gmFunctionObject * functionObject = NULL;
gmFunctionObject ** functionObjects = NULL;
bool error = true, debug = false;
char * stringTable = NULL;
char * sourceCode = NULL;
char * byteCode = NULL;
unsigned int i, j;
gmuint32 numFunctions = 0;
gmuint32 sourceCodeId = 0;
gmuint32 scratchSize = 2048;
gmuint8 * scratch = GM_NEW( gmuint8[scratchSize] );
// Turn garbage collection off.
bool gc = a_machine.IsGCEnabled();
a_machine.EnableGC(false);
// Ensure stream is read from the start
a_stream.Seek( 0 );
// Load the gmlib header
if((a_stream.Read(&header, sizeof(header)) != sizeof(header)) || header.m_id != ID_gml0) { goto done; }
debug = (header.m_flags & 1);
// Load the string table
a_stream.Seek(header.m_stOffset);
if(a_stream.Read(&strings, sizeof(strings)) != sizeof(strings)) { goto done; }
stringTable = GM_NEW( char[strings.m_size] );
if(a_stream.Read(stringTable, strings.m_size) != strings.m_size) { goto done; }
// Read the source code
if(header.m_scOffset && a_machine.GetDebugMode())
{
a_stream.Seek(header.m_scOffset);
if(a_stream.Read(&source, sizeof(source)) != sizeof(source)) { goto done; }
sourceCode = GM_NEW( char[source.m_size] );
if(a_stream.Read(sourceCode, source.m_size) != source.m_size) { goto done; }
sourceCodeId = a_machine.AddSourceCode(sourceCode, a_filename);
delete[] sourceCode;
sourceCode = NULL;
}
// Read in the functions
a_stream.Seek(header.m_fnOffset);
if(a_stream.Read(&numFunctions, sizeof(numFunctions)) != sizeof(numFunctions)) { goto done; }
// Allocate n function objects.
functionObjects = GM_NEW( gmFunctionObject *[numFunctions] );
for(i = 0; i < numFunctions; ++i)
{
functionObjects[i] = a_machine.AllocFunctionObject();
}
// Load each function
for(i = 0; i < numFunctions; ++i)
{
if((a_stream.Read(&function, sizeof(function)) != sizeof(function)) || function.m_func != ID_func) { goto done; }
// Read in the byte code
if(byteCode) { delete[] byteCode; }
// use the byte code allocation for the debug params and locals symbol array
byteCode = GM_NEW( char[function.m_byteCodeLen] );
if(a_stream.Read(byteCode, function.m_byteCodeLen) != function.m_byteCodeLen) { goto done; }
// Load all symbols
union
{
gmuint8 * instruction;
gmuint32 * instruction32;
};
instruction = (gmuint8 *) byteCode;
gmuint8 * end = instruction + function.m_byteCodeLen;
for(;instruction < end;)
{
switch(*(instruction32++))
{
case BC_BRA :
case BC_BRZ :
case BC_BRNZ :
case BC_BRZK :
case BC_BRNZK :
case BC_FOREACH :
case BC_PUSHINT :
case BC_PUSHFP : instruction += sizeof(gmfloat); break;
case BC_CALL :
case BC_GETLOCAL :
case BC_SETLOCAL : instruction += sizeof(gmuint32); break;
case BC_GETDOT :
case BC_SETDOT :
case BC_GETTHIS :
case BC_SETTHIS :
case BC_GETGLOBAL :
case BC_SETGLOBAL :
{
gmptr * reference = (gmptr *) instruction;
GM_ASSERT(*reference >= 0 && *reference < (gmptr) strings.m_size);
*reference = a_machine.AllocPermanantStringObject(&stringTable[*reference])->GetRef();
instruction += sizeof(gmptr);
break;
}
case BC_PUSHSTR :
{
gmptr * reference = (gmptr *) instruction;
GM_ASSERT(*reference >= 0 && *reference < (gmptr) strings.m_size);
*reference = a_machine.AllocStringObject(&stringTable[*reference])->GetRef();
instruction += sizeof(gmptr);
break;
}
case BC_PUSHFN :
{
gmptr * reference = (gmptr *) instruction;
GM_ASSERT(*reference >= 0 && *reference < (gmptr) numFunctions);
*reference = functionObjects[*reference]->GetRef();
instruction += sizeof(gmptr);
break;
}
default : break;
}
}
// Initialise our function object.
gmFunctionInfo functionInfo;
gmFunctionObject * currFunction = functionObjects[function.m_id];
functionInfo.m_id = currFunction->GetRef();
functionInfo.m_root = (function.m_flags & 1);
functionInfo.m_byteCode = byteCode;
functionInfo.m_byteCodeLength = function.m_byteCodeLen;
functionInfo.m_numParams = function.m_numParams;
functionInfo.m_numLocals = function.m_numLocals;
functionInfo.m_maxStackSize = function.m_maxStackSize;
functionInfo.m_symbols = NULL;
functionInfo.m_lineInfo = NULL;
// We have now loaded all objects into the byte code.... Load the debug info
if(debug)
{
gmuint32 stringOffset, lineInfoCount, numSymbols = function.m_numLocals + function.m_numParams;
// debug name
if(a_stream.Read(&stringOffset, sizeof(stringOffset)) != sizeof(stringOffset)) { goto done; }
GM_ASSERT(stringOffset < strings.m_size);
functionInfo.m_debugName = &stringTable[stringOffset];
// Make sure our scratch memory is large enough
if(a_stream.Read(&lineInfoCount, sizeof(lineInfoCount)) != sizeof(lineInfoCount)) { goto done; }
gmuint32 reqdScratchSize = (lineInfoCount * sizeof(gmLineInfo)) + (sizeof(const char *) * numSymbols);
if(scratchSize < reqdScratchSize)
{
scratchSize = reqdScratchSize;
delete[] scratch;
scratch = GM_NEW( gmuint8[scratchSize] );
}
gmLineInfo * lineInfo = (gmLineInfo *) scratch;
functionInfo.m_lineInfo = lineInfo;
functionInfo.m_symbols = (const char **) (scratch + (lineInfoCount * sizeof(gmLineInfo)));
functionInfo.m_lineInfoCount = lineInfoCount;
// Debug line info
for(j = 0; j < lineInfoCount; ++j)
{
gmlLineInfo libLineInfo;
if(a_stream.Read(&libLineInfo, sizeof(libLineInfo)) != sizeof(libLineInfo)) { goto done; }
lineInfo[j].m_address = libLineInfo.m_byteCodeAddress;
lineInfo[j].m_lineNumber = libLineInfo.m_lineNumber;
}
// Debug symbols
for(j = 0; j < numSymbols; ++j)
{
if(a_stream.Read(&stringOffset, sizeof(stringOffset)) != sizeof(stringOffset)) { goto done; }
GM_ASSERT(stringOffset < strings.m_size);
functionInfo.m_symbols[j] = &stringTable[stringOffset];
}
}
// AND FINALLY, INITIALISE OUR FUNCTION
currFunction->Init(&a_machine, debug && a_machine.GetDebugMode(), functionInfo, sourceCodeId);
if(functionInfo.m_root)
{
functionObject = currFunction;
}
}
error = false;
done:
// turn gc off.
a_machine.EnableGC(gc);
if(stringTable) { delete[] stringTable; }
if(sourceCode) { delete[] sourceCode; }
if(functionObjects) { delete[] functionObjects; }
if(byteCode) { delete[] byteCode; }
if(scratch) { delete[] scratch; }
if(error)
{
a_machine.GetLog().LogEntry("Error loading library");
return NULL;
}
return functionObject;
}
+124
View File
@@ -0,0 +1,124 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#include "gmConfig.h"
#include "gmCodeGenHooks.h"
#include "gmListDouble.h"
#include "gmMemChain.h"
#include "gmStream.h"
#include "gmStreamBuffer.h"
class gmMachine;
class gmFunctionObject;
/// \class gmLibHooks
/// \brief gmLibHooks is a compiler hook class that allows compiling to a lib for disk storage.
class gmLibHooks : public gmCodeGenHooks
{
public:
// a_source and must exist untill destruction of the libhooks
gmLibHooks(gmStream &a_stream, const char * a_source);
virtual ~gmLibHooks();
virtual bool Begin(bool a_debug);
virtual bool AddFunction(gmFunctionInfo &a_info);
virtual bool End(int a_errors);
virtual gmptr GetFunctionId();
virtual gmptr GetSymbolId(const char * a_symbol);
virtual gmptr GetStringId(const char * a_string);
virtual bool SwapEndian() const { return m_swapEndian; }
/// \brief BindLib will bind the lib to the machine, and return the root function for executing.
static gmFunctionObject * BindLib(gmMachine &a_machine, gmStream &a_stream, const char * a_filename);
private:
class USymbol : public gmListDoubleNode<USymbol>
{
public:
USymbol();
~USymbol();
char * m_string;
gmptr m_offset; // offset into symbol table.
};
gmStream * m_stream;
bool m_swapEndian;
bool m_debug;
const char * m_source;
gmptr m_symbolOffset;
gmptr m_functionId;
gmStreamBufferDynamic m_functionStream;
gmListDouble<USymbol> m_symbols;
gmMemChain m_allocator;
};
/*
Library file format for .gmlib files (gm library files) (native endian)
// header
'gml0' [4 bytes]
flags [4 bytes] // 0x01 - debug lib,
string_table_offset [4 bytes] // relative to start of lib
source_code_offset [4 bytes] // 0 for not there, otherwise relative to start of lib
functions_offset [4 bytes] // relative to start of lib
string_table
{
string_table_size [4 bytes]
data [1 byte ] * string_table_size of 0 terminated strings
}
source_code
{
source_code_size [4 bytes]
flags [4 bytes] // possibly encryption and compression flags
data [1 byte ] * source code size.
}
functions
{
num_functions [4 bytes] // number of functions
function[]
{
'func' [4 bytes]
function_id [4 bytes] // FUNCTION IDS ARE FROM 0-(num_functions-1), BC_PUSHFN calls reference this id.
flags [4 bytes] // 0x01 - is root
num_params [4 bytes]
num_locals [4 bytes]
max_stack_size [4 bytes]
byte_code_length [4 bytes]
byte_code [1 byte ] * byte_code_length // contains offsets rel to string tab
// and function ids for certian op codes.
// byte code must be changed on load such that BC_PUSHFN calls reference their respective function object,
// and BC_PUSHSTR reference their string object created by their string table offset.
if(debug lib)
{
debug_name_offset [4 bytes]
line_info_count [4 bytes]
line_info[]
{
byte_code_address [4 bytes]
line_number [4 bytes]
}
symbol_name_offsets[] [4 bytes] * (num_params + num_locals) (~0) on no offset
}
}
}
*/
@@ -0,0 +1,16 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#include "gmConfig.h"
#include "gmListDouble.h"
// Must be last header
#include "memdbgon.h"
+377
View File
@@ -0,0 +1,377 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#ifndef _GMLISTDOUBLEI_H_
#define _GMLISTDOUBLEI_H_
#include "gmConfig.h"
#include "gmIterator.h"
template <class T>
class gmListDouble;
/// \class gmListDoubleNode
/// \brief intrusive node, must inherit this class templated to your class
template <class T>
class gmListDoubleNode
{
public:
inline gmListDoubleNode() {}
/// \brief Nullify()
inline void Nullify()
{
m_next = m_prev = NULL;
}
/// \brief Remove from list. Allows node to unlink from unknown list.
inline void RemoveAndNullify()
{
m_next->m_prev = m_prev;
m_prev->m_next = m_next;
this->Nullify();
}
/// \brief Remove from list, must be in list.
inline void Remove()
{
m_next->m_prev = m_prev;
m_prev->m_next = m_next;
}
/// \brief Returns true if node is linked to list, assumes the list is being managed correctly with nullify()
inline bool IsLinked() const
{
return (m_next != NULL);
}
private:
gmListDoubleNode * m_next;
gmListDoubleNode * m_prev;
friend class gmListDouble<T>;
};
/// \class gmListDoubleNodeObj
/// \brief List node that stores object pointer.
/// Allows node to be contained rather than be inherited.
template<class T>
class gmListDoubleNodeObj : public gmListDoubleNode< gmListDoubleNodeObj<T> >
{
public:
/// \brief Construct, Nullifies link and object
inline gmListDoubleNodeObj()
{
this->Nullify();
SetObject(NULL);
}
/// \brief Construct, Nullifies link and set object
inline gmListDoubleNodeObj(T* a_obj)
{
this->Nullify();
SetObject(a_obj);
}
inline T* GetObject() { return m_object; }
inline void SetObject(T* a_obj) { m_object = a_obj; }
private:
T* m_object;
};
/// \class gmListDouble
/// \brief Templated intrusive doubly linked list using a sentinal node
template <class T>
class gmListDouble
{
public:
/// \class Iterator
class Iterator
{
public:
GM_INCLUDE_ITERATOR_KERNEL(T)
inline Iterator() { m_node = NULL; m_list = NULL; }
inline Iterator(T * a_node, const gmListDouble * a_list) { m_node = a_node; m_list = a_list; }
inline void Inc() { m_node = m_list->GetNext(m_node); }
inline void Dec() { m_node = m_list->GetPrev(m_node); }
inline T* Resolve() { return m_node; }
inline const T* Resolve() const { return m_node; }
inline bool IsValid() const { return (m_list && m_list->IsValid(m_node)); }
private:
T * m_node;
const gmListDouble * m_list;
};
// methods
inline gmListDouble();
inline ~gmListDouble();
inline void InsertAfter(T * a_cursor, T * a_elem);
inline void InsertBefore(T * a_cursor, T * a_elem);
inline void InsertLast(T * a_elem);
inline void InsertFirst(T * a_elem);
inline void Remove(T * a_elem);
inline void RemoveAndNullify(T * a_elem);
inline T * Remove(Iterator &a_it); // iterator is incremented
inline T * RemoveAndNullify(Iterator &a_it); // iterator is incremented
inline T * RemoveGetNext(T * a_elem);
inline T * RemoveLast(); // returns NULL if no elements left in list
inline T * RemoveLastAndNullify();
inline T * RemoveFirst(); // returns NULL if no elements left in list
inline T * RemoveFirstAndNullify();
inline void RemoveAll();
inline void RemoveAndNullifyAll();
inline void RemoveAndDeleteAll();
inline int Count() const;
inline bool IsValid(const T* a_elem) const { return (a_elem != &m_sentinel); }
inline T* GetFirst() const { return (T*) m_sentinel.m_next; }
inline T* GetLast() const { return (T*) m_sentinel.m_prev; }
inline T* GetNext(const T* a_elem) const { return (T*) a_elem->gmListDoubleNode<T>::m_next; }
inline T* GetPrev(const T* a_elem) const { return (T*) a_elem->gmListDoubleNode<T>::m_prev; }
inline bool IsEmpty() const { return (m_sentinel.m_next == &m_sentinel); }
inline Iterator First(void) const { return Iterator(static_cast<T*>(m_sentinel.m_next), this); }
inline Iterator Last(void) const { return Iterator(static_cast<T*>(m_sentinel.m_prev), this); }
private:
gmListDoubleNode<T> m_sentinel;
};
//
// implementation of gmListDouble
//
template <class T>
inline gmListDouble<T>::gmListDouble()
{
m_sentinel.m_next = &m_sentinel;
m_sentinel.m_prev = &m_sentinel;
}
template <class T>
inline gmListDouble<T>::~gmListDouble()
{
}
template <class T>
inline void gmListDouble<T>::InsertAfter(T * a_cursor, T * a_elem)
{
a_elem->gmListDoubleNode<T>::m_next = a_cursor->gmListDoubleNode<T>::m_next;
a_elem->gmListDoubleNode<T>::m_prev = a_cursor;
a_elem->gmListDoubleNode<T>::m_prev->m_next = a_elem;
a_elem->gmListDoubleNode<T>::m_next->m_prev = a_elem;
}
template <class T>
inline void gmListDouble<T>::InsertBefore(T * a_cursor, T * a_elem)
{
a_elem->gmListDoubleNode<T>::m_next = a_cursor;
a_elem->gmListDoubleNode<T>::m_prev = a_cursor->gmListDoubleNode<T>::m_prev;
a_elem->gmListDoubleNode<T>::m_prev->m_next = a_elem;
a_elem->gmListDoubleNode<T>::m_next->m_prev = a_elem;
}
template <class T>
inline void gmListDouble<T>::InsertLast(T * a_elem)
{
a_elem->gmListDoubleNode<T>::m_prev = m_sentinel.m_prev;
a_elem->gmListDoubleNode<T>::m_next = &m_sentinel;
m_sentinel.m_prev->m_next = a_elem;
m_sentinel.m_prev = a_elem;
}
template <class T>
inline void gmListDouble<T>::InsertFirst(T * a_elem)
{
a_elem->gmListDoubleNode<T>::m_prev = &m_sentinel;
a_elem->gmListDoubleNode<T>::m_next = m_sentinel.m_next;
m_sentinel.m_next->m_prev = a_elem;
m_sentinel.m_next = a_elem;
}
template <class T>
inline void gmListDouble<T>::Remove(T * a_elem)
{
a_elem->gmListDoubleNode<T>::m_next->m_prev = a_elem->gmListDoubleNode<T>::m_prev;
a_elem->gmListDoubleNode<T>::m_prev->m_next = a_elem->gmListDoubleNode<T>::m_next;
}
template <class T>
inline void gmListDouble<T>::RemoveAndNullify(T * a_elem)
{
a_elem->gmListDoubleNode<T>::m_next->m_prev = a_elem->gmListDoubleNode<T>::m_prev;
a_elem->gmListDoubleNode<T>::m_prev->m_next = a_elem->gmListDoubleNode<T>::m_next;
a_elem->gmListDoubleNode<T>::m_next = a_elem->gmListDoubleNode<T>::m_prev = NULL;
}
template <class T>
inline T * gmListDouble<T>::Remove(Iterator &a_it)
{
T* node = a_it.Resolve();
a_it.Inc();
Remove(node);
return node;
}
template <class T>
inline T * gmListDouble<T>::RemoveAndNullify(Iterator &a_it)
{
T* node = a_it.Resolve();
a_it.Inc();
RemoveAndNullify(node);
return node;
}
template <class T>
inline T * gmListDouble<T>::RemoveGetNext(T * a_elem)
{
gmListDoubleNode<T> * next = a_elem->gmListDoubleNode<T>::m_next;
a_elem->gmListDoubleNode<T>::m_next->m_prev = a_elem->gmListDoubleNode<T>::m_prev;
a_elem->gmListDoubleNode<T>::m_prev->m_next = a_elem->gmListDoubleNode<T>::m_next;
a_elem->gmListDoubleNode<T>::m_next = a_elem->gmListDoubleNode<T>::m_prev = NULL;
return static_cast<T *>(next);
}
template <class T>
inline T * gmListDouble<T>::RemoveLast()
{
if(m_sentinel.m_prev == &m_sentinel) return NULL;
gmListDoubleNode<T> * temp;
temp = m_sentinel.m_prev;
temp->m_next->m_prev = temp->m_prev;
temp->m_prev->m_next = temp->m_next;
return static_cast<T*>(temp);
}
template <class T>
inline T * gmListDouble<T>::RemoveLastAndNullify()
{
if(m_sentinel.m_prev == &m_sentinel) return NULL;
gmListDoubleNode<T> * temp;
temp = m_sentinel.m_prev;
temp->m_next->m_prev = temp->m_prev;
temp->m_prev->m_next = temp->m_next;
temp->m_next = temp->m_prev = NULL;
return static_cast<T*>(temp);
}
template <class T>
inline T * gmListDouble<T>::RemoveFirst()
{
if(m_sentinel.m_next == &m_sentinel) return NULL;
gmListDoubleNode<T> * temp;
temp = m_sentinel.m_next;
temp->m_next->m_prev = temp->m_prev;
temp->m_prev->m_next = temp->m_next;
return static_cast<T*>(temp);
}
template <class T>
inline T * gmListDouble<T>::RemoveFirstAndNullify()
{
if(m_sentinel.m_next == &m_sentinel) return NULL;
gmListDoubleNode<T> * temp;
temp = m_sentinel.m_next;
temp->m_next->m_prev = temp->m_prev;
temp->m_prev->m_next = temp->m_next;
temp->m_next = temp->m_prev = NULL;
return static_cast<T*>(temp);
}
template <class T>
inline void gmListDouble<T>::RemoveAll()
{
m_sentinel.m_next = &m_sentinel;
m_sentinel.m_prev = &m_sentinel;
}
template <class T>
inline void gmListDouble<T>::RemoveAndNullifyAll()
{
gmListDoubleNode<T> * node = m_sentinel.m_next, * temp;
while(node != &m_sentinel)
{
temp = node;
node = node->m_next;
temp->m_next = temp->m_prev = NULL;
}
m_sentinel.m_next = &m_sentinel;
m_sentinel.m_prev = &m_sentinel;
}
template <class T>
inline void gmListDouble<T>::RemoveAndDeleteAll()
{
gmListDoubleNode<T> * node = m_sentinel.m_next, * temp;
while(node != &m_sentinel)
{
temp = node;
node = node->m_next;
delete ((T *) temp);
}
m_sentinel.m_next = &m_sentinel;
m_sentinel.m_prev = &m_sentinel;
}
template <class T>
inline int gmListDouble<T>::Count() const
{
int count = 0;
gmListDoubleNode<T> * node = m_sentinel.m_next;
while(node != &m_sentinel)
{
node = node->m_next;
++count;
}
return count;
}
#endif // _GMLISTDOUBLEI_H_
+120
View File
@@ -0,0 +1,120 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#include "gmConfig.h"
#include "gmLog.h"
#include "gmMem.h"
// Must be last header
#include "memdbgon.h"
gmLog::gmLog() :
m_mem(1, GMLOG_CHAINSIZE)
{
m_first = NULL;
m_last = NULL;
m_curr = NULL;
m_memApproxLimit = -1;
}
gmLog::~gmLog()
{
}
void gmLog::Reset()
{
m_first = NULL;
m_last = NULL;
m_curr = NULL;
m_mem.Reset();
}
void gmLog::ResetAndFreeMemory()
{
m_first = NULL;
m_last = NULL;
m_curr = NULL;
m_mem.ResetAndFreeMemory();
}
void GM_CDECL gmLog::LogEntry(const char * a_format, ...)
{
va_list ap;
char buffer[GMLOG_CHAINSIZE];
va_start(ap, a_format);
_gmvsnprintf(buffer, GMLOG_CHAINSIZE, a_format, ap);
va_end(ap);
strcat(buffer, GM_NL);
if( (m_memApproxLimit > 0) && (m_mem.GetSystemMemUsed() > (unsigned int)m_memApproxLimit) )
{
m_mem.Reset();
}
// add to entry list
Entry * entry = (Entry *) m_mem.AllocBytes(sizeof(Entry) + sizeof(int), GM_DEFAULT_ALLOC_ALIGNMENT);
if(entry != NULL)
{
char * text = (char *) m_mem.AllocBytes(strlen(buffer) + 1, GM_DEFAULT_ALLOC_ALIGNMENT);
if(text)
{
strcpy(text, buffer);
entry->m_text = text;
entry->m_next = NULL;
if(m_last)
{
m_last->m_next = entry;
m_last = entry;
}
else
{
m_first = m_last = entry;
}
}
}
}
const char * gmLog::GetEntry(bool &a_first)
{
if(a_first == true)
{
a_first = false;
m_curr = m_first;
}
if(m_curr)
{
const char * text = m_curr->m_text;
m_curr = m_curr->m_next;
return text;
}
return NULL;
}
void gmLog::SetMemLimit(int a_limit)
{
m_memApproxLimit = a_limit;
}
+61
View File
@@ -0,0 +1,61 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#ifndef _GMLOG_H_
#define _GMLOG_H_
#include "gmConfig.h"
#include "gmMemChain.h"
/// \class gmLog is a log class for the compiler. The log is passed between the parser, analyser, and code generator.
/// the log class will batch the errors into a list of error strings.
class gmLog
{
public:
gmLog();
~gmLog();
/// \brief Reset() will remove log entries, but not free the chain mem.
void Reset();
/// \brief ResetAndFreeMemory()
void ResetAndFreeMemory();
/// \brief LogEntry() will log an entry to the log. the length of log must not exceed GMLOG_CHAINSIZE
/// \param a_format
void GM_CDECL LogEntry(const char * a_format, ...);
/// \brief GetEntry() will return a log entry, or NULL if there are no more entries.
/// \param a_first is true for the first call to get entry, this will be set to false for successive calls.
/// \return a log entry, or NULL if no more entries exist
const char * GetEntry(bool &a_first);
/// \brief Set approximate memory limit for error logs.
/// \param a_limit The approximate memory limit in bytes or <= 0 for no limit.
void SetMemLimit(int a_limit);
private:
struct Entry
{
const char * m_text;
Entry * m_next;
};
Entry * m_first; ///< Message list first ptr
Entry * m_last; ///< Message list last ptr
Entry * m_curr; ///< Current message
int m_memApproxLimit; ///< Approximate memory limit
gmMemChain m_mem;
};
#endif // _GMLOG_H_
File diff suppressed because it is too large Load Diff
+692
View File
@@ -0,0 +1,692 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in this file
*/
#ifndef _GMMACHINE_H_
#define _GMMACHINE_H_
/**********************************************************
GameMonkey Script
created by
Matthew Riek and Greg Douglas
**********************************************************/
/**********************************************************
GameMonkey Script License
Copyright (c) 2003 Auran Development Ltd.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the
Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions of
the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
**********************************************************/
#include "gmConfig.h"
#include "gmMemFixed.h"
#include "gmMemFixedSet.h"
#include "gmLog.h"
#include "gmVariable.h"
#include "gmTableObject.h"
#include "gmOperators.h"
#include "gmFunctionObject.h"
#include "gmHash.h"
#include "gmArraySimple.h"
#include "gmIncGC.h"
#undef GetObject //Argh Windows defines this in WINGDI.H
#define GM_VERSION "1.24"
// fwd decls
class gmStringObject;
class gmUserObject;
class gmMachine;
class gmThread;
struct gmStackFrame;
struct gmSignal;
class gmSourceEntry;
class gmStream;
class gmBlockList;
enum gmMachineCommand
{
MC_COLLECT_GARBAGE = 0, // called when a gc cycle is run, a_context is the gc mark
MC_THREAD_EXCEPTION, // called when a thread causes an exception. a_context is the thread. (which is about to die) return false to kill thread, else it will hang around for debugging
MC_THREAD_CREATE, // called when a thread is created. a_context is the thread.
MC_THREAD_DESTROY, // called when a thread is destroyed. a_context is the thread. (which is about to die)
};
/// \brief gmMachineCallback is a callback used for debugger tie ins etc.
/// \return depends on a_command
typedef bool (GM_CDECL *gmMachineCallback)(gmMachine * a_machine, gmMachineCommand a_command, const void * a_context);
#if GM_USE_INCGC
typedef void (GM_CDECL *gmGCDestructCallback)(gmMachine * a_machine, gmUserObject* a_object);
typedef bool (GM_CDECL *gmGCTraceCallback)(gmMachine * a_machine, gmUserObject* a_object, gmGarbageCollector* a_gc, const int a_workLeftToGo, int& a_workDone);
#else //GM_USE_INCGC
typedef void (GM_CDECL *gmGarbageCollectCallback)(gmMachine * a_machine, gmUserObject * a_object, gmuint32 a_mark);
#endif //GM_USE_INCGC
/// \brief gmAsStringCallback is a callback registered with a gm type, which will convert the type to a string for print and debug purposes, the callback must
/// convert the type to a string < 256 characters long.
/// \param a_bufferSize at least 256 chars
typedef void (GM_CDECL *gmAsStringCallback)(gmUserObject * a_userObj, char * a_buffer, int a_bufferSize);
typedef void (GM_CDECL *gmPrintCallback)(gmMachine * a_machine, const char * a_string);
typedef bool (GM_CDECL *gmThreadIteratorCallback)(gmThread * a_thread, void * a_context);
typedef bool (GM_CDECL *gmUserBreakCallback)(gmThread * a_thread);
// the following callbacks return true if the thread is to yield after completion of the callback.
typedef bool (GM_CDECL *gmDebugLineCallback)(gmThread * a_thread);
typedef bool (GM_CDECL *gmDebugCallCallback)(gmThread * a_thread);
typedef bool (GM_CDECL *gmDebugRetCallback)(gmThread * a_thread);
typedef bool (GM_CDECL *gmDebugIsBrokenCallback)(gmThread * a_thread); // returns true if the thread is broken, or if the thread is pending delete after exception
/// \struct gmFunctionEntry
struct gmFunctionEntry
{
const char * m_name;
gmCFunction m_function;
const void * m_userData; ///< Optional user ptr or value to assist binding
};
/// \struct gmThreadInfo
struct gmThreadInfo
{
int m_threadId;
int m_threadState;
void * m_threadDebugUser;
};
/// \struct gmSignal
struct gmSignal
{
int m_srcThreadId; ///< id of the signalling thread.
int m_dstThreadId; ///< id of dst thread.
gmVariable m_signal; ///< signal.
gmSignal * m_nextSignal; ///< next thread in the threads signal list.
};
/// \class gmBlock
class gmBlock : public gmListDoubleNode<gmBlock>
{
public:
bool m_signalled; ///< has this block been signalled?
int m_srcThreadId; ///< id of the thread to cause the unblock
gmVariable m_block; ///< the block object, this will be pushed on the stack if unblocked.
gmThread * m_thread; ///< the thread this block belongs to
gmBlock * m_nextBlock; ///< next block on this thread
class gmBlockList * m_list; ///< parent block list.
};
/// \class gmMachine
/// \brief the gmMachine object represents a scripting instance. many script threads may run on the one gmMachine.
/// gmMachine manages script source, script memory, garbage collection, function type overrides, operator overrides,
/// threads etc.
class gmMachine
{
public:
gmMachine();
virtual ~gmMachine();
/// \brief ResetAndFreeMemory() will reset the gmMachine.
void ResetAndFreeMemory();
/// \brief Init() will recreate the machine... only need to call this if you have called ResetAndFreeMemory()
/// \sa ResetAndFreeMemory();
void Init();
static gmMachineCallback s_machineCallback; ///< global machine callback
static gmPrintCallback s_printCallback; ///< global print callback
static gmUserBreakCallback s_userBreakCallback; ///< global execution break callback
//
//
// Registry
//
//
/// \brief RegisterLibrary() will register an array of functions to the machine as globals.
/// \param a_asTable as non-null will create a table in global scope and place the functions in the table.
/// \param a_newTable will create a new table for the functions; if false it will insert them into the existing table (if found)
void RegisterLibrary(gmFunctionEntry * a_functions, int a_numFunctions, const char * a_asTable = NULL, bool a_newTable = true);
/// \brief RegisterTypeLibrary() will register an array of functions to the machine as type variables.
void RegisterTypeLibrary(gmType a_type, gmFunctionEntry * a_functions, int a_numFunctions);
/// \brief RegisterLibraryFunction() will register a single functions to the machine as global.
/// \param a_name Script function name
/// \param a_function C function
/// \param a_asTable as non-null will use/create a table in global scope and place the functions in the table.
void RegisterLibraryFunction(const char * a_name, gmCFunction a_function, const char * a_asTable = NULL, const void* a_userData = NULL)
{
gmFunctionEntry entry = {a_name, a_function, a_userData};
RegisterLibrary(&entry, 1, a_asTable, false);
}
/// \brief RegisterTypeLibraryFunction() will register a single functions to the machine as type variable.
/// \param a_type Valid/Registered type identifier
/// \param a_name Script function name
/// \param a_function C function
void RegisterTypeLibraryFunction(gmType a_type, const char * a_name, gmCFunction a_function, const void* a_userData = NULL)
{
gmFunctionEntry entry = {a_name, a_function, a_userData};
RegisterTypeLibrary(a_type, &entry, 1);
}
/// \brief CreateUserType() will create a new user type such that you may bind your own operator functions etc.
/// \return the new user type.
gmType CreateUserType(const char * a_name);
/// \brief RegisterTypeVariable() will register a variable for a given gm type. This allows a default library
/// to bind type specific functions and constants to that type. ie, binding a ToFloat() function to the
/// int type.
void RegisterTypeVariable(gmType a_type, const char * a_variableName, const gmVariable &a_variable);
/// \brief RegisterTypeOperator() will let you regiester operators for user types only.
/// \param a_operator
/// \param a_function
/// \param a_nativeFunction
bool RegisterTypeOperator(gmType a_type, gmOperator a_operator, gmFunctionObject * a_function, gmOperatorFunction a_nativeFunction = NULL);
/// \brief GetTypeVariable() will lookup the type variables for the given variable key.
inline gmVariable GetTypeVariable(gmType a_type, const gmVariable &a_key) const;
/// \brief GetTypeNativeOperator() will lookup a type for a native operator
inline gmOperatorFunction GetTypeNativeOperator(gmType a_type, gmOperator a_operator);
/// \brief GetTypeOperator() will lookup a type for a GM function operator
inline gmFunctionObject * GetTypeOperator(gmType a_type, gmOperator a_operator);
/// \brief GetTypeName() will return the name of a_type
const char * GetTypeName(gmType a_type);
/// \brief Access the table for a 'type', or return NULL.
gmTableObject * GetTypeTable(gmType a_type);
//
//
// Thread Interface
//
//
/// \brief CheckSyntax()
/// \brief return the number of errors
int CheckSyntax(const char * a_string);
/// \brief ExecuteString() will compile a_string to byte code. If no errors occured, the code will be executed
/// in a new thread.
/// \param a_string is null terminated script string.
/// \param a_threadId is set to the id of the thread and may be NULL.
/// \param a_now is true, and execution will occur immediataly, and not at the next Execute() call.
/// \param a_filename Name of the source file, used for debug purposes, or NULL.
/// \param a_this The 'this' pointer passed to the un-named function. Or NULL to pass a null gmVariable.
/// \return the number of errors from compiling the script.
/// \sa GetCompileLog()
int ExecuteString(const char * a_string, int * a_threadId = NULL, bool a_now = true, const char * a_filename = NULL, gmVariable* a_this = NULL);
/// \brief ExecuteLib() will execute a pre-compiled lib in a new thread
/// \param a_stream is a stream to pull the lib from
/// \param a_threadId is set to the id of the thread and may be NULL.
/// \param a_now is true, and execution will occur immediataly, and not at the next Execute() call.
/// \param a_filename is the filename the lib came from for debugging purposes.
/// \return false on lib error
bool ExecuteLib(gmStream &a_stream, int * a_threadId = NULL, bool a_now = true, const char * a_filename = NULL, gmVariable* a_this = NULL);
/// \brief ExecuteFunction() will execute a thread on the passed function
bool ExecuteFunction(gmFunctionObject * a_function, int * a_threadId = NULL, bool a_now = true, gmVariable* a_this = NULL);
/// \brief CompileStringToLib() will compile a_string to byte code suitable for storage in a file.
/// \param a_string is null terminated script string.
/// \param a_stream is the file stream to compile the lib to
/// \return the number of errors from compiling the script.
/// \sa GetCompileLog()
int CompileStringToLib(const char * a_string, gmStream &a_stream);
/// \brief CompileStringToFunction()
gmFunctionObject * CompileStringToFunction(const char * a_string, int *a_errorCount = NULL, const char * a_filename = NULL);
/// \brief BindLibToFunction() Bind a precompiled library to a function.
/// \param a_stream is a stream to pull the lib from
/// \param a_filename is the filename the lib came from for debugging purposes.
/// \return Root function from Lib or NULL if failed
gmFunctionObject * BindLibToFunction(gmStream &a_stream, const char * a_filename = NULL);
/// \brief GetLog() will get the compile and runtime log of the last script compiled. log any runtime errors from
/// linked c functions to this log.
inline gmLog &GetLog() { return m_log; }
/// \brief CreateThread() will create a new thread. This thread will be placed in the running thread list.
/// \param a_this
/// \param a_function
/// \param a_threadId is set to the id of the created thread and may be NULL.
/// \return a thread, or NULL if the thread finished executing or could not be created.
gmThread * CreateThread(const gmVariable &a_this, const gmVariable &a_function, int * a_threadId = NULL);
gmThread * CreateThread(int * a_threadId = NULL);
/// \brief GetThread() will return the thread given a thread id.
/// \return NULL on error.
gmThread * GetThread(int a_threadId);
/// \brief Signal() will signal a single thread with the given variable
/// \param a_signal is the signal var
/// \param a_dstThreadId is the thread id we wish to unblock. dstThread id of GM_INVALID_THREAD is all threads
/// \param a_srcThreadId is the thread id of the signaling object
/// \param a_persist is true and the signal may persist if it had the potential to unblock a SYS_PENDING thread.
/// \return true if the signal fired, false otherwise
bool Signal(const gmVariable &a_signal, int a_dstThreadId, int a_srcThreadId);
/// \brief Sys_Block() will add a block to a thread.
/// \return the index of the block that was signalled, or -1 if the blocks were added and the thread is to enter a blocked state.
int Sys_Block(gmThread * a_thread, int m_numBlocks, const gmVariable * a_blocks);
/// \brief KillThread()
void KillThread(int a_threadId);
/// \brief ForEachThread()
void ForEachThread(gmThreadIteratorCallback a_callback, void * a_context);
/// \brief Sys_SwitchState() will change the executing state of a thread. if a_to is gmThread::KILLED, the thread
/// is reset.
void Sys_SwitchState(gmThread * a_thread, int a_to);
/// \brief KillExceptionThreads()
void KillExceptionThreads();
/// \brief Execute() will execute all running threads
/// \param m_deltaTime is the time in milliseconds since the machine was last updated.
/// \return number of running sleeping and blocked threads.
int Execute(gmuint32 a_delta);
/// \brief GetTime() will return the machine time in milliseconds.
inline gmuint32 GetTime() const { return m_time; }
/// \brief Presize() will presize the pools
void Presize(int a_pool8,
int a_pool16,
int a_pool24,
int a_pool32,
int a_pool64,
int a_pool128,
int a_pool256,
int a_pool512,
int a_gmStringObj,
int a_gmTableObj,
int a_gmFunctionObj,
int a_gmUserObj,
int a_gmStackFrame
);
//
//
// Garbage Collection Interface
//
//
/// \brief EnableGC() allows you to turn garbage collection on and off.... useful for when you are allocating many
/// objects from c \ c++, but have not yet added them to a thread stack or referenced table \ object such that
/// they will be marked should the garbage collection fire. make sure you turn gc back on again!!!
inline void EnableGC(bool a_state = true) { m_gcEnabled = a_state; }
inline bool IsGCEnabled() const { return m_gcEnabled; }
/// \brief CollectGarbage() will perform a garbage collection sweep iff the desired byte memory usage is less than
/// the current memory usage.
/// NOTE: If incremental collector is used, this may perform a FULL collect immediately.
/// \param a_forceFullCollect Force full collection immediately if garbage collection is not disabled
/// \return true if the garbage collector was run.
bool CollectGarbage(bool a_forceFullCollect = false);
/// \brief GetCurrentMemoryUsage() will return the number of bytes currently allocated by the system.
inline int GetCurrentMemoryUsage() const { return m_currentMemoryUsage + m_fixedSet.GetMemUsed(); }
/// \brief Set the machine to run a gc sweep whenever memory exceeds this limit.
inline void SetDesiredByteMemoryUsageHard(int a_desiredByteMemoryUsageHard);
/// \brief Set the machine to run a gc sweep whenever memory exceeds this limit.
inline void SetDesiredByteMemoryUsageSoft(int a_desiredByteMemoryUsageSoft);
/// \brief Return the number of bytes currently allocated by the system.
inline int GetDesiredByteMemoryUsageHard() const { return m_desiredByteMemoryUsageHard; }
/// \brief Return the number of bytes currently allocated by the system.
inline int GetDesiredByteMemoryUsageSoft() const { return m_desiredByteMemoryUsageSoft; }
/// \brief SetAutoMemoryUsage() will enable auto adjustment of the memory limit occuring with subsequent garbage collects.
inline void SetAutoMemoryUsage(bool a_enableAutoAdjust);
/// \brief Is automatic memory limit calculation enabled?
inline bool GetAutoMemoryUsage() const { return m_autoMem; }
/// \brief GetSystemMemUsed will return the number of bytes allocated by the system. This is slow, call for debug only
unsigned int GetSystemMemUsed() const;
/// \brief Adjust the amount of memory known to be used by the machine.
/// Use this function carefully with user types to allow the machine
/// to consider external memory allocated or freed in association
/// with a user type that will be garbage collected by the machine.
/// Make sure the function is called as positive on the alloc and negative on the free.
inline void AdjustKnownMemoryUsed(int a_amountUsedOrFreed) { m_currentMemoryUsage += a_amountUsedOrFreed; }
/// \brief RegisterUserCallbacks() will register user type garbage collect methods.
#if GM_USE_INCGC
void RegisterUserCallbacks(gmType a_type, gmGCTraceCallback a_gcTrace, gmGCDestructCallback a_gcDestruct, gmAsStringCallback a_asString = NULL);
/// \brief GetUserMarkCallback() will return the gc mark call back for a user type
inline gmGCTraceCallback GetUserGCTraceCallback(gmType a_type) const { return m_types[a_type].m_gcTrace; }
/// \brief GetUserGCCallback() will return the gc destruct call back for a user type
inline gmGCDestructCallback GetUserGCDestructCallback(gmType a_type) const { return m_types[a_type].m_gcDestruct; }
#else //GM_USE_INCGC
void RegisterUserCallbacks(gmType a_type, gmGarbageCollectCallback a_mark, gmGarbageCollectCallback a_gc, gmAsStringCallback a_asString = NULL);
/// \brief GetUserMarkCallback() will return the gc mark call back for a user type
inline gmGarbageCollectCallback GetUserMarkCallback(gmType a_type) const { return m_types[a_type].m_mark; }
/// \brief GetUserGCCallback() will return the gc destruct call back for a user type
inline gmGarbageCollectCallback GetUserGCCallback(gmType a_type) const { return m_types[a_type].m_gc; }
#endif //GM_USE_INCGC
/// \brief Return the callback associated with a_type
inline gmAsStringCallback GetUserAsStringCallback(gmType a_type) const { return m_types[a_type].m_asString; }
//
//
// Object Interface
//
//
/// \brief GetGlobals() will return the gmTableObject used to store global variables. note that global variables
/// are common to all threads.
inline gmTableObject * GetGlobals() { return m_global; }
inline gmTableObject * GetTrueGlobals() { return m_trueGlobal; }
void SetGlobals( gmTableObject *pNewGlobals ) { m_global = pNewGlobals; }
/// \brief GetObject() will convert a gmptr (machine pointer size int) into an object pointer. use this whenever
/// converting from a gmVariable m_value.m_ref to an object.
inline gmObject * GetObject(gmptr a_ref);
/// \brief AllocStringObject() will create a constant string object from the unique string pool.
/// \param a_length is the string length not including '\0' terminator, (-1) if unknown
gmStringObject * AllocStringObject(const char * a_string, int a_length = -1);
/// \brief AllocPermanantStringObject() will create a constant string object from the unique string pool. this
/// string will not be garbage collected. (m_mark == GM_PERSIST)
/// \param a_length is the string length not including '\0' terminator, (-1) if unknown
gmStringObject * AllocPermanantStringObject(const char * a_string, int a_length = -1);
/// \brief AllocTableObject() will create a new empty table.
gmTableObject * AllocTableObject();
/// \brief AllocFunctionObject() will create a new function.
gmFunctionObject * AllocFunctionObject(gmCFunction a_function = NULL);
/// \brief AllocUserObject() will create a new user object.
/// \param a_user is a hook to tie the user object to any system.
/// \param a_userType is the user type user for operator binding etc.
/// \sa CreateUserType()
gmUserObject * AllocUserObject(void * a_user, int a_userType);
//
//
// Debug Interface
//
//
void * m_debugUser; ///< user hook for debugger
/// \brief SetDebugMode() will compile byte code with debug info.
/// This includes storing the source code in the gmMachine, and compiling line and symbol information
/// into the gmFunctionObjects.
inline void SetDebugMode(bool a_debug) { m_debug = a_debug; }
/// \brief GetDebugMode()
inline bool GetDebugMode() const { return m_debug; }
/// \brief AddSourceCode() will add source code to the machine, and return a unique id.
/// This is used when debug mode is set so the remote debugger can retrieve source as needed
/// for debugging.
/// \return a unique id for the source code, or 0 on error.
gmuint32 AddSourceCode(const char * a_source, const char * a_filename);
/// \brief GetSourceCode() will get source code debugging purposes.
/// \return true if the source was found, a_source and a_filename are set to the source and filname for the unique id.
bool GetSourceCode(gmuint32 a_id, const char * &a_source, const char * &a_filename);
/// \brief GetInstructionAtBreakPoint will return the insturction at the given break point, or NULL if the break
/// point could not be found. only works in debug mode. This is quite slow.
const void * GetInstructionAtBreakPoint(gmuint32 a_sourceId, int a_line);
// debugger hooks
gmDebugLineCallback m_line;
gmDebugCallCallback m_call;
gmDebugRetCallback m_return;
gmDebugIsBrokenCallback m_isBroken;
//
//
// Implementation allocators
//
//
inline gmStackFrame * Sys_AllocStackFrame() { return (gmStackFrame *) m_memStackFrames.Alloc(); }
inline void Sys_FreeStackFrame(gmStackFrame * a_frame) { m_memStackFrames.Free(a_frame); }
void Sys_FreeUniqueString(const char * a_string);
inline void * Sys_Alloc(int a_size);
inline void Sys_Free(void * a_mem) { m_fixedSet.Free(a_mem); }
void Sys_RemoveBlocks(gmThread * a_thread);
void Sys_RemoveSignals(gmThread * a_thread);
void Sys_SignalCreateThread(gmThread * a_thread);
#if GM_USE_INCGC
static void GM_CDECL ScanRootsCallBack(gmMachine* a_machine, gmGarbageCollector* a_gc);
inline gmGarbageCollector* GetGC() {GM_ASSERT(m_gc); return m_gc;}
inline void DestructDeleteObject(gmObject* a_object) {FreeObject(a_object);}
gmGarbageCollector* m_gc;
#endif //GM_USE_INCGC
inline int GetStatsGCNumFullCollects() { return m_statsGCFullCollect; }
inline int GetStatsGCNumIncCollects() { return m_statsGCIncCollect; }
inline int GetStatsGCNumWarnings() { return m_statsGCWarnings; }
/// \brief Is GC actually running a cycle
bool IsGCRunning();
/// \brief Add a CPP owned gmObject so machine knows about it to handle GC.
void AddCPPOwnedGMObject(gmObject * a_obj);
/// \brief Remove a CPP owned gmObject.
void RemoveCPPOwnedGMObject(gmObject * a_obj);
/// \brief Check if gmObject is in CPP owned list
bool IsCPPOwnedGMObject(gmObject * a_obj);
/// \brief Remove all CPP owned gmObjects.
void RemoveAllCPPOwnedGMObjects();
protected:
// Threads
int m_threadId; ///< cycling thread number
gmListDouble<gmThread> m_runningThreads;
gmListDouble<gmThread> m_blockedThreads;
gmListDouble<gmThread> m_sleepingThreads; ///< sorted by timez
gmListDouble<gmThread> m_killedThreads;
gmListDouble<gmThread> m_exceptionThreads; ///< dead threads, hanging around for debugging
gmHash<int, gmThread> m_threads;
int GetThreadId();
gmuint32 m_time; ///< machine time in milliseconds. (gives us 50 days)
gmThread * m_nextThread; ///< Set when cycling through threads, allows remove during iteration
bool m_nextThreadValid; ///< Set to true when m_nextThread is in use, even if it is null, which occurs on last thread.
// Objects
void FreeObject(gmObject * a_obj); ///< FreeObject() does not Destruct the object.
gmObject * CheckReference(gmptr a_ref);
gmTableObject * m_global; ///< global variables
gmTableObject * m_trueGlobal;
gmObject * m_objects; ///< list of all objects
// Allocators
gmMemFixed m_memStringObj; ///< memory for String objects
gmMemFixed m_memTableObj; ///< memory for Table objects
gmMemFixed m_memFunctionObj; ///< memory for Function objects
gmMemFixed m_memUserObj; ///< memory for User objects
gmMemFixed m_memStackFrames; ///< memory for stack frame structures
gmMemFixedSet m_fixedSet; ///< string and small variable sized stuff allocator.
// Garbage Collection
int m_desiredByteMemoryUsageHard; ///< The hard upper memory limit
int m_desiredByteMemoryUsageSoft; ///< The soft limit where incremental GC may start
int m_currentMemoryUsage; ///< Current (known) used memory
bool m_autoMem; ///< Automatically adjust memory limit(s)
gmuint32 m_mark; ///< The mark phase Id for atomic GC
bool m_gcEnabled; ///< GC enabled/disabled
int m_framesSinceLastIncCollect; ///< number of frames or oportunities since last inc GC end
int m_gcPhaseCount; ///< GC phase, 2 phases required for full GC
int m_statsGCFullCollect; ///< How many times a full collect has occured
int m_statsGCIncCollect; ///< How many times incremental collect has started
int m_statsGCWarnings; ///< The incGC thinks it is being used inefficiently. It this number is large and growing rapidly the hard and soft limits may need calibrating.
// String Table
gmHash<const char *, gmStringObject> m_strings;
// Types
class Type
{
public:
gmStringObject * m_name; ///< type name
gmOperatorFunction m_nativeOperators[O_MAXOPERATORS]; ///< stack operators
gmptr m_operators[O_MAXOPERATORS]; ///< slow script call operators
gmTableObject * m_variables; ///< user type variables
#if GM_USE_INCGC
gmGCDestructCallback m_gcDestruct; ///< user type gc destruct callback
gmGCTraceCallback m_gcTrace; ///< user type gc trace callback
#else //GM_USE_INCGC
gmGarbageCollectCallback m_mark; ///< user type gc mark callback
gmGarbageCollectCallback m_gc; ///< user type gc callback
#endif //GM_USE_INCGC
gmAsStringCallback m_asString; ///< user type AsString callback
void Init();
};
gmArraySimple<Type> m_types; ///< Variable types
#if GM_USE_INCGC
#if !GC_KEEP_PERSISTANT_SEPARATE
gmArraySimple<gmObject*> m_permanantStrings;
#endif //!GC_KEEP_PERSISTANT_SEPARATE
#endif //GM_USE_INCGC
void ResetDefaultTypes();
// Blocking
gmHash<gmVariable, gmBlockList, gmVariable> m_blocks; // current registered blocks.
// C++ owned gmObjects
class ObjHashNode : public gmHashNode<gmObject *, ObjHashNode>
{
public:
gmObject * m_obj;
const gmObject * GetKey() const { return m_obj; }
};
gmHash<gmObject*, ObjHashNode> m_cppOwnedGMObjs; ///< cpp owned gmObjects
// Debugging
bool m_debug;
gmListDouble<gmSourceEntry> m_source;
gmLog m_log;
};
//
//
// INLINE IMPLEMENTATION
//
//
inline void gmMachine::SetDesiredByteMemoryUsageHard(int a_desiredByteMemoryUsageHard)
{
m_desiredByteMemoryUsageHard = a_desiredByteMemoryUsageHard;
}
inline void gmMachine::SetDesiredByteMemoryUsageSoft(int a_desiredByteMemoryUsageSoft)
{
m_desiredByteMemoryUsageSoft = a_desiredByteMemoryUsageSoft;
}
inline void gmMachine::SetAutoMemoryUsage(bool a_enableAutoAdjust)
{
m_autoMem = a_enableAutoAdjust;
}
inline gmObject * gmMachine::GetObject(gmptr a_ref)
{
#if GMMACHINE_SUPERPARANOIDGC
return CheckReference(a_ref);
#else // GMMACHINE_SUPERPARANOIDGC
return (gmObject *) a_ref;
#endif // GMMACHINE_SUPERPARANOIDGC
}
inline gmVariable gmMachine::GetTypeVariable(gmType a_type, const gmVariable &a_key) const
{
return m_types[a_type].m_variables->Get(a_key);
}
inline gmOperatorFunction gmMachine::GetTypeNativeOperator(gmType a_type, gmOperator a_operator)
{
return m_types[a_type].m_nativeOperators[a_operator];
}
inline gmFunctionObject * gmMachine::GetTypeOperator(gmType a_type, gmOperator a_operator)
{
return (gmFunctionObject *) GetObject(m_types[a_type].m_operators[a_operator]);
}
inline void * gmMachine::Sys_Alloc(int a_size)
{
return m_fixedSet.Alloc(a_size);
}
#endif // _GMMACHINE_H_
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,21 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#ifndef _GMMACHINELIB_H_
#define _GMMACHINELIB_H_
class gmMachine;
#define GM_STATE_NUM_PARAMS 1 // requried for gmThread::PushStackFrame() for state(fp) implementation
void gmMachineLib(gmMachine * a_machine);
#endif // _GMMACHINELIB_H_
+16
View File
@@ -0,0 +1,16 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#include "gmConfig.h"
#include "gmMem.h"
// Must be last header
#include "memdbgon.h"
+71
View File
@@ -0,0 +1,71 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#ifndef _GMMEM_H_
#define _GMMEM_H_
// include malloc and alloca if required within gmconfig_p.h
#include "gmConfig.h"
// define _gmDumpLeaks within gmconfig_p.h to dump memory leaks
#ifndef _gmDumpLeaks
#define _gmDumpLeaks()
#endif
/// \brief Align pointer
#define _gmAlignMem(PTR, ALIGN) (void*)(((gmuptr)(PTR) + (ALIGN) - 1) & ~((ALIGN)-1))
/// \brief gmConstructElement will construct a single object at location
template<class TYPE>
inline TYPE* gmConstructElement(TYPE* a_element)
{
return (TYPE*) GM_PLACEMENT_NEW( TYPE, (void*)a_element );
}
/// \brief gmDestructElement will destruct a single object at location
template<class TYPE>
inline void gmDestructElement(TYPE* a_element)
{
a_element->~TYPE();
}
/// \brief gmConstructElements will construct multiple objects at location
template<class TYPE>
inline void gmConstructElements(TYPE* a_elements, int a_count)
{
// Call the constructor(s)
for (; a_count--; a_elements++)
{
GM_PLACEMENT_NEW( TYPE, (void*)a_elements );
}
}
/// \brief gmDestructElements will destruct multiple objects at location
template<class TYPE>
inline void gmDestructElements(TYPE* a_elements, int a_count)
{
// Call the destructor(s)
for (; a_count--; a_elements++)
{
a_elements->~TYPE();
}
}
#endif // _GMMEM_H_
+238
View File
@@ -0,0 +1,238 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#include "gmConfig.h"
#include "gmMemChain.h"
#include "gmMem.h"
// Must be last header
#include "memdbgon.h"
gmMemChain::gmMemChain(unsigned int a_elementSize, unsigned int a_numElementsInChunk)
{
m_chunkSize = a_numElementsInChunk * a_elementSize;
m_elementSize = a_elementSize;
m_rootChunk = NULL;
m_currentChunk = NULL;
}
gmMemChain::~gmMemChain()
{
FreeChunks();
}
void gmMemChain::FreeChunks()
{
if(m_rootChunk)
{
MemChunk *curChunk;
curChunk = m_rootChunk;
while(curChunk)
{
MemChunk *chunkToFree;
chunkToFree = curChunk;
curChunk = curChunk->m_nextChunk;
delete [] (char*)chunkToFree;
}
}
}
void gmMemChain::ResetAndFreeMemory()
{
//Actually free the memory
FreeChunks();
//Clear pointers to chunk chain
m_rootChunk = NULL;
m_currentChunk = NULL;
}
void gmMemChain::Presize(int a_kbytes)
{
int numChunks = ((a_kbytes + 1) * 1024) / m_chunkSize;
MemChunk *curChunk;
curChunk = m_rootChunk;
while(curChunk)
{
--numChunks;
curChunk = curChunk->m_nextChunk;
}
MemChunk * currentChunk = m_currentChunk;
while(numChunks > 0)
{
NewChunk();
--numChunks;
}
if(currentChunk)
{
m_currentChunk = currentChunk;
}
}
gmMemChain::MemChunk* gmMemChain::NewChunk()
{
MemChunk* newChunk;
//Is an unused chunk already allocated?
if(m_currentChunk && m_currentChunk->m_nextChunk)
{
newChunk = m_currentChunk->m_nextChunk;
newChunk->m_curAddress = newChunk->m_minAddress;
m_currentChunk = newChunk;
}
else //No, allocate a new one
{
char * mem = (char *) GM_NEW( char[sizeof(MemChunk) + m_chunkSize] );
newChunk = (MemChunk *) mem;
//Allocate memory and set address space
newChunk->m_minAddress = mem + sizeof(MemChunk);
newChunk->m_lastAddress = (void*)((unsigned int)newChunk->m_minAddress + m_chunkSize);
newChunk->m_curAddress = newChunk->m_minAddress;
//Link new chunk to chain
newChunk->m_lastChunk = m_currentChunk;
newChunk->m_nextChunk = NULL;
if(m_currentChunk)
{
m_currentChunk->m_nextChunk = newChunk;
}
m_currentChunk = newChunk;
if(!m_rootChunk)
{
m_rootChunk = m_currentChunk;
}
}
return newChunk;
}
void* gmMemChain::Alloc()
{
void *retPtr;
//Allocate first chunk if none exist
if(!m_rootChunk)
{
NewChunk();
}
//Advance ptr, allocate new chunk if necessary
if(((gmuptr)m_currentChunk->m_curAddress + m_elementSize) > (gmuptr)m_currentChunk->m_lastAddress)
{
NewChunk();
}
retPtr = m_currentChunk->m_curAddress;
m_currentChunk->m_curAddress = (void*)((gmuptr)m_currentChunk->m_curAddress + m_elementSize);
return retPtr;
}
void* gmMemChain::AllocBytes(unsigned int a_numBytes, unsigned int a_alignNumBytes)
{
GM_ASSERT((a_numBytes + a_alignNumBytes) <= m_chunkSize); // Chunk size is too small to alloc that many elements at once and should be increased
void *retPtr;
//Allocate first chunk if none exist
if(!m_rootChunk)
{
NewChunk();
}
//Align and record the pointer.
retPtr = _gmAlignMem(m_currentChunk->m_curAddress, a_alignNumBytes);
//Create new chunk if not enough memory in this one.
if(((gmuptr)retPtr + a_numBytes) > (gmuptr)m_currentChunk->m_lastAddress)
{
NewChunk();
//Align and record the pointer.
retPtr = _gmAlignMem(m_currentChunk->m_curAddress, a_alignNumBytes);
}
//Advance memory ptr for next allocation.
m_currentChunk->m_curAddress = (void*)((gmuptr)retPtr + a_numBytes);
return retPtr;
}
void* gmMemChain::Alloc(unsigned int a_numElementsToAlloc)
{
unsigned int allocSize;
void *retPtr;
//Allocate first chunk if none exist
if(!m_rootChunk)
{
NewChunk();
}
//Advance ptr, allocate new chunk if necessary
allocSize = a_numElementsToAlloc * m_elementSize;
GM_ASSERT(allocSize <= m_chunkSize); // Chunk size is too small to alloc that many elements at once and should be increased
if(((unsigned int)m_currentChunk->m_curAddress + allocSize) > (unsigned int)m_currentChunk->m_lastAddress)
{
NewChunk();
}
retPtr = m_currentChunk->m_curAddress;
m_currentChunk->m_curAddress = (void*)((unsigned int)m_currentChunk->m_curAddress + allocSize);
return retPtr;
}
void gmMemChain::Reset()
{
m_currentChunk = m_rootChunk;
if(m_currentChunk)
{
m_currentChunk->m_curAddress = m_currentChunk->m_minAddress;
}
}
unsigned int gmMemChain::GetSystemMemUsed() const
{
MemChunk * chunk = m_rootChunk;
unsigned int total = 0;
while(chunk)
{
total += m_chunkSize;
chunk = chunk->m_nextChunk;
}
return total;
}
+71
View File
@@ -0,0 +1,71 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#ifndef _GMMEMCHAIN_H_
#define _GMMEMCHAIN_H_
#include "gmConfig.h"
/// \class gmMemChain
/// \brief gmMemChain is a simple memory allocator that allows many small allocations from one heap allocation.
class gmMemChain
{
public:
gmMemChain(unsigned int a_elementSize, unsigned int a_numElementsInChunk);
virtual ~gmMemChain();
/// \brief Alloc()
void* Alloc(unsigned int a_numElements);
void* Alloc();
/// \brief Alloc memory
/// \param a_numBytes Number of bytes ot allocate.
/// \param a_alignNumBytes Number of bytes to align to.
void* AllocBytes(unsigned int a_numBytes, unsigned int a_alignNumBytes = 1);
/// \brief Reset()
void Reset();
/// \brief ResetAndFreeMemory()
void ResetAndFreeMemory();
/// \brief Presize
void Presize(int a_kbytes);
/// \brief GetElementSize()
inline unsigned int GetElementSize() { return m_elementSize; }
/// \brief GetSystemMemUsed will return the number of bytes allocated by the system.
unsigned int GetSystemMemUsed() const;
protected:
struct MemChunk
{
MemChunk* m_nextChunk;
MemChunk* m_lastChunk;
void* m_curAddress;
void* m_minAddress;
void* m_lastAddress;
};
unsigned int m_chunkSize; //!< Size of memory chunk
unsigned int m_elementSize; //!< Size of element
MemChunk* m_rootChunk; //!< First chunk in chain
MemChunk* m_currentChunk; //!< Current chunk alloc's use
inline MemChunk* NewChunk();
inline void FreeChunks();
};
#endif // _GMMEMCHAIN_H_
@@ -0,0 +1,17 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#include "gmConfig.h"
#include "gmMemFixed.h"
// Must be last header
#include "memdbgon.h"
+182
View File
@@ -0,0 +1,182 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#ifndef _GMMEMFIXED_H_
#define _GMMEMFIXED_H_
#include "gmMemChain.h"
/// \class gmMemFixed
/// \brief Fixed memory allocator, wrapper on chain memory allocator to provide memory reuse.
/// Performance note: use can cause more random memory access
class gmMemFixed
{
public:
inline gmMemFixed(unsigned int a_elementSize, unsigned int a_growSize = 64);
inline ~gmMemFixed();
/// \brief Alloc() an element
inline void* Alloc();
/// \brief Free() an element
inline void Free(void* a_ptr);
/// \brief Reset()
inline void Reset();
/// \brief ResetAndFreeMemory()
inline void ResetAndFreeMemory();
/// \brief Presize
inline void Presize(int a_kbytes) { m_memChain.Presize(a_kbytes); }
inline unsigned int GetElementSize();
/// \brief GetSystemMemUsed will return the number of bytes allocated by the system.
inline unsigned int GetSystemMemUsed() const { return m_memChain.GetSystemMemUsed(); }
#ifdef GM_DEBUG_BUILD
/// \brief GetMemUsed()
inline unsigned int GetMemUsed() const { return m_memUsed; }
#endif // GM_DEBUG_BUILD
protected:
// Free list node structure to simplify coding of recycle list.
struct FreeListNode
{
FreeListNode * m_next;
};
FreeListNode* m_freeList; //!< List of memory block we can reuse
gmMemChain m_memChain; //!< The chain memory used to actually allocate chunks
#ifdef GM_DEBUG_BUILD
int m_memUsed;
#endif // GM_DEBUG_BUILD
};
gmMemFixed::gmMemFixed(unsigned int a_elementSize, unsigned int a_growSize)
: m_memChain(a_elementSize, a_growSize)
{
GM_ASSERT(a_elementSize >= sizeof(FreeListNode));
m_freeList = NULL;
#ifdef GM_DEBUG_BUILD
m_memUsed = 0;
#endif // GM_DEBUG_BUILD
}
gmMemFixed::~gmMemFixed()
{
ResetAndFreeMemory();
}
void* gmMemFixed::Alloc()
{
void* newMemPtr;
//Is one available on the free list?
newMemPtr = m_freeList;
if(m_freeList)
{
m_freeList = m_freeList->m_next;
}
else
{
//No, so get chain to alloc a new one
newMemPtr = m_memChain.Alloc();
}
#ifdef GM_DEBUG_BUILD
m_memUsed += m_memChain.GetElementSize();
#endif // GM_DEBUG_BUILD
#if 0
// clear new mem pointer to 0xB00BFEED
int * n = (int *) newMemPtr;
int c = m_memChain.GetElementSize() / sizeof(int);
while(c--) *(n++) = 0xB00BFEED;
#endif
return newMemPtr;
}
void gmMemFixed::Free(void* a_ptr)
{
#if 0
// make sure a_ptr is not already in list (freeing something twice)
FreeListNode * node = m_freeList;
while(node)
{
GM_ASSERT(a_ptr != node);
node = node->m_next;
}
#endif
if(a_ptr)
{
#if 0
// clear new mem pointer to 0xFEEDFACE
int * n = (int *) a_ptr;
int c = m_memChain.GetElementSize() / sizeof(int);
while(c--) *(n++) = 0xFEEDFACE;
#endif
//Add pointer to free list so we can reuse it
((FreeListNode*)a_ptr)->m_next = m_freeList;
m_freeList = (FreeListNode*)a_ptr;
#ifdef GM_DEBUG_BUILD
m_memUsed -= m_memChain.GetElementSize();
GM_ASSERT(m_memUsed >= 0);
#endif // GM_DEBUG_BUILD
}
}
void gmMemFixed::ResetAndFreeMemory()
{
m_freeList = NULL;
#ifdef GM_DEBUG_BUILD
m_memUsed = 0;
#endif // GM_DEBUG_BUILD
m_memChain.ResetAndFreeMemory();
}
void gmMemFixed::Reset()
{
m_freeList = NULL;
#ifdef GM_DEBUG_BUILD
m_memUsed = 0;
#endif // GM_DEBUG_BUILD
m_memChain.Reset();
}
unsigned int gmMemFixed::GetElementSize()
{
return (unsigned int)m_memChain.GetElementSize();
}
#endif // _GMMEMFIXED_H_
@@ -0,0 +1,140 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#include "gmConfig.h"
#include "gmMemFixedSet.h"
// Must be last header
#include "memdbgon.h"
void gmMemFixedSet::Reset()
{
m_mem8.Reset();
m_mem16.Reset();
m_mem24.Reset();
m_mem32.Reset();
m_mem64.Reset();
m_mem128.Reset();
m_mem256.Reset();
m_mem512.Reset();
FreeBigAllocs(); //This actually frees the big fellas
}
void gmMemFixedSet::ResetAndFreeMemory()
{
m_mem8.ResetAndFreeMemory();
m_mem16.ResetAndFreeMemory();
m_mem24.ResetAndFreeMemory();
m_mem32.ResetAndFreeMemory();
m_mem64.ResetAndFreeMemory();
m_mem128.ResetAndFreeMemory();
m_mem256.ResetAndFreeMemory();
m_mem512.ResetAndFreeMemory();
FreeBigAllocs();
}
unsigned int gmMemFixedSet::GetSystemMemUsed() const
{
unsigned int total = 0;
total += m_mem8.GetSystemMemUsed();
total += m_mem16.GetSystemMemUsed();
total += m_mem24.GetSystemMemUsed();
total += m_mem32.GetSystemMemUsed();
total += m_mem64.GetSystemMemUsed();
total += m_mem128.GetSystemMemUsed();
total += m_mem256.GetSystemMemUsed();
total += m_mem512.GetSystemMemUsed();
BigMemNode* curNode = m_bigAllocs.GetFirst();
while(m_bigAllocs.IsValid(curNode))
{
total += curNode->m_size;
curNode = m_bigAllocs.GetNext(curNode);
}
return total;
}
void gmMemFixedSet::PrintStats() const
{
#ifdef GM_DEBUG_BUILD
int used;
const char * msg = "%d btye pool uses %dk of memory, thats %d allocations\n";
used = m_mem8.GetSystemMemUsed();
GM_PRINTF(msg, 8, used / 1024, used / 8);
used = m_mem16.GetSystemMemUsed();
GM_PRINTF(msg, 16, used / 1024, used / 16);
used = m_mem24.GetSystemMemUsed();
GM_PRINTF(msg, 24, used / 1024, used / 24);
used = m_mem32.GetSystemMemUsed();
GM_PRINTF(msg, 32, used / 1024, used / 32);
used = m_mem64.GetSystemMemUsed();
GM_PRINTF(msg, 64, used / 1024, used / 64);
used = m_mem128.GetSystemMemUsed();
GM_PRINTF(msg, 128, used / 1024, used / 128);
used = m_mem256.GetSystemMemUsed();
GM_PRINTF(msg, 256, used / 1024, used / 256);
used = m_mem512.GetSystemMemUsed();
GM_PRINTF(msg, 512, used / 1024, used / 512);
#endif //GM_DEBUG_BUILD
}
void gmMemFixedSet::Presize(int a_pool8,
int a_pool16,
int a_pool24,
int a_pool32,
int a_pool64,
int a_pool128,
int a_pool256,
int a_pool512
)
{
if( a_pool8 ) { m_mem8.Presize(a_pool8); }
if( a_pool16 ) { m_mem16.Presize(a_pool16); }
if( a_pool24 ) { m_mem24.Presize(a_pool24); }
if( a_pool32 ) { m_mem32.Presize(a_pool32); }
if( a_pool64 ) { m_mem64.Presize(a_pool64); }
if( a_pool128 ) { m_mem128.Presize(a_pool128); }
if( a_pool256 ) { m_mem256.Presize(a_pool256); }
if( a_pool512 ) { m_mem512.Presize(a_pool512); }
}
void gmMemFixedSet::FreeBigAllocs()
{
BigMemNode* curNode = m_bigAllocs.GetFirst();
while(m_bigAllocs.IsValid(curNode))
{
BigMemNode* nodeToDelete = curNode;
curNode = m_bigAllocs.GetNext(curNode);
delete [] (char*)nodeToDelete;
}
m_bigAllocs.RemoveAll();
}
+258
View File
@@ -0,0 +1,258 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#ifndef _GMMEMFIXEDSET_H_
#define _GMMEMFIXEDSET_H_
#include "gmMemFixed.h"
#include "gmListDouble.h"
/// \class gmMemFixedSet
/// \brief Fixed memory allocator, Allows varying size allocations using a set of fixed allocators.
class gmMemFixedSet
{
public:
inline gmMemFixedSet();
inline ~gmMemFixedSet();
/// \brief Alloc() an element
/// \param a_size size of allocation
inline void* Alloc(int a_size);
/// \brief Free() an element
/// \param a_size size of allocation
inline void Free(void* a_ptr);
/// \brief Reset()
void Reset();
/// \brief ResetAndFreeMemory()
void ResetAndFreeMemory();
/// \brief GetMemUsed() will return the number of bytes allocated and returned, not
/// the total number of bytes allocated by the memFixedSet object.
inline unsigned int GetMemUsed() const { return m_memUsed; }
/// \brief GetSystemMemUsed will return the number of bytes allocated by the system.
unsigned int GetSystemMemUsed() const;
/// \brief Presize() will presize the memfixed pools
void Presize(int a_pool8,
int a_pool16,
int a_pool24,
int a_pool32,
int a_pool64,
int a_pool128,
int a_pool256,
int a_pool512
);
/// \brief PrintStats() will print out the stats for gm memory
void PrintStats() const;
protected:
/// \brief Internal data structure for small allocations
struct SmallMemNode
{
int m_size; ///< Allocation size
char* Data() {return (char*)(this + 1);} ///< Get ptr after this structure
};
/// \brief Internal data structure for large allocations
struct BigMemNode: public gmListDoubleNode<BigMemNode>
{
int m_size; ///< Allocation size
char* Data() {return (char*)(this + 1);} ///< Get ptr after this structure
};
void FreeBigAllocs(); ///< Free the big allocations
inline SmallMemNode* GetSmallNodeData(void* a_ptr) { return ((SmallMemNode*)a_ptr)-1; }
inline BigMemNode* GetBigNodeData(void* a_ptr) { return ((BigMemNode*)a_ptr)-1; }
gmMemFixed m_mem8; ///< Memory for 8 bytes and less
gmMemFixed m_mem16; ///< Memory for 16 bytes and less
gmMemFixed m_mem24; ///< Memory for 24 bytes and less
gmMemFixed m_mem32; ///< Memory for 32 bytes and less
gmMemFixed m_mem64; ///< Memory for 64 bytes and less
gmMemFixed m_mem128; ///< Memory for 128 bytes and less
gmMemFixed m_mem256; ///< Memory for 256 bytes and less
gmMemFixed m_mem512; ///< Memory for 512 bytes and less
gmListDouble<BigMemNode> m_bigAllocs; ///< List holding memory for more than 512 bytes
int m_memUsed;
};
gmMemFixedSet::gmMemFixedSet()
: m_mem8(8 + sizeof(SmallMemNode), 64),
m_mem16(16 + sizeof(SmallMemNode), 64),
m_mem24(24 + sizeof(SmallMemNode), 64),
m_mem32(32 + sizeof(SmallMemNode), 64),
m_mem64(64 + sizeof(SmallMemNode), 32),
m_mem128(128 + sizeof(SmallMemNode), 32),
m_mem256(256 + sizeof(SmallMemNode), 16),
m_mem512(512 + sizeof(SmallMemNode), 16)
{
m_memUsed = 0;
}
gmMemFixedSet::~gmMemFixedSet()
{
FreeBigAllocs();
}
void* gmMemFixedSet::Alloc(int a_size)
{
SmallMemNode* node;
if (a_size <= 32)
{
if (a_size <= 8)
{
node = (SmallMemNode*)m_mem8.Alloc();
node->m_size = 8;
m_memUsed += 8;
}
else if (a_size <= 16)
{
node = (SmallMemNode*)m_mem16.Alloc();
node->m_size = 16;
m_memUsed += 16;
}
else if (a_size <= 24)
{
node = (SmallMemNode*)m_mem24.Alloc();
node->m_size = 24;
m_memUsed += 24;
}
else // if (a_size <= 32)
{
GM_ASSERT(a_size <= 32);
node = (SmallMemNode*)m_mem32.Alloc();
node->m_size = 32;
m_memUsed += 32;
}
}
else
{
if (a_size <= 64)
{
node = (SmallMemNode*)m_mem64.Alloc();
node->m_size = 64;
m_memUsed += 64;
}
else if (a_size <= 128)
{
node = (SmallMemNode*)m_mem128.Alloc();
node->m_size = 128;
m_memUsed += 128;
}
else if (a_size <= 256)
{
node = (SmallMemNode*)m_mem256.Alloc();
node->m_size = 256;
m_memUsed += 256;
}
else if (a_size <= 512)
{
node = (SmallMemNode*)m_mem512.Alloc();
node->m_size = 512;
m_memUsed += 512;
}
else
{
BigMemNode* bigNode;
bigNode = (BigMemNode*)GM_NEW( char[a_size + sizeof(*bigNode)] ); // This will be aligned as it calls sys new
m_bigAllocs.InsertFirst(bigNode);
bigNode->m_size = a_size;
m_memUsed += a_size;
return bigNode->Data();
}
}
return node->Data();
}
void gmMemFixedSet::Free(void* a_ptr)
{
SmallMemNode* node = GetSmallNodeData(a_ptr);
int size = node->m_size;
if (size <= 32)
{
if (size == 8)
{
m_mem8.Free(node);
m_memUsed -= 8;
}
else if (size == 16)
{
m_mem16.Free(node);
m_memUsed -= 16;
}
else if (size == 24)
{
m_mem24.Free(node);
m_memUsed -= 24;
}
else // if (size == 32)
{
GM_ASSERT(size == 32);
m_mem32.Free(node);
m_memUsed -= 32;
}
}
else
{
if (size == 64)
{
m_mem64.Free(node);
m_memUsed -= 64;
}
else if (size == 128)
{
m_mem128.Free(node);
m_memUsed -= 128;
}
else if (size == 256)
{
m_mem256.Free(node);
m_memUsed -= 256;
}
else if (size == 512)
{
m_mem512.Free(node);
m_memUsed -= 512;
}
else
{
BigMemNode* bigNode = GetBigNodeData(a_ptr);
m_memUsed -= bigNode->m_size;
m_bigAllocs.Remove(bigNode);
delete [] (char*) bigNode;
}
}
GM_ASSERT(m_memUsed >= 0);
}
#endif // _GMMEMFIXEDSET_H_
+603
View File
@@ -0,0 +1,603 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#include "gmConfig.h"
#include "gmOperators.h"
#include "gmThread.h"
#include "gmStringObject.h"
//#include <math.h>
// Must be last header
#include "memdbgon.h"
const char * gmGetOperatorName(gmOperator a_operator)
{
switch(a_operator)
{
case O_GETDOT : return "getdot";
case O_SETDOT : return "setdot";
case O_GETIND : return "getind";
case O_SETIND : return "setind";
case O_ADD : return "add";
case O_SUB : return "sub";
case O_MUL : return "mul";
case O_DIV : return "div";
case O_REM : return "mod";
case O_BIT_OR : return "bitor";
case O_BIT_XOR : return "bitxor";
case O_BIT_AND : return "bitand";
case O_BIT_SHIFTLEFT : return "shiftleft";
case O_BIT_SHIFTRIGHT : return "shiftright";
case O_BIT_INV : return "bitinv";
case O_LT : return "lt";
case O_GT : return "gt";
case O_LTE : return "lte";
case O_GTE : return "gte";
case O_EQ : return "eq";
case O_NEQ : return "neq";
case O_NEG : return "neg";
case O_POS : return "pos";
case O_NOT : return "not";
#if GM_BOOL_OP
case O_BOOL : return "bool";
#endif // GM_BOOL_OP
default :;
}
return "undefined";
}
gmOperator gmGetOperator(const char * a_operatorName)
{
if(_gmstricmp(a_operatorName, "getdot") == 0) return O_GETDOT;
if(_gmstricmp(a_operatorName, "setdot") == 0) return O_SETDOT;
if(_gmstricmp(a_operatorName, "getind") == 0) return O_GETIND;
if(_gmstricmp(a_operatorName, "setind") == 0) return O_SETIND;
if(_gmstricmp(a_operatorName, "add") == 0) return O_ADD;
if(_gmstricmp(a_operatorName, "sub") == 0) return O_SUB;
if(_gmstricmp(a_operatorName, "mul") == 0) return O_MUL;
if(_gmstricmp(a_operatorName, "div") == 0) return O_DIV;
if(_gmstricmp(a_operatorName, "mod") == 0) return O_REM;
if(_gmstricmp(a_operatorName, "bitor") == 0) return O_BIT_OR;
if(_gmstricmp(a_operatorName, "bitxor") == 0) return O_BIT_XOR;
if(_gmstricmp(a_operatorName, "bitand") == 0) return O_BIT_AND;
if(_gmstricmp(a_operatorName, "shiftleft") == 0) return O_BIT_SHIFTLEFT;
if(_gmstricmp(a_operatorName, "shiftright") == 0) return O_BIT_SHIFTRIGHT;
if(_gmstricmp(a_operatorName, "bitinv") == 0) return O_BIT_INV;
if(_gmstricmp(a_operatorName, "lt") == 0) return O_LT;
if(_gmstricmp(a_operatorName, "gt") == 0) return O_GT;
if(_gmstricmp(a_operatorName, "lte") == 0) return O_LTE;
if(_gmstricmp(a_operatorName, "gte") == 0) return O_GTE;
if(_gmstricmp(a_operatorName, "eq") == 0) return O_EQ;
if(_gmstricmp(a_operatorName, "neq") == 0) return O_NEQ;
if(_gmstricmp(a_operatorName, "neg") == 0) return O_NEG;
if(_gmstricmp(a_operatorName, "pos") == 0) return O_POS;
if(_gmstricmp(a_operatorName, "not") == 0) return O_NOT;
#if GM_BOOL_OP
if(_gmstricmp(a_operatorName, "bool") == 0) return O_BOOL;
#endif // GM_BOOL_OP
return O_MAXOPERATORS;
}
//
// GM_NULL
//
//
// GM_INT
//
void GM_CDECL gmIntOpAdd(gmThread * a_thread, gmVariable * a_operands)
{
a_operands[0].m_value.m_int += a_operands[1].m_value.m_int;
}
void GM_CDECL gmIntOpSub(gmThread * a_thread, gmVariable * a_operands)
{
a_operands[0].m_value.m_int -= a_operands[1].m_value.m_int;
}
void GM_CDECL gmIntOpMul(gmThread * a_thread, gmVariable * a_operands)
{
a_operands[0].m_value.m_int *= a_operands[1].m_value.m_int;
}
void GM_CDECL gmIntOpDiv(gmThread * a_thread, gmVariable * a_operands)
{
#if GMMACHINE_GMCHECKDIVBYZERO
if(a_operands[1].m_value.m_int != 0)
{
a_operands[0].m_value.m_int /= a_operands[1].m_value.m_int;
}
else
{
a_thread->GetMachine()->GetLog().LogEntry("Divide by zero.");
a_operands[0].Nullify();
// NOTE: No proper way to signal exception from here at present
}
#else // GMMACHINE_GMCHECKDIVBYZERO
a_operands[0].m_value.m_int /= a_operands[1].m_value.m_int;
#endif // GMMACHINE_GMCHECKDIVBYZERO
}
void GM_CDECL gmIntOpRem(gmThread * a_thread, gmVariable * a_operands)
{
#if GMMACHINE_GMCHECKDIVBYZERO
if(a_operands[1].m_value.m_int != 0)
{
a_operands[0].m_value.m_int %= a_operands[1].m_value.m_int;
}
else
{
a_thread->GetMachine()->GetLog().LogEntry("Divide by zero.");
a_operands[0].Nullify();
// NOTE: No proper way to signal exception from here at present
}
#else // GMMACHINE_GMCHECKDIVBYZERO
a_operands[0].m_value.m_int %= a_operands[1].m_value.m_int;
#endif // GMMACHINE_GMCHECKDIVBYZERO
}
void GM_CDECL gmIntOpBitOr(gmThread * a_thread, gmVariable * a_operands)
{
a_operands[0].m_value.m_int |= a_operands[1].m_value.m_int;
}
void GM_CDECL gmIntOpBitXor(gmThread * a_thread, gmVariable * a_operands)
{
a_operands[0].m_value.m_int ^= a_operands[1].m_value.m_int;
}
void GM_CDECL gmIntOpBitAnd(gmThread * a_thread, gmVariable * a_operands)
{
a_operands[0].m_value.m_int &= a_operands[1].m_value.m_int;
}
void GM_CDECL gmIntOpBitShiftLeft(gmThread * a_thread, gmVariable * a_operands)
{
a_operands[0].m_value.m_int <<= a_operands[1].m_value.m_int;
}
void GM_CDECL gmIntOpBitShiftRight(gmThread * a_thread, gmVariable * a_operands)
{
a_operands[0].m_value.m_int >>= a_operands[1].m_value.m_int;
}
void GM_CDECL gmIntOpInv(gmThread * a_thread, gmVariable * a_operands)
{
a_operands[0].m_value.m_int = ~a_operands[0].m_value.m_int;
}
void GM_CDECL gmIntOpLT(gmThread * a_thread, gmVariable * a_operands)
{
a_operands[0].m_value.m_int = a_operands[0].m_value.m_int < a_operands[1].m_value.m_int;
}
void GM_CDECL gmIntOpGT(gmThread * a_thread, gmVariable * a_operands)
{
a_operands[0].m_value.m_int = a_operands[0].m_value.m_int > a_operands[1].m_value.m_int;
}
void GM_CDECL gmIntOpLTE(gmThread * a_thread, gmVariable * a_operands)
{
a_operands[0].m_value.m_int = a_operands[0].m_value.m_int <= a_operands[1].m_value.m_int;
}
void GM_CDECL gmIntOpGTE(gmThread * a_thread, gmVariable * a_operands)
{
a_operands[0].m_value.m_int = a_operands[0].m_value.m_int >= a_operands[1].m_value.m_int;
}
void GM_CDECL gmIntOpEQ(gmThread * a_thread, gmVariable * a_operands)
{
a_operands[0].m_value.m_int = (a_operands[0].m_value.m_int == a_operands[1].m_value.m_int);
}
void GM_CDECL gmIntOpNEQ(gmThread * a_thread, gmVariable * a_operands)
{
a_operands[0].m_value.m_int = (a_operands[0].m_value.m_int != a_operands[1].m_value.m_int);
}
void GM_CDECL gmIntOpNEG(gmThread * a_thread, gmVariable * a_operands)
{
a_operands[0].m_value.m_int = -a_operands[0].m_value.m_int;
}
void GM_CDECL gmIntOpPOS(gmThread * a_thread, gmVariable * a_operands)
{
}
void GM_CDECL gmIntOpNOT(gmThread * a_thread, gmVariable * a_operands)
{
a_operands[0].m_value.m_int = !a_operands[0].m_value.m_int;
}
//
// GM_FLOAT
//
#define INTTOFLOAT(A) (((A)->m_type == GM_FLOAT) ? (A)->m_value.m_float : (float) (A)->m_value.m_int)
void GM_CDECL gmFloatOpAdd(gmThread * a_thread, gmVariable * a_operands)
{
a_operands->m_value.m_float = INTTOFLOAT(a_operands) + INTTOFLOAT(a_operands + 1);
a_operands->m_type = GM_FLOAT;
}
void GM_CDECL gmFloatOpSub(gmThread * a_thread, gmVariable * a_operands)
{
a_operands->m_value.m_float = INTTOFLOAT(a_operands) - INTTOFLOAT(a_operands + 1);
a_operands->m_type = GM_FLOAT;
}
void GM_CDECL gmFloatOpMul(gmThread * a_thread, gmVariable * a_operands)
{
a_operands->m_value.m_float = INTTOFLOAT(a_operands) * INTTOFLOAT(a_operands + 1);
a_operands->m_type = GM_FLOAT;
}
void GM_CDECL gmFloatOpDiv(gmThread * a_thread, gmVariable * a_operands)
{
#if GMMACHINE_GMCHECKDIVBYZERO
if(INTTOFLOAT(a_operands + 1) != 0)
{
a_operands->m_value.m_float = INTTOFLOAT(a_operands) / INTTOFLOAT(a_operands + 1);
a_operands->m_type = GM_FLOAT;
}
else
{
a_thread->GetMachine()->GetLog().LogEntry("Divide by zero.");
a_operands->Nullify(); // NOTE: Should probably return +/- INF, not null
// NOTE: No proper way to signal exception from here at present
}
#else // GMMACHINE_GMCHECKDIVBYZERO
a_operands->m_value.m_float = INTTOFLOAT(a_operands) / INTTOFLOAT(a_operands + 1);
a_operands->m_type = GM_FLOAT;
#endif // GMMACHINE_GMCHECKDIVBYZERO
}
void GM_CDECL gmFloatOpRem(gmThread * a_thread, gmVariable * a_operands)
{
#if GMMACHINE_GMCHECKDIVBYZERO
if(INTTOFLOAT(a_operands + 1) != 0)
{
a_operands->m_value.m_float = fmodf(INTTOFLOAT(a_operands), INTTOFLOAT(a_operands + 1));
a_operands->m_type = GM_FLOAT;
}
else
{
a_thread->GetMachine()->GetLog().LogEntry("Divide by zero.");
a_operands->Nullify();
// NOTE: No proper way to signal exception from here at present
}
#else // GMMACHINE_GMCHECKDIVBYZERO
a_operands->m_value.m_float = fmodf(INTTOFLOAT(a_operands), INTTOFLOAT(a_operands + 1));
a_operands->m_type = GM_FLOAT;
#endif // GMMACHINE_GMCHECKDIVBYZERO
}
void GM_CDECL gmFloatOpInc(gmThread * a_thread, gmVariable * a_operands)
{
a_operands->m_value.m_float = INTTOFLOAT(a_operands) + 1.0f;
a_operands->m_type = GM_FLOAT;
}
void GM_CDECL gmFloatOpDec(gmThread * a_thread, gmVariable * a_operands)
{
a_operands->m_value.m_float = INTTOFLOAT(a_operands) - 1.0f;
a_operands->m_type = GM_FLOAT;
}
void GM_CDECL gmFloatOpLT(gmThread * a_thread, gmVariable * a_operands)
{
a_operands->m_value.m_int = (INTTOFLOAT(a_operands) < INTTOFLOAT(a_operands + 1));
a_operands->m_type = GM_INT;
}
void GM_CDECL gmFloatOpGT(gmThread * a_thread, gmVariable * a_operands)
{
a_operands->m_value.m_int = (INTTOFLOAT(a_operands) > INTTOFLOAT(a_operands + 1));
a_operands->m_type = GM_INT;
}
void GM_CDECL gmFloatOpLTE(gmThread * a_thread, gmVariable * a_operands)
{
a_operands->m_value.m_int = (INTTOFLOAT(a_operands) <= INTTOFLOAT(a_operands + 1));
a_operands->m_type = GM_INT;
}
void GM_CDECL gmFloatOpGTE(gmThread * a_thread, gmVariable * a_operands)
{
a_operands->m_value.m_int = (INTTOFLOAT(a_operands) >= INTTOFLOAT(a_operands + 1));
a_operands->m_type = GM_INT;
}
void GM_CDECL gmFloatOpEQ(gmThread * a_thread, gmVariable * a_operands)
{
a_operands->m_value.m_int = (INTTOFLOAT(a_operands) == INTTOFLOAT(a_operands + 1));
a_operands->m_type = GM_INT;
}
void GM_CDECL gmFloatOpNEQ(gmThread * a_thread, gmVariable * a_operands)
{
a_operands->m_value.m_int = (INTTOFLOAT(a_operands) != INTTOFLOAT(a_operands + 1));
a_operands->m_type = GM_INT;
}
void GM_CDECL gmFloatOpNEG(gmThread * a_thread, gmVariable * a_operands)
{
a_operands->m_value.m_float = -INTTOFLOAT(a_operands);
a_operands->m_type = GM_FLOAT;
}
void GM_CDECL gmFloatOpPOS(gmThread * a_thread, gmVariable * a_operands)
{
}
void GM_CDECL gmFloatOpNOT(gmThread * a_thread, gmVariable * a_operands)
{
if(a_operands->m_value.m_float == 0.0f)
{
a_operands->m_value.m_int = 1; a_operands->m_type = GM_INT;
}
else
{
a_operands->m_value.m_int = 0; a_operands->m_type = GM_INT;
}
}
//
// GM_STRING
//
#define GMSTRING_BUFFERSIZE 64
// we could use gmVariable::AsString here, but this is for types <= string.... is a little more efficient.
// a_buffer must be >= 64
inline const char * gmUnknownToString(gmMachine * a_machine, gmVariable * a_unknown, char * a_buffer, int * a_len = NULL)
{
if(a_unknown->m_type == GM_STRING)
{
gmStringObject * str = (gmStringObject *) GM_MOBJECT(a_machine, a_unknown->m_value.m_ref);
if(a_len) { *a_len = str->GetLength(); }
return (const char *) *str;
}
if(a_unknown->m_type == GM_INT)
{
V_snprintf(a_buffer, 64, "%d", a_unknown->m_value.m_int); // this won't be > 64 chars
}
else if(a_unknown->m_type == GM_FLOAT)
{
V_snprintf(a_buffer, 64, "%f", a_unknown->m_value.m_float); // this won't be > 64 chars
}
else
{
strcpy(a_buffer, "null");
}
if(a_len) { *a_len = strlen(a_buffer); }
return a_buffer;
}
void GM_CDECL gmStringOpAdd(gmThread * a_thread, gmVariable * a_operands)
{
gmMachine * machine = a_thread->GetMachine();
char buffer1[GMSTRING_BUFFERSIZE];
char buffer2[GMSTRING_BUFFERSIZE];
int len1 = 0, len2 = 0;
const char * str1 = gmUnknownToString(machine, a_operands, buffer1, &len1);
const char * str2 = gmUnknownToString(machine, a_operands + 1, buffer2, &len2);
char * buffer = (char *) alloca(len1 + len2 + 1);
memcpy(buffer, str1, len1);
memcpy(buffer + len1, str2, len2 + 1);
a_thread->SetTop(a_operands); // so the garbage collector works
a_operands->m_type = GM_STRING;
a_operands->m_value.m_ref = (gmptr) machine->AllocStringObject(buffer, len1 + len2);
}
void GM_CDECL gmStringOpLT(gmThread * a_thread, gmVariable * a_operands)
{
gmMachine * machine = a_thread->GetMachine();
char buffer1[GMSTRING_BUFFERSIZE];
char buffer2[GMSTRING_BUFFERSIZE];
const char * str1 = gmUnknownToString(machine, a_operands, buffer1);
const char * str2 = gmUnknownToString(machine, a_operands + 1, buffer2);
int res = strcmp(str1, str2);
a_operands->m_type = GM_INT;
a_operands->m_value.m_ref = (res == -1) ? 1 : 0;
}
void GM_CDECL gmStringOpGT(gmThread * a_thread, gmVariable * a_operands)
{
gmMachine * machine = a_thread->GetMachine();
char buffer1[GMSTRING_BUFFERSIZE];
char buffer2[GMSTRING_BUFFERSIZE];
const char * str1 = gmUnknownToString(machine, a_operands, buffer1);
const char * str2 = gmUnknownToString(machine, a_operands + 1, buffer2);
int res = strcmp(str1, str2);
a_operands->m_type = GM_INT;
a_operands->m_value.m_ref = (res == 1) ? 1 : 0;
}
void GM_CDECL gmStringOpLTE(gmThread * a_thread, gmVariable * a_operands)
{
gmMachine * machine = a_thread->GetMachine();
char buffer1[GMSTRING_BUFFERSIZE];
char buffer2[GMSTRING_BUFFERSIZE];
const char * str1 = gmUnknownToString(machine, a_operands, buffer1);
const char * str2 = gmUnknownToString(machine, a_operands + 1, buffer2);
int res = strcmp(str1, str2);
a_operands->m_type = GM_INT;
a_operands->m_value.m_ref = (res == 1) ? 0 : 1;
}
void GM_CDECL gmStringOpGTE(gmThread * a_thread, gmVariable * a_operands)
{
gmMachine * machine = a_thread->GetMachine();
char buffer1[GMSTRING_BUFFERSIZE];
char buffer2[GMSTRING_BUFFERSIZE];
const char * str1 = gmUnknownToString(machine, a_operands, buffer1);
const char * str2 = gmUnknownToString(machine, a_operands + 1, buffer2);
int res = strcmp(str1, str2);
a_operands->m_type = GM_INT;
a_operands->m_value.m_ref = (res == -1) ? 0 : 1;
}
void GM_CDECL gmStringOpEQ(gmThread * a_thread, gmVariable * a_operands)
{
gmMachine * machine = a_thread->GetMachine();
char buffer1[GMSTRING_BUFFERSIZE];
char buffer2[GMSTRING_BUFFERSIZE];
const char * str1 = gmUnknownToString(machine, a_operands, buffer1);
const char * str2 = gmUnknownToString(machine, a_operands + 1, buffer2);
int res = strcmp(str1, str2);
a_operands->m_type = GM_INT;
a_operands->m_value.m_ref = (res == 0) ? 1 : 0;
}
void GM_CDECL gmStringOpNEQ(gmThread * a_thread, gmVariable * a_operands)
{
gmMachine * machine = a_thread->GetMachine();
char buffer1[GMSTRING_BUFFERSIZE];
char buffer2[GMSTRING_BUFFERSIZE];
const char * str1 = gmUnknownToString(machine, a_operands, buffer1);
const char * str2 = gmUnknownToString(machine, a_operands + 1, buffer2);
int res = strcmp(str1, str2);
a_operands->m_type = GM_INT;
a_operands->m_value.m_ref = (res == 0) ? 0 : 1;
}
void GM_CDECL gmStringOpNOT(gmThread * a_thread, gmVariable * a_operands)
{
a_operands->m_value.m_int = 0; a_operands->m_type = GM_INT;
}
//
// GM_TABLE
//
void GM_CDECL gmTableGetDot(gmThread * a_thread, gmVariable * a_operands)
{
gmTableObject * table = (gmTableObject *) GM_OBJECT(a_operands->m_value.m_ref);
*a_operands = table->Get(a_operands[1]);
}
void GM_CDECL gmTableSetDot(gmThread * a_thread, gmVariable * a_operands)
{
gmMachine * machine = a_thread->GetMachine();
gmTableObject * table = (gmTableObject *) GM_MOBJECT(machine, a_operands->m_value.m_ref);
table->Set(machine, a_operands[2], a_operands[1]);
}
void GM_CDECL gmTableGetInd(gmThread * a_thread, gmVariable * a_operands)
{
gmTableObject * table = (gmTableObject *) GM_OBJECT(a_operands->m_value.m_ref);
*a_operands = table->Get(a_operands[1]);
}
void GM_CDECL gmTableSetInd(gmThread * a_thread, gmVariable * a_operands)
{
gmMachine * machine = a_thread->GetMachine();
gmTableObject * table = (gmTableObject *) GM_MOBJECT(machine, a_operands->m_value.m_ref);
table->Set(machine, a_operands[1], a_operands[2]);
}
//
// GM_USER
//
//
// MISC.
//
void GM_CDECL gmRefOpEQ(gmThread * a_thread, gmVariable * a_operands)
{
if(a_operands[0].m_type == a_operands[1].m_type && a_operands[0].m_value.m_ref == a_operands[1].m_value.m_ref)
{
a_operands->m_type = GM_INT;
a_operands->m_value.m_int = 1;
}
else
{
a_operands->m_type = GM_INT;
a_operands->m_value.m_int = 0;
}
}
void GM_CDECL gmRefOpNEQ(gmThread * a_thread, gmVariable * a_operands)
{
if(a_operands[0].m_type == a_operands[1].m_type && a_operands[0].m_value.m_ref == a_operands[1].m_value.m_ref)
{
a_operands->m_type = GM_INT;
a_operands->m_value.m_int = 0;
}
else
{
a_operands->m_type = GM_INT;
a_operands->m_value.m_int = 1;
}
}
void GM_CDECL gmRefOpNOT(gmThread * a_thread, gmVariable * a_operands)
{
if(a_operands->m_type == GM_NULL)
{
a_operands->m_type = GM_INT;
a_operands->m_value.m_int = 1;
}
else
{
a_operands->m_type = GM_INT;
a_operands->m_value.m_int = 0;
}
}
void gmInitBasicType(gmType a_type, gmOperatorFunction * a_operators)
{
memset(a_operators, 0, sizeof(gmOperatorFunction) * O_MAXOPERATORS);
if(a_type == GM_NULL)
{
a_operators[O_EQ] = gmRefOpEQ;
a_operators[O_NEQ] = gmRefOpNEQ;
a_operators[O_NOT] = gmRefOpNOT;
}
else if(a_type == GM_INT)
{
a_operators[O_ADD] = gmIntOpAdd;
a_operators[O_SUB] = gmIntOpSub;
a_operators[O_MUL] = gmIntOpMul;
a_operators[O_DIV] = gmIntOpDiv;
a_operators[O_REM] = gmIntOpRem;
a_operators[O_BIT_OR] = gmIntOpBitOr;
a_operators[O_BIT_XOR] = gmIntOpBitXor;
a_operators[O_BIT_AND] = gmIntOpBitAnd;
a_operators[O_BIT_SHIFTLEFT] = gmIntOpBitShiftLeft;
a_operators[O_BIT_SHIFTRIGHT] = gmIntOpBitShiftRight;
a_operators[O_BIT_INV] = gmIntOpInv;
a_operators[O_LT] = gmIntOpLT;
a_operators[O_GT] = gmIntOpGT;
a_operators[O_LTE] = gmIntOpLTE;
a_operators[O_GTE] = gmIntOpGTE;
a_operators[O_EQ] = gmIntOpEQ;
a_operators[O_NEQ] = gmIntOpNEQ;
a_operators[O_NEG] = gmIntOpNEG;
a_operators[O_POS] = gmIntOpPOS;
a_operators[O_NOT] = gmIntOpNOT;
}
else if(a_type == GM_FLOAT)
{
a_operators[O_ADD] = gmFloatOpAdd;
a_operators[O_SUB] = gmFloatOpSub;
a_operators[O_MUL] = gmFloatOpMul;
a_operators[O_DIV] = gmFloatOpDiv;
a_operators[O_REM] = gmFloatOpRem;
a_operators[O_LT] = gmFloatOpLT;
a_operators[O_GT] = gmFloatOpGT;
a_operators[O_LTE] = gmFloatOpLTE;
a_operators[O_GTE] = gmFloatOpGTE;
a_operators[O_EQ] = gmFloatOpEQ;
a_operators[O_NEQ] = gmFloatOpNEQ;
a_operators[O_NEG] = gmFloatOpNEG;
a_operators[O_POS] = gmFloatOpPOS;
a_operators[O_NOT] = gmFloatOpNOT;
}
else if(a_type == GM_STRING)
{
a_operators[O_ADD] = gmStringOpAdd;
a_operators[O_LT] = gmStringOpLT;
a_operators[O_GT] = gmStringOpGT;
a_operators[O_LTE] = gmStringOpLTE;
a_operators[O_GTE] = gmStringOpGTE;
a_operators[O_EQ] = gmStringOpEQ;
a_operators[O_NEQ] = gmStringOpNEQ;
a_operators[O_NOT] = gmStringOpNOT;
}
else if(a_type == GM_TABLE)
{
a_operators[O_GETDOT] = gmTableGetDot;
a_operators[O_SETDOT] = gmTableSetDot;
a_operators[O_GETIND] = gmTableGetInd;
a_operators[O_SETIND] = gmTableSetInd;
a_operators[O_EQ] = gmRefOpEQ;
a_operators[O_NEQ] = gmRefOpNEQ;
a_operators[O_NOT] = gmRefOpNOT;
}
else if(a_type == GM_FUNCTION)
{
a_operators[O_EQ] = gmRefOpEQ;
a_operators[O_NEQ] = gmRefOpNEQ;
a_operators[O_NOT] = gmRefOpNOT;
}
else if(a_type >= GM_USER)
{
a_operators[O_EQ] = gmRefOpEQ;
a_operators[O_NEQ] = gmRefOpNEQ;
a_operators[O_NOT] = gmRefOpNOT;
}
}
+79
View File
@@ -0,0 +1,79 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#ifndef _GMOPERATORS_H_
#define _GMOPERATORS_H_
#include "gmConfig.h"
#include "gmVariable.h"
#include "gmByteCode.h"
struct gmVariable;
class gmThread;
/// \enum gmOperator
enum gmOperator
{
// O_GETDOT to O_NOT MUST MATCH gmByteCode enum
O_GETDOT = 0, // object, "member" (tos is a_operands + 2)
O_SETDOT, // object, value, "member" (tos is a_operands + 3)
O_GETIND, // object, index
O_SETIND, // object, index, value
O_ADD, // op1, op2 (tos is a_operands + 2)
O_SUB, // op1, op2
O_MUL, // op1, op2
O_DIV, // op1, op2
O_REM, // op1, op2
O_BIT_OR, // op1, op2
O_BIT_XOR, // op1, op2
O_BIT_AND, // op1, op2
O_BIT_SHIFTLEFT, // op1, op2 (shift)
O_BIT_SHIFTRIGHT, // op1, op2 (shift)
O_BIT_INV, // op1
O_LT, // op1, op2
O_GT, // op1, op2
O_LTE, // op1, op2
O_GTE, // op1, op2
O_EQ, // op1, op2
O_NEQ, // op1, op2
O_NEG, // op1
O_POS, // op1
O_NOT, // op1
#if GM_BOOL_OP
O_BOOL, // Special case for use in branch tests. Unary
#endif // GM_BOOL_OP
O_MAXOPERATORS,
};
const char * gmGetOperatorName(gmOperator a_operator);
gmOperator gmGetOperator(const char * a_operatorName); // return O_MAXOPERATORS on error
//
// gmOperatorFunction is an operator function that may be used instead of binding a c funciton.
// This style of operator is much quicker, as it does not require a full stack frame push and pop.
// Note though, that if you are creating gmObjects from within one of these operators, you must call
// a_thread->SetTop() with the known top of stack before you allocate your gmObject such that the garbage
// collector can mark all objects properly.
// Be careful using this style of operator and GC.
//
typedef void (GM_CDECL *gmOperatorFunction)(gmThread * a_thread, gmVariable * a_operands);
void gmInitBasicType(gmType a_type, gmOperatorFunction * a_operators);
#endif // _GMOPERATORS_H_
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,51 @@
#ifndef YYSTYPE
#define YYSTYPE int
#endif
#define KEYWORD_LOCAL 258
#define KEYWORD_GLOBAL 259
#define KEYWORD_MEMBER 260
#define KEYWORD_AND 261
#define KEYWORD_OR 262
#define KEYWORD_IF 263
#define KEYWORD_ELSE 264
#define KEYWORD_WHILE 265
#define KEYWORD_FOR 266
#define KEYWORD_FOREACH 267
#define KEYWORD_IN 268
#define KEYWORD_BREAK 269
#define KEYWORD_CONTINUE 270
#define KEYWORD_NULL 271
#define KEYWORD_DOWHILE 272
#define KEYWORD_RETURN 273
#define KEYWORD_FUNCTION 274
#define KEYWORD_TABLE 275
#define KEYWORD_THIS 276
#define KEYWORD_TRUE 277
#define KEYWORD_FALSE 278
#define IDENTIFIER 279
#define CONSTANT_HEX 280
#define CONSTANT_BINARY 281
#define CONSTANT_INT 282
#define CONSTANT_CHAR 283
#define CONSTANT_FLOAT 284
#define CONSTANT_STRING 285
#define SYMBOL_ASGN_BSR 286
#define SYMBOL_ASGN_BSL 287
#define SYMBOL_ASGN_ADD 288
#define SYMBOL_ASGN_MINUS 289
#define SYMBOL_ASGN_TIMES 290
#define SYMBOL_ASGN_DIVIDE 291
#define SYMBOL_ASGN_REM 292
#define SYMBOL_ASGN_BAND 293
#define SYMBOL_ASGN_BOR 294
#define SYMBOL_ASGN_BXOR 295
#define SYMBOL_RIGHT_SHIFT 296
#define SYMBOL_LEFT_SHIFT 297
#define SYMBOL_LTE 298
#define SYMBOL_GTE 299
#define SYMBOL_EQ 300
#define SYMBOL_NEQ 301
#define TOKEN_ERROR 302
extern YYSTYPE gmlval;
+877
View File
@@ -0,0 +1,877 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
%{
#define YYPARSER
#include "gmConfig.h"
#include "gmCodeTree.h"
#define YYSTYPE gmCodeTreeNode *
extern gmCodeTreeNode * g_codeTree;
#define GM_BISON_DEBUG
#ifdef GM_BISON_DEBUG
#define YYDEBUG 1
#define YYERROR_VERBOSE
#endif // GM_BISON_DEBUG
//
// HELPERS
//
void ATTACH(gmCodeTreeNode * &a_res, gmCodeTreeNode * a_a, gmCodeTreeNode * a_b)
{
YYSTYPE t = a_a;
if(t != NULL)
{
while(t->m_sibling != NULL)
{
t = t->m_sibling;
}
t->m_sibling = a_b;
if(a_b) { a_b->m_parent = t; }
a_res = a_a;
}
else
{
a_res = a_b;
}
}
gmCodeTreeNode * CreateOperation(int a_subTypeType, gmCodeTreeNode * a_left = NULL, gmCodeTreeNode * a_right = NULL)
{
gmCodeTreeNode * node = gmCodeTreeNode::Create(CTNT_EXPRESSION, CTNET_OPERATION, gmlineno, a_subTypeType);
node->SetChild(0, a_left);
node->SetChild(1, a_right);
return node;
}
gmCodeTreeNode * CreateAsignExpression(int a_subTypeType, gmCodeTreeNode * a_left, gmCodeTreeNode * a_right)
{
// we need to evaluate the complexety of the l-value... if it is a function call, index or dot to the left of a dot or index, we need to cache
// into a hidden variable.
// todo
gmCodeTreeNode * opNode = CreateOperation(a_subTypeType, a_left, a_right);
return CreateOperation(CTNOT_ASSIGN, a_left, opNode);
}
%}
%token KEYWORD_LOCAL
%token KEYWORD_GLOBAL
%token KEYWORD_MEMBER
%token KEYWORD_AND
%token KEYWORD_OR
%token KEYWORD_IF
%token KEYWORD_ELSE
%token KEYWORD_WHILE
%token KEYWORD_FOR
%token KEYWORD_FOREACH
%token KEYWORD_IN
%token KEYWORD_BREAK
%token KEYWORD_CONTINUE
%token KEYWORD_NULL
%token KEYWORD_DOWHILE
%token KEYWORD_RETURN
%token KEYWORD_FUNCTION
%token KEYWORD_TABLE
%token KEYWORD_THIS
%token KEYWORD_TRUE
%token KEYWORD_FALSE
%token IDENTIFIER
%token CONSTANT_HEX
%token CONSTANT_BINARY
%token CONSTANT_INT
%token CONSTANT_CHAR
%token CONSTANT_FLOAT
%token CONSTANT_FLOAT
%token CONSTANT_STRING
%token SYMBOL_ASGN_BSR
%token SYMBOL_ASGN_BSL
%token SYMBOL_ASGN_ADD
%token SYMBOL_ASGN_MINUS
%token SYMBOL_ASGN_TIMES
%token SYMBOL_ASGN_DIVIDE
%token SYMBOL_ASGN_REM
%token SYMBOL_ASGN_BAND
%token SYMBOL_ASGN_BOR
%token SYMBOL_ASGN_BXOR
%token SYMBOL_RIGHT_SHIFT
%token SYMBOL_LEFT_SHIFT
%token SYMBOL_LTE
%token SYMBOL_GTE
%token SYMBOL_EQ
%token SYMBOL_NEQ
%token TOKEN_ERROR
%start program
%%
program
: statement_list
{
g_codeTree = $1;
}
;
statement_list
: statement
{
$$ = $1;
}
| statement_list statement
{
ATTACH($$, $1, $2);
}
;
statement
: expression_statement
{
$$ = $1;
}
| var_statement
{
$$ = $1;
}
| selection_statement
{
$$ = $1;
}
| iteration_statement
{
$$ = $1;
}
| jump_statement
{
$$ = $1;
}
;
compound_statement
: '{' '}'
{
$$ = gmCodeTreeNode::Create(CTNT_STATEMENT, CTNST_COMPOUND, gmlineno);
}
| '{' statement_list '}'
{
$$ = gmCodeTreeNode::Create(CTNT_STATEMENT, CTNST_COMPOUND, gmlineno);
$$->SetChild(0, $2);
}
;
var_statement
: var_type identifier ';'
{
$$ = gmCodeTreeNode::Create(CTNT_DECLARATION, CTNDT_VARIABLE, gmlineno, (int) $1);
$$->SetChild(0, $2);
}
| var_type identifier '=' constant_expression ';'
{
$$ = gmCodeTreeNode::Create(CTNT_DECLARATION, CTNDT_VARIABLE, gmlineno, (int) $1);
$$->SetChild(0, $2);
ATTACH($$, $$, CreateOperation(CTNOT_ASSIGN, $2, $4));
}
;
var_type
: KEYWORD_LOCAL
{
$$ = (YYSTYPE) CTVT_LOCAL;
}
| KEYWORD_GLOBAL
{
$$ = (YYSTYPE) CTVT_GLOBAL;
}
| KEYWORD_MEMBER
{
$$ = (YYSTYPE) CTVT_MEMBER;
}
;
expression_statement
: ';'
{
$$ = NULL;
}
| assignment_expression ';'
{
$$ = $1;
}
;
selection_statement
: KEYWORD_IF '(' constant_expression ')' compound_statement
{
$$ = gmCodeTreeNode::Create(CTNT_STATEMENT, CTNST_IF, ($3) ? $3->m_lineNumber : gmlineno);
$$->SetChild(0, $3);
$$->SetChild(1, $5);
}
| KEYWORD_IF '(' constant_expression ')' compound_statement KEYWORD_ELSE compound_statement
{
$$ = gmCodeTreeNode::Create(CTNT_STATEMENT, CTNST_IF, ($3) ? $3->m_lineNumber : gmlineno);
$$->SetChild(0, $3);
$$->SetChild(1, $5);
$$->SetChild(2, $7);
}
| KEYWORD_IF '(' constant_expression ')' compound_statement KEYWORD_ELSE selection_statement
{
$$ = gmCodeTreeNode::Create(CTNT_STATEMENT, CTNST_IF, ($3) ? $3->m_lineNumber : gmlineno);
$$->SetChild(0, $3);
$$->SetChild(1, $5);
$$->SetChild(2, $7);
}
;
iteration_statement
: KEYWORD_WHILE '(' constant_expression ')' compound_statement
{
$$ = gmCodeTreeNode::Create(CTNT_STATEMENT, CTNST_WHILE, ($3) ? $3->m_lineNumber : gmlineno);
$$->SetChild(0, $3);
$$->SetChild(1, $5);
}
| KEYWORD_DOWHILE '(' constant_expression ')' compound_statement
{
$$ = gmCodeTreeNode::Create(CTNT_STATEMENT, CTNST_DOWHILE, ($3) ? $3->m_lineNumber : gmlineno);
$$->SetChild(0, $3);
$$->SetChild(1, $5);
}
| KEYWORD_FOR '(' expression_statement constant_expression_statement ')' compound_statement
{
$$ = gmCodeTreeNode::Create(CTNT_STATEMENT, CTNST_FOR, ($3) ? $3->m_lineNumber : gmlineno);
$$->SetChild(0, $3);
$$->SetChild(1, $4);
$$->SetChild(3, $6);
}
| KEYWORD_FOR '(' expression_statement constant_expression_statement assignment_expression ')' compound_statement
{
$$ = gmCodeTreeNode::Create(CTNT_STATEMENT, CTNST_FOR, ($3) ? $3->m_lineNumber : gmlineno);
$$->SetChild(0, $3);
$$->SetChild(1, $4);
$$->SetChild(2, $5);
$$->SetChild(3, $7);
}
| KEYWORD_FOREACH '(' identifier KEYWORD_IN constant_expression ')' compound_statement
{
$$ = gmCodeTreeNode::Create(CTNT_STATEMENT, CTNST_FOREACH, ($5) ? $5->m_lineNumber : gmlineno);
$$->SetChild(0, $5);
$$->SetChild(1, $3);
$$->SetChild(3, $7);
}
| KEYWORD_FOREACH '(' identifier KEYWORD_AND identifier KEYWORD_IN constant_expression')' compound_statement
{
$$ = gmCodeTreeNode::Create(CTNT_STATEMENT, CTNST_FOREACH, ($7) ? $7->m_lineNumber : gmlineno);
$$->SetChild(0, $7);
$$->SetChild(1, $5);
$$->SetChild(2, $3);
$$->SetChild(3, $9);
}
;
jump_statement
: KEYWORD_CONTINUE ';'
{
$$ = gmCodeTreeNode::Create(CTNT_STATEMENT, CTNST_CONTINUE, gmlineno);
}
| KEYWORD_BREAK ';'
{
$$ = gmCodeTreeNode::Create(CTNT_STATEMENT, CTNST_BREAK, gmlineno);
}
| KEYWORD_RETURN ';'
{
$$ = gmCodeTreeNode::Create(CTNT_STATEMENT, CTNST_RETURN, gmlineno);
}
| KEYWORD_RETURN constant_expression ';'
{
$$ = gmCodeTreeNode::Create(CTNT_STATEMENT, CTNST_RETURN, gmlineno);
$$->SetChild(0, $2);
}
;
assignment_expression
: logical_or_expression
{
$$ = $1;
if($$)
{
$$->m_flags |= gmCodeTreeNode::CTN_POP;
}
}
| postfix_expression '=' logical_or_expression
{
$$ = CreateOperation(CTNOT_ASSIGN, $1, $3);
}
| postfix_expression SYMBOL_ASGN_BSR logical_or_expression
{
$$ = CreateAsignExpression(CTNOT_SHIFT_RIGHT, $1, $3);
}
| postfix_expression SYMBOL_ASGN_BSL logical_or_expression
{
$$ = CreateAsignExpression(CTNOT_SHIFT_LEFT, $1, $3);
}
| postfix_expression SYMBOL_ASGN_ADD logical_or_expression
{
$$ = CreateAsignExpression(CTNOT_ADD, $1, $3);
}
| postfix_expression SYMBOL_ASGN_MINUS logical_or_expression
{
$$ = CreateAsignExpression(CTNOT_MINUS, $1, $3);
}
| postfix_expression SYMBOL_ASGN_TIMES logical_or_expression
{
$$ = CreateAsignExpression(CTNOT_TIMES, $1, $3);
}
| postfix_expression SYMBOL_ASGN_DIVIDE logical_or_expression
{
$$ = CreateAsignExpression(CTNOT_DIVIDE, $1, $3);
}
| postfix_expression SYMBOL_ASGN_REM logical_or_expression
{
$$ = CreateAsignExpression(CTNOT_REM, $1, $3);
}
| postfix_expression SYMBOL_ASGN_BAND logical_or_expression
{
$$ = CreateAsignExpression(CTNOT_BIT_AND, $1, $3);
}
| postfix_expression SYMBOL_ASGN_BOR logical_or_expression
{
$$ = CreateAsignExpression(CTNOT_BIT_OR, $1, $3);
}
| postfix_expression SYMBOL_ASGN_BXOR logical_or_expression
{
$$ = CreateAsignExpression(CTNOT_BIT_XOR, $1, $3);
}
;
constant_expression_statement
: ';'
{
$$ = NULL;
}
| constant_expression ';'
{
$$ = $1;
}
;
constant_expression
: logical_or_expression
{
$$ = $1;
}
;
logical_or_expression
: logical_and_expression
{
$$ = $1;
}
| logical_or_expression KEYWORD_OR logical_and_expression
{
$$ = CreateOperation(CTNOT_OR, $1, $3);
}
;
logical_and_expression
: inclusive_or_expression
{
$$ = $1;
}
| logical_and_expression KEYWORD_AND inclusive_or_expression
{
$$ = CreateOperation(CTNOT_AND, $1, $3);
}
;
inclusive_or_expression
: exclusive_or_expression
{
$$ = $1;
}
| inclusive_or_expression '|' exclusive_or_expression
{
$$ = CreateOperation(CTNOT_BIT_OR, $1, $3);
$$->ConstantFold();
}
;
exclusive_or_expression
: and_expression
{
$$ = $1;
}
| exclusive_or_expression '^' and_expression
{
$$ = CreateOperation(CTNOT_BIT_XOR, $1, $3);
$$->ConstantFold();
}
;
and_expression
: equality_expression
{
$$ = $1;
}
| and_expression '&' equality_expression
{
$$ = CreateOperation(CTNOT_BIT_AND, $1, $3);
$$->ConstantFold();
}
;
equality_expression
: relational_expression
{
$$ = $1;
}
| equality_expression SYMBOL_EQ relational_expression
{
$$ = CreateOperation(CTNOT_EQ, $1, $3);
}
| equality_expression SYMBOL_NEQ relational_expression
{
$$ = CreateOperation(CTNOT_NEQ, $1, $3);
}
;
relational_expression
: shift_expression
{
$$ = $1;
}
| relational_expression '<' shift_expression
{
$$ = CreateOperation(CTNOT_LT, $1, $3);
}
| relational_expression '>' shift_expression
{
$$ = CreateOperation(CTNOT_GT, $1, $3);
}
| relational_expression SYMBOL_LTE shift_expression
{
$$ = CreateOperation(CTNOT_LTE, $1, $3);
}
| relational_expression SYMBOL_GTE shift_expression
{
$$ = CreateOperation(CTNOT_GTE, $1, $3);
}
;
shift_expression
: additive_expression
{
$$ = $1;
}
| shift_expression SYMBOL_LEFT_SHIFT additive_expression
{
$$ = CreateOperation(CTNOT_SHIFT_LEFT, $1, $3);
$$->ConstantFold();
}
| shift_expression SYMBOL_RIGHT_SHIFT additive_expression
{
$$ = CreateOperation(CTNOT_SHIFT_RIGHT, $1, $3);
$$->ConstantFold();
}
;
additive_expression
: multiplicative_expression
{
$$ = $1;
}
| additive_expression '+' multiplicative_expression
{
$$ = CreateOperation(CTNOT_ADD, $1, $3);
$$->ConstantFold();
}
| additive_expression '-' multiplicative_expression
{
$$ = CreateOperation(CTNOT_MINUS, $1, $3);
$$->ConstantFold();
}
;
multiplicative_expression
: unary_expression
{
$$ = $1;
}
| multiplicative_expression '*' unary_expression
{
$$ = CreateOperation(CTNOT_TIMES, $1, $3);
$$->ConstantFold();
}
| multiplicative_expression '/' unary_expression
{
$$ = CreateOperation(CTNOT_DIVIDE, $1, $3);
$$->ConstantFold();
}
| multiplicative_expression '%' unary_expression
{
$$ = CreateOperation(CTNOT_REM, $1, $3);
$$->ConstantFold();
}
;
unary_expression
: postfix_expression
{
$$ = $1;
}
| unary_operator unary_expression
{
$$ = $1;
$$->SetChild(0, $2);
$$->ConstantFold();
}
;
unary_operator
: '+'
{
$$ = CreateOperation(CTNOT_UNARY_PLUS);
}
| '-'
{
$$ = CreateOperation(CTNOT_UNARY_MINUS);
}
| '~'
{
$$ = CreateOperation(CTNOT_UNARY_COMPLEMENT);
}
| '!'
{
$$ = CreateOperation(CTNOT_UNARY_NOT);
}
;
postfix_expression
: primary_expression
{
$$ = $1;
}
| postfix_expression '[' constant_expression ']'
{
$$ = CreateOperation(CTNOT_ARRAY_INDEX, $1, $3);
}
| postfix_expression '(' ')'
{
$$ = gmCodeTreeNode::Create(CTNT_EXPRESSION, CTNET_CALL, gmlineno);
$$->SetChild(0, $1);
}
| postfix_expression '(' argument_expression_list ')'
{
$$ = gmCodeTreeNode::Create(CTNT_EXPRESSION, CTNET_CALL, gmlineno);
$$->SetChild(0, $1);
$$->SetChild(1, $3);
}
| postfix_expression ':' identifier '(' ')'
{
$$ = gmCodeTreeNode::Create(CTNT_EXPRESSION, CTNET_CALL, gmlineno);
$$->SetChild(0, $3);
$$->SetChild(2, $1);
}
| postfix_expression ':' identifier '(' argument_expression_list ')'
{
$$ = gmCodeTreeNode::Create(CTNT_EXPRESSION, CTNET_CALL, gmlineno);
$$->SetChild(0, $3);
$$->SetChild(1, $5);
$$->SetChild(2, $1);
}
| postfix_expression '.' identifier
{
$$ = CreateOperation(CTNOT_DOT, $1, $3);
}
;
argument_expression_list
: constant_expression
{
$$ = $1;
}
| argument_expression_list ',' constant_expression
{
ATTACH($$, $1, $3);
}
;
table_constructor
: KEYWORD_TABLE '(' ')'
{
$$ = gmCodeTreeNode::Create(CTNT_EXPRESSION, CTNET_TABLE, gmlineno);
}
| KEYWORD_TABLE '(' field_list ')'
{
$$ = gmCodeTreeNode::Create(CTNT_EXPRESSION, CTNET_TABLE, gmlineno);
$$->SetChild(0, $3);
}
| '{' '}'
{
$$ = gmCodeTreeNode::Create(CTNT_EXPRESSION, CTNET_TABLE, gmlineno);
}
| '{' field_list '}'
{
$$ = gmCodeTreeNode::Create(CTNT_EXPRESSION, CTNET_TABLE, gmlineno);
$$->SetChild(0, $2);
}
| '{' field_list ',' '}'
{
$$ = gmCodeTreeNode::Create(CTNT_EXPRESSION, CTNET_TABLE, gmlineno);
$$->SetChild(0, $2);
}
;
function_constructor
: KEYWORD_FUNCTION '(' parameter_list ')' compound_statement
{
$$ = gmCodeTreeNode::Create(CTNT_EXPRESSION, CTNET_FUNCTION, gmlineno);
$$->SetChild(0, $3);
$$->SetChild(1, $5);
}
| KEYWORD_FUNCTION '(' ')' compound_statement
{
$$ = gmCodeTreeNode::Create(CTNT_EXPRESSION, CTNET_FUNCTION, gmlineno);
$$->SetChild(1, $4);
}
;
field_list
: field
{
$$ = $1;
}
| field_list ',' field
{
ATTACH($$, $1, $3);
}
;
field
: constant_expression
{
$$ = $1;
}
| identifier '=' constant_expression
{
$$ = CreateOperation(CTNOT_ASSIGN_FIELD, $1, $3);
}
;
parameter_list
: parameter
{
$$ = $1;
}
| parameter_list ',' parameter
{
ATTACH($$, $1, $3);
}
;
parameter
: identifier
{
$$ = gmCodeTreeNode::Create(CTNT_DECLARATION, CTNDT_PARAMETER, gmlineno);
$$->SetChild(0, $1);
}
| identifier '=' constant_expression
{
$$ = gmCodeTreeNode::Create(CTNT_DECLARATION, CTNDT_PARAMETER, gmlineno);
$$->SetChild(0, $1);
$$->SetChild(1, $3);
}
;
primary_expression
: identifier
{
$$ = $1;
}
| '.' identifier
{
$$ = $2;
$$->m_flags |= gmCodeTreeNode::CTN_MEMBER;
}
| KEYWORD_THIS
{
$$ = gmCodeTreeNode::Create(CTNT_EXPRESSION, CTNET_THIS, gmlineno);
}
| constant
{
$$ = $1;
}
| table_constructor
{
$$ = $1;
}
| function_constructor
{
$$ = $1;
}
| '(' constant_expression ')'
{
$$ = $2;
}
;
identifier
: IDENTIFIER
{
$$ = gmCodeTreeNode::Create(CTNT_EXPRESSION, CTNET_IDENTIFIER, gmlineno);
$$->m_data.m_string = (char *) gmCodeTree::Get().Alloc(strlen(gmtext) + 1);
strcpy($$->m_data.m_string, gmtext);
}
;
constant
: CONSTANT_HEX
{
$$ = gmCodeTreeNode::Create(CTNT_EXPRESSION, CTNET_CONSTANT, gmlineno, CTNCT_INT);
$$->m_data.m_iValue = strtoul(gmtext + 2, NULL, 16);
}
| CONSTANT_BINARY
{
$$ = gmCodeTreeNode::Create(CTNT_EXPRESSION, CTNET_CONSTANT, gmlineno, CTNCT_INT);
$$->m_data.m_iValue = strtoul(gmtext + 2, NULL, 2);
}
| CONSTANT_INT
{
$$ = gmCodeTreeNode::Create(CTNT_EXPRESSION, CTNET_CONSTANT, gmlineno, CTNCT_INT);
$$->m_data.m_iValue = atoi(gmtext);
}
| KEYWORD_TRUE
{
$$ = gmCodeTreeNode::Create(CTNT_EXPRESSION, CTNET_CONSTANT, gmlineno, CTNCT_INT);
$$->m_data.m_iValue = 1;
}
| KEYWORD_FALSE
{
$$ = gmCodeTreeNode::Create(CTNT_EXPRESSION, CTNET_CONSTANT, gmlineno, CTNCT_INT);
$$->m_data.m_iValue = 0;
}
| CONSTANT_CHAR
{
$$ = gmCodeTreeNode::Create(CTNT_EXPRESSION, CTNET_CONSTANT, gmlineno, CTNCT_INT);
char * c = (char *) gmCodeTree::Get().Alloc(strlen(gmtext) + 1);
strcpy(c, gmtext);
int result = 0;
int shr = 0;
while(*c)
{
if(c[0] == '\'')
{
++c;
continue;
}
else if(c[0] == '\\')
{
if(shr) result <<= 8;
switch(c[1])
{
case 'a' : result |= (unsigned char) '\a'; break;
case 'b' : result |= (unsigned char) '\b'; break;
case 'f' : result |= (unsigned char) '\f'; break;
case 'n' : result |= (unsigned char) '\n'; break;
case 'r' : result |= (unsigned char) '\r'; break;
case 't' : result |= (unsigned char) '\t'; break;
case 'v' : result |= (unsigned char) '\v'; break;
case '\'' : result |= (unsigned char) '\''; break;
case '\"' : result |= (unsigned char) '\"'; break;
case '\\' : result |= (unsigned char) '\\'; break;
default: result |= (unsigned char) c[1];
}
++shr;
c += 2;
continue;
}
if(shr) result <<= 8;
result |= (unsigned char) *(c++);
++shr;
}
if(shr > 4 && gmCodeTree::Get().GetLog()) gmCodeTree::Get().GetLog()->LogEntry("truncated char, line %d", gmlineno);
$$->m_data.m_iValue = result;
}
| CONSTANT_FLOAT
{
$$ = gmCodeTreeNode::Create(CTNT_EXPRESSION, CTNET_CONSTANT, gmlineno, CTNCT_FLOAT);
$$->m_data.m_fValue = (float) atof(gmtext);
}
| constant_string_list
{
$$ = $1;
}
| KEYWORD_NULL
{
$$ = gmCodeTreeNode::Create(CTNT_EXPRESSION, CTNET_CONSTANT, gmlineno, CTNCT_NULL);
$$->m_data.m_iValue = 0;
}
;
constant_string_list
: CONSTANT_STRING
{
$$ = gmCodeTreeNode::Create(CTNT_EXPRESSION, CTNET_CONSTANT, gmlineno, CTNCT_STRING);
$$->m_data.m_string = (char *) gmCodeTree::Get().Alloc(strlen(gmtext) + 1);
strcpy($$->m_data.m_string, gmtext);
if(gmtext[0] == '"')
{
gmProcessDoubleQuoteString($$->m_data.m_string);
}
else if(gmtext[0] == '`')
{
gmProcessSingleQuoteString($$->m_data.m_string);
}
}
| constant_string_list CONSTANT_STRING
{
$$ = $1;
int alen = strlen($$->m_data.m_string);
int blen = strlen(gmtext);
char * str = (char *) gmCodeTree::Get().Alloc(alen + blen + 1);
if(str)
{
memcpy(str, $1->m_data.m_string, alen);
memcpy(str + alen, gmtext, blen);
str[alen + blen] = '\0';
if(str[alen] == '"')
{
gmProcessDoubleQuoteString(str + alen);
}
else if(str[alen] == '`')
{
gmProcessSingleQuoteString(str + alen);
}
$$->m_data.m_string = str;
}
}
;
%%
#include <stdio.h>
File diff suppressed because it is too large Load Diff
+32
View File
@@ -0,0 +1,32 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#ifndef _GMSCANNER_H_
#define _GMSCANNER_H_
#include "gmConfig.h"
//
// gmscanner.cpp, gmparser.cpp.h and gmparser.cpp are created by flex and bison
// see fontend.bat for more details.
//
// fwd decl for flex
typedef struct yy_buffer_state * YY_BUFFER_STATE;
YY_BUFFER_STATE gm_scan_string(const char * str);
YY_BUFFER_STATE gm_scan_bytes(const char *bytes, int len);
void gm_delete_buffer(YY_BUFFER_STATE b);
int gmlex();
extern char * gmtext;
extern int gmlineno;
#endif // _GMSCANNER_H_
+137
View File
@@ -0,0 +1,137 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
%option yylineno
DIGIT [0-9]
LETTER [a-zA-Z_]
HEX [a-fA-F0-9]
BINARY [0-1]
SCI [Ee][+-]?{DIGIT}+
FL (f|F)
%{
#include <stdio.h>
#include "gmConfig.h"
#include "gmParser.cpp.h"
%}
%%
"/*" {
int c;
while((c = yyinput()) != 0)
{
if(c == '*')
{
if((c = yyinput()) == '/')
break;
else
unput(c);
}
}
}
"//"[^\n]* { /* eat up comments */ }
"local" { return(KEYWORD_LOCAL); }
"global" { return(KEYWORD_GLOBAL); }
"member" { return(KEYWORD_MEMBER); }
"and" { return(KEYWORD_AND); }
"or" { return(KEYWORD_OR); }
"if" { return(KEYWORD_IF); }
"else" { return(KEYWORD_ELSE); }
"while" { return(KEYWORD_WHILE); }
"for" { return(KEYWORD_FOR); }
"foreach" { return(KEYWORD_FOREACH); }
"in" { return(KEYWORD_IN); }
"dowhile" { return(KEYWORD_DOWHILE); }
"break" { return(KEYWORD_BREAK); }
"continue" { return(KEYWORD_CONTINUE); }
"null" { return(KEYWORD_NULL); }
"return" { return(KEYWORD_RETURN); }
"function" { return(KEYWORD_FUNCTION); }
"table" { return(KEYWORD_TABLE); }
"this" { return(KEYWORD_THIS); }
"true" { return(KEYWORD_TRUE); }
"false" { return(KEYWORD_FALSE); }
{LETTER}({LETTER}|{DIGIT})* { return(IDENTIFIER); }
0[xX]{HEX}+ { return(CONSTANT_HEX); }
0[bB]{BINARY}+ { return(CONSTANT_BINARY); }
{DIGIT}+ { return(CONSTANT_INT); }
'(\\.|[^\\'])+' { return(CONSTANT_CHAR); }
{DIGIT}+{SCI}{FL}? { return(CONSTANT_FLOAT); }
{DIGIT}*"."{DIGIT}+({SCI})?{FL}? { return(CONSTANT_FLOAT); }
{DIGIT}+"."{DIGIT}*({SCI})?{FL}? { return(CONSTANT_FLOAT); }
\"(\\.|[^\\"])*\" { return(CONSTANT_STRING); }
`([^`]|`{2})*` { return(CONSTANT_STRING); }
"&&" { return(KEYWORD_AND); }
"||" { return(KEYWORD_OR); }
">>=" { return(SYMBOL_ASGN_BSR); }
"<<=" { return(SYMBOL_ASGN_BSL); }
"+=" { return(SYMBOL_ASGN_ADD); }
"-=" { return(SYMBOL_ASGN_MINUS); }
"*=" { return(SYMBOL_ASGN_TIMES); }
"/=" { return(SYMBOL_ASGN_DIVIDE); }
"%=" { return(SYMBOL_ASGN_REM); }
"&=" { return(SYMBOL_ASGN_BAND); }
"|=" { return(SYMBOL_ASGN_BOR); }
"^=" { return(SYMBOL_ASGN_BXOR); }
">>" { return(SYMBOL_RIGHT_SHIFT); }
"<<" { return(SYMBOL_LEFT_SHIFT); }
"<=" { return(SYMBOL_LTE); }
">=" { return(SYMBOL_GTE); }
"==" { return(SYMBOL_EQ); }
"!=" { return(SYMBOL_NEQ); }
";" { return(';'); }
"{" { return('{'); }
"}" { return('}'); }
"," { return(','); }
"=" { return('='); }
"(" { return('('); }
")" { return(')'); }
"[" { return('['); }
"]" { return(']'); }
"." { return('.'); }
"!" { return('!'); }
"-" { return('-'); }
"+" { return('+'); }
"*" { return('*'); }
"/" { return('/'); }
"%" { return('%'); }
"<" { return('<'); }
">" { return('>'); }
"&" { return('&'); }
"|" { return('|'); }
"^" { return('^'); }
"~" { return('~'); }
":" { return(':'); }
":" { return(':'); }
[ \t\v\r\n\f] { }
. { return(TOKEN_ERROR); }
%%
// yywrap
int gmwrap()
{
return(1);
}
+14
View File
@@ -0,0 +1,14 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#include "gmConfig.h"
#include "gmStream.h"
+143
View File
@@ -0,0 +1,143 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#ifndef _GMSTREAM_H_
#define _GMSTREAM_H_
#include "gmMem.h"
#include "gmUtil.h"
/// \class gmStream
/// \brief gmStream is an abstract stream class
class gmStream
{
public:
enum
{
ILLEGAL_POS = -1,
};
enum Flags
{
F_EOS = (1 << 0), ///< is the stream at End Of Stream
F_READ = (1 << 1), ///< is the stream readable
F_WRITE = (1 << 2), ///< is the stream writable
F_ERROR = (1 << 3), ///< has a stream error occured
F_SIZE = (1 << 4), ///< are size operations valid on this stream?
F_SEEK = (1 << 5), ///< are seeking operations valid on this stream?
F_TELL = (1 << 6), ///< are tell operations valid on this stream?
F_USER = (1 << 6),
};
gmStream() : m_flags(0), m_swapEndian(false) {}
virtual ~gmStream() {}
/// \brief Seek()
/// \return the p_pos before the seek if seek is supported, else ILLEGAL_POS.
virtual unsigned int Seek(unsigned int p_pos) = 0;
/// \brief Tell()
/// \return the p_pos if tell is supported, else ILLEGAL_POS
virtual unsigned int Tell() const { return (unsigned int) ILLEGAL_POS; }
/// \brief GetSize() will return the size of the stream if the stream supports this feature.
/// \return ILLEGAL_POS if GetSize is not supported
virtual unsigned int GetSize() const { return (unsigned int) ILLEGAL_POS; }
/// \brief Read() will read p_n bytes from the stream into p_buffer.
/// \return the number of bytes successfully read.
virtual unsigned int Read(void * p_buffer, unsigned int p_n) = 0;
/// \brief Write() will write p_n bytes from p_buffer to the stream.
/// \return the number of bytes successfully written
virtual unsigned int Write(const void * p_buffer, unsigned int p_n) = 0;
/// \brief GetFlags() will return the current stream flags
inline Flags GetFlags() const { return (Flags) m_flags; }
//
// Streaming interface (quite slow, but helpful...)
//
/// \brief SetSwapEndianOnWrite()
inline void SetSwapEndianOnWrite(bool a_state) { m_swapEndian = a_state; }
inline bool GetSwapEndianOnWrite() const { return m_swapEndian; }
/// \brief Explicitly set endian, will swap if necessary.
inline void SetEndianOnWrite(gmEndian a_endian)
{
int isLittle = gmIsLittleEndian();
bool needSwap = false;
if( ((a_endian == GM_ENDIAN_LITTLE) && !isLittle)
|| ((a_endian == GM_ENDIAN_BIG) && isLittle) )
{
needSwap = true;
}
SetSwapEndianOnWrite(needSwap);
};
/// \brief Get the endian based on current machine and swap flag.
inline gmEndian GetEndianOnWrite() const
{
int isLittle = gmIsLittleEndian();
if(m_swapEndian)
{
isLittle = !isLittle;
}
if(isLittle)
{
return GM_ENDIAN_LITTLE;
}
else
{
return GM_ENDIAN_BIG;
}
}
template<class T>
gmStream &operator <<(T a_v)
{
if(m_swapEndian)
SwapEndian(a_v);
Write(&a_v, sizeof(T));
return *this;
}
template<class T>
gmStream &operator >>(T &a_v)
{
Read(&a_v, sizeof(T));
return *this;
}
protected:
unsigned int m_flags;
bool m_swapEndian;
private:
inline void SwapEndian(gmuint8 &a_x) { }
inline void SwapEndian(gmint8 &a_x) { }
inline void SwapEndian(gmuint16 &a_x) { a_x = (gmuint16)((a_x << 8) | ((a_x >> 8) & 0xff)); }
inline void SwapEndian(gmint16 &a_x) { a_x = (gmint16)((a_x << 8) | ((a_x >> 8) & 0xff)); }
inline void SwapEndian(gmuint32 &a_x) { a_x = (a_x << 24) | ((a_x << 8) & 0x00ff0000) | ((a_x >> 8) & 0x0000ff00) | ((a_x >> 24) & 0x000000ff); }
inline void SwapEndian(gmint32 &a_x) { a_x = (a_x << 24) | ((a_x << 8) & 0x00ff0000) | ((a_x >> 8) & 0x0000ff00) | ((a_x >> 24) & 0x000000ff); }
inline void SwapEndian(gmfloat &a_x) { SwapEndian((gmuint32 &) a_x); }
};
#endif // _GMSTREAM_H_
@@ -0,0 +1,173 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#include "gmConfig.h"
#include "gmStreamBuffer.h"
gmStreamBufferStatic::gmStreamBufferStatic()
{
m_cursor = 0;
m_size = 0;
m_stream = NULL;
}
gmStreamBufferStatic::gmStreamBufferStatic(const void * p_buffer, unsigned int a_size)
{
Open(p_buffer, a_size);
}
gmStreamBufferStatic::~gmStreamBufferStatic()
{
}
unsigned int gmStreamBufferStatic::Seek(unsigned int p_pos)
{
int oldCursor = m_cursor;
int cursor = p_pos;
if(cursor < 0) return (unsigned int)ILLEGAL_POS;
if((unsigned int) cursor > m_size) return (unsigned int)ILLEGAL_POS;
m_cursor = cursor;
return oldCursor;
}
unsigned int gmStreamBufferStatic::Tell() const
{
return m_cursor;
}
unsigned int gmStreamBufferStatic::GetSize() const
{
return m_size;
}
unsigned int gmStreamBufferStatic::Read(void * p_buffer, unsigned int p_n)
{
unsigned int remain = m_size - m_cursor;
if(p_n > remain)
{
m_flags |= F_EOS;
p_n = remain;
}
memcpy(p_buffer, &m_stream[m_cursor], p_n);
m_cursor += p_n;
return p_n;
}
unsigned int gmStreamBufferStatic::Write(const void * p_buffer, unsigned int p_n)
{
m_flags |= F_ERROR;
return 0;
}
void gmStreamBufferStatic::Open(const void * p_buffer, unsigned int a_size)
{
m_cursor = 0;
m_size = a_size;
m_stream = (const char *) p_buffer;
m_flags = F_READ | F_SIZE | F_SEEK | F_TELL;
}
//
// gmStreamBufferDynamic
//
gmStreamBufferDynamic::gmStreamBufferDynamic()
{
m_cursor = 0;
m_flags = F_READ | F_WRITE | F_SIZE | F_SEEK | F_TELL;
}
gmStreamBufferDynamic::~gmStreamBufferDynamic()
{
}
unsigned int gmStreamBufferDynamic::Seek(unsigned int p_pos)
{
int oldCursor = m_cursor;
int cursor = p_pos;
if(cursor < 0)
return (unsigned int)ILLEGAL_POS;
if((unsigned int) cursor > m_stream.Count())
return (unsigned int)ILLEGAL_POS;
m_cursor = cursor;
return oldCursor;
}
unsigned int gmStreamBufferDynamic::Tell() const
{
return m_cursor;
}
unsigned int gmStreamBufferDynamic::GetSize() const
{
return m_stream.Count();
}
unsigned int gmStreamBufferDynamic::Read(void * p_buffer, unsigned int p_n)
{
unsigned int remain = m_stream.Count() - m_cursor;
if(p_n > remain)
{
// set eof
m_flags |= F_EOS;
p_n = remain;
}
memcpy(p_buffer, m_stream.GetData() + m_cursor, p_n);
m_cursor += p_n;
return p_n;
}
unsigned int gmStreamBufferDynamic::Write(const void * p_buffer, unsigned int p_n)
{
unsigned int remain = m_stream.Count() - m_cursor;
if(p_n > remain)
{
// grow the stream
m_stream.SetCount(m_cursor + p_n);
}
memcpy(m_stream.GetData() + m_cursor, p_buffer, p_n);
m_cursor += p_n;
return p_n;
}
void gmStreamBufferDynamic::Reset()
{
m_cursor = 0;
m_flags = F_READ | F_WRITE | F_SIZE | F_SEEK | F_TELL;
m_stream.Reset();
}
void gmStreamBufferDynamic::ResetAndFreeMemory()
{
m_cursor = 0;
m_flags = F_READ | F_WRITE | F_SIZE | F_SEEK | F_TELL;
m_stream.ResetAndFreeMemory();
}

Some files were not shown because too many files have changed in this diff Show More