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_scriptnode.h
//
// A node in the script tree built by the parser for compilation
//

#ifndef AS_SCRIPTNODE_H
#define AS_SCRIPTNODE_H

#include "as_config.h"
#include "as_tokendef.h"

BEGIN_AS_NAMESPACE

enum eScriptNode
{
	snUndefined,
	snScript,
	snFunction,
	snConstant,
	snDataType,
	snIdentifier,
	snParameterList,
	snStatementBlock,
	snDeclaration,
	snExpressionStatement,
	snIf,
	snFor,
	snForEach,
	snWhile,
	snReturn,
	snExpression,
	snExprTerm,
	snFunctionCall,
	snConstructCall,
	snArgList,
	snVariadic,
	snExprPreOp,
	snExprPostOp,
	snExprOperator,
	snExprValue,
	snBreak,
	snContinue,
	snDoWhile,
	snAssignment,
	snCondition,
	snSwitch,
	snCase,
	snImport,
	snClass,
	snInitList,
	snInterface,
	snEnum,
	snTypedef,
	snCast,
	snVariableAccess,
	snFuncDef,
	snVirtualProperty,
	snNamespace,
	snUsing,
	snMixin,
	snListPattern,
	snNamedArgument,
	snScope,
	snTryCatch
};

struct sToken
{
	eTokenType type;
	size_t pos;
	size_t length;
};

class asCScriptEngine;

class asCScriptNode
{
public:
	asCScriptNode(eScriptNode nodeType);

	void Destroy(asCScriptEngine *engine);
	asCScriptNode *CreateCopy(asCScriptEngine *engine);

	void SetToken(sToken *token);
	void AddChildLast(asCScriptNode *node);
	void DisconnectParent();

	void UpdateSourcePos(size_t pos, size_t length);

	eScriptNode nodeType;
	eTokenType tokenType;
	size_t tokenPos;
	size_t tokenLength;

	asCScriptNode *parent;
	asCScriptNode *next;
	asCScriptNode *prev;
	asCScriptNode *firstChild;
	asCScriptNode *lastChild;

protected:
	// Must call Destroy instead
	~asCScriptNode() {}
};

END_AS_NAMESPACE

#endif


code:
/*
   AngelCode Scripting Library
   Copyright (c) 2003-2023 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
*/

#include <new>
#include "as_config.h"
#include "as_scriptengine.h"
#include "as_scriptobject.h"
#include "as_texts.h"

BEGIN_AS_NAMESPACE

// This helper function will call the default factory, that is a script function
asIScriptObject *ScriptObjectFactory(const asCObjectType *objType, asCScriptEngine *engine)
{
	asIScriptContext *ctx = 0;
	int r = 0;
	bool isNested = false;

	// Use nested call in the context if there is an active context
	ctx = asGetActiveContext();
	if( ctx )
	{
		// It may not always be possible to reuse the current context,
		// in which case we'll have to create a new one any way.
		if( ctx->GetEngine() == objType->GetEngine() && ctx->PushState() == asSUCCESS )
			isNested = true;
		else
			ctx = 0;
	}

	if( ctx == 0 )
	{
		// Request a context from the engine
		ctx = engine->RequestContext();
		if( ctx == 0 )
		{
			// TODO: How to best report this failure?
			return 0;
		}
	}

	r = ctx->Prepare(engine->scriptFunctions[objType->beh.factory]);
	if( r < 0 )
	{
		if( isNested )
			ctx->PopState();
		else
			engine->ReturnContext(ctx);
		return 0;
	}

	for(;;)
	{
		r = ctx->Execute();

		// We can't allow this execution to be suspended
		// so resume the execution immediately
		if( r != asEXECUTION_SUSPENDED )
			break;
	}

	if( r != asEXECUTION_FINISHED )
	{
		if( isNested )
		{
			ctx->PopState();

			// If the execution was aborted or an exception occurred,
			// then we should forward that to the outer execution.
			if( r == asEXECUTION_EXCEPTION )
			{
				// TODO: How to improve this exception
				ctx->SetException(TXT_EXCEPTION_IN_NESTED_CALL);
			}
			else if( r == asEXECUTION_ABORTED )
				ctx->Abort();
		}
		else
			engine->ReturnContext(ctx);
		return 0;
	}

	asIScriptObject *ptr = (asIScriptObject*)ctx->GetReturnAddress();

	// Increase the reference, because the context will release its pointer
	ptr->AddRef();

	if( isNested )
		ctx->PopState();
	else
		engine->ReturnContext(ctx);

	return ptr;
}

// This helper function will call the copy factory, that is a script function
// TODO: Clean up: This function is almost identical to ScriptObjectFactory. Should make better use of the identical code.
asIScriptObject *ScriptObjectCopyFactory(const asCObjectType *objType, void *origObj, asCScriptEngine *engine)
{
	asIScriptContext *ctx = 0;
	int r = 0;
	bool isNested = false;

	// Use nested call in the context if there is an active context
	ctx = asGetActiveContext();
	if (ctx)
	{
		// It may not always be possible to reuse the current context,
		// in which case we'll have to create a new one any way.
		if (ctx->GetEngine() == objType->GetEngine() && ctx->PushState() == asSUCCESS)
			isNested = true;
		else
			ctx = 0;
	}

	if (ctx == 0)
	{
		// Request a context from the engine
		ctx = engine->RequestContext();
		if (ctx == 0)
		{
			// TODO: How to best report this failure?
			return 0;
		}
	}

	r = ctx->Prepare(engine->scriptFunctions[objType->beh.copyfactory]);
	if (r < 0)
	{
		if (isNested)
			ctx->PopState();
		else
			engine->ReturnContext(ctx);
		return 0;
	}

	// Let the context handle the case for argument by ref (&) or by handle (@)
	ctx->SetArgObject(0, origObj);

	for (;;)
	{
		r = ctx->Execute();

		// We can't allow this execution to be suspended
		// so resume the execution immediately
		if (r != asEXECUTION_SUSPENDED)
			break;
	}

	if (r != asEXECUTION_FINISHED)
	{
		if (isNested)
		{
			ctx->PopState();

			// If the execution was aborted or an exception occurred,
			// then we should forward that to the outer execution.
			if (r == asEXECUTION_EXCEPTION)
			{
				// TODO: How to improve this exception
				ctx->SetException(TXT_EXCEPTION_IN_NESTED_CALL);
			}
			else if (r == asEXECUTION_ABORTED)
				ctx->Abort();
		}
		else
			engine->ReturnContext(ctx);
		return 0;
	}

	asIScriptObject *ptr = (asIScriptObject*)ctx->GetReturnAddress();

	// Increase the reference, because the context will release its pointer
	ptr->AddRef();

	if (isNested)
		ctx->PopState();
	else
		engine->ReturnContext(ctx);

	return ptr;
}

#if defined(AS_MAX_PORTABILITY) || defined(AS_NO_CLASS_METHODS)

static void ScriptObject_AddRef_Generic(asIScriptGeneric *gen)
{
	asCScriptObject *self = (asCScriptObject*)gen->GetObject();
	self->AddRef();
}

static void ScriptObject_Release_Generic(asIScriptGeneric *gen)
{
	asCScriptObject *self = (asCScriptObject*)gen->GetObject();
	self->Release();
}

static void ScriptObject_GetRefCount_Generic(asIScriptGeneric *gen)
{
	asCScriptObject *self = (asCScriptObject*)gen->GetObject();
	*(int*)gen->GetAddressOfReturnLocation() = self->GetRefCount();
}

static void ScriptObject_SetFlag_Generic(asIScriptGeneric *gen)
{
	asCScriptObject *self = (asCScriptObject*)gen->GetObject();
	self->SetFlag();
}

static void ScriptObject_GetFlag_Generic(asIScriptGeneric *gen)
{
	asCScriptObject *self = (asCScriptObject*)gen->GetObject();
	*(bool*)gen->GetAddressOfReturnLocation() = self->GetFlag();
}

static void ScriptObject_GetWeakRefFlag_Generic(asIScriptGeneric *gen)
{
	asCScriptObject *self = (asCScriptObject*)gen->GetObject();
	*(asILockableSharedBool**)gen->GetAddressOfReturnLocation() = self->GetWeakRefFlag();
}

static void ScriptObject_EnumReferences_Generic(asIScriptGeneric *gen)
{
	asCScriptObject *self = (asCScriptObject*)gen->GetObject();
	asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0);
	self->EnumReferences(engine);
}

static void ScriptObject_ReleaseAllHandles_Generic(asIScriptGeneric *gen)
{
	asCScriptObject *self = (asCScriptObject*)gen->GetObject();
	asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0);
	self->ReleaseAllHandles(engine);
}

static void ScriptObject_Assignment_Generic(asIScriptGeneric *gen)
{
	asCScriptObject *other = *(asCScriptObject**)gen->GetAddressOfArg(0);
	asCScriptObject *self = (asCScriptObject*)gen->GetObject();

	*self = *other;

	*(asCScriptObject**)gen->GetAddressOfReturnLocation() = self;
}

static void ScriptObject_Construct_Generic(asIScriptGeneric *gen)
{
	asCObjectType *objType = *(asCObjectType**)gen->GetAddressOfArg(0);
	asCScriptObject *self = (asCScriptObject*)gen->GetObject();

	ScriptObject_Construct(objType, self);
}

#endif

