Files
Descent3/AngelScript/scriptarray/scriptarray.cpp
Kevin Bentley df209742fc Initial import
2024-04-15 21:43:29 -06:00

643 lines
20 KiB
C++

#include <new>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "scriptarray.h"
BEGIN_AS_NAMESPACE
struct SArrayBuffer
{
asDWORD numElements;
asBYTE data[1];
};
static CScriptArray* ScriptArrayFactory2(asIObjectType *ot, asUINT length)
{
CScriptArray *a = new CScriptArray(length, ot);
// It's possible the constructor raised a script exception, in which case we
// need to free the memory and return null instead, else we get a memory leak.
asIScriptContext *ctx = asGetActiveContext();
if( ctx && ctx->GetState() == asEXECUTION_EXCEPTION )
{
delete a;
return 0;
}
return a;
}
static CScriptArray* ScriptArrayFactory(asIObjectType *ot)
{
return ScriptArrayFactory2(ot, 0);
}
// This optional callback is called when the template type is first used by the compiler.
// It allows the application to validate if the template can be instanciated for the requested
// subtype at compile time, instead of at runtime.
static bool ScriptArrayTemplateCallback(asIObjectType *ot)
{
// Make sure the subtype can be instanciated with a default factory/constructor,
// otherwise we won't be able to instanciate the elements
int typeId = ot->GetSubTypeId();
if( (typeId & asTYPEID_MASK_OBJECT) && !(typeId & asTYPEID_OBJHANDLE) )
{
asIObjectType *subtype = ot->GetEngine()->GetObjectTypeById(typeId);
asDWORD flags = subtype->GetFlags();
if( (flags & asOBJ_VALUE) && !(flags & asOBJ_POD) )
{
// Verify that there is a default constructor
for( int n = 0; n < subtype->GetBehaviourCount(); n++ )
{
asEBehaviours beh;
int funcId = subtype->GetBehaviourByIndex(n, &beh);
if( beh != asBEHAVE_CONSTRUCT ) continue;
asIScriptFunction *func = ot->GetEngine()->GetFunctionDescriptorById(funcId);
if( func->GetParamCount() == 0 )
{
// Found the default constructor
return true;
}
}
// There is no default constructor
return false;
}
else if( (flags & asOBJ_REF) )
{
// Verify that there is a default factory
for( int n = 0; n < subtype->GetFactoryCount(); n++ )
{
int funcId = subtype->GetFactoryIdByIndex(n);
asIScriptFunction *func = ot->GetEngine()->GetFunctionDescriptorById(funcId);
if( func->GetParamCount() == 0 )
{
// Found the default factory
return true;
}
}
// No default factory
return false;
}
}
// The type is ok
return true;
}
// Registers the template array type
void RegisterScriptArray(asIScriptEngine *engine)
{
if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") == 0 )
RegisterScriptArray_Native(engine);
else
RegisterScriptArray_Generic(engine);
}
void RegisterScriptArray_Native(asIScriptEngine *engine)
{
int r;
// Register the array type as a template
r = engine->RegisterObjectType("array<class T>", 0, asOBJ_REF | asOBJ_GC | asOBJ_TEMPLATE); assert( r >= 0 );
// Register a callback for validating the subtype before it is used
r = engine->RegisterObjectBehaviour("array<T>", asBEHAVE_TEMPLATE_CALLBACK, "bool f(int&in)", asFUNCTION(ScriptArrayTemplateCallback), asCALL_CDECL); assert( r >= 0 );
// Templates receive the object type as the first parameter. To the script writer this is hidden
r = engine->RegisterObjectBehaviour("array<T>", asBEHAVE_FACTORY, "array<T>@ f(int&in)", asFUNCTIONPR(ScriptArrayFactory, (asIObjectType*), CScriptArray*), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("array<T>", asBEHAVE_FACTORY, "array<T>@ f(int&in, uint)", asFUNCTIONPR(ScriptArrayFactory2, (asIObjectType*, asUINT), CScriptArray*), asCALL_CDECL); assert( r >= 0 );
// The memory management methods
r = engine->RegisterObjectBehaviour("array<T>", asBEHAVE_ADDREF, "void f()", asMETHOD(CScriptArray,AddRef), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("array<T>", asBEHAVE_RELEASE, "void f()", asMETHOD(CScriptArray,Release), asCALL_THISCALL); assert( r >= 0 );
// The index operator returns the template subtype
r = engine->RegisterObjectBehaviour("array<T>", asBEHAVE_INDEX, "T &f(uint)", asMETHOD(CScriptArray, At), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("array<T>", asBEHAVE_INDEX, "const T &f(uint) const", asMETHOD(CScriptArray, At), asCALL_THISCALL); assert( r >= 0 );
// The assignment operator
r = engine->RegisterObjectMethod("array<T>", "array<T> &opAssign(const array<T>&in)", asMETHOD(CScriptArray, operator=), asCALL_THISCALL); assert( r >= 0 );
// Other methods
r = engine->RegisterObjectMethod("array<T>", "uint length() const", asMETHOD(CScriptArray, GetSize), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("array<T>", "void resize(uint)", asMETHOD(CScriptArray, Resize), asCALL_THISCALL); assert( r >= 0 );
// Register GC behaviours in case the array needs to be garbage collected
r = engine->RegisterObjectBehaviour("array<T>", asBEHAVE_GETREFCOUNT, "int f()", asMETHOD(CScriptArray, GetRefCount), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("array<T>", asBEHAVE_SETGCFLAG, "void f()", asMETHOD(CScriptArray, SetFlag), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("array<T>", asBEHAVE_GETGCFLAG, "bool f()", asMETHOD(CScriptArray, GetFlag), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("array<T>", asBEHAVE_ENUMREFS, "void f(int&in)", asMETHOD(CScriptArray, EnumReferences), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("array<T>", asBEHAVE_RELEASEREFS, "void f(int&in)", asMETHOD(CScriptArray, ReleaseAllHandles), asCALL_THISCALL); assert( r >= 0 );
}
CScriptArray &CScriptArray::operator=(const CScriptArray &other)
{
// Only perform the copy if the array types are the same
if( &other != this &&
other.GetArrayObjectType() == GetArrayObjectType() )
{
if( buffer )
{
DeleteBuffer(buffer);
buffer = 0;
}
// Copy all elements from the other array
CreateBuffer(&buffer, other.buffer->numElements);
CopyBuffer(buffer, other.buffer);
}
return *this;
}
CScriptArray::CScriptArray(asUINT length, asIObjectType *ot)
{
refCount = 1;
gcFlag = false;
objType = ot;
objType->AddRef();
buffer = 0;
// Determine element size
// TODO: Should probably store the template sub type id as well
int typeId = objType->GetSubTypeId();
if( typeId & asTYPEID_MASK_OBJECT )
{
elementSize = sizeof(asPWORD);
}
else
{
elementSize = objType->GetEngine()->GetSizeOfPrimitiveType(typeId);
}
isArrayOfHandles = typeId & asTYPEID_OBJHANDLE ? true : false;
// Make sure the array size isn't too large for us to handle
if( !CheckMaxSize(length) )
{
// Don't continue with the initialization
return;
}
CreateBuffer(&buffer, length);
// Notify the GC of the successful creation
if( objType->GetFlags() & asOBJ_GC )
objType->GetEngine()->NotifyGarbageCollectorOfNewObject(this, objType->GetTypeId());
}
CScriptArray::~CScriptArray()
{
if( buffer )
{
DeleteBuffer(buffer);
buffer = 0;
}
if( objType ) objType->Release();
}
asUINT CScriptArray::GetSize()
{
return buffer->numElements;
}
void CScriptArray::Resize(asUINT numElements)
{
// Don't do anything if the size is already correct
if( numElements == buffer->numElements )
return;
// Make sure the array size isn't too large for us to handle
if( !CheckMaxSize(numElements) )
{
// Don't resize the array
return;
}
SArrayBuffer *newBuffer;
int typeId = objType->GetSubTypeId();
if( typeId & asTYPEID_MASK_OBJECT )
{
// Allocate memory for the buffer
newBuffer = (SArrayBuffer*)new asBYTE[sizeof(SArrayBuffer)-1+sizeof(void*)*numElements];
newBuffer->numElements = numElements;
// Copy the elements from the old buffer
int c = numElements > buffer->numElements ? buffer->numElements : numElements;
asDWORD **d = (asDWORD**)newBuffer->data;
asDWORD **s = (asDWORD**)buffer->data;
for( int n = 0; n < c; n++ )
d[n] = s[n];
if( numElements > buffer->numElements )
{
Construct(newBuffer, buffer->numElements, numElements);
}
else if( numElements < buffer->numElements )
{
Destruct(buffer, numElements, buffer->numElements);
}
}
else
{
// Allocate memory for the buffer
newBuffer = (SArrayBuffer*)new asBYTE[sizeof(SArrayBuffer)-1+elementSize*numElements];
newBuffer->numElements = numElements;
int c = numElements > buffer->numElements ? buffer->numElements : numElements;
memcpy(newBuffer->data, buffer->data, c*elementSize);
}
// Release the old buffer
delete[] (asBYTE*)buffer;
buffer = newBuffer;
}
// internal
bool CScriptArray::CheckMaxSize(asUINT numElements)
{
// This code makes sure the size of the buffer that is allocated
// for the array doesn't overflow and becomes smaller than requested
asUINT maxSize = 0xFFFFFFFFul - sizeof(SArrayBuffer) + 1;
if( objType->GetSubTypeId() & asTYPEID_MASK_OBJECT )
{
maxSize /= sizeof(void*);
}
else
{
maxSize /= elementSize;
}
if( numElements > maxSize )
{
asIScriptContext *ctx = asGetActiveContext();
if( ctx )
{
// Set a script exception
ctx->SetException("Too large array size");
}
return false;
}
// OK
return true;
}
asIObjectType *CScriptArray::GetArrayObjectType() const
{
return objType;
}
int CScriptArray::GetArrayTypeId() const
{
return objType->GetTypeId();
}
int CScriptArray::GetElementTypeId() const
{
return objType->GetSubTypeId();
}
// Return a pointer to the array element. Returns 0 if the index is out of bounds
void *CScriptArray::At(asUINT index)
{
if( index >= buffer->numElements )
{
// If this is called from a script we raise a script exception
asIScriptContext *ctx = asGetActiveContext();
if( ctx )
ctx->SetException("Index out of bounds");
return 0;
}
else
{
int typeId = objType->GetSubTypeId();
if( (typeId & asTYPEID_MASK_OBJECT) && !isArrayOfHandles )
return (void*)((size_t*)buffer->data)[index];
else
return buffer->data + elementSize*index;
}
}
// internal
void CScriptArray::CreateBuffer(SArrayBuffer **buf, asUINT numElements)
{
int typeId = objType->GetSubTypeId();
if( typeId & asTYPEID_MASK_OBJECT )
{
*buf = (SArrayBuffer*)new asBYTE[sizeof(SArrayBuffer)-1+sizeof(void*)*numElements];
(*buf)->numElements = numElements;
}
else
{
*buf = (SArrayBuffer*)new asBYTE[sizeof(SArrayBuffer)-1+elementSize*numElements];
(*buf)->numElements = numElements;
}
Construct(*buf, 0, numElements);
}
// internal
void CScriptArray::DeleteBuffer(SArrayBuffer *buf)
{
Destruct(buf, 0, buf->numElements);
// Free the buffer
delete[] (asBYTE*)buf;
}
// internal
void CScriptArray::Construct(SArrayBuffer *buf, asUINT start, asUINT end)
{
int typeId = objType->GetSubTypeId();
if( isArrayOfHandles )
{
// Set all object handles to null
asDWORD *d = (asDWORD*)(buf->data + start * sizeof(void*));
memset(d, 0, (end-start)*sizeof(void*));
}
else if( typeId & asTYPEID_MASK_OBJECT )
{
asDWORD **max = (asDWORD**)(buf->data + end * sizeof(void*));
asDWORD **d = (asDWORD**)(buf->data + start * sizeof(void*));
asIScriptEngine *engine = objType->GetEngine();
for( ; d < max; d++ )
*d = (asDWORD*)engine->CreateScriptObject(typeId);
}
}
// internal
void CScriptArray::Destruct(SArrayBuffer *buf, asUINT start, asUINT end)
{
int typeId = objType->GetSubTypeId();
if( typeId & asTYPEID_MASK_OBJECT )
{
asIScriptEngine *engine = objType->GetEngine();
asDWORD **max = (asDWORD**)(buf->data + end * sizeof(void*));
asDWORD **d = (asDWORD**)(buf->data + start * sizeof(void*));
for( ; d < max; d++ )
{
if( *d )
engine->ReleaseScriptObject(*d, typeId);
}
}
}
// internal
void CScriptArray::CopyBuffer(SArrayBuffer *dst, SArrayBuffer *src)
{
asIScriptEngine *engine = objType->GetEngine();
if( isArrayOfHandles )
{
// Copy the references and increase the reference counters
if( dst->numElements > 0 && src->numElements > 0 )
{
int typeId = objType->GetSubTypeId();
int count = dst->numElements > src->numElements ? src->numElements : dst->numElements;
asDWORD **max = (asDWORD**)(dst->data + count * sizeof(void*));
asDWORD **d = (asDWORD**)dst->data;
asDWORD **s = (asDWORD**)src->data;
for( ; d < max; d++, s++ )
{
*d = *s;
if( *d )
engine->AddRefScriptObject(*d, typeId);
}
}
}
else
{
int typeId = objType->GetSubTypeId();
if( dst->numElements > 0 && src->numElements > 0 )
{
int count = dst->numElements > src->numElements ? src->numElements : dst->numElements;
if( typeId & asTYPEID_MASK_OBJECT )
{
// Call the assignment operator on all of the objects
asDWORD **max = (asDWORD**)(dst->data + count * sizeof(void*));
asDWORD **d = (asDWORD**)dst->data;
asDWORD **s = (asDWORD**)src->data;
for( ; d < max; d++, s++ )
engine->CopyScriptObject(*d, *s, typeId);
}
else
{
// Primitives are copied byte for byte
memcpy(dst->data, src->data, count*elementSize);
}
}
}
}
// GC behaviour
void CScriptArray::EnumReferences(asIScriptEngine *engine)
{
// If the array is holding handles, then we need to notify the GC of them
int typeId = objType->GetSubTypeId();
if( typeId & asTYPEID_MASK_OBJECT )
{
void **d = (void**)buffer->data;
for( asUINT n = 0; n < buffer->numElements; n++ )
{
if( d[n] )
engine->GCEnumCallback(d[n]);
}
}
}
// GC behaviour
void CScriptArray::ReleaseAllHandles(asIScriptEngine *engine)
{
int subTypeId = objType->GetSubTypeId();
asIObjectType *subType = engine->GetObjectTypeById(subTypeId);
if( subType && subType->GetFlags() & asOBJ_GC )
{
void **d = (void**)buffer->data;
for( asUINT n = 0; n < buffer->numElements; n++ )
{
if( d[n] )
{
engine->ReleaseScriptObject(d[n], subTypeId);
d[n] = 0;
}
}
}
}
void CScriptArray::AddRef()
{
// Clear the GC flag then increase the counter
gcFlag = false;
refCount++;
}
void CScriptArray::Release()
{
// Now do the actual releasing (clearing the flag set by GC)
gcFlag = false;
if( --refCount == 0 )
{
delete this;
}
}
// GC behaviour
int CScriptArray::GetRefCount()
{
return refCount;
}
// GC behaviour
void CScriptArray::SetFlag()
{
gcFlag = true;
}
// GC behaviour
bool CScriptArray::GetFlag()
{
return gcFlag;
}
//--------------------------------------------
// Generic calling conventions
static void ScriptArrayFactory_Generic(asIScriptGeneric *gen)
{
asIObjectType *ot = *(asIObjectType**)gen->GetAddressOfArg(0);
*(CScriptArray**)gen->GetAddressOfReturnLocation() = ScriptArrayFactory(ot);
}
static void ScriptArrayFactory2_Generic(asIScriptGeneric *gen)
{
asIObjectType *ot = *(asIObjectType**)gen->GetAddressOfArg(0);
asUINT length = gen->GetArgDWord(1);
*(CScriptArray**)gen->GetAddressOfReturnLocation() = ScriptArrayFactory2(ot, length);
}
static void ScriptArrayTemplateCallback_Generic(asIScriptGeneric *gen)
{
asIObjectType *ot = *(asIObjectType**)gen->GetAddressOfArg(0);
*(bool*)gen->GetAddressOfReturnLocation() = ScriptArrayTemplateCallback(ot);
}
static void ScriptArrayAssignment_Generic(asIScriptGeneric *gen)
{
CScriptArray *other = (CScriptArray*)gen->GetArgObject(0);
CScriptArray *self = (CScriptArray*)gen->GetObject();
*self = *other;
gen->SetReturnObject(self);
}
static void ScriptArrayAt_Generic(asIScriptGeneric *gen)
{
asUINT index = gen->GetArgDWord(0);
CScriptArray *self = (CScriptArray*)gen->GetObject();
gen->SetReturnAddress(self->At(index));
}
static void ScriptArrayLength_Generic(asIScriptGeneric *gen)
{
CScriptArray *self = (CScriptArray*)gen->GetObject();
gen->SetReturnDWord(self->GetSize());
}
static void ScriptArrayResize_Generic(asIScriptGeneric *gen)
{
asUINT size = gen->GetArgDWord(0);
CScriptArray *self = (CScriptArray*)gen->GetObject();
self->Resize(size);
}
static void ScriptArrayAddRef_Generic(asIScriptGeneric *gen)
{
CScriptArray *self = (CScriptArray*)gen->GetObject();
self->AddRef();
}
static void ScriptArrayRelease_Generic(asIScriptGeneric *gen)
{
CScriptArray *self = (CScriptArray*)gen->GetObject();
self->Release();
}
static void ScriptArrayGetRefCount_Generic(asIScriptGeneric *gen)
{
CScriptArray *self = (CScriptArray*)gen->GetObject();
*(int*)gen->GetAddressOfReturnLocation() = self->GetRefCount();
}
static void ScriptArraySetFlag_Generic(asIScriptGeneric *gen)
{
CScriptArray *self = (CScriptArray*)gen->GetObject();
self->SetFlag();
}
static void ScriptArrayGetFlag_Generic(asIScriptGeneric *gen)
{
CScriptArray *self = (CScriptArray*)gen->GetObject();
*(bool*)gen->GetAddressOfReturnLocation() = self->GetFlag();
}
static void ScriptArrayEnumReferences_Generic(asIScriptGeneric *gen)
{
CScriptArray *self = (CScriptArray*)gen->GetObject();
asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0);
self->EnumReferences(engine);
}
static void ScriptArrayReleaseAllHandles_Generic(asIScriptGeneric *gen)
{
CScriptArray *self = (CScriptArray*)gen->GetObject();
asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0);
self->ReleaseAllHandles(engine);
}
void RegisterScriptArray_Generic(asIScriptEngine *engine)
{
int r;
r = engine->RegisterObjectType("array<class T>", 0, asOBJ_REF | asOBJ_GC | asOBJ_TEMPLATE); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("array<T>", asBEHAVE_FACTORY, "array<T>@ f(int&in)", asFUNCTION(ScriptArrayFactory_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("array<T>", asBEHAVE_TEMPLATE_CALLBACK, "bool f(int&in)", asFUNCTION(ScriptArrayTemplateCallback_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("array<T>", asBEHAVE_FACTORY, "array<T>@ f(int&in, uint)", asFUNCTION(ScriptArrayFactory2_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("array<T>", asBEHAVE_ADDREF, "void f()", asFUNCTION(ScriptArrayAddRef_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("array<T>", asBEHAVE_RELEASE, "void f()", asFUNCTION(ScriptArrayRelease_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("array<T>", asBEHAVE_INDEX, "T &f(uint)", asFUNCTION(ScriptArrayAt_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("array<T>", asBEHAVE_INDEX, "const T &f(uint) const", asFUNCTION(ScriptArrayAt_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("array<T>", "array<T> &opAssign(const array<T>&in)", asFUNCTION(ScriptArrayAssignment_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("array<T>", "uint length() const", asFUNCTION(ScriptArrayLength_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("array<T>", "void resize(uint)", asFUNCTION(ScriptArrayResize_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("array<T>", asBEHAVE_GETREFCOUNT, "int f()", asFUNCTION(ScriptArrayGetRefCount_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("array<T>", asBEHAVE_SETGCFLAG, "void f()", asFUNCTION(ScriptArraySetFlag_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("array<T>", asBEHAVE_GETGCFLAG, "bool f()", asFUNCTION(ScriptArrayGetFlag_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("array<T>", asBEHAVE_ENUMREFS, "void f(int&in)", asFUNCTION(ScriptArrayEnumReferences_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("array<T>", asBEHAVE_RELEASEREFS, "void f(int&in)", asFUNCTION(ScriptArrayReleaseAllHandles_Generic), asCALL_GENERIC); assert( r >= 0 );
}
END_AS_NAMESPACE