code:
/*
   AngelCode Scripting Library
   Copyright (c) 2003-2025 Andreas Jonsson

   This software is provided 'as-is', without any express or implied
   warranty. In no event will the authors be held liable for any
   damages arising from the use of this software.

   Permission is granted to anyone to use this software for any
   purpose, including commercial applications, and to alter it and
   redistribute it freely, subject to the following restrictions:

   1. The origin of this software must not be misrepresented; you
      must not claim that you wrote the original software. If you use
      this software in a product, an acknowledgment in the product
      documentation would be appreciated but is not required.

   2. Altered source versions must be plainly marked as such, and
      must not be misrepresented as being the original software.

   3. This notice may not be removed or altered from any source
      distribution.

   The original version of this library can be located at:
   http://www.angelcode.com/angelscript/

   Andreas Jonsson
   andreas@angelcode.com
*/

//
// as_context.h
//
// This class handles the execution of the byte code
//

#ifndef AS_CONTEXT_H
#define AS_CONTEXT_H

#include "as_config.h"
#include "as_atomic.h"
#include "as_array.h"
#include "as_string.h"
#include "as_objecttype.h"
#include "as_callfunc.h"

BEGIN_AS_NAMESPACE

class asCScriptFunction;
class asCScriptEngine;

class asCContext : public asIScriptContext
{
public:
	// Memory management
	int  AddRef() const;
	int  Release() const;

	// Miscellaneous
	asIScriptEngine *GetEngine() const;

	// Execution
	int             Prepare(asIScriptFunction *func);
	int             Unprepare();
	int             Execute();
	int             Abort();
	int             Suspend();
	asEContextState GetState() const;
	int             PushState();
	int             PopState();
	bool            IsNested(asUINT *nestCount = 0) const;

	// Object pointer for calling class methods
	int SetObject(void *obj);

	// Arguments
	int   SetArgByte(asUINT arg, asBYTE value);
	int   SetArgWord(asUINT arg, asWORD value);
	int   SetArgDWord(asUINT arg, asDWORD value);
	int   SetArgQWord(asUINT arg, asQWORD value);
	int   SetArgFloat(asUINT arg, float value);
	int   SetArgDouble(asUINT arg, double value);
	int   SetArgAddress(asUINT arg, void *addr);
	int   SetArgObject(asUINT arg, void *obj);
	int   SetArgVarType(asUINT arg, void *ptr, int typeId);
	void *GetAddressOfArg(asUINT arg);

	// Return value
	asBYTE  GetReturnByte();
	asWORD  GetReturnWord();
	asDWORD GetReturnDWord();
	asQWORD GetReturnQWord();
	float   GetReturnFloat();
	double  GetReturnDouble();
	void   *GetReturnAddress();
	void   *GetReturnObject();
	void   *GetAddressOfReturnValue();

	// Exception handling
	int                SetException(const char *descr, bool allowCatch = true);
	int                GetExceptionLineNumber(int *column, const char **sectionName);
	asIScriptFunction *GetExceptionFunction();
	const char *       GetExceptionString();
	bool               WillExceptionBeCaught();
	int                SetExceptionCallback(const asSFuncPtr &callback, void *obj, int callConv);
	void               ClearExceptionCallback();

	// Debugging
	int                SetLineCallback(const asSFuncPtr &callback, void *obj, int callConv);
	void               ClearLineCallback();
	asUINT             GetCallstackSize() const;
	asIScriptFunction *GetFunction(asUINT stackLevel);
	int                GetLineNumber(asUINT stackLevel, int *column, const char **sectionName);
	int                GetVarCount(asUINT stackLevel);
	int                GetVar(asUINT varIndex, asUINT stackLevel, const char** name, int* typeId, asETypeModifiers* typeModifiers, bool* isVarOnHeap, int* stackOffset);
#ifdef AS_DEPRECATED
	const char        *GetVarName(asUINT varIndex, asUINT stackLevel);
#endif
	const char        *GetVarDeclaration(asUINT varIndex, asUINT stackLevel, bool includeNamespace);
#ifdef AS_DEPRECATED
	int                GetVarTypeId(asUINT varIndex, asUINT stackLevel);
#endif
	void              *GetAddressOfVar(asUINT varIndex, asUINT stackLevel, bool dontDereference, bool returnAddressOfUnitializedObjects);
	bool               IsVarInScope(asUINT varIndex, asUINT stackLevel);
	int                GetThisTypeId(asUINT stackLevel);
    void              *GetThisPointer(asUINT stackLevel);
	asIScriptFunction *GetSystemFunction();

	// User data
	void *SetUserData(void *data, asPWORD type);
	void *GetUserData(asPWORD type) const;

	// Serialization
	int StartDeserialization();
	int FinishDeserialization();
	int PushFunction(asIScriptFunction *func, void *obj);
	int GetStateRegisters(asUINT stackLevel, asIScriptFunction** callingSystemFunction, asIScriptFunction** initialFunction, asDWORD* origStackPointer, asDWORD* argumentsSize, asQWORD* valueRegister, void** objectRegister, asITypeInfo** objectTypeRegister);
	int GetCallStateRegisters(asUINT stackLevel, asDWORD* stackFramePointer, asIScriptFunction** currentFunction, asDWORD* programPointer, asDWORD* stackPointer, asDWORD* stackIndex);
	int SetStateRegisters(asUINT stackLevel, asIScriptFunction* callingSystemFunction, asIScriptFunction* initialFunction, asDWORD origStackPointer, asDWORD argumentsSize, asQWORD valueRegister, void* objectRegister, asITypeInfo* objectTypeRegister);
	int SetCallStateRegisters(asUINT stackLevel, asDWORD stackFramePointer, asIScriptFunction* currentFunction, asDWORD programPointer, asDWORD stackPointer, asDWORD stackIndex);
	int GetArgsOnStackCount(asUINT stackLevel);
	int GetArgOnStack(asUINT stackLevel, asUINT arg, int* typeId, asUINT *flags, void** address);

public:
	// Internal public functions
	asCContext(asCScriptEngine *engine, bool holdRef);
	virtual ~asCContext();
	asCScriptFunction *GetRealFunc(asCScriptFunction * m_currentFunction, void ** objType);
	int DeserializeProgramPointer(int programPointer, asCScriptFunction * currentFunction, void * object, asDWORD *& p, asCScriptFunction *& realFunc);

//protected:
	friend class asCScriptEngine;

	void CallLineCallback();
	void CallExceptionCallback();

	int  CallGeneric(asCScriptFunction *func);
#ifndef AS_NO_EXCEPTIONS
	void HandleAppException();
#endif
	void DetachEngine();

	void ExecuteNext();
	void CleanStack(bool catchException = false);
	bool CleanStackFrame(bool catchException = false);
	void CleanArgsOnStack();
	void CleanReturnObject();
	void DetermineLiveObjects(asCArray<int> &liveObjects, asUINT stackLevel);

	int  PushCallState();
	void PopCallState();
	void CallScriptFunction(asCScriptFunction *func);
	void CallInterfaceMethod(asCScriptFunction *func);
	void PrepareScriptFunction();

	void SetProgramPointer();

	bool ReserveStackSpace(asUINT size);
	int DetermineStackIndex(asDWORD *ptr) const;

	asDWORD *DeserializeStackPointer(asDWORD);
	asDWORD  SerializeStackPointer(asDWORD *) const;

	void SetInternalException(const char *descr, bool allowCatch = true);
	bool FindExceptionTryCatch();

	// Must be protected for multiple accesses
	mutable asCAtomic m_refCount;

	bool             m_holdEngineRef;
	asCScriptEngine *m_engine;

	asEContextState m_status;
	bool            m_doSuspend;
	bool            m_doAbort;
	bool            m_externalSuspendRequest;

	asCScriptFunction *m_currentFunction;
	asCScriptFunction *m_callingSystemFunction;

	// The call stack holds program pointer, stack pointer, etc for caller functions
	asCArray<size_t>    m_callStack;

	// Dynamically growing local stack
	asCArray<asDWORD *> m_stackBlocks;
	asUINT              m_stackBlockSize;
	asUINT              m_stackIndex;
	asDWORD            *m_originalStackPointer;
	asUINT              m_originalStackIndex;

	// Exception handling
	bool      m_isStackMemoryNotAllocated;
	bool      m_needToCleanupArgs;
	bool      m_inExceptionHandler;
	asCString m_exceptionString;
	int       m_exceptionFunction;
	int       m_exceptionSectionIdx;
	int       m_exceptionLine;
	int       m_exceptionColumn;
	bool      m_exceptionWillBeCaught;

	// The last prepared function, and some cached values related to it
	asCScriptFunction *m_initialFunction;
	int                m_returnValueSize;
	int                m_argumentsSize;

	// Cache for GetArgsOnStack
	asCArray<int>      m_argsOnStackCache;
	asUINT             m_argsOnStackCacheProgPos;
	asCScriptFunction* m_argsOnStackCacheFunc;

	// callbacks
	bool                       m_lineCallback;
	asSSystemFunctionInterface m_lineCallbackFunc;
	void *                     m_lineCallbackObj;

	bool                       m_exceptionCallback;
	asSSystemFunctionInterface m_exceptionCallbackFunc;
	void *                     m_exceptionCallbackObj;

	asCArray<asPWORD> m_userData;

	// Registers available to JIT compiler functions
	asSVMRegisters m_regs;
};

// We need at least 2 PTRs on the stack reserved for exception handling
// We need at least 1 PTR on the stack reserved for calling system functions
const int RESERVE_STACK = 2 * AS_PTR_SIZE;

// For each script function call we push 9 PTRs on the call stack
const int CALLSTACK_FRAME_SIZE = 9;

// TODO: Move these to as_utils.h
int     as_powi(int base, int exponent, bool& isOverflow);
asDWORD as_powu(asDWORD base, asDWORD exponent, bool& isOverflow);
asINT64 as_powi64(asINT64 base, asINT64 exponent, bool& isOverflow);
asQWORD as_powu64(asQWORD base, asQWORD exponent, bool& isOverflow);

// Optional template version of powi if overflow detection is not used.
#if 0
template <class T>
T as_powi(T base, T exponent)
{
	// Test for sign bit (huge number is OK)
	if( exponent & (T(1)<<(sizeof(T)*8-1)) )
		return 0;
	else
	{
		int result = 1;
		while( exponent )
		{
			if( exponent & 1 )
				result *= base;
			exponent >>= 1;
			base *= base;
		}
		return result;
	}
}
#endif

END_AS_NAMESPACE

#endif

repo_name: Antidote/openrpgeditor

license: mit


code:
/*
   AngelCode Scripting Library
   Copyright (c) 2003-2017 Andreas Jonsson

   This software is provided 'as-is', without any express or implied
   warranty. In no event will the authors be held liable for any
   damages arising from the use of this software.

   Permission is granted to anyone to use this software for any
   purpose, including commercial applications, and to alter it and
   redistribute it freely, subject to the following restrictions:

   1. The origin of this software must not be misrepresented; you
      must not claim that you wrote the original software. If you use
      this software in a product, an acknowledgment in the product
      documentation would be appreciated but is not required.

   2. Altered source versions must be plainly marked as such, and
      must not be misrepresented as being the original software.

   3. This notice may not be removed or altered from any source
      distribution.

   The original version of this library can be located at:
   http://www.angelcode.com/angelscript/

   Andreas Jonsson
   andreas@angelcode.com
*/

//
// as_criticalsection.h
//
// Classes for multi threading support
//

#ifndef AS_CRITICALSECTION_H
#define AS_CRITICALSECTION_H

#include "as_config.h"

BEGIN_AS_NAMESPACE

#ifdef AS_NO_THREADS

#define DECLARECRITICALSECTION(x)
#define ENTERCRITICALSECTION(x)
#define LEAVECRITICALSECTION(x)

inline bool tryEnter() { return true; }
#define TRYENTERCRITICALSECTION(x) tryEnter()

#define DECLAREREADWRITELOCK(x)
#define ACQUIREEXCLUSIVE(x)
#define RELEASEEXCLUSIVE(x)
#define ACQUIRESHARED(x)
#define RELEASESHARED(x)

#else

#define DECLARECRITICALSECTION(x)  asCThreadCriticalSection x;
#define ENTERCRITICALSECTION(x)    x.Enter()
#define LEAVECRITICALSECTION(x)    x.Leave()
#define TRYENTERCRITICALSECTION(x) x.TryEnter()

#define DECLAREREADWRITELOCK(x)    asCThreadReadWriteLock x;
#define ACQUIREEXCLUSIVE(x)        x.AcquireExclusive()
#define RELEASEEXCLUSIVE(x)        x.ReleaseExclusive()
#define ACQUIRESHARED(x)           x.AcquireShared()
#define RELEASESHARED(x)           x.ReleaseShared()

#ifdef AS_POSIX_THREADS

END_AS_NAMESPACE
#include <pthread.h>
BEGIN_AS_NAMESPACE

class asCThreadCriticalSection
{
public:
	asCThreadCriticalSection();
	~asCThreadCriticalSection();

	void Enter();
	void Leave();
	bool TryEnter();

protected:
	pthread_mutex_t cs;
};

class asCThreadReadWriteLock
{
public:
	asCThreadReadWriteLock();
	~asCThreadReadWriteLock();