void RegisterScriptObject(asCScriptEngine *engine)
{
	// Register the default script class behaviours
	int r = 0;
	UNUSED_VAR(r); // It is only used in debug mode
	engine->scriptTypeBehaviours.engine = engine;
	engine->scriptTypeBehaviours.flags = asOBJ_SCRIPT_OBJECT | asOBJ_REF | asOBJ_GC;
	engine->scriptTypeBehaviours.name = "$obj";
#if !defined(AS_MAX_PORTABILITY) && !defined(AS_NO_CLASS_METHODS)
	r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_CONSTRUCT, "void f(int&in)", asFUNCTION(ScriptObject_Construct), asCALL_CDECL_OBJLAST, 0); asASSERT( r >= 0 );
	r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_ADDREF, "void f()", asMETHOD(asCScriptObject,AddRef), asCALL_THISCALL, 0); asASSERT( r >= 0 );
	r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_RELEASE, "void f()", asMETHOD(asCScriptObject,Release), asCALL_THISCALL, 0); asASSERT( r >= 0 );
	r = engine->RegisterMethodToObjectType(&engine->scriptTypeBehaviours, "int &opAssign(int &in)", asFUNCTION(ScriptObject_Assignment), asCALL_CDECL_OBJLAST); asASSERT( r >= 0 );

	// Weakref behaviours
	r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_GET_WEAKREF_FLAG, "int &f()", asMETHOD(asCScriptObject,GetWeakRefFlag), asCALL_THISCALL, 0); asASSERT( r >= 0 );

	// Register GC behaviours
	r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_GETREFCOUNT, "int f()", asMETHOD(asCScriptObject,GetRefCount), asCALL_THISCALL, 0); asASSERT( r >= 0 );
	r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_SETGCFLAG, "void f()", asMETHOD(asCScriptObject,SetFlag), asCALL_THISCALL, 0); asASSERT( r >= 0 );
	r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_GETGCFLAG, "bool f()", asMETHOD(asCScriptObject,GetFlag), asCALL_THISCALL, 0); asASSERT( r >= 0 );
	r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_ENUMREFS, "void f(int&in)", asMETHOD(asCScriptObject,EnumReferences), asCALL_THISCALL, 0); asASSERT( r >= 0 );
	r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_RELEASEREFS, "void f(int&in)", asMETHOD(asCScriptObject,ReleaseAllHandles), asCALL_THISCALL, 0); asASSERT( r >= 0 );
#else
	r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_CONSTRUCT, "void f(int&in)", asFUNCTION(ScriptObject_Construct_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 );
	r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_ADDREF, "void f()", asFUNCTION(ScriptObject_AddRef_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 );
	r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_RELEASE, "void f()", asFUNCTION(ScriptObject_Release_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 );
	r = engine->RegisterMethodToObjectType(&engine->scriptTypeBehaviours, "int &opAssign(int &in)", asFUNCTION(ScriptObject_Assignment_Generic), asCALL_GENERIC); asASSERT( r >= 0 );

	// Weakref behaviours
	r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_GET_WEAKREF_FLAG, "int &f()", asFUNCTION(ScriptObject_GetWeakRefFlag_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 );

	// Register GC behaviours
	r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_GETREFCOUNT, "int f()", asFUNCTION(ScriptObject_GetRefCount_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 );
	r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_SETGCFLAG, "void f()", asFUNCTION(ScriptObject_SetFlag_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 );
	r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_GETGCFLAG, "bool f()", asFUNCTION(ScriptObject_GetFlag_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 );
	r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_ENUMREFS, "void f(int&in)", asFUNCTION(ScriptObject_EnumReferences_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 );
	r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_RELEASEREFS, "void f(int&in)", asFUNCTION(ScriptObject_ReleaseAllHandles_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 );
#endif
}

void ScriptObject_Construct(asCObjectType *objType, asCScriptObject *self)
{
	new(self) asCScriptObject(objType);
}

void ScriptObject_ConstructUnitialized(asCObjectType *objType, asCScriptObject *self)
{
	new(self) asCScriptObject(objType, false);
}

asCScriptObject::asCScriptObject(asCObjectType *ot, bool doInitialize)
{
	refCount.set(1);
	objType = ot;
	objType->AddRef();
	isDestructCalled = false;
	extra = 0;
	hasRefCountReachedZero = false;

	// Notify the garbage collector of this object
	if( objType->flags & asOBJ_GC )
		objType->engine->gc.AddScriptObjectToGC(this, objType);

	// Initialize members to zero. Technically we only need to zero the pointer
	// members, but just the memset is faster than having to loop and check the datatypes
	memset((void*)(this+1), 0, objType->size - sizeof(asCScriptObject));

	if( doInitialize )
	{
#ifdef AS_NO_MEMBER_INIT
		// When member initialization is disabled the constructor must make sure
		// to allocate and initialize all members with the default constructor
		for( asUINT n = 0; n < objType->properties.GetLength(); n++ )
		{
			asCObjectProperty *prop = objType->properties[n];
			if( prop->type.IsObject() && !prop->type.IsObjectHandle() )
			{
				if( prop->type.IsReference() || prop->type.GetTypeInfo()->flags & asOBJ_REF )
				{
					asPWORD *ptr = reinterpret_cast<asPWORD*>(reinterpret_cast<asBYTE*>(this) + prop->byteOffset);
					if( prop->type.GetTypeInfo()->flags & asOBJ_SCRIPT_OBJECT )
						*ptr = (asPWORD)ScriptObjectFactory(prop->type.GetTypeInfo(), ot->engine);
					else
						*ptr = (asPWORD)AllocateUninitializedObject(prop->type.GetTypeInfo(), ot->engine);
				}
			}
		}
#endif
	}
	else
	{
		// When the object is created without initialization, all non-handle members must be allocated, but not initialized
		asCScriptEngine *engine = objType->engine;
		for( asUINT n = 0; n < objType->properties.GetLength(); n++ )
		{
			asCObjectProperty *prop = objType->properties[n];
			if( prop->type.IsObject() && !prop->type.IsObjectHandle() )
			{
				if( prop->type.IsReference() || (prop->type.GetTypeInfo()->flags & asOBJ_REF) )
				{
					asPWORD *ptr = reinterpret_cast<asPWORD*>(reinterpret_cast<asBYTE*>(this) + prop->byteOffset);
					*ptr = (asPWORD)AllocateUninitializedObject(CastToObjectType(prop->type.GetTypeInfo()), engine);
				}
			}
		}
	}
}

void asCScriptObject::Destruct()
{
	// Call the destructor, which will also call the GCObject's destructor
	this->~asCScriptObject();

	// Free the memory
#ifndef WIP_16BYTE_ALIGN
	userFree(this);
#else
	// Script object memory is allocated through asCScriptEngine::CallAlloc()
	// This free call must match the allocator used in CallAlloc().
	userFreeAligned(this);
#endif
}

asCScriptObject::~asCScriptObject()
{
	if( extra )
	{
		if( extra->weakRefFlag )
		{
			extra->weakRefFlag->Release();
			extra->weakRefFlag = 0;
		}

		if( objType->engine )
		{
			// Clean the user data
			for( asUINT n = 0; n < extra->userData.GetLength(); n += 2 )
			{
				if( extra->userData[n+1] )
				{
					for( asUINT c = 0; c < objType->engine->cleanScriptObjectFuncs.GetLength(); c++ )
						if( objType->engine->cleanScriptObjectFuncs[c].type == extra->userData[n] )
							objType->engine->cleanScriptObjectFuncs[c].cleanFunc(this);
				}
			}
		}

		asDELETE(extra, SExtra);
	}

	// The engine pointer should be available from the objectType
	asCScriptEngine *engine = objType->engine;

	// Destroy all properties
	// In most cases the members are initialized in the order they have been declared,
	// so it's safer to uninitialize them from last to first. The order may be different
	// depending on the use of inheritance and or initialization in the declaration.
	// TODO: Should the order of initialization be stored by the compiler so that the
	//       reverse order can be guaranteed during the destruction?
	for( int n = (int)objType->properties.GetLength()-1; n >= 0; n-- )
	{
		asCObjectProperty *prop = objType->properties[n];
		if( prop->type.IsObject() )
		{
			// Destroy the object
			asCObjectType *propType = CastToObjectType(prop->type.GetTypeInfo());
			if( prop->type.IsReference() || propType->flags & asOBJ_REF )
			{
				void **ptr = (void**)(((char*)this) + prop->byteOffset);
				if( *ptr )
				{
					FreeObject(*ptr, propType, engine);
					*(asDWORD*)ptr = 0;
				}
			}
			else
			{
				// The object is allocated inline. As only POD objects may be allocated inline
				// it is not a problem to call the destructor even if the object may never have
				// been initialized, e.g. if an exception interrupted the constructor.
				asASSERT( propType->flags & asOBJ_POD );

				void *ptr = (void**)(((char*)this) + prop->byteOffset);
				if( propType->beh.destruct )
					engine->CallObjectMethod(ptr, propType->beh.destruct);
			}
		}
		else if( prop->type.IsFuncdef() )
		{
			// Release the function descriptor
			asCScriptFunction **ptr = (asCScriptFunction**)(((char*)this) + prop->byteOffset);
			if (*ptr)
			{
				(*ptr)->Release();
				*ptr = 0;
			}
		}
	}

	objType->Release();
	objType = 0;

	// Something is really wrong if the refCount is not 0 by now
	asASSERT( refCount.get() == 0 );
}

asILockableSharedBool *asCScriptObject::GetWeakRefFlag() const
{
	// If the object's refCount has already reached zero then the object is already
	// about to be destroyed so it's ok to return null if the weakRefFlag doesn't already
	// exist
	if( (extra && extra->weakRefFlag) || hasRefCountReachedZero )
		return extra->weakRefFlag;

	// Lock globally so no other thread can attempt
	// to create a shared bool at the same time.
	// TODO: runtime optimize: Instead of locking globally, it would be possible to have
	//                         a critical section per object type. This would reduce the
	//                         chances of two threads lock on the same critical section.
	asAcquireExclusiveLock();

	// Make sure another thread didn't create the
	// flag while we waited for the lock
	if( !extra )
		extra = asNEW(SExtra);
	if( !extra->weakRefFlag )
		extra->weakRefFlag = asNEW(asCLockableSharedBool);

	asReleaseExclusiveLock();

	return extra->weakRefFlag;
}

