#include #include #include "scriptdictionary.h" BEGIN_AS_NAMESPACE using namespace std; //-------------------------------------------------------------------------- // CScriptDictionary implementation CScriptDictionary::CScriptDictionary(asIScriptEngine *engine) { // We start with one reference refCount = 1; // Keep a reference to the engine for as long as we live // We don't increment the reference counter, because the // engine will hold a pointer to the object. this->engine = engine; // Notify the garbage collector of this object // TODO: The type id should be cached engine->NotifyGarbageCollectorOfNewObject(this, engine->GetTypeIdByDecl("dictionary")); } CScriptDictionary::~CScriptDictionary() { // Delete all keys and values DeleteAll(); } void CScriptDictionary::AddRef() { // We need to clear the GC flag refCount = (refCount & 0x7FFFFFFF) + 1; } void CScriptDictionary::Release() { // We need to clear the GC flag refCount = (refCount & 0x7FFFFFFF) - 1; if (refCount == 0) delete this; } int CScriptDictionary::GetRefCount() { return refCount & 0x7FFFFFFF; } void CScriptDictionary::SetGCFlag() { refCount |= 0x80000000; } bool CScriptDictionary::GetGCFlag() { return (refCount & 0x80000000) ? true : false; } void CScriptDictionary::EnumReferences(asIScriptEngine *engine) { // Call the gc enum callback for each of the objects map::iterator it; for (it = dict.begin(); it != dict.end(); it++) { if (it->second.typeId & asTYPEID_MASK_OBJECT) engine->GCEnumCallback(it->second.valueObj); } } void CScriptDictionary::ReleaseAllReferences(asIScriptEngine * /*engine*/) { // We're being told to release all references in // order to break circular references for dead objects DeleteAll(); } CScriptDictionary &CScriptDictionary::operator=(const CScriptDictionary & /*other*/) { // Do nothing // TODO: Should do a shallow copy of the dictionary return *this; } void CScriptDictionary::Set(const string &key, void *value, int typeId) { valueStruct valStruct = {{0}, 0}; valStruct.typeId = typeId; if (typeId & asTYPEID_OBJHANDLE) { // We're receiving a reference to the handle, so we need to dereference it valStruct.valueObj = *(void **)value; engine->AddRefScriptObject(valStruct.valueObj, typeId); } else if (typeId & asTYPEID_MASK_OBJECT) { // Create a copy of the object valStruct.valueObj = engine->CreateScriptObjectCopy(value, typeId); } else { // Copy the primitive value // We receive a pointer to the value. int size = engine->GetSizeOfPrimitiveType(typeId); memcpy(&valStruct.valueInt, value, size); } map::iterator it; it = dict.find(key); if (it != dict.end()) { FreeValue(it->second); // Insert the new value it->second = valStruct; } else { dict.insert(map::value_type(key, valStruct)); } } // This overloaded method is implemented so that all integer and // unsigned integers types will be stored in the dictionary as int64 // through implicit conversions. This simplifies the management of the // numeric types when the script retrieves the stored value using a // different type. void CScriptDictionary::Set(const string &key, asINT64 &value) { Set(key, &value, asTYPEID_INT64); } // This overloaded method is implemented so that all floating point types // will be stored in the dictionary as double through implicit conversions. // This simplifies the management of the numeric types when the script // retrieves the stored value using a different type. void CScriptDictionary::Set(const string &key, double &value) { Set(key, &value, asTYPEID_DOUBLE); } // Returns true if the value was successfully retrieved bool CScriptDictionary::Get(const string &key, void *value, int typeId) const { map::const_iterator it; it = dict.find(key); if (it != dict.end()) { // Return the value if (typeId & asTYPEID_OBJHANDLE) { // A handle can be retrieved if the stored type is a handle of same or compatible type // or if the stored type is an object that implements the interface that the handle refer to. if ((it->second.typeId & asTYPEID_MASK_OBJECT) && engine->IsHandleCompatibleWithObject(it->second.valueObj, it->second.typeId, typeId)) { engine->AddRefScriptObject(it->second.valueObj, it->second.typeId); *(void **)value = it->second.valueObj; return true; } } else if (typeId & asTYPEID_MASK_OBJECT) { // Verify that the copy can be made bool isCompatible = false; if (it->second.typeId == typeId) isCompatible = true; // Copy the object into the given reference if (isCompatible) { engine->CopyScriptObject(value, it->second.valueObj, typeId); return true; } } else { if (it->second.typeId == typeId) { int size = engine->GetSizeOfPrimitiveType(typeId); memcpy(value, &it->second.valueInt, size); return true; } // We know all numbers are stored as either int64 or double, since we register overloaded functions for those if (it->second.typeId == asTYPEID_INT64 && typeId == asTYPEID_DOUBLE) { *(double *)value = double(it->second.valueInt); return true; } else if (it->second.typeId == asTYPEID_DOUBLE && typeId == asTYPEID_INT64) { *(asINT64 *)value = asINT64(it->second.valueFlt); return true; } } } // AngelScript has already initialized the value with a default value, // so we don't have to do anything if we don't find the element, or if // the element is incompatible with the requested type. return false; } bool CScriptDictionary::Get(const string &key, asINT64 &value) const { return Get(key, &value, asTYPEID_INT64); } bool CScriptDictionary::Get(const string &key, double &value) const { return Get(key, &value, asTYPEID_DOUBLE); } bool CScriptDictionary::Exists(const string &key) const { map::const_iterator it; it = dict.find(key); if (it != dict.end()) { return true; } return false; } void CScriptDictionary::Delete(const string &key) { map::iterator it; it = dict.find(key); if (it != dict.end()) { FreeValue(it->second); dict.erase(it); } } void CScriptDictionary::DeleteAll() { map::iterator it; for (it = dict.begin(); it != dict.end(); it++) { FreeValue(it->second); } dict.clear(); } void CScriptDictionary::FreeValue(valueStruct &value) { // If it is a handle or a ref counted object, call release if (value.typeId & asTYPEID_MASK_OBJECT) { // Let the engine release the object engine->ReleaseScriptObject(value.valueObj, value.typeId); value.valueObj = 0; value.typeId = 0; } // For primitives, there's nothing to do } //-------------------------------------------------------------------------- // Generic wrappers void ScriptDictionaryFactory_Generic(asIScriptGeneric *gen) { *(CScriptDictionary **)gen->GetAddressOfReturnLocation() = new CScriptDictionary(gen->GetEngine()); } void ScriptDictionaryAddRef_Generic(asIScriptGeneric *gen) { CScriptDictionary *dict = (CScriptDictionary *)gen->GetObject(); dict->AddRef(); } void ScriptDictionaryRelease_Generic(asIScriptGeneric *gen) { CScriptDictionary *dict = (CScriptDictionary *)gen->GetObject(); dict->Release(); } void ScriptDictionarySet_Generic(asIScriptGeneric *gen) { CScriptDictionary *dict = (CScriptDictionary *)gen->GetObject(); string *key = *(string **)gen->GetAddressOfArg(0); void *ref = *(void **)gen->GetAddressOfArg(1); int typeId = gen->GetArgTypeId(1); dict->Set(*key, ref, typeId); } void ScriptDictionarySetInt_Generic(asIScriptGeneric *gen) { CScriptDictionary *dict = (CScriptDictionary *)gen->GetObject(); string *key = *(string **)gen->GetAddressOfArg(0); void *ref = *(void **)gen->GetAddressOfArg(1); dict->Set(*key, *(asINT64 *)ref); } void ScriptDictionarySetFlt_Generic(asIScriptGeneric *gen) { CScriptDictionary *dict = (CScriptDictionary *)gen->GetObject(); string *key = *(string **)gen->GetAddressOfArg(0); void *ref = *(void **)gen->GetAddressOfArg(1); dict->Set(*key, *(double *)ref); } void ScriptDictionaryGet_Generic(asIScriptGeneric *gen) { CScriptDictionary *dict = (CScriptDictionary *)gen->GetObject(); string *key = *(string **)gen->GetAddressOfArg(0); void *ref = *(void **)gen->GetAddressOfArg(1); int typeId = gen->GetArgTypeId(1); *(bool *)gen->GetAddressOfReturnLocation() = dict->Get(*key, ref, typeId); } void ScriptDictionaryGetInt_Generic(asIScriptGeneric *gen) { CScriptDictionary *dict = (CScriptDictionary *)gen->GetObject(); string *key = *(string **)gen->GetAddressOfArg(0); void *ref = *(void **)gen->GetAddressOfArg(1); *(bool *)gen->GetAddressOfReturnLocation() = dict->Get(*key, *(asINT64 *)ref); } void ScriptDictionaryGetFlt_Generic(asIScriptGeneric *gen) { CScriptDictionary *dict = (CScriptDictionary *)gen->GetObject(); string *key = *(string **)gen->GetAddressOfArg(0); void *ref = *(void **)gen->GetAddressOfArg(1); *(bool *)gen->GetAddressOfReturnLocation() = dict->Get(*key, *(double *)ref); } void ScriptDictionaryExists_Generic(asIScriptGeneric *gen) { CScriptDictionary *dict = (CScriptDictionary *)gen->GetObject(); string *key = *(string **)gen->GetAddressOfArg(0); bool ret = dict->Exists(*key); *(bool *)gen->GetAddressOfReturnLocation() = ret; } void ScriptDictionaryDelete_Generic(asIScriptGeneric *gen) { CScriptDictionary *dict = (CScriptDictionary *)gen->GetObject(); string *key = *(string **)gen->GetAddressOfArg(0); dict->Delete(*key); } void ScriptDictionaryDeleteAll_Generic(asIScriptGeneric *gen) { CScriptDictionary *dict = (CScriptDictionary *)gen->GetObject(); dict->DeleteAll(); } static void ScriptDictionaryGetRefCount_Generic(asIScriptGeneric *gen) { CScriptDictionary *self = (CScriptDictionary *)gen->GetObject(); *(int *)gen->GetAddressOfReturnLocation() = self->GetRefCount(); } static void ScriptDictionarySetGCFlag_Generic(asIScriptGeneric *gen) { CScriptDictionary *self = (CScriptDictionary *)gen->GetObject(); self->SetGCFlag(); } static void ScriptDictionaryGetGCFlag_Generic(asIScriptGeneric *gen) { CScriptDictionary *self = (CScriptDictionary *)gen->GetObject(); *(bool *)gen->GetAddressOfReturnLocation() = self->GetGCFlag(); } static void ScriptDictionaryEnumReferences_Generic(asIScriptGeneric *gen) { CScriptDictionary *self = (CScriptDictionary *)gen->GetObject(); asIScriptEngine *engine = *(asIScriptEngine **)gen->GetAddressOfArg(0); self->EnumReferences(engine); } static void ScriptDictionaryReleaseAllReferences_Generic(asIScriptGeneric *gen) { CScriptDictionary *self = (CScriptDictionary *)gen->GetObject(); asIScriptEngine *engine = *(asIScriptEngine **)gen->GetAddressOfArg(0); self->ReleaseAllReferences(engine); } //-------------------------------------------------------------------------- // Register the type void RegisterScriptDictionary(asIScriptEngine *engine) { if (strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY")) RegisterScriptDictionary_Generic(engine); else RegisterScriptDictionary_Native(engine); } void RegisterScriptDictionary_Native(asIScriptEngine *engine) { int r; r = engine->RegisterObjectType("dictionary", sizeof(CScriptDictionary), asOBJ_REF | asOBJ_GC); assert(r >= 0); // Use the generic interface to construct the object since we need the engine pointer, we could also have retrieved // the engine pointer from the active context r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_FACTORY, "dictionary@ f()", asFUNCTION(ScriptDictionaryFactory_Generic), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_ADDREF, "void f()", asMETHOD(CScriptDictionary, AddRef), asCALL_THISCALL); assert(r >= 0); r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_RELEASE, "void f()", asMETHOD(CScriptDictionary, Release), asCALL_THISCALL); assert(r >= 0); r = engine->RegisterObjectMethod("dictionary", "void set(const string &in, ?&in)", asMETHODPR(CScriptDictionary, Set, (const string &, void *, int), void), asCALL_THISCALL); assert(r >= 0); r = engine->RegisterObjectMethod("dictionary", "bool get(const string &in, ?&out) const", asMETHODPR(CScriptDictionary, Get, (const string &, void *, int) const, bool), asCALL_THISCALL); assert(r >= 0); r = engine->RegisterObjectMethod("dictionary", "void set(const string &in, int64&in)", asMETHODPR(CScriptDictionary, Set, (const string &, asINT64 &), void), asCALL_THISCALL); assert(r >= 0); r = engine->RegisterObjectMethod("dictionary", "bool get(const string &in, int64&out) const", asMETHODPR(CScriptDictionary, Get, (const string &, asINT64 &) const, bool), asCALL_THISCALL); assert(r >= 0); r = engine->RegisterObjectMethod("dictionary", "void set(const string &in, double&in)", asMETHODPR(CScriptDictionary, Set, (const string &, double &), void), asCALL_THISCALL); assert(r >= 0); r = engine->RegisterObjectMethod("dictionary", "bool get(const string &in, double&out) const", asMETHODPR(CScriptDictionary, Get, (const string &, double &) const, bool), asCALL_THISCALL); assert(r >= 0); r = engine->RegisterObjectMethod("dictionary", "bool exists(const string &in) const", asMETHOD(CScriptDictionary, Exists), asCALL_THISCALL); assert(r >= 0); r = engine->RegisterObjectMethod("dictionary", "void delete(const string &in)", asMETHOD(CScriptDictionary, Delete), asCALL_THISCALL); assert(r >= 0); r = engine->RegisterObjectMethod("dictionary", "void deleteAll()", asMETHOD(CScriptDictionary, DeleteAll), asCALL_THISCALL); assert(r >= 0); // Register GC behaviours r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_GETREFCOUNT, "int f()", asMETHOD(CScriptDictionary, GetRefCount), asCALL_THISCALL); assert(r >= 0); r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_SETGCFLAG, "void f()", asMETHOD(CScriptDictionary, SetGCFlag), asCALL_THISCALL); assert(r >= 0); r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_GETGCFLAG, "bool f()", asMETHOD(CScriptDictionary, GetGCFlag), asCALL_THISCALL); assert(r >= 0); r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_ENUMREFS, "void f(int&in)", asMETHOD(CScriptDictionary, EnumReferences), asCALL_THISCALL); assert(r >= 0); r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_RELEASEREFS, "void f(int&in)", asMETHOD(CScriptDictionary, ReleaseAllReferences), asCALL_THISCALL); assert(r >= 0); } void RegisterScriptDictionary_Generic(asIScriptEngine *engine) { int r; r = engine->RegisterObjectType("dictionary", sizeof(CScriptDictionary), asOBJ_REF | asOBJ_GC); assert(r >= 0); r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_FACTORY, "dictionary@ f()", asFUNCTION(ScriptDictionaryFactory_Generic), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_ADDREF, "void f()", asFUNCTION(ScriptDictionaryAddRef_Generic), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_RELEASE, "void f()", asFUNCTION(ScriptDictionaryRelease_Generic), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterObjectMethod("dictionary", "void set(const string &in, ?&in)", asFUNCTION(ScriptDictionarySet_Generic), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterObjectMethod("dictionary", "bool get(const string &in, ?&out) const", asFUNCTION(ScriptDictionaryGet_Generic), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterObjectMethod("dictionary", "void set(const string &in, int64&in)", asFUNCTION(ScriptDictionarySetInt_Generic), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterObjectMethod("dictionary", "bool get(const string &in, int64&out) const", asFUNCTION(ScriptDictionaryGetInt_Generic), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterObjectMethod("dictionary", "void set(const string &in, double&in)", asFUNCTION(ScriptDictionarySetFlt_Generic), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterObjectMethod("dictionary", "bool get(const string &in, double&out) const", asFUNCTION(ScriptDictionaryGetFlt_Generic), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterObjectMethod("dictionary", "bool exists(const string &in) const", asFUNCTION(ScriptDictionaryExists_Generic), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterObjectMethod("dictionary", "void delete(const string &in)", asFUNCTION(ScriptDictionaryDelete_Generic), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterObjectMethod("dictionary", "void deleteAll()", asFUNCTION(ScriptDictionaryDeleteAll_Generic), asCALL_GENERIC); assert(r >= 0); // Register GC behaviours r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_GETREFCOUNT, "int f()", asFUNCTION(ScriptDictionaryGetRefCount_Generic), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_SETGCFLAG, "void f()", asFUNCTION(ScriptDictionarySetGCFlag_Generic), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_GETGCFLAG, "bool f()", asFUNCTION(ScriptDictionaryGetGCFlag_Generic), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_ENUMREFS, "void f(int&in)", asFUNCTION(ScriptDictionaryEnumReferences_Generic), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_RELEASEREFS, "void f(int&in)", asFUNCTION(ScriptDictionaryReleaseAllReferences_Generic), asCALL_GENERIC); assert(r >= 0); } END_AS_NAMESPACE