	void AcquireExclusive();
	void ReleaseExclusive();
	bool TryAcquireExclusive();

	void AcquireShared();
	void ReleaseShared();
	bool TryAcquireShared();

protected:
	pthread_rwlock_t lock;
};

#elif defined(AS_WINDOWS_THREADS)

END_AS_NAMESPACE
#ifdef AS_XBOX360
#include <xtl.h>
#else
#ifndef WIN32_LEAN_AND_MEAN
  #define WIN32_LEAN_AND_MEAN
#endif
#ifndef _WIN32_WINNT
  #define _WIN32_WINNT 0x0600 // We need this to get the declaration for Windows Phone compatible Ex functions
#endif
#include <windows.h>
#endif
BEGIN_AS_NAMESPACE

// Undefine macros that cause problems in our code
#undef GetObject
#undef RegisterClass

class asCThreadCriticalSection
{
public:
	asCThreadCriticalSection();
	~asCThreadCriticalSection();

	void Enter();
	void Leave();
	bool TryEnter();

protected:
	CRITICAL_SECTION cs;
};

class asCThreadReadWriteLock
{
public:
	asCThreadReadWriteLock();
	~asCThreadReadWriteLock();

	void AcquireExclusive();
	void ReleaseExclusive();

	void AcquireShared();
	void ReleaseShared();

protected:
	// The Slim Read Write Lock object, SRWLOCK, is more efficient
	// but it is only available from Windows Vista so we cannot use it and
	// maintain compatibility with olders versions of Windows.

	// Critical sections and semaphores are available on Windows XP and onwards.
	// Windows XP is oldest version we support with multithreading.

	// The implementation is based on the following article, that shows
	// how to implement a fair read/write lock that doesn't risk starving
	// the writers:

	// http://doc.qt.nokia.com/qq/qq11-mutex.html

	// TODO: Allow use of SRWLOCK through configuration in as_config.h

	CRITICAL_SECTION    writeLock;
	HANDLE              readLocks;
};

// This constant really should be a member of asCThreadReadWriteLock,
// but it gives a compiler error on MSVC6 so I'm leaving it outside
static const asUINT maxReaders = 10;

#endif

#endif

END_AS_NAMESPACE

#endif


code:
/*
   AngelCode Scripting Library
   Copyright (c) 2003-2024 Andreas Jonsson

   This software is provided 'as-is', without any express or implied
   warranty. In no event will the authors be held liable for any
   damages arising from the use of this software.

   Permission is granted to anyone to use this software for any
   purpose, including commercial applications, and to alter it and
   redistribute it freely, subject to the following restrictions:

   1. The origin of this software must not be misrepresented; you
      must not claim that you wrote the original software. If you use
      this software in a product, an acknowledgment in the product
      documentation would be appreciated but is not required.

   2. Altered source versions must be plainly marked as such, and
      must not be misrepresented as being the original software.

   3. This notice may not be removed or altered from any source
      distribution.

   The original version of this library can be located at:
   http://www.angelcode.com/angelscript/

   Andreas Jonsson
   andreas@angelcode.com
*/

//
// as_datatype.cpp
//
// This class describes the datatype for expressions during compilation
//

#include "as_config.h"
#include "as_datatype.h"
#include "as_tokendef.h"
#include "as_typeinfo.h"
#include "as_objecttype.h"
#include "as_scriptengine.h"
#include "as_tokenizer.h"

BEGIN_AS_NAMESPACE

asCDataType::asCDataType()
{
	tokenType              = ttUnrecognizedToken;
	typeInfo               = 0;
	isReference            = false;
	isReadOnly             = false;
	isAuto                 = false;
	isObjectHandle         = false;
	isConstHandle          = false;
	isHandleToAsHandleType = false;
	ifHandleThenConst      = false;
}

asCDataType::asCDataType(const asCDataType &dt)
{
	tokenType              = dt.tokenType;
	typeInfo               = dt.typeInfo;
	isReference            = dt.isReference;
	isReadOnly             = dt.isReadOnly;
	isAuto                 = dt.isAuto;
	isObjectHandle         = dt.isObjectHandle;
	isConstHandle          = dt.isConstHandle;
	isHandleToAsHandleType = dt.isHandleToAsHandleType;
	ifHandleThenConst      = dt.ifHandleThenConst;
}

asCDataType::~asCDataType()
{
}

bool asCDataType::IsValid() const
{
	if( tokenType == ttUnrecognizedToken &&
		!isObjectHandle )
		return false;

	return true;
}

asCDataType asCDataType::CreateType(asCTypeInfo *ti, bool isConst)
{
	asCDataType dt;

	dt.tokenType        = ttIdentifier;
	dt.typeInfo         = ti;
	dt.isReadOnly       = isConst;

	return dt;
}

asCDataType asCDataType::CreateById(asCScriptEngine* engine, int typeId, bool isConst)
{
	if (typeId & asTYPEID_OBJHANDLE)
	{
		return CreateObjectHandle((asCTypeInfo*)engine->GetTypeInfoById(typeId), isConst);
	}
	else if (typeId & ~asTYPEID_MASK_SEQNBR)
	{
		return CreateType((asCTypeInfo*)engine->GetTypeInfoById(typeId), isConst);
	}
	else
	{
		eTokenType tt;

		switch (typeId)
		{
		case asTYPEID_VOID: tt = ttVoid; break;
		case asTYPEID_BOOL: tt = ttBool; break;
		case asTYPEID_INT8: tt = ttInt8; break;
		case asTYPEID_INT16: tt = ttInt16; break;
		case asTYPEID_INT32: tt = ttInt; break;
		case asTYPEID_INT64: tt = ttInt64; break;
		case asTYPEID_UINT8: tt = ttUInt8; break;
		case asTYPEID_UINT16: tt = ttUInt16; break;
		case asTYPEID_UINT32: tt = ttUInt; break;
		case asTYPEID_UINT64: tt = ttUInt64; break;
		case asTYPEID_FLOAT: tt = ttFloat; break;
		case asTYPEID_DOUBLE: tt = ttDouble; break;
		default: tt = ttInt; break;
		}

		return CreatePrimitive(tt, isConst);
	}
}

asCDataType asCDataType::CreateAuto(bool isConst)
{
	asCDataType dt;

	dt.tokenType        = ttIdentifier;
	dt.isReadOnly       = isConst;
	dt.isAuto           = true;

	return dt;
}

asCDataType asCDataType::CreateObjectHandle(asCTypeInfo *ot, bool isConst)
{
	asCDataType dt;

	asASSERT(CastToObjectType(ot));

	dt.tokenType        = ttIdentifier;
	dt.typeInfo         = ot;
	dt.isObjectHandle   = true;
	dt.isConstHandle    = isConst;

	return dt;
}

asCDataType asCDataType::CreatePrimitive(eTokenType tt, bool isConst)
{
	asCDataType dt;

	dt.tokenType        = tt;
	dt.isReadOnly       = isConst;

	return dt;
}

asCDataType asCDataType::CreateNullHandle()
{
	asCDataType dt;

	dt.tokenType = ttUnrecognizedToken;
	dt.isReadOnly = true;
	dt.isObjectHandle = true;
	dt.isConstHandle = true;

	return dt;
}

bool asCDataType::IsNullHandle() const
{
	if( tokenType == ttUnrecognizedToken &&
		typeInfo == 0 &&
		isObjectHandle  )
		return true;

	return false;
}

asCString asCDataType::Format(asSNameSpace *currNs, bool includeNamespace) const
{
	if( IsNullHandle() )
		return "<null handle>";

	asCString str;

	if( isReadOnly )
		str = "const ";

	// If the type is not declared in the current namespace, then the namespace
	// must always be informed to guarantee that the correct type is informed
	if (includeNamespace || (typeInfo && typeInfo->nameSpace != currNs))
	{
		if (typeInfo && typeInfo->nameSpace && typeInfo->nameSpace->name != "")
			str += typeInfo->nameSpace->name + "::";
	}
	if (typeInfo && typeInfo->nameSpace == 0)
	{
		// If funcDef->nameSpace is null it means the funcDef was declared as member of
		// another type, in which case the scope should be built with the name of that type
		asCDataType dt = asCDataType::CreateType(CastToFuncdefType(typeInfo)->parentClass, false);
		str += dt.Format(currNs, includeNamespace) + "::";
	}

	if( tokenType != ttIdentifier )
	{
		str += asCTokenizer::GetDefinition(tokenType);
	}
	else if( IsArrayType() && typeInfo && !typeInfo->engine->ep.expandDefaultArrayToTemplate )
	{
		asCObjectType *ot = CastToObjectType(typeInfo);
		asASSERT( ot && ot->templateSubTypes.GetLength() == 1 );
		str += ot->templateSubTypes[0].Format(currNs, includeNamespace);
		str += "[]";
	}
	else if(typeInfo)
	{
		str += typeInfo->name;
		asCObjectType *ot = CastToObjectType(typeInfo);
		if( ot && ot->templateSubTypes.GetLength() > 0 )
		{
			str += "<";
			for( asUINT subtypeIndex = 0; subtypeIndex < ot->templateSubTypes.GetLength(); subtypeIndex++ )
			{
				str += ot->templateSubTypes[subtypeIndex].Format(currNs, includeNamespace);
				if( subtypeIndex != ot->templateSubTypes.GetLength()-1 )
					str += ",";
			}
			str += ">";
		}
	}
	else if( isAuto )
	{
		str += "<auto>";
	}
	else
	{
		str = "<unknown>";
	}

	if( isObjectHandle )
	{
		str += "@";
		if( isConstHandle )
			str += "const";
	}

	if( isReference )
		str += "&";

	return str;
}

asCDataType &asCDataType::operator =(const asCDataType &dt)
{
	tokenType              = dt.tokenType;
	isReference            = dt.isReference;
	typeInfo               = dt.typeInfo;
	isReadOnly             = dt.isReadOnly;
	isObjectHandle         = dt.isObjectHandle;
	isConstHandle          = dt.isConstHandle;
	isAuto                 = dt.isAuto;
	isHandleToAsHandleType = dt.isHandleToAsHandleType;
	ifHandleThenConst      = dt.ifHandleThenConst;

	return (asCDataType &)*this;
}

int asCDataType::MakeHandle(bool b, bool acceptHandleForScope)
{
	if( !b )
	{
		isObjectHandle = false;
		isConstHandle = false;
		isHandleToAsHandleType = false;
	}
	else
	{
		if( isAuto )
		{
			isObjectHandle = true;
		}
		else if( !isObjectHandle )
		{
			// Only reference types are allowed to be handles,
			// but not nohandle reference types, and not scoped references
			// (except when returned from registered function)
			// funcdefs are special reference types and support handles
			// value types with asOBJ_ASHANDLE are treated as a handle
			if( (!typeInfo ||
				!((typeInfo->flags & asOBJ_REF) || (typeInfo->flags & asOBJ_TEMPLATE_SUBTYPE) || (typeInfo->flags & asOBJ_ASHANDLE) || (typeInfo->flags & asOBJ_FUNCDEF)) ||
				(typeInfo->flags & asOBJ_NOHANDLE) ||
				((typeInfo->flags & asOBJ_SCOPED) && !acceptHandleForScope)) )
				return -1;

			isObjectHandle = b;
			isConstHandle = false;

			// ASHANDLE supports being handle, but as it really is a value type it will not be marked as a handle
			if( (typeInfo->flags & asOBJ_ASHANDLE) )
			{
				isObjectHandle = false;
				isHandleToAsHandleType = true;
			}
		}
	}

	return 0;
}

int asCDataType::MakeArray(asCScriptEngine *engine, asCModule *module)
{
	if( engine->defaultArrayObjectType == 0 )
		return asINVALID_TYPE;

	bool tmpIsReadOnly = isReadOnly;
	isReadOnly = false;
	asCArray<asCDataType> subTypes;
	subTypes.PushLast(*this);
	asCObjectType *at = engine->GetTemplateInstanceType(engine->defaultArrayObjectType, subTypes, module);
	if (at == 0)
		return asNOT_SUPPORTED;

	isReadOnly = tmpIsReadOnly;

	isObjectHandle = false;
	isConstHandle = false;

	typeInfo = at;
	tokenType = ttIdentifier;

	return 0;
}

int asCDataType::MakeReference(bool b)
{
	isReference = b;

	return 0;
}

int asCDataType::MakeReadOnly(bool b)
{
	if( isObjectHandle )
	{
		isConstHandle = b;
		return 0;
	}

	isReadOnly = b;
	return 0;
}

int asCDataType::MakeHandleToConst(bool b)
{
	if( !isObjectHandle ) return -1;

	isReadOnly = b;
	return 0;
}

bool asCDataType::SupportHandles() const
{
	if( typeInfo &&
		(typeInfo->flags & (asOBJ_REF | asOBJ_ASHANDLE | asOBJ_FUNCDEF)) &&
		!(typeInfo->flags & asOBJ_NOHANDLE) &&
		!isObjectHandle )
		return true;

	return false;
}