void *asCScriptObject::GetUserData(asPWORD type) const
{
	if( !extra )
		return 0;

	// There may be multiple threads reading, but when
	// setting the user data nobody must be reading.
	// TODO: runtime optimize: Would it be worth it to have a rwlock per object type?
	asAcquireSharedLock();

	for( asUINT n = 0; n < extra->userData.GetLength(); n += 2 )
	{
		if( extra->userData[n] == type )
		{
			void *userData = reinterpret_cast<void*>(extra->userData[n+1]);
			asReleaseSharedLock();
			return userData;
		}
	}

	asReleaseSharedLock();

	return 0;
}

void *asCScriptObject::SetUserData(void *data, asPWORD type)
{
	// Lock globally so no other thread can attempt
	// to manipulate the extra data at the same time.
	// TODO: runtime optimize: Instead of locking globally, it would be possible to have
	//                         a critical section per object type. This would reduce the
	//                         chances of two threads lock on the same critical section.
	asAcquireExclusiveLock();

	// Make sure another thread didn't create the
	// flag while we waited for the lock
	if( !extra )
		extra = asNEW(SExtra);

	// It is not intended to store a lot of different types of userdata,
	// so a more complex structure like a associative map would just have
	// more overhead than a simple array.
	for( asUINT n = 0; n < extra->userData.GetLength(); n += 2 )
	{
		if( extra->userData[n] == type )
		{
			void *oldData = reinterpret_cast<void*>(extra->userData[n+1]);
			extra->userData[n+1] = reinterpret_cast<asPWORD>(data);

			asReleaseExclusiveLock();

			return oldData;
		}
	}

	extra->userData.PushLast(type);
	extra->userData.PushLast(reinterpret_cast<asPWORD>(data));

	asReleaseExclusiveLock();

	return 0;
}

asIScriptEngine *asCScriptObject::GetEngine() const
{
	return objType->engine;
}

int asCScriptObject::AddRef() const
{
	// Warn in case the application tries to increase the refCount after it has reached zero.
	// This may happen for example if the application calls a method on the class while it is
	// being destroyed. The application shouldn't do this because it may cause application
	// crashes if members that have already been destroyed are accessed accidentally.
	if( hasRefCountReachedZero )
	{
		if( objType && objType->engine )
		{
			asCString msg;
			msg.Format(TXT_RESURRECTING_SCRIPTOBJECT_s, objType->name.AddressOf());
			objType->engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, msg.AddressOf());
		}
	}

	// Increase counter and clear flag set by GC
	gcFlag = false;
	return refCount.atomicInc();
}

int asCScriptObject::Release() const
{
	// Clear the flag set by the GC
	gcFlag = false;

	// If the weak ref flag exists it is because someone held a weak ref
	// and that someone may add a reference to the object at any time. It
	// is ok to check the existance of the weakRefFlag without locking here
	// because if the refCount is 1 then no other thread is currently
	// creating the weakRefFlag.
	if( refCount.get() == 1 && extra && extra->weakRefFlag )
	{
		// Set the flag to tell others that the object is no longer alive
		// We must do this before decreasing the refCount to 0 so we don't
		// end up with a race condition between this thread attempting to
		// destroy the object and the other that temporary added a strong
		// ref from the weak ref.
		extra->weakRefFlag->Set(true);
	}

	// Call the script destructor behaviour if the reference counter is 1.
	if( refCount.get() == 1 && !isDestructCalled )
	{
		// This cast is OK since we are the last reference
		const_cast<asCScriptObject*>(this)->CallDestructor();
	}

	// Now do the actual releasing
	int r = refCount.atomicDec();
	if( r == 0 )
	{
		// Flag this object as being destroyed so the application
		// can be warned if the code attempts to resurrect the object
		// during the destructor. This also avoids a recursive call
		// to the destructor which would crash the application if it
		// really does resurrect the object.
		if( !hasRefCountReachedZero )
		{
			hasRefCountReachedZero = true;

			// This cast is OK since we are the last reference
			const_cast<asCScriptObject*>(this)->Destruct();
		}
		return 0;
	}

	return r;
}

void asCScriptObject::CallDestructor()
{
	// Only allow the destructor to be called once
	if( isDestructCalled ) return;

	asIScriptContext *ctx = 0;
	bool isNested = false;
	bool doAbort = false;

	// Make sure the destructor is called once only, even if the
	// reference count is increased and then decreased again
	isDestructCalled = true;

	// Call the destructor for this class and all the super classes
	asCObjectType *ot = objType;
	while( ot )
	{
		int funcIndex = ot->beh.destruct;
		if( funcIndex )
		{
			if( ctx == 0 )
			{
				// Check for active context first as it is quicker
				// to reuse than to set up a new one.
				ctx = asGetActiveContext();
				if( ctx )
				{
					if( ctx->GetEngine() == objType->GetEngine() && ctx->PushState() == asSUCCESS )
						isNested = true;
					else
						ctx = 0;
				}

				if( ctx == 0 )
				{
					// Request a context from the engine
					ctx = objType->engine->RequestContext();
					if( ctx == 0 )
					{
						// TODO: How to best report this failure?
						return;
					}
				}
			}

			int r = ctx->Prepare(objType->engine->scriptFunctions[funcIndex]);
			if( r >= 0 )
			{
				ctx->SetObject(this);

				for(;;)
				{
					r = ctx->Execute();

					// If the script tries to suspend itself just restart it
					if( r != asEXECUTION_SUSPENDED )
						break;
				}

				// Exceptions in the destructor will be ignored, as there is not much
				// that can be done about them. However a request to abort the execution
				// will be forwarded to the outer execution, in case of a nested call.
				if( r == asEXECUTION_ABORTED )
					doAbort = true;

				// Observe, even though the current destructor was aborted or an exception
				// occurred, we still try to execute the base class' destructor if available
				// in order to free as many resources as possible.
			}
		}

		ot = ot->derivedFrom;
	}

	if( ctx )
	{
		if( isNested )
		{
			ctx->PopState();

			// Forward any request to abort the execution to the outer call
			if( doAbort )
				ctx->Abort();
		}
		else
		{
			// Return the context to engine
			objType->engine->ReturnContext(ctx);
		}
	}
}

asITypeInfo *asCScriptObject::GetObjectType() const
{
	return objType;
}

int asCScriptObject::GetRefCount()
{
	return refCount.get();
}

void asCScriptObject::SetFlag()
{
	gcFlag = true;
}

bool asCScriptObject::GetFlag()
{
	return gcFlag;
}

// interface
int asCScriptObject::GetTypeId() const
{
	asCDataType dt = asCDataType::CreateType(objType, false);
	return objType->engine->GetTypeIdFromDataType(dt);
}

asUINT asCScriptObject::GetPropertyCount() const
{
	return asUINT(objType->properties.GetLength());
}

int asCScriptObject::GetPropertyTypeId(asUINT prop) const
{
	if( prop >= objType->properties.GetLength() )
		return asINVALID_ARG;

	return objType->engine->GetTypeIdFromDataType(objType->properties[prop]->type);
}

const char *asCScriptObject::GetPropertyName(asUINT prop) const
{
	if( prop >= objType->properties.GetLength() )
		return 0;

	return objType->properties[prop]->name.AddressOf();
}

void *asCScriptObject::GetAddressOfProperty(asUINT prop)
{
	if( prop >= objType->properties.GetLength() )
		return 0;

	// Objects are stored by reference, so this must be dereferenced
	asCDataType *dt = &objType->properties[prop]->type;
	if( dt->IsObject() && !dt->IsObjectHandle() &&
		(dt->IsReference() || dt->GetTypeInfo()->flags & asOBJ_REF) )
		return *(void**)(((char*)this) + objType->properties[prop]->byteOffset);

	return (void*)(((char*)this) + objType->properties[prop]->byteOffset);
}

void asCScriptObject::EnumReferences(asIScriptEngine *engine)
{
	// We'll notify the GC of all object handles that we're holding
	for( asUINT n = 0; n < objType->properties.GetLength(); n++ )
	{
		asCObjectProperty *prop = objType->properties[n];
		void *ptr = 0;
		if (prop->type.IsObject())
		{
			if (prop->type.IsReference() || (prop->type.GetTypeInfo()->flags & asOBJ_REF))
				ptr = *(void**)(((char*)this) + prop->byteOffset);
			else
				ptr = (void*)(((char*)this) + prop->byteOffset);

			// The members of the value type needs to be enumerated
			// too, since the value type may be holding a reference.
			if ((prop->type.GetTypeInfo()->flags & asOBJ_VALUE) && (prop->type.GetTypeInfo()->flags & asOBJ_GC))
			{
				reinterpret_cast<asCScriptEngine*>(engine)->CallObjectMethod(ptr, engine, CastToObjectType(prop->type.GetTypeInfo())->beh.gcEnumReferences);
			}
		}
		else if (prop->type.IsFuncdef())
			ptr = *(void**)(((char*)this) + prop->byteOffset);

		if (ptr)
			((asCScriptEngine*)engine)->GCEnumCallback(ptr);
	}
}

