#include "scripthandle.h" #include #include #include BEGIN_AS_NAMESPACE static void Construct(CScriptHandle* self) { new (self) CScriptHandle(); } static void Construct(CScriptHandle* self, const CScriptHandle& o) { new (self) CScriptHandle(o); } // This one is not static because it needs to be friend with the CScriptHandle class void Construct(CScriptHandle* self, void* ref, int typeId) { new (self) CScriptHandle(ref, typeId); } static void Destruct(CScriptHandle* self) { self->~CScriptHandle(); } CScriptHandle::CScriptHandle() { m_ref = 0; m_type = 0; } CScriptHandle::CScriptHandle(const CScriptHandle& other) { m_ref = other.m_ref; m_type = other.m_type; AddRefHandle(); } CScriptHandle::CScriptHandle(void* ref, asITypeInfo* type) { m_ref = ref; m_type = type; AddRefHandle(); } // This constructor shouldn't be called from the application // directly as it requires an active script context CScriptHandle::CScriptHandle(void* ref, int typeId) { m_ref = 0; m_type = 0; Assign(ref, typeId); } CScriptHandle::~CScriptHandle() { ReleaseHandle(); } void CScriptHandle::ReleaseHandle() { if (m_ref && m_type) { asIScriptEngine* engine = m_type->GetEngine(); engine->ReleaseScriptObject(m_ref, m_type); engine->Release(); m_ref = 0; m_type = 0; } } void CScriptHandle::AddRefHandle() { if (m_ref && m_type) { asIScriptEngine* engine = m_type->GetEngine(); engine->AddRefScriptObject(m_ref, m_type); // Hold on to the engine so it isn't destroyed while // a reference to a script object is still held engine->AddRef(); } } CScriptHandle& CScriptHandle::operator=(const CScriptHandle& other) { Set(other.m_ref, other.m_type); return *this; } void CScriptHandle::Set(void* ref, asITypeInfo* type) { if (m_ref == ref) return; ReleaseHandle(); m_ref = ref; m_type = type; AddRefHandle(); } void* CScriptHandle::GetRef() { return m_ref; } asITypeInfo* CScriptHandle::GetType() const { return m_type; } int CScriptHandle::GetTypeId() const { if (m_type == 0) return 0; return m_type->GetTypeId() | asTYPEID_OBJHANDLE; } // This method shouldn't be called from the application // directly as it requires an active script context CScriptHandle& CScriptHandle::Assign(void* ref, int typeId) { // When receiving a null handle we just clear our memory if (typeId == 0) { Set(0, 0); return *this; } // Dereference received handles to get the object if (typeId & asTYPEID_OBJHANDLE) { // Store the actual reference ref = *(void**)ref; typeId &= ~asTYPEID_OBJHANDLE; } // Get the object type asIScriptContext* ctx = asGetActiveContext(); asIScriptEngine* engine = ctx->GetEngine(); asITypeInfo* type = engine->GetTypeInfoById(typeId); // If the argument is another CScriptHandle, we should copy the content instead if (type && strcmp(type->GetName(), "ref") == 0) { CScriptHandle* r = (CScriptHandle*)ref; ref = r->m_ref; type = r->m_type; } Set(ref, type); return *this; } bool CScriptHandle::operator==(const CScriptHandle& o) const { if (m_ref == o.m_ref && m_type == o.m_type) return true; // TODO: If type is not the same, we should attempt to do a dynamic cast, // which may change the pointer for application registered classes return false; } bool CScriptHandle::operator!=(const CScriptHandle& o) const { return !(*this == o); } bool CScriptHandle::Equals(void* ref, int typeId) const { // Null handles are received as reference to a null handle if (typeId == 0) ref = 0; // Dereference handles to get the object if (typeId & asTYPEID_OBJHANDLE) { // Compare the actual reference ref = *(void**)ref; typeId &= ~asTYPEID_OBJHANDLE; } // TODO: If typeId is not the same, we should attempt to do a dynamic cast, // which may change the pointer for application registered classes if (ref == m_ref) return true; return false; } // AngelScript: used as '@obj = cast(ref);' void CScriptHandle::Cast(void** outRef, int typeId) { // If we hold a null handle, then just return null if (m_type == 0) { *outRef = 0; return; } // It is expected that the outRef is always a handle assert(typeId & asTYPEID_OBJHANDLE); // Compare the type id of the actual object typeId &= ~asTYPEID_OBJHANDLE; asIScriptEngine* engine = m_type->GetEngine(); asITypeInfo* type = engine->GetTypeInfoById(typeId); *outRef = 0; // RefCastObject will increment the refCount of the returned object if successful engine->RefCastObject(m_ref, m_type, type, outRef); } void CScriptHandle::EnumReferences(asIScriptEngine* inEngine) { // If we're holding a reference, we'll notify the garbage collector of it if (m_ref) inEngine->GCEnumCallback(m_ref); // The object type itself is also garbage collected if (m_type) inEngine->GCEnumCallback(m_type); } void CScriptHandle::ReleaseReferences(asIScriptEngine*) { // Simply clear the content to release the references Set(0, 0); } void RegisterScriptHandle_Native(asIScriptEngine* engine) { [[maybe_unused]] int r; #if AS_CAN_USE_CPP11 // With C++11 it is possible to use asGetTypeTraits to automatically determine the flags that represent the C++ // class r = engine->RegisterObjectType("ref", sizeof(CScriptHandle), asOBJ_VALUE | asOBJ_ASHANDLE | asOBJ_GC | asGetTypeTraits()); assert(r >= 0); #else r = engine->RegisterObjectType("ref", sizeof(CScriptHandle), asOBJ_VALUE | asOBJ_ASHANDLE | asOBJ_GC | asOBJ_APP_CLASS_CDAK); assert(r >= 0); #endif r = engine->RegisterObjectBehaviour("ref", asBEHAVE_CONSTRUCT, "void f()", asFUNCTIONPR(Construct, (CScriptHandle*), void), asCALL_CDECL_OBJFIRST); assert(r >= 0); r = engine->RegisterObjectBehaviour("ref", asBEHAVE_CONSTRUCT, "void f(const ref &in)", asFUNCTIONPR(Construct, (CScriptHandle*, const CScriptHandle&), void), asCALL_CDECL_OBJFIRST); assert(r >= 0); r = engine->RegisterObjectBehaviour("ref", asBEHAVE_CONSTRUCT, "void f(const ?&in)", asFUNCTIONPR(Construct, (CScriptHandle*, void*, int), void), asCALL_CDECL_OBJFIRST); assert(r >= 0); r = engine->RegisterObjectBehaviour("ref", asBEHAVE_DESTRUCT, "void f()", asFUNCTIONPR(Destruct, (CScriptHandle*), void), asCALL_CDECL_OBJFIRST); assert(r >= 0); r = engine->RegisterObjectBehaviour("ref", asBEHAVE_ENUMREFS, "void f(int&in)", asMETHOD(CScriptHandle, EnumReferences), asCALL_THISCALL); assert(r >= 0); r = engine->RegisterObjectBehaviour("ref", asBEHAVE_RELEASEREFS, "void f(int&in)", asMETHOD(CScriptHandle, ReleaseReferences), asCALL_THISCALL); assert(r >= 0); r = engine->RegisterObjectMethod("ref", "void opCast(?&out)", asMETHODPR(CScriptHandle, Cast, (void**, int), void), asCALL_THISCALL); assert(r >= 0); r = engine->RegisterObjectMethod("ref", "ref &opHndlAssign(const ref &in)", asMETHOD(CScriptHandle, operator=), asCALL_THISCALL); assert(r >= 0); r = engine->RegisterObjectMethod("ref", "ref &opHndlAssign(const ?&in)", asMETHOD(CScriptHandle, Assign), asCALL_THISCALL); assert(r >= 0); r = engine->RegisterObjectMethod("ref", "bool opEquals(const ref &in) const", asMETHODPR(CScriptHandle, operator==,(const CScriptHandle&) const, bool), asCALL_THISCALL); assert(r >= 0); r = engine->RegisterObjectMethod("ref", "bool opEquals(const ?&in) const", asMETHODPR(CScriptHandle, Equals, (void*, int)const, bool), asCALL_THISCALL); assert(r >= 0); } void CScriptHandle_Construct_Generic(asIScriptGeneric* gen) { CScriptHandle* self = reinterpret_cast(gen->GetObject()); new (self) CScriptHandle(); } void CScriptHandle_ConstructCopy_Generic(asIScriptGeneric* gen) { CScriptHandle* other = reinterpret_cast(gen->GetArgAddress(0)); CScriptHandle* self = reinterpret_cast(gen->GetObject()); new (self) CScriptHandle(*other); } void CScriptHandle_ConstructVar_Generic(asIScriptGeneric* gen) { void* ref = gen->GetArgAddress(0); int typeId = gen->GetArgTypeId(0); CScriptHandle* self = reinterpret_cast(gen->GetObject()); Construct(self, ref, typeId); } void CScriptHandle_Destruct_Generic(asIScriptGeneric* gen) { CScriptHandle* self = reinterpret_cast(gen->GetObject()); self->~CScriptHandle(); } void CScriptHandle_Cast_Generic(asIScriptGeneric* gen) { void** ref = reinterpret_cast(gen->GetArgAddress(0)); int typeId = gen->GetArgTypeId(0); CScriptHandle* self = reinterpret_cast(gen->GetObject()); self->Cast(ref, typeId); } void CScriptHandle_Assign_Generic(asIScriptGeneric* gen) { CScriptHandle* other = reinterpret_cast(gen->GetArgAddress(0)); CScriptHandle* self = reinterpret_cast(gen->GetObject()); *self = *other; gen->SetReturnAddress(self); } void CScriptHandle_AssignVar_Generic(asIScriptGeneric* gen) { void* ref = gen->GetArgAddress(0); int typeId = gen->GetArgTypeId(0); CScriptHandle* self = reinterpret_cast(gen->GetObject()); self->Assign(ref, typeId); gen->SetReturnAddress(self); } void CScriptHandle_Equals_Generic(asIScriptGeneric* gen) { CScriptHandle* other = reinterpret_cast(gen->GetArgAddress(0)); CScriptHandle* self = reinterpret_cast(gen->GetObject()); gen->SetReturnByte(*self == *other); } void CScriptHandle_EqualsVar_Generic(asIScriptGeneric* gen) { void* ref = gen->GetArgAddress(0); int typeId = gen->GetArgTypeId(0); CScriptHandle* self = reinterpret_cast(gen->GetObject()); gen->SetReturnByte(self->Equals(ref, typeId)); } void CScriptHandle_EnumReferences_Generic(asIScriptGeneric* gen) { CScriptHandle* self = reinterpret_cast(gen->GetObject()); self->EnumReferences(gen->GetEngine()); } void CScriptHandle_ReleaseReferences_Generic(asIScriptGeneric* gen) { CScriptHandle* self = reinterpret_cast(gen->GetObject()); self->ReleaseReferences(gen->GetEngine()); } void RegisterScriptHandle_Generic(asIScriptEngine* engine) { [[maybe_unused]] int r; r = engine->RegisterObjectType("ref", sizeof(CScriptHandle), asOBJ_VALUE | asOBJ_ASHANDLE | asOBJ_GC | asOBJ_APP_CLASS_CDAK); assert(r >= 0); r = engine->RegisterObjectBehaviour("ref", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(CScriptHandle_Construct_Generic), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterObjectBehaviour("ref", asBEHAVE_CONSTRUCT, "void f(const ref &in)", asFUNCTION(CScriptHandle_ConstructCopy_Generic), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterObjectBehaviour("ref", asBEHAVE_CONSTRUCT, "void f(const ?&in)", asFUNCTION(CScriptHandle_ConstructVar_Generic), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterObjectBehaviour("ref", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(CScriptHandle_Destruct_Generic), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterObjectBehaviour("ref", asBEHAVE_ENUMREFS, "void f(int&in)", asFUNCTION(CScriptHandle_EnumReferences_Generic), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterObjectBehaviour("ref", asBEHAVE_RELEASEREFS, "void f(int&in)", asFUNCTION(CScriptHandle_ReleaseReferences_Generic), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterObjectMethod("ref", "void opCast(?&out)", asFUNCTION(CScriptHandle_Cast_Generic), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterObjectMethod("ref", "ref &opHndlAssign(const ref &in)", asFUNCTION(CScriptHandle_Assign_Generic), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterObjectMethod("ref", "ref &opHndlAssign(const ?&in)", asFUNCTION(CScriptHandle_AssignVar_Generic), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterObjectMethod("ref", "bool opEquals(const ref &in) const", asFUNCTION(CScriptHandle_Equals_Generic), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterObjectMethod("ref", "bool opEquals(const ?&in) const", asFUNCTION(CScriptHandle_EqualsVar_Generic), asCALL_GENERIC); assert(r >= 0); } void RegisterScriptHandle(asIScriptEngine* engine) { if (strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY")) RegisterScriptHandle_Generic(engine); else RegisterScriptHandle_Native(engine); } END_AS_NAMESPACE