bool asCDataType::CanBeInstantiated() const
{
	if( GetSizeOnStackDWords() == 0 ) // Void
		return false;

	if( !IsObject() && !IsFuncdef() ) // Primitives
		return true;

	if (IsNullHandle()) // null
		return false;

	if( IsObjectHandle() && !(typeInfo->flags & asOBJ_NOHANDLE) ) // Handles
		return true;

	// Funcdefs cannot be instantiated without being handles
	// The exception being delegates, but these can only be created as temporary objects
	if (IsFuncdef())
		return false;

	asCObjectType *ot = CastToObjectType(typeInfo);
	if( ot && (ot->flags & asOBJ_REF) && ot->beh.factories.GetLength() == 0 ) // ref types without factories
		return false;

	if( ot && (ot->flags & asOBJ_ABSTRACT) && !IsObjectHandle() ) // Can't instantiate abstract classes
		return false;

	return true;
}

bool asCDataType::IsAbstractClass() const
{
	return typeInfo && (typeInfo->flags & asOBJ_ABSTRACT) ? true : false;
}

bool asCDataType::IsInterface() const
{
	if (typeInfo == 0)
		return false;

	asCObjectType *ot = CastToObjectType(typeInfo);
	return ot && ot->IsInterface();
}

bool asCDataType::CanBeCopied() const
{
	// All primitives can be copied
	if( IsPrimitive() ) return true;

	// Plain-old-data structures can always be copied
	if( typeInfo->flags & asOBJ_POD ) return true;

	// It must be possible to instantiate the type
	if( !CanBeInstantiated() ) return false;

	// It must have a default constructor or factory and the opAssign
	// Alternatively it must have the copy constructor
	asCObjectType *ot = CastToObjectType(typeInfo);
	if (ot && (((ot->beh.construct != 0 || ot->beh.factory != 0) && ot->beh.copy != 0) ||
		       (ot->beh.copyconstruct != 0 || ot->beh.copyfactory != 0)) )
		return true;

	return false;
}

bool asCDataType::IsReadOnly() const
{
	if( isObjectHandle )
		return isConstHandle;

	return isReadOnly;
}

bool asCDataType::IsHandleToConst() const
{
	if( !isObjectHandle ) return false;
	return isReadOnly;
}

bool asCDataType::IsObjectConst() const
{
	if( IsObjectHandle() )
		return IsHandleToConst();

	return IsReadOnly();
}

// TODO: 3.0.0: This should be removed
bool asCDataType::IsArrayType() const
{
	// This is only true if the type used is the default array type, i.e. the one used for the [] syntax form
	if( typeInfo && typeInfo->engine->defaultArrayObjectType )
		return typeInfo->name == typeInfo->engine->defaultArrayObjectType->name;

	return false;
}

bool asCDataType::IsTemplate() const
{
	if( typeInfo && (typeInfo->flags & asOBJ_TEMPLATE) )
		return true;

	return false;
}

bool asCDataType::IsScriptObject() const
{
	if( typeInfo && (typeInfo->flags & asOBJ_SCRIPT_OBJECT) )
		return true;

	return false;
}

asCDataType asCDataType::GetSubType(asUINT subtypeIndex) const
{
	asASSERT(typeInfo);
	asCObjectType *ot = CastToObjectType(typeInfo);
	return ot->templateSubTypes[subtypeIndex];
}

bool asCDataType::operator !=(const asCDataType &dt) const
{
	return !(*this == dt);
}

bool asCDataType::operator ==(const asCDataType &dt) const
{
	if( !IsEqualExceptRefAndConst(dt) ) return false;
	if( isReference != dt.isReference ) return false;
	if( isReadOnly != dt.isReadOnly ) return false;
	if( isConstHandle != dt.isConstHandle ) return false;

	return true;
}

bool asCDataType::IsEqualExceptRef(const asCDataType &dt) const
{
	if( !IsEqualExceptRefAndConst(dt) ) return false;
	if( isReadOnly != dt.isReadOnly ) return false;
	if( isConstHandle != dt.isConstHandle ) return false;

	return true;
}

bool asCDataType::IsEqualExceptRefAndConst(const asCDataType &dt) const
{
	// Check base type
	if( tokenType != dt.tokenType ) return false;
	if( typeInfo != dt.typeInfo ) return false;
	if( isObjectHandle != dt.isObjectHandle ) return false;
	if( isObjectHandle )
		if( isReadOnly != dt.isReadOnly ) return false;

	return true;
}

bool asCDataType::IsEqualExceptConst(const asCDataType &dt) const
{
	if( !IsEqualExceptRefAndConst(dt) ) return false;
	if( isReference != dt.isReference ) return false;

	return true;
}

bool asCDataType::IsPrimitive() const
{
	// Enumerations are primitives
	if( IsEnumType() )
		return true;

	// A registered object is never a primitive neither is a pointer nor an array
	if( typeInfo )
		return false;

	// Null handle doesn't have a typeInfo, but it is not a primitive
	if( tokenType == ttUnrecognizedToken )
		return false;

	return true;
}

bool asCDataType::IsMathType() const
{
	if( tokenType == ttInt || tokenType == ttInt8 || tokenType == ttInt16 || tokenType == ttInt64 ||
		tokenType == ttUInt || tokenType == ttUInt8 || tokenType == ttUInt16 || tokenType == ttUInt64 ||
		tokenType == ttFloat || tokenType == ttDouble )
		return true;

	return false;
}

bool asCDataType::IsIntegerType() const
{
	if( tokenType == ttInt ||
		tokenType == ttInt8 ||
		tokenType == ttInt16 ||
		tokenType == ttInt64 )
		return true;

	// Enums are also integer types
	return IsEnumType();
}

bool asCDataType::IsUnsignedType() const
{
	if( tokenType == ttUInt ||
		tokenType == ttUInt8 ||
		tokenType == ttUInt16 ||
		tokenType == ttUInt64 )
		return true;

	return false;
}

bool asCDataType::IsFloatType() const
{
	if( tokenType == ttFloat )
		return true;

	return false;
}

bool asCDataType::IsDoubleType() const
{
	if( tokenType == ttDouble )
		return true;

	return false;
}

bool asCDataType::IsBooleanType() const
{
	if( tokenType == ttBool )
		return true;

	return false;
}

bool asCDataType::IsObject() const
{
	if( IsPrimitive() )
		return false;

	// Null handle doesn't have an object type but should still be considered an object
	if( typeInfo == 0 )
		return IsNullHandle();

	// Template subtypes shouldn't be considered objects
	return CastToObjectType(typeInfo) ? true : false;
}

bool asCDataType::IsFuncdef() const
{
	if (typeInfo && (typeInfo->flags & asOBJ_FUNCDEF))
		return true;

	return false;
}

int asCDataType::GetSizeInMemoryBytes() const
{
	if( isObjectHandle )
		return 4 * AS_PTR_SIZE;

	if( typeInfo != 0 )
		return typeInfo->size;

	if( tokenType == ttVoid )
		return 0;

	if( tokenType == ttInt8 ||
		tokenType == ttUInt8 )
		return 1;

	if( tokenType == ttInt16 ||
		tokenType == ttUInt16 )
		return 2;

	if( tokenType == ttDouble ||
		tokenType == ttInt64 ||
		tokenType == ttUInt64 )
		return 8;

	if( tokenType == ttBool )
		return AS_SIZEOF_BOOL;

	// ?& is actually a reference + an int
	if (tokenType == ttQuestion)
		return AS_PTR_SIZE * 4 + 4;

	// null handle
	if( tokenType == ttUnrecognizedToken )
		return 4*AS_PTR_SIZE;

	return 4;
}

int asCDataType::GetSizeInMemoryDWords() const
{
	int s = GetSizeInMemoryBytes();
	if( s == 0 ) return 0;
	if( s <= 4 ) return 1;

	// Pad the size to 4 bytes
	if( s & 0x3 )
		s += 4 - (s & 0x3);

	return s/4;
}

int asCDataType::GetSizeOnStackDWords() const
{
	// If the type is the variable type then the typeid is stored on the stack too
	int size = tokenType == ttQuestion ? 1 : 0;

	if( isReference ) return AS_PTR_SIZE + size;

	// TODO: bug: Registered value types are also stored on the stack. Before changing though, check how GetSizeOnStackDWords is used
	//            When called to determine size of type as parameter then it is correct, as objects are implicitly passed by reference in AngelScript
	//            To correct this it would be necessary to know if the method is called for a parameter, or for a local variable
	if( typeInfo && !IsEnumType() ) return AS_PTR_SIZE + size;

	return GetSizeInMemoryDWords() + size;
}

#ifdef WIP_16BYTE_ALIGN
int  asCDataType::GetAlignment() const
{
	if( typeInfo == NULL )
	{
		// TODO: Small primitives should not be aligned to 4 byte boundaries
		return 4; //Default alignment
	}

	return typeInfo->alignment;
}
#endif

asSTypeBehaviour *asCDataType::GetBehaviour() const
{
	if (!typeInfo) return 0;
	asCObjectType *ot = CastToObjectType(typeInfo);
	return ot ? &ot->beh : 0;
}

bool asCDataType::IsEnumType() const
{
	// Do a sanity check on the objectType, to verify that we aren't trying to access memory after it has been released
	asASSERT(typeInfo == 0 || typeInfo->name.GetLength() < 100);

	if (typeInfo && (typeInfo->flags & asOBJ_ENUM))
		return true;

	return false;
}

END_AS_NAMESPACE


code:
/*
   AngelCode Scripting Library
   Copyright (c) 2003-2024 Andreas Jonsson

   This software is provided 'as-is', without any express or implied
   warranty. In no event will the authors be held liable for any
   damages arising from the use of this software.

   Permission is granted to anyone to use this software for any
   purpose, including commercial applications, and to alter it and
   redistribute it freely, subject to the following restrictions:

   1. The origin of this software must not be misrepresented; you
      must not claim that you wrote the original software. If you use
      this software in a product, an acknowledgment in the product
      documentation would be appreciated but is not required.

   2. Altered source versions must be plainly marked as such, and
      must not be misrepresented as being the original software.

   3. This notice may not be removed or altered from any source
      distribution.

   The original version of this library can be located at:
   http://www.angelcode.com/angelscript/

   Andreas Jonsson
   andreas@angelcode.com
*/

//
// as_datatype.h
//
// This class describes the datatype for expressions during compilation
//

#ifndef AS_DATATYPE_H
#define AS_DATATYPE_H

#include "as_tokendef.h"
#include "as_string.h"

BEGIN_AS_NAMESPACE

struct asSTypeBehaviour;
class asCScriptEngine;
class asCTypeInfo;
class asCScriptFunction;
class asCModule;
class asCObjectType;
class asCEnumType;
struct asSNameSpace;

// TODO: refactor: Reference should not be part of the datatype. This should be stored separately, e.g. in asCExprValue
//                 MakeReference, MakeReadOnly, IsReference, IsReadOnly should be removed

class asCDataType
{
public:
	asCDataType();
	asCDataType(const asCDataType &);
	~asCDataType();

	bool IsValid() const;

	asCString Format(asSNameSpace *currNs, bool includeNamespace = false) const;

	static asCDataType CreatePrimitive(eTokenType tt, bool isConst);
	static asCDataType CreateType(asCTypeInfo *ti, bool isConst);
	static asCDataType CreateById(asCScriptEngine* engine, int typeId, bool isConst);
	static asCDataType CreateAuto(bool isConst);
	static asCDataType CreateObjectHandle(asCTypeInfo *ot, bool isConst);
	static asCDataType CreateNullHandle();

	int MakeHandle(bool b, bool acceptHandleForScope = false);
	int MakeArray(asCScriptEngine *engine, asCModule *requestingModule);
	int MakeReference(bool b);
	int MakeReadOnly(bool b);
	int MakeHandleToConst(bool b);
	void SetIfHandleThenConst(bool b) { ifHandleThenConst = b; }
	bool HasIfHandleThenConst() const { return ifHandleThenConst; }

	bool IsTemplate()             const;
	bool IsScriptObject()         const;
	bool IsPrimitive()            const;
	bool IsMathType()             const;
	bool IsObject()               const;
	bool IsReference()            const {return isReference;}
	bool IsAuto()                 const {return isAuto;}
	bool IsReadOnly()             const;
	bool IsIntegerType()          const;
	bool IsUnsignedType()         const;
	bool IsFloatType()            const;
	bool IsDoubleType()           const;
	bool IsBooleanType()          const;
	bool IsObjectHandle()         const {return isObjectHandle;}
	bool IsHandleToAuto()         const {return isAuto && isObjectHandle;}
	bool IsHandleToConst()        const;
	bool IsArrayType()            const;
	bool IsEnumType()             const;
	bool IsAnyType()              const {return tokenType == ttQuestion;}
	bool IsHandleToAsHandleType() const {return isHandleToAsHandleType;}
	bool IsAbstractClass()        const;
	bool IsInterface()            const;
	bool IsFuncdef()              const;

	bool IsObjectConst()    const;

	bool IsEqualExceptRef(const asCDataType &)             const;
	bool IsEqualExceptRefAndConst(const asCDataType &)     const;
	bool IsEqualExceptConst(const asCDataType &)           const;
	bool IsNullHandle()                                    const;

	bool SupportHandles() const;
	bool CanBeInstantiated() const;
	bool CanBeCopied() const;

	bool operator ==(const asCDataType &) const;
	bool operator !=(const asCDataType &) const;

	asCDataType        GetSubType(asUINT subtypeIndex = 0)    const;
	eTokenType         GetTokenType()  const {return tokenType;}
	asCTypeInfo       *GetTypeInfo() const { return typeInfo; }