void asCScriptObject::ReleaseAllHandles(asIScriptEngine *engine)
{
	for( asUINT n = 0; n < objType->properties.GetLength(); n++ )
	{
		asCObjectProperty *prop = objType->properties[n];

		if (prop->type.IsObject())
		{
			if (prop->type.IsObjectHandle())
			{
				void **ptr = (void**)(((char*)this) + prop->byteOffset);
				if (*ptr)
				{
					asASSERT((prop->type.GetTypeInfo()->flags & asOBJ_NOCOUNT) || prop->type.GetBehaviour()->release);
					if (prop->type.GetBehaviour()->release)
						((asCScriptEngine*)engine)->CallObjectMethod(*ptr, prop->type.GetBehaviour()->release);
					*ptr = 0;
				}
			}
			else if ((prop->type.GetTypeInfo()->flags & asOBJ_VALUE) && (prop->type.GetTypeInfo()->flags & asOBJ_GC))
			{
				// The members of the members needs to be released
				// too, since they may be holding a reference. Even
				// if the member is a value type.
				void *ptr = 0;
				if (prop->type.IsReference())
					ptr = *(void**)(((char*)this) + prop->byteOffset);
				else
					ptr = (void*)(((char*)this) + prop->byteOffset);

				reinterpret_cast<asCScriptEngine*>(engine)->CallObjectMethod(ptr, engine, CastToObjectType(prop->type.GetTypeInfo())->beh.gcReleaseAllReferences);
			}
		}
		else if (prop->type.IsFuncdef())
		{
			asCScriptFunction **ptr = (asCScriptFunction**)(((char*)this) + prop->byteOffset);
			if (*ptr)
			{
				(*ptr)->Release();
				*ptr = 0;
			}
		}
	}
}

asCScriptObject &ScriptObject_Assignment(asCScriptObject *other, asCScriptObject *self)
{
	return (*self = *other);
}

asCScriptObject &asCScriptObject::operator=(const asCScriptObject &other)
{
	CopyFromAs(&other, objType);
	return *this;
}

// internal
int asCScriptObject::CopyFromAs(const asCScriptObject *other, asCObjectType *in_objType)
{
	if( other != this )
	{
		if( !other->objType->DerivesFrom(in_objType) )
		{
			// We cannot allow a value assignment from a type that isn't the same or
			// derives from this type as the member properties may not have the same layout
			asIScriptContext *ctx = asGetActiveContext();
			ctx->SetException(TXT_MISMATCH_IN_VALUE_ASSIGN);
			return asERROR;
		}

		// If the script class implements the opAssign method, it should be called
		asCScriptEngine *engine = in_objType->engine;
		asCScriptFunction *func = engine->scriptFunctions[in_objType->beh.copy];
		if( func->funcType == asFUNC_SYSTEM )
		{
			// If derived, use the base class' assignment operator to copy the inherited
			// properties. Then only copy new properties for the derived class
			if( in_objType->derivedFrom )
				CopyFromAs(other, in_objType->derivedFrom);

			for( asUINT n = in_objType->derivedFrom ? in_objType->derivedFrom->properties.GetLength() : 0;
			     n < in_objType->properties.GetLength();
				 n++ )
			{
				asCObjectProperty *prop = in_objType->properties[n];
				if( prop->type.IsObject() )
				{
					void **dst = (void**)(((char*)this) + prop->byteOffset);
					void **src = (void**)(((char*)other) + prop->byteOffset);
					if( !prop->type.IsObjectHandle() )
					{
						if( prop->type.IsReference() || (prop->type.GetTypeInfo()->flags & asOBJ_REF) )
							CopyObject(*src, *dst, CastToObjectType(prop->type.GetTypeInfo()), engine);
						else
							CopyObject(src, dst, CastToObjectType(prop->type.GetTypeInfo()), engine);
					}
					else
						CopyHandle((asPWORD*)src, (asPWORD*)dst, CastToObjectType(prop->type.GetTypeInfo()), engine);
				}
				else if (prop->type.IsFuncdef())
				{
					asCScriptFunction **dst = (asCScriptFunction**)(((char*)this) + prop->byteOffset);
					asCScriptFunction **src = (asCScriptFunction**)(((char*)other) + prop->byteOffset);
					if (*dst)
						(*dst)->Release();
					*dst = *src;
					if (*dst)
						(*dst)->AddRef();
				}
				else
				{
					void *dst = ((char*)this) + prop->byteOffset;
					void *src = ((char*)other) + prop->byteOffset;
					memcpy(dst, src, prop->type.GetSizeInMemoryBytes());
				}
			}
		}
		else
		{
			// Reuse the active context or create a new one to call the script class' opAssign method
			asIScriptContext *ctx = 0;
			int r = 0;
			bool isNested = false;

			ctx = asGetActiveContext();
			if( ctx )
			{
				if( ctx->GetEngine() == engine && ctx->PushState() == asSUCCESS )
					isNested = true;
				else
					ctx = 0;
			}

			if( ctx == 0 )
			{
				// Request a context from the engine
				ctx = engine->RequestContext();
				if( ctx == 0 )
					return asERROR;
			}

			r = ctx->Prepare(engine->scriptFunctions[in_objType->beh.copy]);
			if( r < 0 )
			{
				if( isNested )
					ctx->PopState();
				else
					engine->ReturnContext(ctx);
				return r;
			}

			r = ctx->SetArgAddress(0, const_cast<asCScriptObject*>(other));
			asASSERT( r >= 0 );
			r = ctx->SetObject(this);
			asASSERT( r >= 0 );

			for(;;)
			{
				r = ctx->Execute();

				// We can't allow this execution to be suspended
				// so resume the execution immediately
				if( r != asEXECUTION_SUSPENDED )
					break;
			}

			if( r != asEXECUTION_FINISHED )
			{
				if( isNested )
				{
					ctx->PopState();

					// If the execution was aborted or an exception occurred,
					// then we should forward that to the outer execution.
					if( r == asEXECUTION_EXCEPTION )
					{
						// TODO: How to improve this exception
						ctx->SetException(TXT_EXCEPTION_IN_NESTED_CALL);
					}
					else if( r == asEXECUTION_ABORTED )
						ctx->Abort();
				}
				else
				{
					// Return the context to the engine
					engine->ReturnContext(ctx);
				}
				return asERROR;
			}

			if( isNested )
				ctx->PopState();
			else
			{
				// Return the context to the engine
				engine->ReturnContext(ctx);
			}
		}
	}

	return asSUCCESS;
}

int asCScriptObject::CopyFrom(const asIScriptObject *other)
{
	if( other == 0 ) return asINVALID_ARG;

	if( GetTypeId() != other->GetTypeId() )
		return asINVALID_TYPE;

	*this = *(asCScriptObject*)other;

	return asSUCCESS;
}

void *asCScriptObject::AllocateUninitializedObject(asCObjectType *in_objType, asCScriptEngine *engine)
{
	void *ptr = 0;

	if( in_objType->flags & asOBJ_SCRIPT_OBJECT )
	{
		ptr = engine->CallAlloc(in_objType);
		ScriptObject_ConstructUnitialized(in_objType, reinterpret_cast<asCScriptObject*>(ptr));
	}
	else if( in_objType->flags & asOBJ_TEMPLATE )
	{
		// Templates store the original factory that takes the object
		// type as a hidden parameter in the construct behaviour
		ptr = engine->CallGlobalFunctionRetPtr(in_objType->beh.construct, in_objType);
	}
	else if( in_objType->flags & asOBJ_REF )
	{
		ptr = engine->CallGlobalFunctionRetPtr(in_objType->beh.factory);
	}
	else
	{
		ptr = engine->CallAlloc(in_objType);
		int funcIndex = in_objType->beh.construct;
		if( funcIndex )
			engine->CallObjectMethod(ptr, funcIndex);
	}

	return ptr;
}

void asCScriptObject::FreeObject(void *ptr, asCObjectType *in_objType, asCScriptEngine *engine)
{
	if( in_objType->flags & asOBJ_REF )
	{
		asASSERT( (in_objType->flags & asOBJ_NOCOUNT) || in_objType->beh.release );
		if(in_objType->beh.release )
			engine->CallObjectMethod(ptr, in_objType->beh.release);
	}
	else
	{
		if( in_objType->beh.destruct )
			engine->CallObjectMethod(ptr, in_objType->beh.destruct);

		engine->CallFree(ptr);
	}
}

void asCScriptObject::CopyObject(const void *src, void *dst, asCObjectType *in_objType, asCScriptEngine *engine)
{
	int funcIndex = in_objType->beh.copy;
	if( funcIndex )
	{
		asCScriptFunction *func = engine->scriptFunctions[in_objType->beh.copy];
		if( func->funcType == asFUNC_SYSTEM )
			engine->CallObjectMethod(dst, const_cast<void*>(src), funcIndex);
		else
		{
			// Call the script class' opAssign method
			asASSERT(in_objType->flags & asOBJ_SCRIPT_OBJECT );
			reinterpret_cast<asCScriptObject*>(dst)->CopyFrom(reinterpret_cast<const asCScriptObject*>(src));
		}
	}
	else if( in_objType->size && (in_objType->flags & asOBJ_POD) )
		memcpy(dst, src, in_objType->size);
}

void asCScriptObject::CopyHandle(asPWORD *src, asPWORD *dst, asCObjectType *in_objType, asCScriptEngine *engine)
{
	// asOBJ_NOCOUNT doesn't have addref or release behaviours
	asASSERT( (in_objType->flags & asOBJ_NOCOUNT) || (in_objType->beh.release && in_objType->beh.addref) );

	if( *dst && in_objType->beh.release )
		engine->CallObjectMethod(*(void**)dst, in_objType->beh.release);
	*dst = *src;
	if( *dst && in_objType->beh.addref )
		engine->CallObjectMethod(*(void**)dst, in_objType->beh.addref);
}

// TODO: weak: Should move to its own file
asCLockableSharedBool::asCLockableSharedBool() : value(false)
{
	refCount.set(1);
}

int asCLockableSharedBool::AddRef() const
{
	return refCount.atomicInc();
}

int asCLockableSharedBool::Release() const
{
	int r = refCount.atomicDec();
	if( r == 0 )
		asDELETE(const_cast<asCLockableSharedBool*>(this), asCLockableSharedBool);
	return r;
}

bool asCLockableSharedBool::Get() const
{
	return value;
}

void asCLockableSharedBool::Set(bool v)
{
	// Make sure the value is not changed while another thread
	// is inspecting it and taking a decision on what to do.
	Lock();
	value = v;
	Unlock();
}

void asCLockableSharedBool::Lock() const
{
	ENTERCRITICALSECTION(lock);
}

