mirror of
https://github.com/nillerusr/source-engine.git
synced 2026-08-31 06:29:20 +00:00
WIP: port alien swarm and extend engine for asw
This commit is contained in:
@@ -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_
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
_____ __ ___ __ ____ _ __
|
||||
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
|
||||
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
|
||||
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
|
||||
/___/ /_/
|
||||
|
||||
See Copyright Notice in gmMachine.h
|
||||
|
||||
*/
|
||||
|
||||
#include "gmConfig.h"
|
||||
#include "gmCall.h"
|
||||
|
||||
// Must be last header
|
||||
#include "memdbgon.h"
|
||||
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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, ¶mThis);
|
||||
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(<);
|
||||
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 locale’s 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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
$
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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_
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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_
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
_____ __ ___ __ ____ _ __
|
||||
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
|
||||
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
|
||||
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
|
||||
/___/ /_/
|
||||
|
||||
See Copyright Notice in gmMachine.h
|
||||
|
||||
*/
|
||||
|
||||
#include "gmConfig.h"
|
||||
#include "gmHash.h"
|
||||
|
||||
|
||||
// Must be last header
|
||||
#include "memdbgon.h"
|
||||
@@ -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_
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
@@ -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_
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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_
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
_____ __ ___ __ ____ _ __
|
||||
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
|
||||
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
|
||||
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
|
||||
/___/ /_/
|
||||
|
||||
See Copyright Notice in gmMachine.h
|
||||
|
||||
*/
|
||||
|
||||
#include "gmConfig.h"
|
||||
#include "gmMem.h"
|
||||
|
||||
// Must be last header
|
||||
#include "memdbgon.h"
|
||||
@@ -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_
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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_
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -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
@@ -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_
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
_____ __ ___ __ ____ _ __
|
||||
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
|
||||
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
|
||||
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
|
||||
/___/ /_/
|
||||
|
||||
See Copyright Notice in gmMachine.h
|
||||
|
||||
*/
|
||||
|
||||
#include "gmConfig.h"
|
||||
#include "gmStream.h"
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user