	int  GetSizeOnStackDWords()  const;
	int  GetSizeInMemoryBytes()  const;
	int  GetSizeInMemoryDWords() const;
#ifdef WIP_16BYTE_ALIGN
	int  GetAlignment()          const;
#endif

	void SetTokenType(eTokenType tt)         {tokenType = tt;}
	void SetTypeInfo(asCTypeInfo *ti)       {typeInfo = ti;}

	asCDataType &operator =(const asCDataType &);

	asSTypeBehaviour *GetBehaviour() const;

protected:
	// Base object type
	eTokenType tokenType;

	// Top level
	bool isReference : 1;
	bool isReadOnly : 1;
	bool isObjectHandle : 1;
	bool isConstHandle : 1;
	bool isAuto : 1;
	bool isHandleToAsHandleType : 1; // Used by the compiler to know how to initialize the object
	bool ifHandleThenConst : 1; // Used when creating template instances to determine if a handle should be const or not
	char dummy : 1;

	// Behaviour type
	asCTypeInfo *typeInfo;
};

END_AS_NAMESPACE

#endif


code:
/*
   AngelCode Scripting Library
   Copyright (c) 2003-2016 Andreas Jonsson

   This software is provided 'as-is', without any express or implied
   warranty. In no event will the authors be held liable for any
   damages arising from the use of this software.

   Permission is granted to anyone to use this software for any
   purpose, including commercial applications, and to alter it and
   redistribute it freely, subject to the following restrictions:

   1. The origin of this software must not be misrepresented; you
      must not claim that you wrote the original software. If you use
      this software in a product, an acknowledgment in the product
      documentation would be appreciated but is not required.

   2. Altered source versions must be plainly marked as such, and
      must not be misrepresented as being the original software.

   3. This notice may not be removed or altered from any source
      distribution.

   The original version of this library can be located at:
   http://www.angelcode.com/angelscript/

   Andreas Jonsson
   andreas@angelcode.com
*/

//
// as_debug.h
//

#ifndef AS_DEBUG_H
#define AS_DEBUG_H

#include "as_config.h"

#if defined(AS_DEBUG)

#ifndef AS_WII
// The Wii SDK doesn't have these, we'll survive without AS_DEBUG

#ifndef _WIN32_WCE
// Neither does WinCE

#ifndef AS_PSVITA
// Possible on PSVita, but requires SDK access

#if !defined(_MSC_VER) && (defined(__GNUC__) || defined(AS_MARMALADE))

#ifdef __ghs__
// WIIU defines __GNUC__ but types are not defined here in 'conventional' way
#include <types.h>
typedef signed char int8_t;
typedef unsigned char uint8_t;
typedef signed short int16_t;
typedef unsigned short uint16_t;
typedef signed int int32_t;
typedef unsigned int uint32_t;
typedef signed long long int64_t;
typedef unsigned long long uint64_t;
typedef float float32_t;
typedef double float64_t;
#else
// Define mkdir for GNUC
#include <sys/stat.h>
#include <sys/types.h>
#define _mkdir(dirname) mkdir(dirname, S_IRWXU)
#endif
#else
#include <direct.h>
#endif

#endif // AS_PSVITA
#endif // _WIN32_WCE
#endif // AS_WII

#endif // !defined(AS_DEBUG)

#if defined(_MSC_VER) && defined(AS_PROFILE)
// Currently only do profiling with MSVC++

#include <mmsystem.h>
#include <direct.h>
#include "as_string.h"
#include "as_map.h"
#include "as_string_util.h"

BEGIN_AS_NAMESPACE

struct TimeCount
{
	double time;
	int    count;
	double max;
	double min;
};

class CProfiler
{
public:
	CProfiler()
	{
		// We need to know how often the clock is updated
		__int64 tps;
		if( !QueryPerformanceFrequency((LARGE_INTEGER *)&tps) )
			usePerformance = false;
		else
		{
			usePerformance = true;
			ticksPerSecond = double(tps);
		}

		timeOffset = GetTime();
	}

	~CProfiler()
	{
		WriteSummary();
	}

	double GetTime()
	{
		if( usePerformance )
		{
			__int64 ticks;
			QueryPerformanceCounter((LARGE_INTEGER *)&ticks);

			return double(ticks)/ticksPerSecond - timeOffset;
		}

		return double(timeGetTime())/1000.0 - timeOffset;
	}

	double Begin(const char *name)
	{
		double time = GetTime();

		// Add the scope to the key
		if( key.GetLength() )
			key += "|";
		key += name;

		// Compensate for the time spent writing to the file
		timeOffset += GetTime() - time;

		return time;
	}

	void End(const char * /*name*/, double beginTime)
	{
		double time = GetTime();

		double elapsed = time - beginTime;

		// Update the profile info for this scope
		asSMapNode<asCString, TimeCount> *cursor;
		if( map.MoveTo(&cursor, key) )
		{
			cursor->value.time += elapsed;
			cursor->value.count++;
			if( cursor->value.max < elapsed )
				cursor->value.max = elapsed;
			if( cursor->value.min > elapsed )
				cursor->value.min = elapsed;
		}
		else
		{
			TimeCount tc = {elapsed, 1, elapsed, elapsed};
			map.Insert(key, tc);
		}

		// Remove the inner most scope from the key
		int n = key.FindLast("|");
		if( n > 0 )
			key.SetLength(n);
		else
			key.SetLength(0);

		// Compensate for the time spent writing to the file
		timeOffset += GetTime() - time;
	}

protected:
	void WriteSummary()
	{
		// Write the analyzed info into a file for inspection
		_mkdir("AS_DEBUG");
		FILE *fp;
		#if _MSC_VER >= 1500 && !defined(AS_MARMALADE)
			fopen_s(&fp, "AS_DEBUG/profiling_summary.txt", "wt");
		#else
			fp = fopen("AS_DEBUG/profiling_summary.txt", "wt");
		#endif
		if( fp == 0 )
			return;

		fprintf(fp, "%-60s %10s %15s %15s %15s %15s\n\n", "Scope", "Count", "Tot time", "Avg time", "Max time", "Min time");

		asSMapNode<asCString, TimeCount> *cursor;
		map.MoveLast(&cursor);
		while( cursor )
		{
			asCString key = cursor->key;
			int count;
			int n = key.FindLast("|", &count);
			if( count )
			{
				key = asCString("                                               ", count) + key.SubString(n+1);
			}

			fprintf(fp, "%-60s %10d %15.6f %15.6f %15.6f %15.6f\n", key.AddressOf(), cursor->value.count, cursor->value.time, cursor->value.time / cursor->value.count, cursor->value.max, cursor->value.min);

			map.MovePrev(&cursor, cursor);
		}

		fclose(fp);
	}

	double  timeOffset;
	double  ticksPerSecond;
	bool    usePerformance;

	asCString                    key;
	asCMap<asCString, TimeCount> map;
};

extern CProfiler g_profiler;

class CProfilerScope
{
public:
	CProfilerScope(const char *name)
	{
		this->name = name;
		beginTime = g_profiler.Begin(name);
	}

	~CProfilerScope()
	{
		g_profiler.End(name, beginTime);
	}

protected:
	const char *name;
	double      beginTime;
};

#define TimeIt(x) CProfilerScope profilescope(x)

END_AS_NAMESPACE

#else // !(_MSC_VER && AS_PROFILE)

// Define it so nothing is done
#define TimeIt(x)

#endif // !(_MSC_VER && AS_PROFILE)

#endif // defined(AS_DEBUG_H)


code:
/*
   AngelCode Scripting Library
   Copyright (c) 2003-2018 Andreas Jonsson

   This software is provided 'as-is', without any express or implied
   warranty. In no event will the authors be held liable for any
   damages arising from the use of this software.

   Permission is granted to anyone to use this software for any
   purpose, including commercial applications, and to alter it and
   redistribute it freely, subject to the following restrictions:

   1. The origin of this software must not be misrepresented; you
      must not claim that you wrote the original software. If you use
      this software in a product, an acknowledgment in the product
      documentation would be appreciated but is not required.

   2. Altered source versions must be plainly marked as such, and
      must not be misrepresented as being the original software.

   3. This notice may not be removed or altered from any source
      distribution.

   The original version of this library can be located at:
   http://www.angelcode.com/angelscript/

   Andreas Jonsson
   andreas@angelcode.com
*/

//
// as_gc.cpp
//
// The implementation of the garbage collector
//

#include <stdlib.h>

#include "as_gc.h"
#include "as_scriptengine.h"
#include "as_scriptobject.h"
#include "as_texts.h"

BEGIN_AS_NAMESPACE

asCGarbageCollector::asCGarbageCollector()
{
	engine          = 0;
	detectState     = clearCounters_init;
	destroyNewState = destroyGarbage_init;
	destroyOldState = destroyGarbage_init;
	numDestroyed    = 0;
	numNewDestroyed = 0;
	numDetected     = 0;
	numAdded        = 0;
	isProcessing    = false;

	seqAtSweepStart[0] = 0;
	seqAtSweepStart[1] = 0;
	seqAtSweepStart[2] = 0;

	circularRefDetectCallbackFunc  = 0;
	circularRefDetectCallbackParam = 0;
}

asCGarbageCollector::~asCGarbageCollector()
{
	// This local typedef is done to workaround a compiler error on
	// MSVC6 when using the typedef declared in the class definition
	typedef asSMapNode_t node_t;
	for( asUINT n = 0; n < freeNodes.GetLength(); n++ )
		asDELETE(freeNodes[n], node_t);
	freeNodes.SetLength(0);
}

int asCGarbageCollector::AddScriptObjectToGC(void *obj, asCObjectType *objType)
{
	if( obj == 0 || objType == 0 )
	{
		engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_GC_RECEIVED_NULL_PTR);
		return asINVALID_ARG;
	}

	engine->CallObjectMethod(obj, objType->beh.addref);
	asSObjTypePair ot = {obj, objType, 0};

	// Invoke the garbage collector to destroy a little garbage as new comes in
	// This will maintain the number of objects in the GC at a maintainable level without
	// halting the application, and without burdening the application with manually invoking the
	// garbage collector.
	if( engine->ep.autoGarbageCollect && gcNewObjects.GetLength() )
	{
		// If the GC is already processing in another thread, then don't try this again
		if( TRYENTERCRITICALSECTION(gcCollecting) )
		{
			// Skip this if the GC is already running in this thread
			if( !isProcessing )
			{
				isProcessing = true;

				// TODO: The number of iterations should be dynamic, and increase
				//       if the number of objects in the garbage collector grows high

				// Run one step of DetectGarbage
				if( gcOldObjects.GetLength() )
				{
					IdentifyGarbageWithCyclicRefs();
					DestroyOldGarbage();
				}

				// Run a few steps of DestroyGarbage
				int iter = (int)gcNewObjects.GetLength();
				if( iter > 10 ) iter = 10;
				while( iter-- > 0 )
					DestroyNewGarbage();

				isProcessing = false;
			}

			LEAVECRITICALSECTION(gcCollecting);
		}
	}

	// Add the data to the gcObjects array in a critical section as
	// another thread might be calling this method at the same time
	ENTERCRITICALSECTION(gcCritical);
	ot.seqNbr = numAdded++;
	gcNewObjects.PushLast(ot);
	LEAVECRITICALSECTION(gcCritical);

	return ot.seqNbr;
}

int asCGarbageCollector::GetObjectInGC(asUINT idx, asUINT *seqNbr, void **obj, asITypeInfo **type)
{
	if( seqNbr ) *seqNbr = 0;
	if( obj )    *obj    = 0;
	if( type )   *type   = 0;

	ENTERCRITICALSECTION(gcCritical);
	asSObjTypePair *o = 0;
	asUINT newObjs = asUINT(gcNewObjects.GetLength());
	if( idx < newObjs )
		o = &gcNewObjects[idx];
	else if( idx < gcOldObjects.GetLength() + newObjs )
		o = &gcOldObjects[idx-newObjs];
	else
	{
		LEAVECRITICALSECTION(gcCritical);
		return asINVALID_ARG;
	}
	if( seqNbr ) *seqNbr = o->seqNbr;
	if( obj )    *obj    = o->obj;
	if( type )   *type   = o->type;
	LEAVECRITICALSECTION(gcCritical);

	return asSUCCESS;
}