void asCLockableSharedBool::Unlock() const
{
	LEAVECRITICALSECTION(lock);
}

// Interface
// Auxiliary function to allow applications to create shared
// booleans without having to implement the logic for them
AS_API asILockableSharedBool *asCreateLockableSharedBool()
{
	return asNEW(asCLockableSharedBool);
}

END_AS_NAMESPACE

repo_name: Antidote/openrpgeditor

license: mit


code:
/*
   AngelCode Scripting Library
   Copyright (c) 2003-2023 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_scriptobject.h
//
// A generic class for handling script declared structures
//

#ifndef AS_SCRIPTOBJECT_H
#define AS_SCRIPTOBJECT_H

#include "as_config.h"
#include "as_atomic.h"

BEGIN_AS_NAMESPACE

class asCObjectType;

// TODO: Add const overload for GetAddressOfProperty

// TODO: weak: Should move to its own file
class asCLockableSharedBool : public asILockableSharedBool
{
public:
	asCLockableSharedBool();
	int AddRef() const;
	int Release() const;

	bool Get() const;
	void Set(bool);

	void Lock() const;
	void Unlock() const;

protected:
	mutable asCAtomic refCount;
	bool      value;
	DECLARECRITICALSECTION(mutable lock)
};

class asCScriptObject : public asIScriptObject
{
public:
//===================================
// From asIScriptObject
//===================================

	// Memory management
	int                    AddRef() const;
	int                    Release() const;
	asILockableSharedBool *GetWeakRefFlag() const;

	// Type info
	int            GetTypeId() const;
	asITypeInfo   *GetObjectType() const;

	// Class properties
	asUINT      GetPropertyCount() const;
	int         GetPropertyTypeId(asUINT prop) const;
	const char *GetPropertyName(asUINT prop) const;
	void       *GetAddressOfProperty(asUINT prop);

	// Miscellaneous
	asIScriptEngine *GetEngine() const;
	int              CopyFrom(const asIScriptObject *other);

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

//====================================
// Internal
//====================================
	asCScriptObject(asCObjectType *objType, bool doInitialize = true);
	virtual ~asCScriptObject();

	asCScriptObject &operator=(const asCScriptObject &other);

	// GC methods
	void Destruct();
	int  GetRefCount();
	void SetFlag();
	bool GetFlag();
	void EnumReferences(asIScriptEngine *engine);
	void ReleaseAllHandles(asIScriptEngine *engine);

	// Used for properties
	void *AllocateUninitializedObject(asCObjectType *objType, asCScriptEngine *engine);
	void FreeObject(void *ptr, asCObjectType *objType, asCScriptEngine *engine);
	void CopyObject(const void *src, void *dst, asCObjectType *objType, asCScriptEngine *engine);
	void CopyHandle(asPWORD *src, asPWORD *dst, asCObjectType *objType, asCScriptEngine *engine);
	int  CopyFromAs(const asCScriptObject *other, asCObjectType *objType);

	void CallDestructor();

//=============================================
// Properties
//=============================================
public:
	// This is public to allow external code (e.g. JIT compiler) to do asOFFSET to
	// access the members directly without having to modify the code to add friend
	asCObjectType* objType;

protected:
	mutable asCAtomic refCount;
	mutable asBYTE    gcFlag:1;
	mutable asBYTE    hasRefCountReachedZero:1;
	bool              isDestructCalled;

	// Most script classes instances won't have neither the weakRefFlags nor
	// userData so we only allocate this if requested. Even when used it is
	// not something that will be accessed all the time so having the extra
	// indirection will not affect the performance significantly.
	struct SExtra
	{
		SExtra() : weakRefFlag(0) {};
		asCLockableSharedBool *weakRefFlag;
		asCArray<asPWORD>      userData;
	};
	mutable SExtra *extra;
};

void ScriptObject_Construct(asCObjectType *objType, asCScriptObject *self);
asCScriptObject &ScriptObject_Assignment(asCScriptObject *other, asCScriptObject *self);

void ScriptObject_ConstructUnitialized(asCObjectType *objType, asCScriptObject *self);

void RegisterScriptObject(asCScriptEngine *engine);

asIScriptObject *ScriptObjectFactory(const asCObjectType *objType, asCScriptEngine *engine);
asIScriptObject *ScriptObjectCopyFactory(const asCObjectType *objType, void *origObj, asCScriptEngine *engine);

END_AS_NAMESPACE

#endif


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
*/

#include "as_config.h"

#include <stdarg.h>		// va_list, va_start(), etc
#include <stdlib.h>		// strtod(), strtol()
#include <string.h>		// some compilers declare memcpy() here

#if !defined(AS_NO_MEMORY_H)
#include <memory.h>
#endif

#include "as_string.h"
#include "as_string_util.h"

asCString::asCString()
{
	length = 0;
	local[0] = 0;
}

// Copy constructor
asCString::asCString(const asCString &str)
{
	length = 0;
	local[0] = 0;

	Assign(str.AddressOf(), str.length);
}

#ifdef AS_CAN_USE_CPP11
asCString::asCString(asCString &&str)
{
	if( str.length <= 11 )
	{
		length = str.length;
		memcpy(local, str.local, length);
		local[length] = 0;
	}
	else
	{
		dynamic = str.dynamic;
		length = str.length;
	}

	str.dynamic = 0;
	str.length = 0;
}
#endif // c++11

asCString::asCString(const char *str, size_t len)
{
	length = 0;
	local[0] = 0;

	Assign(str, len);
}

asCString::asCString(const char *str)
{
	length = 0;
	local[0] = 0;

	size_t len = strlen(str);
	Assign(str, len);
}

asCString::asCString(char ch)
{
	length = 0;
	local[0] = 0;

	Assign(&ch, 1);
}

asCString::~asCString()
{
	if( length > 11 && dynamic )
	{
		asDELETEARRAY(dynamic);
	}
}

char *asCString::AddressOf()
{
	if( length <= 11 )
		return local;
	else
		return dynamic;
}

const char *asCString::AddressOf() const
{
	if( length <= 11 )
		return local;
	else
		return dynamic;
}

void asCString::SetLength(size_t len)
{
	Allocate(len, true);
}

void asCString::Allocate(size_t len, bool keepData)
{
	// If we stored the capacity of the dynamically allocated buffer it would be possible
	// to save some memory allocations if a string decreases in size then increases again,
	// but this would require extra bytes in the string object itself, or a decrease of
	// the static buffer, which in turn would mean extra memory is needed. I've tested each
	// of these options, and it turned out that the current choice is what best balanced
	// the number of allocations against the size of the allocations.

	if( len > 11 && len > length )
	{
		// Allocate a new dynamic buffer if the new one is larger than the old
		char *buf = asNEWARRAY(char,len+1);
		if( buf == 0 )
		{
			// Out of memory. Return without modifying anything
			return;
		}

		if( keepData )
		{
			int l = (int)len < (int)length ? (int)len : (int)length;
			memcpy(buf, AddressOf(), l);
		}

		if( length > 11 )
		{
			asDELETEARRAY(dynamic);
		}

		dynamic = buf;
	}
	else if( len <= 11 && length > 11 )
	{
		// Free the dynamic buffer, since it is no longer needed
		char *buf = dynamic;
		if( keepData )
		{
			memcpy(&local, buf, len);
		}
		asDELETEARRAY(buf);
	}

	length = (int)len;

	// Make sure the buffer is null terminated
	AddressOf()[length] = 0;
}

void asCString::Assign(const char *str, size_t len)
{
	Allocate(len, false);

	// Copy the string
	memcpy(AddressOf(), str, length);
	AddressOf()[length] = 0;
}

asCString &asCString::operator =(const char *str)
{
	size_t len = str ? strlen(str) : 0;
	Assign(str, len);

	return *this;
}

asCString &asCString::operator =(const asCString &str)
{
	Assign(str.AddressOf(), str.length);

	return *this;
}

#ifdef AS_CAN_USE_CPP11
asCString &asCString::operator =(asCString &&str)
{
	if( this != &str )
	{
		if( length > 11 && dynamic )
		{
			asDELETEARRAY(dynamic);
		}

		if ( str.length <= 11 )
		{
			length = str.length;

			memcpy(local, str.local, length);
			local[length] = 0;
		}
		else
		{
			dynamic = str.dynamic;
			length = str.length;
		}

		str.dynamic = 0;
		str.length = 0;
	}

	return *this;
}
#endif // c++11

asCString &asCString::operator =(char ch)
{
	Assign(&ch, 1);

	return *this;
}

void asCString::Concatenate(const char *str, size_t len)
{
	asUINT oldLength = length;
	SetLength(length + len);

	memcpy(AddressOf() + oldLength, str, len);
	AddressOf()[length] = 0;
}

asCString &asCString::operator +=(const char *str)
{
	size_t len = strlen(str);
	Concatenate(str, len);

	return *this;
}

asCString &asCString::operator +=(const asCString &str)
{
	Concatenate(str.AddressOf(), str.length);

	return *this;
}

asCString &asCString::operator +=(char ch)
{
	Concatenate(&ch, 1);

	return *this;
}

size_t asCString::GetLength() const
{
	return length;
}

// Returns the length
size_t asCString::Format(const char *format, ...)
{
	va_list args;
	va_start(args, format);

	const size_t startSize = 1024;
	char tmp[startSize];
	int r = asVSNPRINTF(tmp, startSize-1, format, args);

	if( r > 0 && r < int(startSize) )
	{
		Assign(tmp, r);
	}
	else
	{
		// TODO: For some reason this doesn't work properly on Linux. Perhaps the
		//       problem is related to vsnprintf not keeping the state of va_arg.
		//       Perhaps I need to rewrite this in some way to keep the state
		size_t n = startSize*2;
		asCString str; // Use temporary string in case the current buffer is a parameter
		str.Allocate(n, false);

		while( (r = asVSNPRINTF(str.AddressOf(), n, format, args)) < 0 || r >= int(n) )
		{
			n *= 2;
			str.Allocate(n, false);
		}

		Assign(str.AddressOf(), r);
	}

	va_end(args);

	return length;
}