// TODO: Should have a flag to tell the garbage collector to automatically determine how many iterations are needed
//       It should then gather statistics such as how many objects has been created since last run, and how many objects
//       are destroyed per iteration, and how many objects are detected as cyclic garbage per iteration.
//       It should try to reach a stable number of objects, i.e. so that on average the number of objects added to
//       the garbage collector is the same as the number of objects destroyed. And it should try to minimize the number
//       of iterations of detections that must be executed per cycle while still identifying the cyclic garbage
//       These variables should also be available for inspection through the gcstatistics.
int asCGarbageCollector::GarbageCollect(asDWORD flags, asUINT iterations)
{
	// If the GC is already processing in another thread, then don't enter here again
	if( TRYENTERCRITICALSECTION(gcCollecting) )
	{
		// If the GC is already processing in this thread, then don't enter here again
		if( isProcessing )
		{
			LEAVECRITICALSECTION(gcCollecting);
			return 1;
		}

		isProcessing = true;

		bool doDetect  = (flags & asGC_DETECT_GARBAGE)  || !(flags & asGC_DESTROY_GARBAGE);
		bool doDestroy = (flags & asGC_DESTROY_GARBAGE) || !(flags & asGC_DETECT_GARBAGE);

		if( flags & asGC_FULL_CYCLE )
		{
			// Reset the state
			if( doDetect )
			{
				// Move all new objects to the old list, so we guarantee that all is detected
				MoveAllObjectsToOldList();
				detectState  = clearCounters_init;
			}
			if( doDestroy )
			{
				destroyNewState = destroyGarbage_init;
				destroyOldState = destroyGarbage_init;
			}

			// The full cycle only works with the objects in the old list so that the
			// set of objects scanned for garbage is fixed even if new objects are added
			// by other threads in parallel.
			unsigned int count = (unsigned int)(gcOldObjects.GetLength());
			for(;;)
			{
				// Detect all garbage with cyclic references
				if( doDetect )
					while( IdentifyGarbageWithCyclicRefs() == 1 ) {}

				// Now destroy all known garbage
				if( doDestroy )
				{
					if( !doDetect )
						while( DestroyNewGarbage() == 1 ) {}
					while( DestroyOldGarbage() == 1 ) {}
				}

				// Run another iteration if any garbage was destroyed
				if( count != (unsigned int)(gcOldObjects.GetLength()) )
					count = (unsigned int)(gcOldObjects.GetLength());
				else
					break;
			}

			isProcessing = false;
			LEAVECRITICALSECTION(gcCollecting);
			return 0;
		}
		else
		{
			while( iterations-- > 0 )
			{
				// Destroy the garbage that we know of
				if( doDestroy )
				{
					DestroyNewGarbage();
					DestroyOldGarbage();
				}

				// Run another incremental step of the identification of cyclic references
				if( doDetect && gcOldObjects.GetLength() > 0 )
					IdentifyGarbageWithCyclicRefs();
			}
		}

		isProcessing = false;
		LEAVECRITICALSECTION(gcCollecting);
	}

	// Return 1 to indicate that the cycle wasn't finished
	return 1;
}

// TODO: Additional statistics to gather
//
//       - How many objects are added on average between each destroyed object
//       - How many objects are added on average between each detected object
//       - how many iterations are needed for each destroyed object
//       - how many iterations are needed for each detected object
//
//       The average must have a decay so that long running applications will not suffer
//       from objects being created early on in the application and then never destroyed.
//
//       This ought to be possible to accomplish by holding two buckets.
//       Numbers will be accumulated in one bucket while the other is held fixed.
//       When returning the average it should use a weighted average between the two buckets using the size as weight.
//       When a bucket is filled up, the buckets are switched, and then new bucket is emptied to gather new statistics.
void asCGarbageCollector::GetStatistics(asUINT *currentSize, asUINT *totalDestroyed, asUINT *totalDetected, asUINT *newObjects, asUINT *totalNewDestroyed) const
{
	// It is not necessary to protect this with critical sections, however
	// as it is not protected the variables can be filled in slightly different
	// moments and might not match perfectly when inspected by the application
	// afterwards.

	if( currentSize )
		*currentSize = (asUINT)(gcNewObjects.GetLength() + gcOldObjects.GetLength());

	if( totalDestroyed )
		*totalDestroyed = numDestroyed;

	if( totalDetected )
		*totalDetected = numDetected;

	if( newObjects )
		*newObjects = (asUINT)gcNewObjects.GetLength();

	if( totalNewDestroyed )
		*totalNewDestroyed = numNewDestroyed;
}

asCGarbageCollector::asSObjTypePair asCGarbageCollector::GetNewObjectAtIdx(int idx)
{
	// We need to protect this access with a critical section as
	// another thread might be appending an object at the same time
	ENTERCRITICALSECTION(gcCritical);
	asSObjTypePair gcObj = gcNewObjects[idx];
	LEAVECRITICALSECTION(gcCritical);

	return gcObj;
}

asCGarbageCollector::asSObjTypePair asCGarbageCollector::GetOldObjectAtIdx(int idx)
{
	// We need to protect this access with a critical section as
	// another thread might be appending an object at the same time
	ENTERCRITICALSECTION(gcCritical);
	asSObjTypePair gcObj = gcOldObjects[idx];
	LEAVECRITICALSECTION(gcCritical);

	return gcObj;
}

void asCGarbageCollector::RemoveNewObjectAtIdx(int idx)
{
	// We need to protect this update with a critical section as
	// another thread might be appending an object at the same time
	ENTERCRITICALSECTION(gcCritical);
	if( idx == (int)gcNewObjects.GetLength() - 1)
		gcNewObjects.PopLast();
	else
		gcNewObjects[idx] = gcNewObjects.PopLast();
	LEAVECRITICALSECTION(gcCritical);
}

void asCGarbageCollector::RemoveOldObjectAtIdx(int idx)
{
	// We need to protect this update with a critical section as
	// another thread might be appending an object at the same time
	ENTERCRITICALSECTION(gcCritical);
	if( idx == (int)gcOldObjects.GetLength() - 1)
		gcOldObjects.PopLast();
	else
		gcOldObjects[idx] = gcOldObjects.PopLast();
	LEAVECRITICALSECTION(gcCritical);
}

void asCGarbageCollector::MoveObjectToOldList(int idx)
{
	// We need to protect this update with a critical section as
	// another thread might be appending an object at the same time
	ENTERCRITICALSECTION(gcCritical);
	gcOldObjects.PushLast(gcNewObjects[idx]);
	if( idx == (int)gcNewObjects.GetLength() - 1)
		gcNewObjects.PopLast();
	else
		gcNewObjects[idx] = gcNewObjects.PopLast();
	LEAVECRITICALSECTION(gcCritical);
}

void asCGarbageCollector::MoveAllObjectsToOldList()
{
	// We need to protect this update with a critical section as
	// another thread might be appending an object at the same time
	ENTERCRITICALSECTION(gcCritical);
	if( gcOldObjects.Concatenate(gcNewObjects) )
		gcNewObjects.SetLength(0);
	LEAVECRITICALSECTION(gcCritical);
}

int asCGarbageCollector::DestroyNewGarbage()
{
	// This function will only be called within the critical section gcCollecting
	asASSERT(isProcessing);

	for(;;)
	{
		switch( destroyNewState )
		{
		case destroyGarbage_init:
		{
			// If there are no objects to be freed then don't start
			if( gcNewObjects.GetLength() == 0 )
				return 0;

			// Update the seqAtSweepStart which is used to determine when
			// to move an object from the new set to the old set
			seqAtSweepStart[0] = seqAtSweepStart[1];
			seqAtSweepStart[1] = seqAtSweepStart[2];
			seqAtSweepStart[2] = numAdded;

			destroyNewIdx = (asUINT)-1;
			destroyNewState = destroyGarbage_loop;
		}
		break;

		case destroyGarbage_loop:
		case destroyGarbage_haveMore:
		{
			// If the refCount has reached 1, then only the GC still holds a
			// reference to the object, thus we don't need to worry about the
			// application touching the objects during collection.

			// Destroy all objects that have refCount == 1. If any objects are
			// destroyed, go over the list again, because it may have made more
			// objects reach refCount == 1.
			if( ++destroyNewIdx < gcNewObjects.GetLength() )
			{
				asSObjTypePair gcObj = GetNewObjectAtIdx(destroyNewIdx);
				if( engine->CallObjectMethodRetInt(gcObj.obj, gcObj.type->beh.gcGetRefCount) == 1 )
				{
					// Release the object immediately

					// Make sure the refCount is really 0, because the
					// destructor may have increased the refCount again.
					bool addRef = false;
					if( gcObj.type->flags & asOBJ_SCRIPT_OBJECT )
					{
						// Script objects may actually be resurrected in the destructor
						int refCount = ((asCScriptObject*)gcObj.obj)->Release();
						if( refCount > 0 ) addRef = true;
					}
					else
						engine->CallObjectMethod(gcObj.obj, gcObj.type->beh.release);

					// Was the object really destroyed?
					if( !addRef )
					{
						numDestroyed++;
						numNewDestroyed++;
						RemoveNewObjectAtIdx(destroyNewIdx);
						destroyNewIdx--;
					}
					else
					{
						// Since the object was resurrected in the
						// destructor, we must add our reference again
						engine->CallObjectMethod(gcObj.obj, gcObj.type->beh.addref);
					}

					destroyNewState = destroyGarbage_haveMore;
				}
				// Check if this object has been inspected 3 times already, and if so move it to the
				// set of old objects that are less likely to become garbage in a short time
				// TODO: Is 3 really a good value? Should the number of times be dynamic?
				else if( gcObj.seqNbr < seqAtSweepStart[0] )
				{
					// We've already verified this object multiple times. It is likely
					// to live for quite a long time so we'll move it to the list if old objects
					MoveObjectToOldList(destroyNewIdx);
					destroyNewIdx--;
				}

				// Allow the application to work a little
				return 1;
			}
			else
			{
				if( destroyNewState == destroyGarbage_haveMore )
				{
					// Restart the cycle
					destroyNewState = destroyGarbage_init;
				}
				else
				{
					// Restart the cycle
					destroyNewState = destroyGarbage_init;

					// Return 0 to tell the application that there
					// is no more garbage to destroy at the moment
					return 0;
				}
			}
		}
		break;
		}
	}

	// Shouldn't reach this point
	UNREACHABLE_RETURN;
}

int asCGarbageCollector::ReportAndReleaseUndestroyedObjects()
{
	// This function will only be called as the engine is shutting down

	int items = 0;
	for( asUINT n = 0; n < gcOldObjects.GetLength(); n++ )
	{
		asSObjTypePair gcObj = GetOldObjectAtIdx(n);

		int refCount = 0;
		if( gcObj.type->beh.gcGetRefCount && engine->scriptFunctions[gcObj.type->beh.gcGetRefCount] )
			refCount = engine->CallObjectMethodRetInt(gcObj.obj, gcObj.type->beh.gcGetRefCount);

		// Report the object as not being properly destroyed
		asCString msg;
		msg.Format(TXT_d_GC_CANNOT_FREE_OBJ_OF_TYPE_s_REF_COUNT_d, gcObj.seqNbr, gcObj.type->name.AddressOf(), refCount - 1);
		engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, msg.AddressOf());

		// Add additional info for builtin types
		if( gcObj.type->name == "$func" )
		{
			// Unfortunately we can't show the function declaration here, because the engine may have released the parameter list already so the declaration would only be misleading
			// We need to show the function type too as for example delegates do not have a name
			msg.Format(TXT_PREV_FUNC_IS_NAMED_s_TYPE_IS_d, reinterpret_cast<asCScriptFunction*>(gcObj.obj)->GetName(), reinterpret_cast<asCScriptFunction*>(gcObj.obj)->GetFuncType());
			engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, msg.AddressOf());
		}
		else if( gcObj.type->name == "$obj" )
		{
			msg.Format(TXT_PREV_TYPE_IS_NAMED_s, reinterpret_cast<asCObjectType*>(gcObj.obj)->GetName());
			engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, msg.AddressOf());
		}

		// Release the reference that the GC holds if the release functions is still available
		if( gcObj.type->beh.release && engine->scriptFunctions[gcObj.type->beh.release] )
			engine->CallObjectMethod(gcObj.obj, gcObj.type->beh.release);

		items++;
	}
	return items;
}

int asCGarbageCollector::DestroyOldGarbage()
{
	// This function will only be called within the critical section gcCollecting
	asASSERT(isProcessing);

	for(;;)
	{
		switch( destroyOldState )
		{
		case destroyGarbage_init:
		{
			// If there are no objects to be freed then don't start
			if( gcOldObjects.GetLength() == 0 )
				return 0;

			destroyOldIdx = (asUINT)-1;
			destroyOldState = destroyGarbage_loop;
		}
		break;

		case destroyGarbage_loop:
		case destroyGarbage_haveMore:
		{
			// If the refCount has reached 1, then only the GC still holds a
			// reference to the object, thus we don't need to worry about the
			// application touching the objects during collection.

			// Destroy all objects that have refCount == 1. If any objects are
			// destroyed, go over the list again, because it may have made more
			// objects reach refCount == 1.
			if( ++destroyOldIdx < gcOldObjects.GetLength() )
			{
				asSObjTypePair gcObj = GetOldObjectAtIdx(destroyOldIdx);

				if( gcObj.type->beh.gcGetRefCount == 0 )
				{
					// If circular references are formed with registered types that hasn't
					// registered the GC behaviours, then the engine may be forced to free
					// the object type before the actual object instance. In this case we
					// will be forced to skip the destruction of the objects, so as not to
					// crash the application.
					asCString msg;
					msg.Format(TXT_d_GC_CANNOT_FREE_OBJ_OF_TYPE_s, gcObj.seqNbr, gcObj.type->name.AddressOf());
					engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, msg.AddressOf());

					// Just remove the object, as we will not bother to destroy it
					numDestroyed++;
					RemoveOldObjectAtIdx(destroyOldIdx);
					destroyOldIdx--;
				}
				else if( engine->CallObjectMethodRetInt(gcObj.obj, gcObj.type->beh.gcGetRefCount) == 1 )
				{
					// Release the object immediately

					// Make sure the refCount is really 0, because the
					// destructor may have increased the refCount again.
					bool addRef = false;
					if( gcObj.type->flags & asOBJ_SCRIPT_OBJECT )
					{
						// Script objects may actually be resurrected in the destructor
						int refCount = ((asCScriptObject*)gcObj.obj)->Release();
						if( refCount > 0 ) addRef = true;
					}
					else
						engine->CallObjectMethod(gcObj.obj, gcObj.type->beh.release);

					// Was the object really destroyed?
					if( !addRef )
					{
						numDestroyed++;
						RemoveOldObjectAtIdx(destroyOldIdx);
						destroyOldIdx--;
					}
					else
					{
						// Since the object was resurrected in the
						// destructor, we must add our reference again
						engine->CallObjectMethod(gcObj.obj, gcObj.type->beh.addref);
					}

					destroyOldState = destroyGarbage_haveMore;
				}

				// Allow the application to work a little
				return 1;
			}
			else
			{
				if( destroyOldState == destroyGarbage_haveMore )
				{
					// Restart the cycle
					destroyOldState = destroyGarbage_init;
				}
				else
				{
					// Restart the cycle
					destroyOldState = destroyGarbage_init;

					// Return 0 to tell the application that there
					// is no more garbage to destroy at the moment
					return 0;
				}
			}
		}
		break;
		}
	}

	// Shouldn't reach this point
	UNREACHABLE_RETURN;
}

int asCGarbageCollector::IdentifyGarbageWithCyclicRefs()
{
	// This function will only be called within the critical section gcCollecting
	asASSERT(isProcessing);

	for(;;)
	{
		switch( detectState )
		{
		case clearCounters_init:
			detectState = clearCounters_loop;
		break;

		case clearCounters_loop:
		{
			// Decrease reference counter for all objects removed from the map
			asSMapNode<void*, asSIntTypePair> *cursor = 0;
			gcMap.MoveFirst(&cursor);
			if( cursor )
			{
				void *obj = gcMap.GetKey(cursor);
				asSIntTypePair it = gcMap.GetValue(cursor);

				engine->CallObjectMethod(obj, it.type->beh.release);

				ReturnNode(gcMap.Remove(cursor));

				return 1;
			}

			detectState = buildMap_init;
		}
		break;

		case buildMap_init:
			detectIdx = 0;
			detectState = buildMap_loop;
		break;

		case buildMap_loop:
		{
			// Build a map of objects that will be checked, the map will
			// hold the object pointer as key, and the gcCount and the
			// object's type as value. As objects are added to the map the
			// gcFlag must be set in the objects, so we can be verify if
			// the object is accessed during the GC cycle.

			// If an object is removed from the gcObjects list during the
			// iteration of this step, it is possible that an object won't
			// be used during the analyzing for cyclic references. This
			// isn't a problem, as the next time the GC cycle starts the
			// object will be verified.
			if( detectIdx < gcOldObjects.GetLength() )
			{
				// Add the gc count for this object
				asSObjTypePair gcObj = GetOldObjectAtIdx(detectIdx);

				int refCount = 0;
				if( gcObj.type->beh.gcGetRefCount )
					refCount = engine->CallObjectMethodRetInt(gcObj.obj, gcObj.type->beh.gcGetRefCount);

				if( refCount > 1 )
				{
					asSIntTypePair it = {refCount-1, gcObj.type};

					gcMap.Insert(GetNode(gcObj.obj, it));

					// Increment the object's reference counter when putting it in the map
					engine->CallObjectMethod(gcObj.obj, gcObj.type->beh.addref);

					// Mark the object so that we can
					// see if it has changed since read
					engine->CallObjectMethod(gcObj.obj, gcObj.type->beh.gcSetFlag);
				}

				detectIdx++;

				// Let the application work a little
				return 1;
			}
			else
				detectState = countReferences_init;
		}
		break;

		case countReferences_init:
		{
			gcMap.MoveFirst(&gcMapCursor);
			detectState = countReferences_loop;
		}
		break;

		case countReferences_loop:
		{
			// Call EnumReferences on all objects in the map to count the number
			// of references reachable from between objects in the map. If all
			// references for an object in the map is reachable from other objects
			// in the map, then we know that no outside references are held for
			// this object, thus it is a potential dead object in a circular reference.

			// If the gcFlag is cleared for an object we consider the object alive
			// and referenced from outside the GC, thus we don't enumerate its references.

			// Any new objects created after this step in the GC cycle won't be
			// in the map, and is thus automatically considered alive.
			if( gcMapCursor )
			{
				void *obj = gcMap.GetKey(gcMapCursor);
				asCObjectType *type = gcMap.GetValue(gcMapCursor).type;
				gcMap.MoveNext(&gcMapCursor, gcMapCursor);

				if( engine->CallObjectMethodRetBool(obj, type->beh.gcGetFlag) )
				{
					engine->CallObjectMethod(obj, engine, type->beh.gcEnumReferences);
				}

				// Allow the application to work a little
				return 1;
			}
			else
				detectState = detectGarbage_init;
		}
		break;

		case detectGarbage_init:
		{
			gcMap.MoveFirst(&gcMapCursor);
			liveObjects.SetLength(0);
			detectState = detectGarbage_loop1;
		}
		break;

		case detectGarbage_loop1:
		{
			// All objects that are known not to be dead must be removed from the map,
			// along with all objects they reference. What remains in the map after
			// this pass is sure to be dead objects in circular references.

			// An object is considered alive if its gcFlag is cleared, or all the
			// references were not found in the map.

			// Add all alive objects from the map to the liveObjects array
			if( gcMapCursor )
			{
				asSMapNode<void*, asSIntTypePair> *cursor = gcMapCursor;
				gcMap.MoveNext(&gcMapCursor, gcMapCursor);

				void *obj = gcMap.GetKey(cursor);
				asSIntTypePair it = gcMap.GetValue(cursor);

				bool gcFlag = engine->CallObjectMethodRetBool(obj, it.type->beh.gcGetFlag);
				if( !gcFlag || it.i > 0 )
				{
					liveObjects.PushLast(obj);
				}

				// Allow the application to work a little
				return 1;
			}
			else
				detectState = detectGarbage_loop2;
		}
		break;

		case detectGarbage_loop2:
		{
			// In this step we are actually removing the alive objects from the map.
			// As the object is removed, all the objects it references are added to the
			// liveObjects list, by calling EnumReferences. Only objects still in the map
			// will be added to the liveObjects list.
			if( liveObjects.GetLength() )
			{
				void *gcObj = liveObjects.PopLast();
				asCObjectType *type = 0;

				// Remove the object from the map to mark it as alive
				asSMapNode<void*, asSIntTypePair> *cursor = 0;
				if( gcMap.MoveTo(&cursor, gcObj) )
				{
					type = gcMap.GetValue(cursor).type;
					ReturnNode(gcMap.Remove(cursor));

					// We need to decrease the reference count again as we remove the object from the map
					engine->CallObjectMethod(gcObj, type->beh.release);

					// Enumerate all the object's references so that they too can be marked as alive
					engine->CallObjectMethod(gcObj, engine, type->beh.gcEnumReferences);
				}

				// Allow the application to work a little
				return 1;
			}
			else
				detectState = verifyUnmarked_init;
		}
		break;

		case verifyUnmarked_init:
			gcMap.MoveFirst(&gcMapCursor);
			detectState = verifyUnmarked_loop;
			break;

		case verifyUnmarked_loop:
		{
			// In this step we must make sure that none of the objects still in the map
			// has been touched by the application. If they have then we must run the
			// detectGarbage loop once more.
			if( gcMapCursor )
			{
				void *gcObj = gcMap.GetKey(gcMapCursor);
				asCObjectType *type = gcMap.GetValue(gcMapCursor).type;

				bool gcFlag = engine->CallObjectMethodRetBool(gcObj, type->beh.gcGetFlag);
				if( !gcFlag )
				{
					// The unmarked object was touched, rerun the detectGarbage loop
					detectState = detectGarbage_init;
				}
				else
					gcMap.MoveNext(&gcMapCursor, gcMapCursor);

				// Allow the application to work a little
				return 1;
			}
			else
			{
				// No unmarked object was touched, we can now be sure
				// that objects that have gcCount == 0 really is garbage
				detectState = breakCircles_init;
			}
		}
		break;

		case breakCircles_init:
		{
			gcMap.MoveFirst(&gcMapCursor);
			detectState = breakCircles_loop;

			// If the application has requested a callback for detected circular references,
			// then make that callback now for all the objects in the list. This step is not
			// done in incremental steps as it is only meant for debugging purposes and thus
			// doesn't require interactivity
			if (gcMapCursor && circularRefDetectCallbackFunc)
			{
				while (gcMapCursor)
				{
					void *gcObj = gcMap.GetKey(gcMapCursor);
					asCObjectType *type = gcMap.GetValue(gcMapCursor).type;
					circularRefDetectCallbackFunc(type, gcObj, circularRefDetectCallbackParam);

					gcMap.MoveNext(&gcMapCursor, gcMapCursor);
				}

				// Reset iterator
				gcMap.MoveFirst(&gcMapCursor);
			}
		}
		break;

		case breakCircles_loop:
		case breakCircles_haveGarbage:
		{
			// All objects in the map are now known to be dead objects
			// kept alive through circular references. To be able to free
			// these objects we need to force the breaking of the circle
			// by having the objects release their references.
			if( gcMapCursor )
			{
				numDetected++;
				void *gcObj = gcMap.GetKey(gcMapCursor);
				asCObjectType *type = gcMap.GetValue(gcMapCursor).type;
				if( type->flags & asOBJ_SCRIPT_OBJECT )
				{
					// For script objects we must call the class destructor before
					// releasing the references, otherwise the destructor may not
					// be able to perform the necessary clean-up as the handles will
					// be null.
					reinterpret_cast<asCScriptObject*>(gcObj)->CallDestructor();
				}
				engine->CallObjectMethod(gcObj, engine, type->beh.gcReleaseAllReferences);

				gcMap.MoveNext(&gcMapCursor, gcMapCursor);

				detectState = breakCircles_haveGarbage;

				// Allow the application to work a little
				return 1;
			}
			else
			{
				// If no garbage was detected we can finish now
				if( detectState != breakCircles_haveGarbage )
				{
					// Restart the GC
					detectState = clearCounters_init;
					return 0;
				}
				else
				{
					// Restart the GC
					detectState = clearCounters_init;
					return 1;
				}
			}
		}
		} // switch
	}

	// Shouldn't reach this point
	UNREACHABLE_RETURN;
}

asCGarbageCollector::asSMapNode_t *asCGarbageCollector::GetNode(void *obj, asSIntTypePair it)
{
	// This function will only be called within the critical section gcCollecting
	asASSERT(isProcessing);

	asSMapNode_t *node;
	if( freeNodes.GetLength() )
		node = freeNodes.PopLast();
	else
	{
		node = asNEW(asSMapNode_t);
		if( !node )
		{
			// Out of memory
			return 0;
		}
	}

	node->Init(obj, it);
	return node;
}

void asCGarbageCollector::ReturnNode(asSMapNode_t *node)
{
	// This function will only be called within the critical section gcCollecting
	asASSERT(isProcessing);

	if( node )
		freeNodes.PushLast(node);
}

void asCGarbageCollector::GCEnumCallback(void *reference)
{
	// This function will only be called within the critical section gcCollecting
	asASSERT(isProcessing);

	if( detectState == countReferences_loop )
	{
		// Find the reference in the map
		asSMapNode<void*, asSIntTypePair> *cursor = 0;
		if( gcMap.MoveTo(&cursor, reference) )
		{
			// Decrease the counter in the map for the reference
			gcMap.GetValue(cursor).i--;
		}
	}
	else if( detectState == detectGarbage_loop2 )
	{
		// Find the reference in the map
		asSMapNode<void*, asSIntTypePair> *cursor = 0;
		if( gcMap.MoveTo(&cursor, reference) )
		{
			// Add the object to the list of objects to mark as alive
			liveObjects.PushLast(reference);
		}
	}
}

END_AS_NAMESPACE


code:
/*
   AngelCode Scripting Library
   Copyright (c) 2003-2018 Andreas Jonsson

   This software is provided 'as-is', without any express or implied
   warranty. In no event will the authors be held liable for any
   damages arising from the use of this software.

   Permission is granted to anyone to use this software for any
   purpose, including commercial applications, and to alter it and
   redistribute it freely, subject to the following restrictions:

   1. The origin of this software must not be misrepresented; you
      must not claim that you wrote the original software. If you use
      this software in a product, an acknowledgment in the product
      documentation would be appreciated but is not required.

   2. Altered source versions must be plainly marked as such, and
      must not be misrepresented as being the original software.

   3. This notice may not be removed or altered from any source
      distribution.

   The original version of this library can be located at:
   http://www.angelcode.com/angelscript/

   Andreas Jonsson
   andreas@angelcode.com
*/

//
// as_gc.h
//
// The garbage collector is used to resolve cyclic references
//

#ifndef AS_GC_H
#define AS_GC_H

#include "as_config.h"
#include "as_array.h"
#include "as_map.h"
#include "as_thread.h"

BEGIN_AS_NAMESPACE

class asCScriptEngine;
class asCObjectType;

class asCGarbageCollector
{
public:
	asCGarbageCollector();
	~asCGarbageCollector();