char &asCString::operator [](size_t index)
{
	asASSERT(index < length);

	return AddressOf()[index];
}

const char &asCString::operator [](size_t index) const
{
	asASSERT(index < length);

	return AddressOf()[index];
}

asCString asCString::SubString(size_t in_start, size_t in_length) const
{
	if( in_start >= GetLength() || in_length == 0 )
		return asCString("");

	if( in_length == (size_t)(-1) ) in_length = GetLength() - in_start;

	asCString tmp;
	tmp.Assign(AddressOf() + in_start, in_length);

	return tmp;
}

int asCString::Compare(const char *str) const
{
	return asCompareStrings(AddressOf(), length, str, strlen(str));
}

int asCString::Compare(const asCString &str) const
{
	return asCompareStrings(AddressOf(), length, str.AddressOf(), str.GetLength());
}

int asCString::Compare(const char *str, size_t len) const
{
	return asCompareStrings(AddressOf(), length, str, len);
}

size_t asCString::RecalculateLength()
{
	SetLength(strlen(AddressOf()));

	return length;
}

int asCString::FindLast(const char *str, int *count) const
{
	// There is no strstr that starts from the end, so
	// we'll iterate until we find the last occurrance.
	// This shouldn't cause a performance problem because
	// it is not expected that this will be done very often,
	// and then only on quite short strings anyway.

	if( count ) *count = 0;

	const char *last = 0;
	const char *curr = AddressOf()-1;
	while( (curr = strstr(curr+1, str)) != 0 )
	{
		if( count ) (*count)++;
		last = curr;
	}

	if( last )
		return int(last - AddressOf());

	return -1;
}

//-----------------------------------------------------------------------------
// Helper functions

bool operator ==(const asCString &a, const char *b)
{
	return a.Compare(b) == 0;
}

bool operator !=(const asCString &a, const char *b)
{
	return a.Compare(b) != 0;
}

bool operator ==(const asCString &a, const asCString &b)
{
	return a.Compare(b) == 0;
}

bool operator !=(const asCString &a, const asCString &b)
{
	return a.Compare(b) != 0;
}

bool operator ==(const char *a, const asCString &b)
{
	return b.Compare(a) == 0;
}

bool operator !=(const char *a, const asCString &b)
{
	return b.Compare(a) != 0;
}

bool operator <(const asCString &a, const asCString &b)
{
	return a.Compare(b) < 0;
}

asCString operator +(const asCString &a, const asCString &b)
{
	asCString res = a;
	res += b;

	return res;
}

asCString operator +(const char *a, const asCString &b)
{
	asCString res = a;
	res += b;

	return res;
}

asCString operator +(const asCString &a, const char *b)
{
	asCString res = a;
	res += b;

	return res;
}

// wrapper class

asCStringPointer::asCStringPointer()
	: string(0), length(0), cstring(0)
{
}

asCStringPointer::asCStringPointer(const char *str, size_t len)
	: string(str), length(len), cstring(0)
{
}

asCStringPointer::asCStringPointer(asCString *cstr)
	: string(0), length(0), cstring(cstr)
{
}

const char *asCStringPointer::AddressOf() const
{
	return string ? string : cstring->AddressOf();
}

size_t asCStringPointer::GetLength() const
{
	return string ? length : cstring->GetLength();
}

bool asCStringPointer::operator==(const asCStringPointer& other) const
{
	return asCompareStrings(AddressOf(), GetLength(), other.AddressOf(), other.GetLength()) == 0;
}

bool asCStringPointer::operator<(const asCStringPointer& other) const
{
	return asCompareStrings(AddressOf(), GetLength(), other.AddressOf(), other.GetLength()) < 0;
}


code:
/*
   AngelCode Scripting Library
   Copyright (c) 2003-2014 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
*/

// This class has been designed to be easy to use, but not necessarily efficiency.
// It doesn't use shared string memory, or reference counting. It keeps track of
// string length, memory size. It also makes sure that the string is null-terminated.

#ifndef AS_STRING_H
#define AS_STRING_H

#include <stdio.h>
#include <string.h>

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

#ifdef AS_CAN_USE_CPP11
	asCString(asCString &&);
	asCString &operator =(asCString &&);
#endif // c++11

	asCString(const asCString &);
	asCString(const char *);
	asCString(const char *, size_t length);
	explicit asCString(char);

	void   Allocate(size_t len, bool keepData);
	void   SetLength(size_t len);
	size_t GetLength() const;

	void Concatenate(const char *str, size_t length);
	asCString &operator +=(const asCString &);
	asCString &operator +=(const char *);
	asCString &operator +=(char);

	void Assign(const char *str, size_t length);
	asCString &operator =(const asCString &);
	asCString &operator =(const char *);
	asCString &operator =(char);

	asCString SubString(size_t start, size_t length = (size_t)(-1)) const;

	int FindLast(const char *str, int *count = 0) const;

	size_t Format(const char *fmt, ...);

	int Compare(const char *str) const;
	int Compare(const asCString &str) const;
	int Compare(const char *str, size_t length) const;

	char *AddressOf();
	const char *AddressOf() const;
	char &operator [](size_t index);
	const char &operator[](size_t index) const;
	size_t RecalculateLength();

protected:
	unsigned int length;
	union
	{
		char *dynamic;
		char local[12];
	};
};

// Helper functions

bool operator ==(const asCString &, const asCString &);
bool operator !=(const asCString &, const asCString &);

bool operator ==(const asCString &, const char *);
bool operator !=(const asCString &, const char *);

bool operator ==(const char *, const asCString &);
bool operator !=(const char *, const asCString &);

bool operator <(const asCString &, const asCString &);

asCString operator +(const asCString &, const char *);
asCString operator +(const char *, const asCString &);
asCString operator +(const asCString &, const asCString &);

// a wrapper for using the pointer of asCString in asCMap
class asCStringPointer
{
public:
	asCStringPointer();
	asCStringPointer(const char *str, size_t len);
	asCStringPointer(asCString *cstr);

	const char *AddressOf() const;
	size_t GetLength() const;

	bool operator==(const asCStringPointer& other) const;
	bool operator<(const asCStringPointer& other) const;

private:
	// Either string/length or cstring is stored
	const char *string;
	size_t      length;
	asCString  *cstring;
};

#endif


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

*/

#include "as_config.h"

#include <string.h>     // some compilers declare memcpy() here
#include <math.h>       // pow()

#if !defined(AS_NO_MEMORY_H)
#include <memory.h>
#endif

#include "as_string.h"
#include "as_string_util.h"

BEGIN_AS_NAMESPACE

int asCompareStrings(const char *str1, size_t len1, const char *str2, size_t len2)
{
	if( len1 == 0 )
	{
		if( str2 == 0 || len2 == 0 ) return 0; // Equal

		return 1; // The other string is larger than this
	}

	if( str2 == 0 )
	{
		if( len1 == 0 )
			return 0; // Equal

		return -1; // The other string is smaller than this
	}

	if( len2 < len1 )
	{
		int result = memcmp(str1, str2, len2);
		if( result == 0 ) return -1; // The other string is smaller than this

		return result;
	}

	int result = memcmp(str1, str2, len1);
	if( result == 0 && len1 < len2 ) return 1; // The other string is larger than this

	return result;
}

double asStringScanDouble(const char *string, size_t *numScanned)
{
	// I decided to do my own implementation of strtod() because this function
	// doesn't seem to be present on all systems. iOS 5 for example doesn't appear
	// to include the function in the standard lib.

	// Another reason is that the standard implementation of strtod() is dependent
	// on the locale on some systems, i.e. it may use comma instead of dot for
	// the decimal indicator. This can be avoided by forcing the locale to "C" with
	// setlocale(), but this is another thing that is highly platform dependent.

	double value = 0;
	double fraction = 0.1;
	int exponent = 0;
	bool negativeExponent = false;
	int c = 0;

	// The tokenizer separates the sign from the number in
	// two tokens so we'll never have a sign to parse here

	// Parse the integer value
	for( ;; )
	{
		if( string[c] >= '0' && string[c] <= '9' )
			value = value*10 + double(string[c] - '0');
		else
			break;

		c++;
	}

	if( string[c] == '.' )
	{
		c++;

		// Parse the fraction
		for( ;; )
		{
			if( string[c] >= '0' && string[c] <= '9' )
				value += fraction * double(string[c] - '0');
			else
				break;

			c++;
			fraction *= 0.1;
		}
	}

	if( string[c] == 'e' || string[c] == 'E' )
	{
		c++;

		// Parse the sign of the exponent
		if( string[c] == '-' )
		{
			negativeExponent = true;
			c++;
		}
		else if( string[c] == '+' )
			c++;

		// Parse the exponent value
		for( ;; )
		{
			if( string[c] >= '0' && string[c] <= '9' )
				exponent = exponent*10 + int(string[c] - '0');
			else
				break;

			c++;
		}
	}

	if( exponent )
	{
		if( negativeExponent )
			exponent = -exponent;
		value *= pow(10.0, exponent);
	}

	if( numScanned )
		*numScanned = c;

	return value;
}

// Converts a character to the decimal number based on the radix
// Returns -1 if the character is not valid for the radix
static int asCharToNbr(char ch, int radix)
{
	if( ch >= '0' && ch <= '9' ) return ((ch -= '0') < radix ? ch : -1);
	if( ch >= 'A' && ch <= 'Z' ) return ((ch -= 'A'-10) < radix ? ch : -1);
	if( ch >= 'a' && ch <= 'z' ) return ((ch -= 'a'-10) < radix ? ch : -1);
	return -1;
}