	int    GarbageCollect(asDWORD flags, asUINT iterations);
	void   GetStatistics(asUINT *currentSize, asUINT *totalDestroyed, asUINT *totalDetected, asUINT *newObjects, asUINT *totalNewDestroyed) const;
	void   GCEnumCallback(void *reference);
	int    AddScriptObjectToGC(void *obj, asCObjectType *objType);
	int    GetObjectInGC(asUINT idx, asUINT *seqNbr, void **obj, asITypeInfo **type);

	int    ReportAndReleaseUndestroyedObjects();

	asCScriptEngine *engine;

	// Callback for when circular reference are detected
	asCIRCULARREFFUNC_t circularRefDetectCallbackFunc;
	void *              circularRefDetectCallbackParam;

protected:
	struct asSObjTypePair {void *obj; asCObjectType *type; asUINT seqNbr;};
	struct asSIntTypePair {int i; asCObjectType *type;};
	typedef asSMapNode<void*, asSIntTypePair> asSMapNode_t;

	enum egcDestroyState
	{
		destroyGarbage_init = 0,
		destroyGarbage_loop,
		destroyGarbage_haveMore
	};

	enum egcDetectState
	{
		clearCounters_init = 0,
		clearCounters_loop,
		buildMap_init,
		buildMap_loop,
		countReferences_init,
		countReferences_loop,
		detectGarbage_init,
		detectGarbage_loop1,
		detectGarbage_loop2,
		verifyUnmarked_init,
		verifyUnmarked_loop,
		breakCircles_init,
		breakCircles_loop,
		breakCircles_haveGarbage
	};

	int            DestroyNewGarbage();
	int            DestroyOldGarbage();
	int            IdentifyGarbageWithCyclicRefs();
	asSObjTypePair GetNewObjectAtIdx(int idx);
	asSObjTypePair GetOldObjectAtIdx(int idx);
	void           RemoveNewObjectAtIdx(int idx);
	void           RemoveOldObjectAtIdx(int idx);
	void           MoveObjectToOldList(int idx);
	void           MoveAllObjectsToOldList();

	// Holds all the objects known by the garbage collector
	asCArray<asSObjTypePair>           gcNewObjects;
	asCArray<asSObjTypePair>           gcOldObjects;

	// This array temporarily holds references to objects known to be live objects
	asCArray<void*>                    liveObjects;

	// This map holds objects currently being searched for cyclic references, it also holds a
	// counter that gives the number of references to the object that the GC can't reach
	asCMap<void*, asSIntTypePair>      gcMap;

	// State variables
	egcDestroyState                    destroyNewState;
	egcDestroyState                    destroyOldState;
	asUINT                             destroyNewIdx;
	asUINT                             destroyOldIdx;
	asUINT                             numDestroyed;
	asUINT                             numNewDestroyed;
	egcDetectState                     detectState;
	asUINT                             detectIdx;
	asUINT                             numDetected;
	asUINT                             numAdded;
	asUINT                             seqAtSweepStart[3];
	asSMapNode_t                      *gcMapCursor;
	bool                               isProcessing;

	// We'll keep a pool of nodes to avoid allocating memory all the time
	asSMapNode_t            *GetNode(void *obj, asSIntTypePair it);
	void                     ReturnNode(asSMapNode_t *node);
	asCArray<asSMapNode_t*>  freeNodes;

	// Critical section for multithreaded access
	DECLARECRITICALSECTION(gcCritical)   // Used for adding/removing objects
	DECLARECRITICALSECTION(gcCollecting) // Used for processing
};

END_AS_NAMESPACE

#endif


code:
/*
   AngelCode Scripting Library
   Copyright (c) 2003-2025 Andreas Jonsson

   This software is provided 'as-is', without any express or implied
   warranty. In no event will the authors be held liable for any
   damages arising from the use of this software.

   Permission is granted to anyone to use this software for any
   purpose, including commercial applications, and to alter it and
   redistribute it freely, subject to the following restrictions:

   1. The origin of this software must not be misrepresented; you
      must not claim that you wrote the original software. If you use
      this software in a product, an acknowledgment in the product
      documentation would be appreciated but is not required.

   2. Altered source versions must be plainly marked as such, and
      must not be misrepresented as being the original software.

   3. This notice may not be removed or altered from any source
      distribution.

   The original version of this library can be located at:
   http://www.angelcode.com/angelscript/

   Andreas Jonsson
   andreas@angelcode.com
*/

//
// as_generic.cpp
//
// This class handles the call to a function registered with asCALL_GENERIC
//

#include "as_datatype.h"
#include "as_generic.h"
#include "as_scriptfunction.h"
#include "as_objecttype.h"
#include "as_scriptengine.h"

BEGIN_AS_NAMESPACE

// TODO: runtime optimize: The access to the arguments should be optimized so that code
//                         doesn't have to count the position of the argument with every call

// internal
asCGeneric::asCGeneric(asCScriptEngine *engine, asCScriptFunction *sysFunction, void *currentObject, asDWORD *stackPointer)
{
	this->engine = engine;
	this->sysFunction = sysFunction;
	this->currentObject = currentObject;
	this->stackPointer = stackPointer;

	objectRegister = 0;
	returnVal = 0;
}

// internal
asCGeneric::~asCGeneric()
{
}

// interface
void *asCGeneric::GetAuxiliary() const
{
	return sysFunction->GetAuxiliary();
}

// interface
asIScriptEngine *asCGeneric::GetEngine() const
{
	return (asIScriptEngine*)engine;
}

// interface
asIScriptFunction *asCGeneric::GetFunction() const
{
	return sysFunction;
}

// interface
void *asCGeneric::GetObject()
{
	return currentObject;
}

// interface
int asCGeneric::GetObjectTypeId() const
{
	asCDataType dt = asCDataType::CreateType(sysFunction->objectType, false);
	return engine->GetTypeIdFromDataType(dt);
}

// interface
int asCGeneric::GetArgCount() const
{
	return (int)sysFunction->parameterTypes.GetLength();
}

// interface
asBYTE asCGeneric::GetArgByte(asUINT arg)
{
	if( arg >= (unsigned)sysFunction->parameterTypes.GetLength() )
		return 0;

	// Verify that the type is correct
	asCDataType *dt = &sysFunction->parameterTypes[arg];
	if( (dt->IsObject() || dt->IsFuncdef()) || dt->IsReference() )
		return 0;

	if( dt->GetSizeInMemoryBytes() != 1 )
		return 0;

	// Determine the position of the argument
	int offset = 0;
	for( asUINT n = 0; n < arg; n++ )
		offset += sysFunction->parameterTypes[n].GetSizeOnStackDWords();

	// Get the value
	return *(asBYTE*)&stackPointer[offset];
}

// interface
asWORD asCGeneric::GetArgWord(asUINT arg)
{
	if( arg >= (unsigned)sysFunction->parameterTypes.GetLength() )
		return 0;

	// Verify that the type is correct
	asCDataType *dt = &sysFunction->parameterTypes[arg];
	if( (dt->IsObject() || dt->IsFuncdef()) || dt->IsReference() )
		return 0;

	if( dt->GetSizeInMemoryBytes() != 2 )
		return 0;

	// Determine the position of the argument
	int offset = 0;
	for( asUINT n = 0; n < arg; n++ )
		offset += sysFunction->parameterTypes[n].GetSizeOnStackDWords();

	// Get the value
	return *(asWORD*)&stackPointer[offset];
}

// interface
asDWORD asCGeneric::GetArgDWord(asUINT arg)
{
	if( arg >= (unsigned)sysFunction->parameterTypes.GetLength() )
		return 0;

	// Verify that the type is correct
	asCDataType *dt = &sysFunction->parameterTypes[arg];
	if( (dt->IsObject() || dt->IsFuncdef()) || dt->IsReference() )
		return 0;

	if( dt->GetSizeInMemoryBytes() != 4 )
		return 0;

	// Determine the position of the argument
	int offset = 0;
	for( asUINT n = 0; n < arg; n++ )
		offset += sysFunction->parameterTypes[n].GetSizeOnStackDWords();

	// Get the value
	return *(asDWORD*)&stackPointer[offset];
}

// interface
asQWORD asCGeneric::GetArgQWord(asUINT arg)
{
	if( arg >= (unsigned)sysFunction->parameterTypes.GetLength() )
		return 0;

	// Verify that the type is correct
	asCDataType *dt = &sysFunction->parameterTypes[arg];
	if( (dt->IsObject() || dt->IsFuncdef()) || dt->IsReference() )
		return 0;

	if( dt->GetSizeInMemoryBytes() != 8 )
		return 0;

	// Determine the position of the argument
	int offset = 0;
	for( asUINT n = 0; n < arg; n++ )
		offset += sysFunction->parameterTypes[n].GetSizeOnStackDWords();

	// Get the value
	return *(asQWORD*)(&stackPointer[offset]);
}

// interface
float asCGeneric::GetArgFloat(asUINT arg)
{
	if( arg >= (unsigned)sysFunction->parameterTypes.GetLength() )
		return 0;

	// Verify that the type is correct
	asCDataType *dt = &sysFunction->parameterTypes[arg];
	if( (dt->IsObject() || dt->IsFuncdef()) || dt->IsReference() )
		return 0;

	if( dt->GetSizeInMemoryBytes() != 4 )
		return 0;

	// Determine the position of the argument
	int offset = 0;
	for( asUINT n = 0; n < arg; n++ )
		offset += sysFunction->parameterTypes[n].GetSizeOnStackDWords();

	// Get the value
	return *(float*)(&stackPointer[offset]);
}

// interface
double asCGeneric::GetArgDouble(asUINT arg)
{
	if( arg >= (unsigned)sysFunction->parameterTypes.GetLength() )
		return 0;

	// Verify that the type is correct
	asCDataType *dt = &sysFunction->parameterTypes[arg];
	if( (dt->IsObject() || dt->IsFuncdef()) || dt->IsReference() )
		return 0;

	if( dt->GetSizeInMemoryBytes() != 8 )
		return 0;

	// Determine the position of the argument
	int offset = 0;
	for( asUINT n = 0; n < arg; n++ )
		offset += sysFunction->parameterTypes[n].GetSizeOnStackDWords();

	// Get the value
	return *(double*)(&stackPointer[offset]);
}

// interface
void *asCGeneric::GetArgAddress(asUINT arg)
{
	if( arg >= (unsigned)sysFunction->parameterTypes.GetLength() )
		return 0;

	// Verify that the type is correct
	asCDataType *dt = &sysFunction->parameterTypes[arg];
	if( !dt->IsReference() && !dt->IsObjectHandle() )
		return 0;

	// Determine the position of the argument
	int offset = 0;
	for( asUINT n = 0; n < arg; n++ )
		offset += sysFunction->parameterTypes[n].GetSizeOnStackDWords();

	// Get the value
	return (void*)*(asPWORD*)(&stackPointer[offset]);
}

// interface
void *asCGeneric::GetArgObject(asUINT arg)
{
	if( arg >= (unsigned)sysFunction->parameterTypes.GetLength() )
		return 0;

	// Verify that the type is correct
	asCDataType *dt = &sysFunction->parameterTypes[arg];
	if( !dt->IsObject() && !dt->IsFuncdef() )
		return 0;

	// Determine the position of the argument
	int offset = 0;
	for( asUINT n = 0; n < arg; n++ )
		offset += sysFunction->parameterTypes[n].GetSizeOnStackDWords();

	// Get the value
	return *(void**)(&stackPointer[offset]);
}

// interface
void *asCGeneric::GetAddressOfArg(asUINT arg)
{
	if( arg >= (unsigned)sysFunction->parameterTypes.GetLength() )
		return 0;

	// Determine the position of the argument
	int offset = 0;
	for( asUINT n = 0; n < arg; n++ )
		offset += sysFunction->parameterTypes[n].GetSizeOnStackDWords();

	// For object variables it's necessary to dereference the pointer to get the address of the value
	if( !sysFunction->parameterTypes[arg].IsReference() &&
		sysFunction->parameterTypes[arg].IsObject() &&
		!sysFunction->parameterTypes[arg].IsObjectHandle() )
		return *(void**)&stackPointer[offset];

	// Get the address of the value
	return &stackPointer[offset];
}

// interface
int asCGeneric::GetArgTypeId(asUINT arg, asDWORD *flags) const
{
	if( arg >= (unsigned)sysFunction->parameterTypes.GetLength() )
		return 0;

	if( flags )
	{
		*flags = sysFunction->inOutFlags[arg];
		*flags |= sysFunction->parameterTypes[arg].IsReadOnly() ? asTM_CONST : 0;
	}

	asCDataType *dt = &sysFunction->parameterTypes[arg];
	if( dt->GetTokenType() != ttQuestion )
		return engine->GetTypeIdFromDataType(*dt);
	else
	{
		int offset = 0;
		for( asUINT n = 0; n < arg; n++ )
			offset += sysFunction->parameterTypes[n].GetSizeOnStackDWords();

		// Skip the actual value to get to the type id
		offset += AS_PTR_SIZE;

		// Get the value
		return stackPointer[offset];
	}
}

// interface
int asCGeneric::SetReturnByte(asBYTE val)
{
	// Verify the type of the return value
	if( (sysFunction->returnType.IsObject() || sysFunction->returnType.IsFuncdef()) || sysFunction->returnType.IsReference() )
		return asINVALID_TYPE;

	if( sysFunction->returnType.GetSizeInMemoryBytes() != 1 )
		return asINVALID_TYPE;

	// Store the value
	*(asBYTE*)&returnVal = val;

	return 0;
}

// interface
int asCGeneric::SetReturnWord(asWORD val)
{
	// Verify the type of the return value
	if( (sysFunction->returnType.IsObject() || sysFunction->returnType.IsFuncdef()) || sysFunction->returnType.IsReference() )
		return asINVALID_TYPE;

	if( sysFunction->returnType.GetSizeInMemoryBytes() != 2 )
		return asINVALID_TYPE;

	// Store the value
	*(asWORD*)&returnVal = val;

	return 0;
}

// interface
int asCGeneric::SetReturnDWord(asDWORD val)
{
	// Verify the type of the return value
	if( (sysFunction->returnType.IsObject() || sysFunction->returnType.IsFuncdef()) || sysFunction->returnType.IsReference() )
		return asINVALID_TYPE;

	if( sysFunction->returnType.GetSizeInMemoryBytes() != 4 )
		return asINVALID_TYPE;

	// Store the value
	*(asDWORD*)&returnVal = val;

	return 0;
}

// interface
int asCGeneric::SetReturnQWord(asQWORD val)
{
	// Verify the type of the return value
	if( (sysFunction->returnType.IsObject() || sysFunction->returnType.IsFuncdef()) || sysFunction->returnType.IsReference() )
		return asINVALID_TYPE;

	if( sysFunction->returnType.GetSizeOnStackDWords() != 2 )
		return asINVALID_TYPE;

	// Store the value
	returnVal = val;

	return 0;
}

// interface
int asCGeneric::SetReturnFloat(float val)
{
	// Verify the type of the return value
	if( (sysFunction->returnType.IsObject() || sysFunction->returnType.IsFuncdef()) || sysFunction->returnType.IsReference() )
		return asINVALID_TYPE;

	if( sysFunction->returnType.GetSizeOnStackDWords() != 1 )
		return asINVALID_TYPE;

	// Store the value
	*(float*)&returnVal = val;

	return 0;
}

// interface
int asCGeneric::SetReturnDouble(double val)
{
	// Verify the type of the return value
	if( (sysFunction->returnType.IsObject() || sysFunction->returnType.IsFuncdef()) || sysFunction->returnType.IsReference() )
		return asINVALID_TYPE;

	if( sysFunction->returnType.GetSizeOnStackDWords() != 2 )
		return asINVALID_TYPE;

	// Store the value
	*(double*)&returnVal = val;

	return 0;
}

// interface
int asCGeneric::SetReturnAddress(void *val)
{
	// Verify the type of the return value
	if( sysFunction->returnType.IsReference() )
	{
		// Store the value
		*(void**)&returnVal = val;
		return 0;
	}
	else if( sysFunction->returnType.IsObjectHandle() )
	{
		// Store the handle without increasing reference
		objectRegister = val;
		return 0;
	}

	return asINVALID_TYPE;
}

// interface
int asCGeneric::SetReturnObject(void *obj)
{
	asCDataType *dt = &sysFunction->returnType;
	if( !dt->IsObject() && !dt->IsFuncdef() )
		return asINVALID_TYPE;

	if( dt->IsReference() )
	{
		*(void**)&returnVal = obj;
		return 0;
	}

	if( dt->IsObjectHandle() )
	{
		// Increase the reference counter
		if (dt->IsFuncdef())
		{
			if (obj)
				reinterpret_cast<asIScriptFunction*>(obj)->AddRef();
		}
		else
		{
			asSTypeBehaviour *beh = &CastToObjectType(dt->GetTypeInfo())->beh;
			if (obj && beh && beh->addref)
				engine->CallObjectMethod(obj, beh->addref);
		}
	}
	else
	{
		// If function returns object by value the memory is already allocated.
		// Here we should just initialize that memory by calling the copy constructor
		// or the default constructor followed by the assignment operator
		void *mem = (void*)*(asPWORD*)&stackPointer[-AS_PTR_SIZE];
		engine->ConstructScriptObjectCopy(mem, obj, CastToObjectType(dt->GetTypeInfo()));
		return 0;
	}

	objectRegister = obj;

	return 0;
}

// internal
void *asCGeneric::GetReturnPointer()
{
	asCDataType &dt = sysFunction->returnType;

	if( (dt.IsObject() ||dt.IsFuncdef()) && !dt.IsReference() )
	{
		// This function doesn't support returning on the stack but the use of
		// the function doesn't require it so we don't need to implement it here.
		asASSERT( !sysFunction->DoesReturnOnStack() );

		return &objectRegister;
	}

	return &returnVal;
}

// interface
void *asCGeneric::GetAddressOfReturnLocation()
{
	asCDataType &dt = sysFunction->returnType;

	if( (dt.IsObject() || dt.IsFuncdef()) && !dt.IsReference() )
	{
		if( sysFunction->DoesReturnOnStack() )
		{
			// The memory is already preallocated on the stack,
			// and the pointer to the location is found before the first arg
			return (void*)*(asPWORD*)&stackPointer[-AS_PTR_SIZE];
		}

		// Reference types store the handle in the objectReference
		return &objectRegister;
	}

	// Primitive types and references are stored in the returnVal property
	return &returnVal;
}

// interface
int asCGeneric::GetReturnTypeId(asDWORD *flags) const
{
	return sysFunction->GetReturnTypeId(flags);
}

asCGenericVariadic::asCGenericVariadic(asCScriptEngine* engine, asCScriptFunction* sysFunction, void* currentObject, asDWORD* stackPointer, asDWORD stackArgCount)
	: asCGeneric(engine, sysFunction, currentObject, stackPointer), argCount(stackArgCount)
{
}

int asCGenericVariadic::GetArgCount() const
{
	return argCount;
}

int asCGenericVariadic::GetArgTypeId(asUINT arg, asDWORD* flags) const
{
	asUINT idx = arg;
	if (idx >= sysFunction->parameterTypes.GetLength() - 1)
		idx = sysFunction->parameterTypes.GetLength() - 1;

	if (flags)
	{
		*flags = sysFunction->inOutFlags[idx];
		*flags |= sysFunction->parameterTypes[idx].IsReadOnly() ? asTM_CONST : 0;
	}

	asCDataType* dt = GetArgDataType(idx);
    if (dt->GetTokenType() != ttQuestion)
		return engine->GetTypeIdFromDataType(*dt);
	else
	{
		int offset = GetArgOffsetOnStack(arg); // Use "arg" here to iterate all arguments

		// Skip the actual value to get to the type id
		offset += AS_PTR_SIZE;

		// Get the value
		return stackPointer[offset];
	}
}

asBYTE asCGenericVariadic::GetArgByte(asUINT arg)
{
	// Verify that the type is correct
	asCDataType* dt = GetArgDataType(arg);
	if ((dt->IsObject() || dt->IsFuncdef()) || dt->IsReference())
		return 0;

	if (dt->GetSizeInMemoryBytes() != 1)
		return 0;

	int offset = GetArgOffsetOnStack(arg);
	// Get the value
	return *(asBYTE*)&stackPointer[offset];
}

asWORD asCGenericVariadic::GetArgWord(asUINT arg)
{
	// Verify that the type is correct
	asCDataType* dt = GetArgDataType(arg);
	if ((dt->IsObject() || dt->IsFuncdef()) || dt->IsReference())
		return 0;

	if (dt->GetSizeInMemoryBytes() != 2)
		return 0;

	int offset = GetArgOffsetOnStack(arg);
	// Get the value
	return *(asWORD*)&stackPointer[offset];
}

asDWORD asCGenericVariadic::GetArgDWord(asUINT arg)
{
	// Verify that the type is correct
	asCDataType* dt = GetArgDataType(arg);
	if ((dt->IsObject() || dt->IsFuncdef()) || dt->IsReference())
		return 0;

	if (dt->GetSizeInMemoryBytes() != 4)
        return 0;

    int offset = GetArgOffsetOnStack(arg);
    // Get the value
    return *(asDWORD*)&stackPointer[offset];
}

asQWORD asCGenericVariadic::GetArgQWord(asUINT arg)
{
	// Verify that the type is correct
	asCDataType* dt = GetArgDataType(arg);
	if ((dt->IsObject() || dt->IsFuncdef()) || dt->IsReference())
		return 0;

	if (dt->GetSizeInMemoryBytes() != 8)
		return 0;

	int offset = GetArgOffsetOnStack(arg);
	// Get the value
	return *(asQWORD*)&stackPointer[offset];
}

float asCGenericVariadic::GetArgFloat(asUINT arg)
{
	// Verify that the type is correct
	asCDataType* dt = GetArgDataType(arg);
	if ((dt->IsObject() || dt->IsFuncdef()) || dt->IsReference())
		return 0;

	if (dt->GetSizeInMemoryBytes() != 4)
		return 0;

	int offset = GetArgOffsetOnStack(arg);
	// Get the value
	return *(float*)&stackPointer[offset];
}

double asCGenericVariadic::GetArgDouble(asUINT arg)
{
	// Verify that the type is correct
	asCDataType* dt = GetArgDataType(arg);
	if ((dt->IsObject() || dt->IsFuncdef()) || dt->IsReference())
		return 0;

	if (dt->GetSizeInMemoryBytes() != 8)
		return 0;

	int offset = GetArgOffsetOnStack(arg);
	// Get the value
	return *(double*)&stackPointer[offset];
}

void* asCGenericVariadic::GetArgAddress(asUINT arg)
{
	// Verify that the type is correct
	asCDataType* dt = GetArgDataType(arg);
	if (!dt->IsReference() && !dt->IsObjectHandle())
		return 0;

	int offset = GetArgOffsetOnStack(arg);
	// Get the value
	return (void*)*(asPWORD*)&stackPointer[offset];
}

void* asCGenericVariadic::GetArgObject(asUINT arg)
{
	// Verify that the type is correct
	asCDataType* dt = GetArgDataType(arg);
	if (!dt->IsObject() && !dt->IsFuncdef())
		return 0;

	int offset = GetArgOffsetOnStack(arg);
	// Get the value
	return *(void**)(&stackPointer[offset]);
}

void* asCGenericVariadic::GetAddressOfArg(asUINT arg)
{
	// Determine the position of the argument
	int offset = GetArgOffsetOnStack(arg);

	// For object variables it's necessary to dereference the pointer to get the address of the value
	if (!sysFunction->parameterTypes[arg].IsReference() &&
		sysFunction->parameterTypes[arg].IsObject() &&
		!sysFunction->parameterTypes[arg].IsObjectHandle())
		return *(void**)&stackPointer[offset];

	// Get the address of the value
	return &stackPointer[offset];
}

void* asCGenericVariadic::GetAddressOfReturnLocation()
{
	asCDataType& dt = sysFunction->returnType;

	if ((dt.IsObject() || dt.IsFuncdef()) && !dt.IsReference())
	{
		if (sysFunction->DoesReturnOnStack())
		{
			// The memory is already preallocated on the stack,
			// and the pointer to the location is found before the first arg
			return (void*)*(asPWORD*)&stackPointer[-(AS_PTR_SIZE + 1)];
		}

		// Reference types store the handle in the objectReference
		return &objectRegister;
	}

	// Primitive types and references are stored in the returnVal property
	return &returnVal;
}

int asCGenericVariadic::SetReturnObject(void* obj)
{
	asCDataType* dt = &sysFunction->returnType;
	if (!dt->IsObject() && !dt->IsFuncdef())
		return asINVALID_TYPE;

	if (dt->IsReference())
	{
		*(void**)&returnVal = obj;
		return 0;
	}

	if (dt->IsObjectHandle())
	{
		// Increase the reference counter
		if (dt->IsFuncdef())
		{
			if (obj)
				reinterpret_cast<asIScriptFunction*>(obj)->AddRef();
		}
		else
		{
			asSTypeBehaviour* beh = &CastToObjectType(dt->GetTypeInfo())->beh;
			if (obj && beh && beh->addref)
				engine->CallObjectMethod(obj, beh->addref);
		}
	}
	else
	{
		// If function returns object by value the memory is already allocated.
		// Here we should just initialize that memory by calling the copy constructor
		// or the default constructor followed by the assignment operator
		void* mem = (void*)*(asPWORD*)&stackPointer[-(AS_PTR_SIZE + 1)];
		engine->ConstructScriptObjectCopy(mem, obj, CastToObjectType(dt->GetTypeInfo()));
		return 0;
	}

	objectRegister = obj;

	return 0;
}

asCDataType* asCGenericVariadic::GetArgDataType(asUINT arg) const
{
	if (arg >= sysFunction->parameterTypes.GetLength() - 1)
		arg = sysFunction->parameterTypes.GetLength() - 1;

	return &sysFunction->parameterTypes[arg];
}

int asCGenericVariadic::GetArgOffsetOnStack(asUINT arg) const
{
	// Determine the position of the argument
	int offset = 0;
	for (asUINT n = 0; n < arg; n++)
	{
		if (n >= sysFunction->parameterTypes.GetLength() - 1)
		{
			asUINT idx = sysFunction->parameterTypes.GetLength() - 1;
			offset += sysFunction->parameterTypes[idx].GetSizeOnStackDWords();
		}
		else
			offset += sysFunction->parameterTypes[n].GetSizeOnStackDWords();
	}

	return offset;
}

END_AS_NAMESPACE