// If base is 0 the string should be prefixed by 0x, 0d, 0o, or 0b to allow the function to automatically determine the radix
asQWORD asStringScanUInt64(const char *string, int base, size_t *numScanned, bool *overflow)
{
	asASSERT(base == 10 || base == 16 || base == 0);

	if (overflow)
		*overflow = false;

	const char *end = string;

	static const asQWORD QWORD_MAX = (~asQWORD(0));

	asQWORD res = 0;
	if( base == 10 )
	{
		while( *end >= '0' && *end <= '9' )
		{
			if( overflow && ((res > QWORD_MAX / 10) || ((asUINT(*end - '0') > (QWORD_MAX - (QWORD_MAX / 10) * 10)) && res == QWORD_MAX / 10)) )
				*overflow = true;
			res *= 10;
			res += *end++ - '0';
		}
	}
	else
	{
		if( base == 0 && string[0] == '0')
		{
			// Determine the radix from the prefix
			switch( string[1] )
			{
			case 'b': case 'B': base = 2; break;
			case 'o': case 'O': base = 8; break;
			case 'd': case 'D': base = 10; break;
			case 'x': case 'X': base = 16; break;
			}
			end += 2;
		}

		asASSERT( base );

		if( base )
		{
			for (int nbr; (nbr = asCharToNbr(*end, base)) >= 0; end++)
			{
				if (overflow && ((res > QWORD_MAX / base) || ((asUINT(nbr) > (QWORD_MAX - (QWORD_MAX / base) * base)) && res == QWORD_MAX / base)) )
					*overflow = true;

				res = res * base + nbr;
			}
		}
	}

	if( numScanned )
		*numScanned = end - string;

	return res;
}

//
// The function will encode the unicode code point into the outEncodedBuffer, and then
// return the length of the encoded value. If the input value is not a valid unicode code
// point, then the function will return -1.
//
// This function is taken from the AngelCode ToolBox.
//
int asStringEncodeUTF8(unsigned int value, char *outEncodedBuffer)
{
	unsigned char *buf = (unsigned char*)outEncodedBuffer;

	int length = -1;

	if( value <= 0x7F )
	{
		buf[0] = static_cast<unsigned char>(value);
		return 1;
	}
	else if( value >= 0x80 && value <= 0x7FF )
	{
		// Encode it with 2 characters
		buf[0] = static_cast<unsigned char>(0xC0 + (value >> 6));
		length = 2;
	}
	else if( (value >= 0x800 && value <= 0xD7FF) || (value >= 0xE000 && value <= 0xFFFF) )
	{
		// Note: Values 0xD800 to 0xDFFF are not valid unicode characters
		buf[0] = static_cast<unsigned char>(0xE0 + (value >> 12));
		length = 3;
	}
	else if( value >= 0x10000 && value <= 0x10FFFF )
	{
		buf[0] = static_cast<unsigned char>(0xF0 + (value >> 18));
		length = 4;
	}

	int n = length-1;
	for( ; n > 0; n-- )
	{
		buf[n] = static_cast<unsigned char>(0x80 + (value & 0x3F));
		value >>= 6;
	}

	return length;
}

//
// The function will decode an UTF8 character and return the unicode code point.
// outLength will receive the number of bytes that were decoded.
//
// This function is taken from the AngelCode ToolBox.
//
int asStringDecodeUTF8(const char *encodedBuffer, unsigned int *outLength)
{
	const unsigned char *buf = (const unsigned char*)encodedBuffer;

	int value = 0;
	int length = -1;
	unsigned char byte = buf[0];
	if( (byte & 0x80) == 0 )
	{
		// This is the only byte
		if( outLength ) *outLength = 1;
		return byte;
	}
	else if( (byte & 0xE0) == 0xC0 )
	{
		// There is one more byte
		value = int(byte & 0x1F);
		length = 2;

		// The value at this moment must not be less than 2, because
		// that should have been encoded with one byte only.
		if( value < 2 )
			length = -1;
	}
	else if( (byte & 0xF0) == 0xE0 )
	{
		// There are two more bytes
		value = int(byte & 0x0F);
		length = 3;
	}
	else if( (byte & 0xF8) == 0xF0 )
	{
		// There are three more bytes
		value = int(byte & 0x07);
		length = 4;
	}

	int n = 1;
	for( ; n < length; n++ )
	{
		byte = buf[n];
		if( (byte & 0xC0) == 0x80 )
			value = (value << 6) + int(byte & 0x3F);
		else
			break;
	}

	if( n == length )
	{
		if( outLength ) *outLength = (unsigned)length;
		return value;
	}

	// The byte sequence isn't a valid UTF-8 byte sequence.
	return -1;
}

//
// The function will encode the unicode code point into the outEncodedBuffer, and then
// return the length of the encoded value. If the input value is not a valid unicode code
// point, then the function will return -1.
//
// This function is taken from the AngelCode ToolBox.
//
int asStringEncodeUTF16(unsigned int value, char *outEncodedBuffer)
{
	if( value < 0x10000 )
	{
#ifndef AS_BIG_ENDIAN
		outEncodedBuffer[0] = (value & 0xFF);
		outEncodedBuffer[1] = ((value >> 8) & 0xFF);
#else
		outEncodedBuffer[1] = (value & 0xFF);
		outEncodedBuffer[0] = ((value >> 8) & 0xFF);
#endif
		return 2;
	}
	else
	{
		value -= 0x10000;
		int surrogate1 = ((value >> 10) & 0x3FF) + 0xD800;
		int surrogate2 = (value & 0x3FF) + 0xDC00;

#ifndef AS_BIG_ENDIAN
		outEncodedBuffer[0] = (surrogate1 & 0xFF);
		outEncodedBuffer[1] = ((surrogate1 >> 8) & 0xFF);
		outEncodedBuffer[2] = (surrogate2 & 0xFF);
		outEncodedBuffer[3] = ((surrogate2 >> 8) & 0xFF);
#else
		outEncodedBuffer[1] = (surrogate1 & 0xFF);
		outEncodedBuffer[0] = ((surrogate1 >> 8) & 0xFF);
		outEncodedBuffer[3] = (surrogate2 & 0xFF);
		outEncodedBuffer[2] = ((surrogate2 >> 8) & 0xFF);
#endif

		return 4;
	}
}

END_AS_NAMESPACE


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
*/

#ifndef AS_STRING_UTIL_H
#define AS_STRING_UTIL_H

#include "as_config.h"

BEGIN_AS_NAMESPACE

int     asCompareStrings(const char *str1, size_t len1, const char *str2, size_t len2);

double  asStringScanDouble(const char *string, size_t *numScanned);
asQWORD asStringScanUInt64(const char *string, int base, size_t *numScanned, bool *overflow);

int     asStringEncodeUTF8(unsigned int value, char *outEncodedBuffer);
int     asStringDecodeUTF8(const char *encodedBuffer, unsigned int *outLength);

int     asStringEncodeUTF16(unsigned int value, char *outEncodedBuffer);

END_AS_NAMESPACE

#endif


code:
/*
   AngelCode Scripting Library
   Copyright (c) 2012-2021 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_symboltable.h
//
//  Created on: Jun 19, 2012
//      Author: Markus Lenger, a.k.a. mlengerx
//
// This class is used for fast symbol lookups while parsing or loading bytecode
//

#ifndef AS_SYMBOLTABLE_H
#define AS_SYMBOLTABLE_H

#include "as_config.h"
#include "as_memory.h"
#include "as_string.h"
#include "as_map.h"
#include "as_datatype.h"
#include "as_namespace.h"

BEGIN_AS_NAMESPACE

// Interface to avoid nested templates which is not well supported by older compilers, e.g. MSVC6
struct asIFilter
{
	virtual bool operator()(const void*) const = 0;
	virtual ~asIFilter() {};
};

// forward declaration
template<class T>
class asCSymbolTable;

// Iterator that allows iterating in index order
template<class T, class T2 = T>
class asCSymbolTableIterator
{
public:
	T2* operator*() const;
	T2* operator->() const;
	asCSymbolTableIterator<T, T2>& operator++(int);
	asCSymbolTableIterator<T, T2>& operator--(int);
	operator bool() const;
	int GetIndex() const { return m_idx; }

private:
	friend class asCSymbolTable<T>;
	asCSymbolTableIterator(asCSymbolTable<T> *table);

	void Next();
	void Previous();

	asCSymbolTable<T>* m_table;
	unsigned int       m_idx;
};

// Symbol table mapping namespace + name to symbols
// The structure keeps the entries indexed in an array so the indices will not change
// There is also a map for a quick lookup. The map supports multiple entries with the same name
template<class T>
class asCSymbolTable
{
public:
	typedef asCSymbolTableIterator<T, T> iterator;
	typedef asCSymbolTableIterator<T, const T> const_iterator;

	asCSymbolTable(asUINT initialCapacity = 0);

	int      GetFirstIndex(const asSNameSpace *ns, const asCString &name, const asIFilter &comparator) const;
	int      GetFirstIndex(const asSNameSpace *ns, const asCString &name) const;
	int      GetLastIndex() const;

	int      GetIndex(const T*) const;

	T*       GetFirst(const asSNameSpace *ns, const asCString &name, const asIFilter &comparator) const;
	T*       GetFirst(const asSNameSpace *ns, const asCString &name);
	const T* GetFirst(const asSNameSpace *ns, const asCString &name) const;
	T*       Get(asUINT index);
	const T* Get(asUINT index) const;
	T*       GetLast();
	const T* GetLast() const;

	const asCArray<asUINT> &GetIndexes(const asSNameSpace *ns, const asCString &name) const;

	asUINT   Put(T* entry);

	asUINT   GetSize() const;

	void SwapWith(asCSymbolTable<T> &other);

	void Clear();
	bool Erase(asUINT idx);
	void Allocate(asUINT elem_cnt, bool keep_data);

	iterator List();
	const_iterator List() const;

private:
	// Don't allow assignment
	asCSymbolTable<T>& operator=(const asCSymbolTable<T> &other) { return *this; }

	friend class asCSymbolTableIterator<T, T>;
	friend class asCSymbolTableIterator<T, const T>;

	void GetKey(const T *entry, asSNameSpaceNamePair &key) const;
	bool CheckIdx(asUINT idx) const;

	asCMap<asSNameSpaceNamePair, asCArray<asUINT> > m_map;
	asCArray<T*>                                    m_entries;
	unsigned int                                    m_size;
};

template<class T>
void asCSymbolTable<T>::SwapWith(asCSymbolTable<T> &other)
{
	m_map.SwapWith(other.m_map);
	m_entries.SwapWith(other.m_entries);

	asUINT tmp = m_size;
	m_size = other.m_size;
	other.m_size = tmp;
}

// Constructor
// initialCapacity gives the number of entries to allocate in advance
template<class T>
asCSymbolTable<T>::asCSymbolTable(asUINT initialCapacity) : m_entries(initialCapacity)
{
	m_size = 0;
}

template<class T>
int asCSymbolTable<T>::GetFirstIndex(
        const asSNameSpace *ns,
        const asCString &name,
        const asIFilter &filter) const
{
	asSNameSpaceNamePair key(ns, name);

	asSMapNode<asSNameSpaceNamePair, asCArray<asUINT> > *cursor;
	if( m_map.MoveTo(&cursor, key) )
	{
		const asCArray<asUINT> &arr = m_map.GetValue(cursor);
		for( asUINT n = 0; n < arr.GetLength(); n++ )
		{
			T *entry = m_entries[arr[n]];
			if( entry && filter(entry) )
				return arr[n];
		}
	}

	return -1;
}

template<class T>
const asCArray<asUINT> &asCSymbolTable<T>::GetIndexes(const asSNameSpace *ns, const asCString &name) const
{
	asSNameSpaceNamePair key(ns, name);

	asSMapNode<asSNameSpaceNamePair, asCArray<asUINT> > *cursor;
	if( m_map.MoveTo(&cursor, key) )
		return m_map.GetValue(cursor);

	static asCArray<asUINT> dummy;
	return dummy;
}

template<class T>
T* asCSymbolTable<T>::GetFirst(const asSNameSpace *ns, const asCString &name, const asIFilter &comp) const
{
	int idx = GetFirstIndex(ns, name, comp);
	if (idx != -1) return m_entries[idx];
	return 0;
}

template<class T>
int asCSymbolTable<T>::GetFirstIndex(const asSNameSpace *ns, const asCString &name) const
{
	asSNameSpaceNamePair key(ns, name);

	asSMapNode<asSNameSpaceNamePair, asCArray<asUINT> > *cursor;
	if( m_map.MoveTo(&cursor, key) )
		return m_map.GetValue(cursor)[0];

	return -1;
}

// Find the index of a certain symbol
// ATTENTION: this function has linear runtime complexity O(n)!!
template<class T>
int asCSymbolTable<T>::GetIndex(const T* entry) const
{
	for( asUINT n = 0; n < m_entries.GetLength(); n++ )
		if( m_entries[n] == entry )
			return n;

	return -1;
}

template<class T>
T* asCSymbolTable<T>::Get(asUINT idx)
{
	if( !CheckIdx(idx) )
		return 0;

	return m_entries[idx];
}

template<class T>
const T* asCSymbolTable<T>::Get(asUINT idx) const
{
	return const_cast< asCSymbolTable<T>* >(this)->Get(idx);
}

template<class T>
T* asCSymbolTable<T>::GetFirst(const asSNameSpace *ns, const asCString &name)
{
	int idx = GetFirstIndex(ns, name);
	return Get(idx);
}

template<class T>
const T* asCSymbolTable<T>::GetFirst(const asSNameSpace *ns, const asCString &name) const
{
	return const_cast< asCSymbolTable<T>* >(this)->GetFirst(ns, name);
}

template<class T>
T* asCSymbolTable<T>::GetLast()
{
	return Get(GetLastIndex());
}

template<class T>
const T* asCSymbolTable<T>::GetLast() const
{
	return const_cast< asCSymbolTable<T>* >(this)->GetLast();
}

// Clear the symbol table
// ATTENTION: The contained symbols are not rleased. This is up to the client
template<class T>
void asCSymbolTable<T>::Clear()
{
	m_entries.SetLength(0);
	m_map.EraseAll();
	m_size = 0;
}

// Pre-allocate slots for elemCnt entries
template<class T>
void asCSymbolTable<T>::Allocate(asUINT elemCnt, bool keepData)
{
	asASSERT( elemCnt >= m_entries.GetLength() );
	m_entries.Allocate(elemCnt, keepData);
	if( !keepData )
		m_map.EraseAll();
}

template<class T>
bool asCSymbolTable<T>::Erase(asUINT idx)
{
	if( !CheckIdx(idx) )
	{
		asASSERT(false);
		return false;
	}

	T *entry = m_entries[idx];
	asASSERT(entry);
	if( !entry )
		return false;

	// Remove the symbol from the lookup map
	asSNameSpaceNamePair key;
	GetKey(entry, key);

	asSMapNode<asSNameSpaceNamePair, asCArray<asUINT> > *cursor;
	if( m_map.MoveTo(&cursor, key) )
	{
		asCArray<asUINT> &arr = m_map.GetValue(cursor);
		arr.RemoveValue(idx);
		if( arr.GetLength() == 0 )
			m_map.Erase(cursor);
	}
	else
		asASSERT(false);

	// Remove the symbol from the indexed array
	if( idx == m_entries.GetLength() - 1 )
		m_entries.PopLast();
	else
	{
		// Must keep the array packed
		int prevIdx = int(m_entries.GetLength()-1);
		m_entries[idx] = m_entries.PopLast();

		// Update the index in the lookup map
		entry = m_entries[idx];
		GetKey(entry, key);
		if( m_map.MoveTo(&cursor, key) )
		{
			asCArray<asUINT> &arr = m_map.GetValue(cursor);
			arr[arr.IndexOf(prevIdx)] = idx;
		}
		else
			asASSERT(false);
	}
	m_size--;

	return true;
}

template<class T>
asUINT asCSymbolTable<T>::Put(T *entry)
{
	asUINT idx = m_entries.GetLength();
	asSNameSpaceNamePair key;
	GetKey(entry, key);

	asSMapNode<asSNameSpaceNamePair, asCArray<asUINT> > *cursor;
	if( m_map.MoveTo(&cursor, key) )
		m_map.GetValue(cursor).PushLast(idx);
	else
	{
		asCArray<asUINT> arr(1);
		arr.PushLast(idx);
		m_map.Insert(key, arr);
	}

	m_entries.PushLast(entry);
	m_size++;
	return idx;
}

// Return key for specified symbol (namespace and name are used to generate the key)
template<class T>
void asCSymbolTable<T>::GetKey(const T *entry, asSNameSpaceNamePair &key) const
{
	key = asSNameSpaceNamePair(entry->nameSpace, entry->name);
}

template<class T>
asUINT asCSymbolTable<T>::GetSize() const
{
	return m_size;
}

template<class T>
bool asCSymbolTable<T>::CheckIdx(asUINT idx) const
{
	return idx < m_entries.GetLength();
}

template<class T>
int asCSymbolTable<T>::GetLastIndex() const
{
	int idx = int(m_entries.GetLength()) - 1;
	asASSERT( idx == -1 || m_entries[idx] );
	return idx;
}

template<class T>
asCSymbolTableIterator<T, T> asCSymbolTable<T>::List()
{
	return asCSymbolTableIterator<T, T>(this);
}

template<class T>
typename asCSymbolTable<T>::const_iterator asCSymbolTable<T>::List() const
{
	return asCSymbolTableIterator<T, const T>(const_cast< asCSymbolTable<T> *>(this));
}

/////////////////////////////////////////////////////////////////////////////////////////////////
// Iterator

template<class T, class T2>
asCSymbolTableIterator<T, T2>::asCSymbolTableIterator(asCSymbolTable<T> *table) : m_table(table), m_idx(0)
{
	asUINT sz = m_table->m_entries.GetLength();
	while( m_idx < sz && m_table->m_entries[m_idx] == 0 )
		m_idx++;
}

template<class T, class T2>
T2* asCSymbolTableIterator<T, T2>::operator*() const
{
	asASSERT(m_table->CheckIdx(m_idx));
	return m_table->m_entries[m_idx];
}

template<class T, class T2>
T2* asCSymbolTableIterator<T, T2>::operator->() const
{
	asASSERT(m_table->CheckIdx(m_idx));
	return m_table->m_entries[m_idx];
}

template<class T, class T2>
asCSymbolTableIterator<T, T2>& asCSymbolTableIterator<T, T2>::operator++(int)
{
	Next();
	return *this;
}

// Return true if more elements are following
// ATTENTION: When deleting the object currently pointed to by this iterator this
// method returns false even though there might be more elements in the list
template<class T, class T2>
asCSymbolTableIterator<T, T2>::operator bool() const
{
	return m_idx < m_table->m_entries.GetLength() && m_table->m_entries[m_idx] != 0;
}

template<class T, class T2>
void asCSymbolTableIterator<T, T2>::Next()
{
	asUINT sz = m_table->m_entries.GetLength();
	m_idx++;
	while( m_idx < sz && m_table->m_entries[m_idx] == 0 )
		m_idx++;
}

template<class T, class T2>
void asCSymbolTableIterator<T, T2>::Previous()
{
	// overflow on stepping over first element
	asUINT sz = m_table->m_entries.GetLength();
	m_idx--;
	while( m_idx < sz && m_table->m_entries[m_idx] == 0 )
		m_idx--;
}

template<class T, class T2>
asCSymbolTableIterator<T, T2>& asCSymbolTableIterator<T, T2>::operator--(int)
{
	Previous();
	return *this;
}

END_AS_NAMESPACE

#endif // AS_SYMBOLTABLE_H