diff options
author | chai <chaifix@163.com> | 2019-08-14 22:50:43 +0800 |
---|---|---|
committer | chai <chaifix@163.com> | 2019-08-14 22:50:43 +0800 |
commit | 15740faf9fe9fe4be08965098bbf2947e096aeeb (patch) | |
tree | a730ec236656cc8cab5b13f088adfaed6bb218fb /Runtime/BaseClasses/BaseObject.cpp |
Diffstat (limited to 'Runtime/BaseClasses/BaseObject.cpp')
-rw-r--r-- | Runtime/BaseClasses/BaseObject.cpp | 1393 |
1 files changed, 1393 insertions, 0 deletions
diff --git a/Runtime/BaseClasses/BaseObject.cpp b/Runtime/BaseClasses/BaseObject.cpp new file mode 100644 index 0000000..1f4c88a --- /dev/null +++ b/Runtime/BaseClasses/BaseObject.cpp @@ -0,0 +1,1393 @@ +#include "UnityPrefix.h" +#include "Configuration/UnityConfigure.h" +#include "BaseObject.h" +#include "Runtime/Serialize/PersistentManager.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "Runtime/Utilities/dynamic_bitset.h" +#include "Runtime/Utilities/CStringHash.h" +#include "Runtime/Utilities/InitializeAndCleanup.h" +#include "Runtime/Scripting/ScriptingUtility.h" +#include "Runtime/Profiler/MemoryProfilerStats.h" +#include "Runtime/Scripting/Backend/ScriptingBackendApi.h" +#include "EventManager.h" +#include "EventIDs.h" +#if ENABLE_MONO +#include "Runtime/Mono/MonoIncludes.h" +#endif + +#include "Runtime/Scripting/ScriptingUtility.h" + +#if defined(__MWERKS__) +#include <hash_map> +#endif + +#include "Configuration/UnityConfigure.h" +#if THREADED_LOADING +#include "Runtime/Threads/ThreadSpecificValue.h" +#include "Runtime/Threads/ProfilerMutex.h" +#endif + +#if !UNITY_EXTERNAL_TOOL +#include "Runtime/Profiler/Profiler.h" +#include "Runtime/Profiler/MemoryProfiler.h" +#endif +#include "Runtime/Allocator/MemoryManager.h" + +using namespace std; + +#define SHOW_REGISTERED_CLASS_INFO 0 + + +#if THREADED_LOADING +#define CHECK_IN_MAIN_THREAD DebugAssertIf(!Thread::EqualsCurrentThreadID(GetPersistentManager().GetMainThreadID())); +#define DEBUG_CHECK_IN_MAIN_THREAD AssertIf(!Thread::EqualsCurrentThreadID(GetPersistentManager().GetMainThreadID())); +#else +#define CHECK_IN_MAIN_THREAD +#define DEBUG_CHECK_IN_MAIN_THREAD +#endif + + +static bool IsDerivedFromRTTI (const Object::RTTI* klass, const Object::RTTI* derivedFrom) +{ + const Object::RTTI* i = klass; + while (i) + { + if (derivedFrom == i) + return true; + + i = i->base; + } + return false; +} + +struct RegisterClassCallbackStruct +{ + RegisterClassCallback* registerClass; + RegisterClassCallback* initClassEarly; + RegisterClassCallback* initClass; + RegisterClassCallback* postInitClass; + RegisterClassCallback* cleanupClass; + + RegisterClassCallbackStruct() + { + registerClass = initClassEarly = initClass = postInitClass = cleanupClass = NULL; + } +}; + +typedef UNITY_VECTOR(kMemPermanent, RegisterClassCallbackStruct) RegisterClassCallbacks; + +Object::IDToPointerMap* Object::ms_IDToPointer = NULL; +UInt32* Object::ms_IsDerivedFromBitMap = NULL; +unsigned Object::ms_MaxClassID = 0; +#if USE_NEW_IS_DERIVED_FROM +UInt32 gClassIDMask[32]; +UInt32* Object::ms_ClassIDMask = 0; +UInt32* Object::ms_ClassIsDerivedFrom = 0; +#endif + +static RegisterClassCallbacks* gRegisterClassCallbacks = NULL; + +#if defined(__MWERKS__) +#error("Metrowerks should not be used") +#endif + +#if DEBUGMODE +RegisteredClassSet* gVerifyRegisteredClasses = NULL; +#endif + +typedef map<char*, SInt32, smaller_cstring> StringToClassIDMap; +typedef pair<const SInt32, Object::RTTI> SInt32RTTIPair; +typedef map<SInt32, Object::RTTI, less<SInt32>, STL_ALLOCATOR(kMemPermanent, SInt32RTTIPair) > RTTIMap; + +static StringToClassIDMap* gStringToClassID = NULL; +static RTTIMap* gRTTI = NULL; +static dynamic_bitset* gRegisteredClassIDs = NULL; +static dynamic_bitset* gIsDerivedFromBitMap = NULL; +static Object::ObjectDestroyCallbackFunction* gDestroyedCallbackFunc = NULL; + +static int* gBaseObjectManagerContainer = NULL; + +namespace BaseObjectManager +{ + void StaticInitialize() + { + gBaseObjectManagerContainer = UNITY_NEW_AS_ROOT(int, kMemBaseObject, "Managers", "BaseObjectManager"); + SET_ALLOC_OWNER(gBaseObjectManagerContainer); + gStringToClassID = UNITY_NEW(StringToClassIDMap, kMemBaseObject); + gRTTI = UNITY_NEW(RTTIMap, kMemBaseObject); + gRegisteredClassIDs = UNITY_NEW(dynamic_bitset, kMemBaseObject); + gIsDerivedFromBitMap = UNITY_NEW(dynamic_bitset, kMemBaseObject); + Object::StaticInitialize(); + } + void StaticDestroy() + { + Object::StaticDestroy(); + UNITY_DELETE(gStringToClassID, kMemBaseObject); + UNITY_DELETE(gRTTI, kMemBaseObject); + UNITY_DELETE(gRegisteredClassIDs, kMemBaseObject); + UNITY_DELETE(gIsDerivedFromBitMap, kMemBaseObject); +#if DEBUGMODE + UNITY_DELETE(gVerifyRegisteredClasses, kMemBaseObject); // allocated on first access +#endif + UNITY_DELETE(gBaseObjectManagerContainer, kMemBaseObject); + } +} + +static RegisterRuntimeInitializeAndCleanup s_BaseObjectManagerCallbacks(BaseObjectManager::StaticInitialize, BaseObjectManager::StaticDestroy); + +#if UNITY_EDITOR +static Object::ObjectDirtyCallbackFunction* gSetDirtyCallbackFunc = NULL; +#endif + +PROFILER_INFORMATION (gObjectCreationMutexLockInfo, "Object.CreateObject mutex lock", kProfilerLoading) + +#if THREADED_LOADING +Mutex gCreateObjectMutex; +#if DEBUGMODE +static UNITY_TLS_VALUE(int) gCheckObjectCreationMutex; +#endif +#endif + +void LockObjectCreation () +{ + #if THREADED_LOADING + LOCK_MUTEX (gCreateObjectMutex, gObjectCreationMutexLockInfo); + #if DEBUGMODE + ++gCheckObjectCreationMutex; + #endif + #endif +} + +void UnlockObjectCreation () +{ + #if THREADED_LOADING + gCreateObjectMutex.Unlock(); + #if DEBUGMODE + --gCheckObjectCreationMutex; + #endif + #endif +} + + +static SInt32 gLowestInstanceID = -10; +static bool gDisableImmediateDestruction = false; + +#if UNITY_EDITOR +InstanceIDResolveCallback* gInstanceIDResolveCallback = NULL; +const void* gInstanceIDResolveContext = NULL; + +void SetInstanceIDResolveCallback (InstanceIDResolveCallback callback, const void* context) +{ + gInstanceIDResolveCallback = callback; + gInstanceIDResolveContext = context; +} +#endif + +void InstanceIDToLocalSerializedObjectIdentifier (SInt32 id, LocalSerializedObjectIdentifier& localIdentifier) +{ + #if UNITY_EDITOR + // Early out if referenced object is null + if (id == 0) + { + localIdentifier.localSerializedFileIndex = 0; + localIdentifier.localIdentifierInFile = 0; + return; + } + + if (gInstanceIDResolveCallback == NULL) + { + GetPersistentManager ().InstanceIDToLocalSerializedObjectIdentifierInternal (id, localIdentifier); + return; + } + else + { + gInstanceIDResolveCallback (id, localIdentifier, const_cast<void*>(gInstanceIDResolveContext)); + } + #else + GetPersistentManager ().InstanceIDToLocalSerializedObjectIdentifierInternal (id, localIdentifier); + #endif +} + +void LocalSerializedObjectIdentifierToInstanceID (const LocalSerializedObjectIdentifier& localIdentifier, SInt32& instanceID) +{ + GetPersistentManager ().LocalSerializedObjectIdentifierToInstanceIDInternal (localIdentifier, instanceID); +} + +Object* ReadObjectFromPersistentManager (int id) +{ + if (id == 0) + return NULL; + else + { + // In the Player it is not possible to call MakeObjectPersistent, + // thus instance id's that are positive are the only ones that can be loaded from disk + #if !UNITY_EDITOR + if (id < 0) + { + #if DEBUGMODE + //AssertIf(GetPersistentManager ().ReadObject (id)); + #endif + return NULL; + } + #endif + + Object* o = GetPersistentManager ().ReadObject (id); + return o; + } +} + +void DestroyWithoutLoadingButDontDestroyFromFile (int instanceID) +{ + GetPersistentManager ().MakeObjectUnpersistent (instanceID, kDontDestroyFromFile); + UnloadObject(Object::IDToPointer(instanceID)); +} + +void DestroySingleObject (Object* o) +{ + if (o == NULL) + return; + + if (o->IsPersistent()) + GetPersistentManager ().MakeObjectUnpersistent (o->GetInstanceID (), kDestroyFromFile); + + // Lock changes to IDToPointer so that we can safely lookup pointers using IDToPointerThreadSafe + LockObjectCreation(); + + delete_object_internal (o); + + UnlockObjectCreation(); +} + + +Object::Object (MemLabelId label, ObjectCreationMode mode) +{ + Assert(label.label < (1 << kMemLabelBits)); + m_MemLabel = label.label; + m_InstanceID = 0; + m_EventIndex = NULL; + + #if ENABLE_SCRIPTING + m_MonoReference = 0; + m_ScriptingObjectPointer = SCRIPTING_NULL; + #endif + + #if !UNITY_RELEASE + m_AwakeCalled = 0; + m_ResetCalled = 0; + m_AwakeThreadedCalled = 0; + m_AwakeDidLoadThreadedCalled = 0; + #endif + + + #if UNITY_EDITOR + m_DirtyIndex = 0; + m_FileIDHint = 0; + #endif + m_HideFlags = 0; + m_TemporaryFlags = 0; + m_IsPersistent = false; + + DebugAssert(GetMemoryManager().GetAllocator(GetMemoryLabel())->Contains(this)); + m_IsRootOwner = GetMemoryManager().GetAllocator(GetMemoryLabel())->GetProfilerHeader(this) != NULL; + #if UNITY_WINRT + m_TemporaryUnusedAssetsFlags = 0; + #endif +} + +void Object::CalculateCachedClassID (Object* obj) +{ + Assert(obj->GetClassIDVirtualInternal() < (1 << kCachedClassIDBits)); + obj->m_CachedClassID = obj->GetClassIDVirtualInternal(); +} + +void Object::InsertObjectInMap( Object* obj ) +{ + SET_ALLOC_OWNER(gBaseObjectManagerContainer); + Assert (ms_IDToPointer->find (obj->GetInstanceID ()) == ms_IDToPointer->end ()); + ms_IDToPointer->insert (make_pair (obj->GetInstanceID (), obj)); + + PROFILER_REGISTER_OBJECT(obj); +} + +void Object::RegisterInstanceID (Object* obj) +{ + CHECK_IN_MAIN_THREAD + + LockObjectCreation(); + Assert (obj != NULL); + AssertIf(obj->m_InstanceID == 0); + InsertObjectInMap (obj); + + UnlockObjectCreation(); +} + +void Object::RegisterInstanceIDNoLock (Object* obj) +{ + CHECK_IN_MAIN_THREAD + Assert (obj != NULL); + AssertIf (obj->m_InstanceID == 0); + CalculateCachedClassID (obj); + InsertObjectInMap (obj); +} + + +Object* Object::AllocateAndAssignInstanceID (Object* obj) +{ + CHECK_IN_MAIN_THREAD + AssertIf(obj->m_InstanceID != 0); + + LockObjectCreation(); + + // Create a new unique instanceID for this Object. + // The created id will be negative beginning with -1 + // Ids loaded from a file will be positive beginning with 1 + gLowestInstanceID-=2; + obj->SetInstanceID (gLowestInstanceID); + AssertIf (obj->GetInstanceID () & 1); + + CalculateCachedClassID (obj); + InsertObjectInMap (obj); + + UnlockObjectCreation(); + + obj->SetDirty(); + + return obj; +} + +Object* Object::AllocateAndAssignInstanceIDNoLock (Object* obj) +{ + CHECK_IN_MAIN_THREAD + AssertIf(obj->m_InstanceID != 0); + + // Create a new unique instanceID for this Object. + // The created id will be negative beginning with -1 + // Ids loaded from a file will be positive beginning with 1 + gLowestInstanceID-=2; + obj->SetInstanceID (gLowestInstanceID); + AssertIf (obj->GetInstanceID () & 1); + + CalculateCachedClassID (obj); + + InsertObjectInMap (obj); + + obj->SetDirty(); + + return obj; +} + +enum { kMonoObjectCachedPtrOffset = 12 }; + + +// This must be executed on the main thread +void delete_object_internal_step1 (Object* object) +{ + PROFILER_UNREGISTER_OBJECT(object); + +#if !UNITY_RELEASE + object->CheckCorrectAwakeUsage(); +#endif + +#if THREADED_LOADING && DEBUGMODE + Assert(gCheckObjectCreationMutex >= 1); +#endif + + // Send destroy message & clear event index + if (object->m_EventIndex != NULL) + { + GetEventManager().InvokeEvent(object->m_EventIndex, object, kWillDestroyEvent); + GetEventManager().RemoveEvent(object->m_EventIndex); + object->m_EventIndex = NULL; + } + + // Remove this objects instanceID from the table. + AssertIf (Object::ms_IDToPointer->find (object->GetInstanceID ()) == Object::ms_IDToPointer->end ()); + Object::ms_IDToPointer->erase (object->GetInstanceID ()); + + if (gDestroyedCallbackFunc) + gDestroyedCallbackFunc (object->GetInstanceID ()); + + object->m_InstanceID = 0; +} + +Object::~Object () +{ + // Ensure PreCleanupObject was called + #if DEBUGMODE + Assert(m_InstanceID == 0); + #endif + +#if ENABLE_MONO //if unity3.5 succesfully shipped with this assert it may be removed. here to verify an assumption made in a refactor. + Assert((m_MonoReference==0) == (m_ScriptingObjectPointer==NULL)); +#endif + +#if ENABLE_SCRIPTING + if (m_ScriptingObjectPointer) + SetCachedScriptingObject(SCRIPTING_NULL); +#endif +} + +bool Object::MainThreadCleanup () +{ + AssertString("MainThreadCleanup is not implemented for this class. See DoesClassRequireMainThreadDeallocation"); + return false; +} + +void Object::SetIsPersistent( bool p ) +{ + PROFILER_CHANGE_PERSISTANCY(GetInstanceID(), m_IsPersistent, p); + m_IsPersistent = p; +} + +#if ENABLE_SCRIPTING + +#if UNITY_PS3 || UNITY_XENON + extern "C" char* GC_clear_stack(char*); +#endif + +void Object::SetupWeakHandle () +{ + if (m_MonoReference != 0) + { + register ScriptingObjectPtr object = scripting_gchandle_get_target(m_MonoReference); + UInt32 weakref = scripting_gchandle_weak_new (object); + SetCachedScriptingObject(SCRIPTING_NULL); + +#if UNITY_PS3 || UNITY_XENON + // we need to make sure the object doesn't continue living on the stack. // fixes http://fogbugz.unity3d.com/default.asp?444901#1065992062 + GC_clear_stack((char*)object); +#endif + object = SCRIPTING_NULL; + m_MonoReference = weakref; + } +} + +bool Object::RevertWeakHandle () +{ + if (m_MonoReference != 0) + { + ScriptingObjectPtr target = scripting_gchandle_get_target (m_MonoReference); + scripting_gchandle_free(m_MonoReference); + m_MonoReference = 0; + if (target) + { + SetCachedScriptingObject(target); +#if UNITY_WINRT + // Restore cached ptr for managed object, not sure why we don't do this for Mono as well, because we reset cachedPtr in SetupWeakHandle + // But on Mono it seems cachedPtr persists ?! + // Maybe it's related to cachedPtr optimization + register ScriptingObjectOfType<Object> instance(m_ScriptingObjectPointer); + instance.SetCachedPtr(this); +#endif + } + return target != SCRIPTING_NULL; + } + else + return false; +} + + +void Object::SetCachedScriptingObject (ScriptingObjectPtr object) +{ + if (object) + { + AssertIf(m_MonoReference != 0); + m_MonoReference = scripting_gchandle_new (object); + m_ScriptingObjectPointer = object; + return; + } + + if (m_ScriptingObjectPointer == SCRIPTING_NULL) + { + AssertString("Dont do this"); + return; + } + + register ScriptingObjectOfType<Object> instance(m_ScriptingObjectPointer); + instance.SetCachedPtr(0); + + scripting_gchandle_free (m_MonoReference); + m_MonoReference = 0; + + m_ScriptingObjectPointer = SCRIPTING_NULL; +#if UNITY_WINRT + instance = ScriptingObjectPtr(SCRIPTING_NULL); +#else + instance = SCRIPTING_NULL; +#endif +} +#endif + +void Object::RegisterDestroyedCallback (ObjectDestroyCallbackFunction* callback) +{ + gDestroyedCallbackFunc = callback; +} + +// Register base class +void Object::RegisterClass () +{ + RegisterClass (ClassID (Object), -1, "Object", sizeof (Object), NULL, true); +} + +void Object::RegisterClass (int inClassID, int inBaseClass, const string& inName, int byteSize, FactoryFunction* inFunc, bool isAbstract) +{ + if (ClassIDToRTTI (inClassID)) + return; + + // Store ClassID -> RTTI + AssertIf (gRTTI->find (inClassID) != gRTTI->end ()); + RTTIMap::iterator baseClass = gRTTI->find (inBaseClass); + AssertIf (baseClass == gRTTI->end () && inBaseClass != -1); + Object::RTTI& classInfo = (*gRTTI)[inClassID]; + classInfo.base = baseClass == gRTTI->end () ? NULL : &baseClass->second; + classInfo.factory = inFunc; + classInfo.className = inName; + classInfo.classID = inClassID; + classInfo.isAbstract = isAbstract; + classInfo.size = byteSize; + + // Store String -> ClassID + AssertIf (gStringToClassID->find (const_cast<char*> (inName.c_str ())) != gStringToClassID->end ()); + (*gStringToClassID)[const_cast<char*> (classInfo.className.c_str ())] = inClassID; +} + +Object* Object::Produce (int classID, int instanceID, MemLabelId memLabel, ObjectCreationMode mode) +{ + // Object is already loaded assert + AssertIf (mode == kCreateObjectDefault && IDToPointer (instanceID) != NULL); + AssertIf (instanceID == 0 && mode == kCreateObjectFromNonMainThread); + #if THREADED_LOADING + AssertIf (!Thread::EqualsCurrentThreadID(GetPersistentManager().GetMainThreadID()) && mode != kCreateObjectFromNonMainThread); + #endif + AssertIf (instanceID & 1); + + // Find the appropriate Factory. + RTTIMap::iterator i; + i = gRTTI->find (classID); + if (i == gRTTI->end () || i->second.factory == NULL) + { + return NULL; + } + + Object* o; + if (instanceID != 0) + { + o = i->second.factory (memLabel, mode); + if (o == NULL) + return NULL; + o->SetInstanceID(instanceID); + CalculateCachedClassID (o); + + // Register instanceID and set dirty + if (mode == kCreateObjectDefault) + { + RegisterInstanceID(o); + o->SetDirty(); + } + else if (mode == kCreateObjectDefaultNoLock) + { + RegisterInstanceIDNoLock(o); + o->SetDirty(); + } + + return o; + } + else + { + AssertIf(mode != kCreateObjectDefaultNoLock && mode != kCreateObjectDefault); + o = i->second.factory (memLabel, mode); + + if (mode == kCreateObjectDefaultNoLock) + AllocateAndAssignInstanceIDNoLock(o); + else + AllocateAndAssignInstanceID(o); + return o; + } +} + +void Object::CheckInstanceIDsLoaded (SInt32* instanceIDs, int size) +{ + for (int i=0;i<size;i++) + { + if (ms_IDToPointer->count (instanceIDs[i])) + instanceIDs[i] = 0; + } +} + +#if !UNITY_RELEASE +Object* Object::IDToPointer (int inInstanceID) +{ + DEBUG_CHECK_IN_MAIN_THREAD + + return Object::IDToPointerNoThreadCheck (inInstanceID); +} +#endif + +Object* Object::IDToPointerThreadSafe (int inInstanceID) +{ + LockObjectCreation(); + Object* obj = Object::IDToPointerNoThreadCheck (inInstanceID); + UnlockObjectCreation(); + return obj; +} + +Object* Object::IDToPointerNoThreadCheck (int inInstanceID) +{ + if( !ms_IDToPointer) return NULL; + + IDToPointerMap::const_iterator i = ms_IDToPointer->find (inInstanceID); + if (i != ms_IDToPointer->end ()) + { + return i->second; + } + else + { + return NULL; + } +} + +#if THREADED_LOADING +Object* InstanceIDToObjectThreadSafe (int instanceID) +{ + if (Thread::EqualsCurrentThreadID (GetPersistentManager().GetMainThreadID())) + return PPtr<Object> (instanceID); + else + { + Object* obj = Object::IDToPointerThreadSafe(instanceID); + if (obj == NULL) + return GetPersistentManager().ReadObjectThreaded(instanceID); + else + return obj; + } +} +#endif + +void Object::FindAllDerivedClasses (int classID, vector<SInt32>* derivedClasses, bool onlyNonAbstract) +{ + AssertIf (derivedClasses == NULL); + RTTIMap::iterator i; + for (i=gRTTI->begin ();i!=gRTTI->end ();i++) + { + if (IsDerivedFromClassID (i->first, classID) && (!onlyNonAbstract || !i->second.isAbstract)) + derivedClasses->push_back (i->first); + } +} + +struct GetConstFirst +{ + template<typename T1, typename T2> + const T1& operator()(const std::pair<T1, T2>& p) const { + return p.first; + } +}; + +struct GetConstSecond +{ + template<typename T1, typename T2> + const T2& operator()(const std::pair<T1, T2>& p) const { + return p.second; + } +}; + +struct IsDerivedFromClass +{ + IsDerivedFromClass(int classID): m_ClassID(classID) {} + bool operator()(const Object::IDToPointerMap::value_type& el) const + { + return el.second->IsDerivedFrom (m_ClassID); + } +private: + int m_ClassID; +}; + +inline int DerivedObjectCount(const Object::IDToPointerMap& objmap, int classID) +{ + return std::count_if(objmap.begin(), objmap.end(), IsDerivedFromClass(classID)); +} + +template<typename ObjectGetter, typename Container, typename Predicate> +inline int FindAllDerivedObjectsImpl (const Object::IDToPointerMap& objmap, int classID, + ObjectGetter getter, Container* derivedObjects, bool sorted, Predicate pred) +{ + if (NULL == derivedObjects) + return DerivedObjectCount(objmap, classID); + + int count = 0; + for (typename Object::IDToPointerMap::const_iterator i = objmap.begin(); + i != objmap.end(); ++i) + { + if (i->second->IsDerivedFrom (classID)) + { + derivedObjects->push_back (getter(*i)); + count++; + } + } + + if (sorted && count) + std::sort(derivedObjects->begin(), derivedObjects->end(), pred); + + return count; +} + +int Object::FindAllDerivedObjects (int classID, vector<SInt32>* derivedObjects, bool sorted) +{ + return FindAllDerivedObjectsImpl (*ms_IDToPointer, classID, + GetConstFirst(), derivedObjects, sorted, std::less<SInt32>()); +} + +struct CompareInstanceID +{ + bool operator () (const Object* lhs, const Object* rhs) const + { + return lhs->GetInstanceID() < rhs->GetInstanceID(); + } +}; + +int Object::FindObjectsOfType (int classID, vector<Object*>* derivedObjects, bool sorted) +{ + return FindAllDerivedObjectsImpl (*ms_IDToPointer, classID, + GetConstSecond(), derivedObjects, sorted, CompareInstanceID()); +} + +int Object::FindObjectsOfType (int classID, dynamic_array<Object*>* derivedObjects, bool sorted) +{ + int count = 0; + IDToPointerMap::iterator i; + for (i=ms_IDToPointer->begin ();i!=ms_IDToPointer->end ();i++) + { + if (i->second->IsDerivedFrom (classID)) + { + if (derivedObjects != NULL) + derivedObjects->push_back (i->second); + count++; + } + } + + + if (sorted && derivedObjects != NULL) + { + CompareInstanceID compare; + sort(derivedObjects->begin(), derivedObjects->end(), compare); + } + + + return count; +} + + +const std::string& Object::ClassIDToString (int ID) +{ + static std::string emptyString; + RTTIMap::iterator i = gRTTI->find (ID); + if (i == gRTTI->end ()) + return emptyString; + else + return i->second.className; +} + +int Object::StringToClassID (const string& classString) +{ + StringToClassIDMap::iterator i; + i = gStringToClassID->find (const_cast<char*> (classString.c_str ())); + if (i == gStringToClassID->end ()) + return -1; + else + return i->second; +} + +int Object::StringToClassIDCaseInsensitive (const string& classString) +{ + StringToClassIDMap::iterator i; + string lowerClass = ToLower(classString); + for (StringToClassIDMap::iterator i = gStringToClassID->begin(); i!=gStringToClassID->end(); i++) + { + if (ToLower(string(i->first)) == lowerClass) + return i->second; + } + return -1; +} + +int Object::StringToClassID (const char* classString) +{ + StringToClassIDMap::iterator i; + i = gStringToClassID->find (const_cast<char*> (classString)); + if (i == gStringToClassID->end ()) + return -1; + else + return i->second; +} + +const std::string& Object::GetClassName () const +{ + return Object::ClassIDToString (GetClassID ()); +} + +int Object::GetSuperClassID (int classID) +{ + RTTIMap::iterator i = gRTTI->find (classID); + AssertIf (i == gRTTI->end ()); + if (i->second.base) + return i->second.base->classID; + else + return ClassID (Object); +} + +Object::RTTI* Object::ClassIDToRTTI (int classID) +{ + RTTIMap::iterator i = gRTTI->find (classID); + if (i == gRTTI->end ()) + return NULL; + else + return &i->second; +} + +struct BuildClassInfo +{ + Object::RTTI* klass; + Object::RTTI* base; + UInt32 id; + UInt32 subclasses; + UInt32 level; + UInt32 newId; + UInt32 subClassIdAssign; + void Clear(){ + base = 0; + klass = 0; + id = (UInt32)-1; + subclasses = 0; + level = 0xffffffff; + newId = -1; + subClassIdAssign = 0; + } + void Init(Object::RTTI* klass, Object::RTTI* base) + { + this->klass = klass; + this->base = base; + } + bool operator <( const BuildClassInfo& o) const + { + return level < o.level; + } +}; +int bitsRequired(int subclasses) +{ + int r = 0; + while(subclasses) + { + r++; + subclasses >>= 1; + } + return r; +} + +void Object::StaticInitialize() +{ + SET_ALLOC_OWNER(gBaseObjectManagerContainer); + Object::ms_IDToPointer = UNITY_NEW(Object::IDToPointerMap (1024 * 128), kMemBaseObject); +} + +void Object::StaticDestroy() +{ + UNITY_DELETE(Object::ms_IDToPointer,kMemBaseObject); +} + +void Object::InitializeAllClasses () +{ + SET_ALLOC_OWNER(gBaseObjectManagerContainer); + + if (gRegisterClassCallbacks == NULL) + return; + +#if SHOW_REGISTERED_CLASS_INFO + int registeredClasses = 0; +#endif + + RegisterClassCallbacks& callbacks = *gRegisterClassCallbacks; + // The callback is the RegisterClass function defined in ObjectDefines.h + // It calls the static Object::RegisterClass function which sets up the rtti system + for (int i=0;i<callbacks.size ();i++) + { + if (callbacks[i].registerClass) + { + callbacks[i].registerClass (); +#if SHOW_REGISTERED_CLASS_INFO + ++registeredClasses; +#endif + } + } + +#if SHOW_REGISTERED_CLASS_INFO + printf_console ("Object::InitializeAllClasses: %d total, %d registered\n", callbacks.size(), registeredClasses); +#endif + + AssertIf (gRTTI->empty ()); + + // Setup ms_IsDerivedFrom lookup bitmap + if (UNITY_EDITOR) + { + ms_MaxClassID = (--gRTTI->end ())->first + 1; + Assert(kLargestEditorClassID == ms_MaxClassID); + } + else + { + ms_MaxClassID = kLargestRuntimeClassID; + } + + gIsDerivedFromBitMap->resize (ms_MaxClassID * ms_MaxClassID, false); + ms_IsDerivedFromBitMap = (UInt32*) gIsDerivedFromBitMap->m_bits; + gRegisteredClassIDs->resize (ms_MaxClassID, false); + for (int i=0;i<ms_MaxClassID;i++) + { + RTTIMap::iterator iRTTI = gRTTI->find (i); + (*gRegisteredClassIDs)[i] = iRTTI != gRTTI->end (); + if ((*gRegisteredClassIDs)[i]) + { + for (int j=0;j<ms_MaxClassID;j++) + { + RTTIMap::iterator jRTTI = gRTTI->find (j); + if (jRTTI != gRTTI->end ()) + (*gIsDerivedFromBitMap)[i * ms_MaxClassID + j] = IsDerivedFromRTTI (&iRTTI->second, &jRTTI->second); + } + } + } + +#if USE_NEW_IS_DERIVED_FROM + ms_ClassIDMask = &gClassIDMask[0]; + ms_ClassIsDerivedFrom = new UInt32[ms_MaxClassID]; + memset(ms_ClassIsDerivedFrom, 0xffffffff, sizeof(UInt32) * ms_MaxClassID); + ms_ClassIsDerivedFrom[0] = 1; + BuildClassInfo* info = (BuildClassInfo*)alloca(sizeof(BuildClassInfo) * ms_MaxClassID); + for(int i = 0; i < ms_MaxClassID; ++i) + info[i].Clear(); + int maxSubClasses[32]; + int bitShift[32]; + memset(maxSubClasses, 0, sizeof(maxSubClasses)); + memset(bitShift, 0, sizeof(bitShift)); + //search for the base class + typedef RTTIMap::iterator itr; + for(itr i = gRTTI->begin(); i != gRTTI->end(); ++i) + { + Object::RTTI& r = i->second; + SInt32 oldId = i->first; + info[oldId].Init(&r, r.base); + info[oldId].id = oldId; + int level = 0; + Object::RTTI* base = r.base; + while(base) + { + ++level; + base = base->base; + } + info[oldId].level = level; + if(r.base) + info[r.base->classID].subclasses++; + } + std::sort(&info[0], ms_MaxClassID + &info[0]); + for(int i = 0; i < ms_MaxClassID; ++i) + { + int level = info[i].level; + maxSubClasses[level] = info[i].subclasses > maxSubClasses[level] ? info[i].subclasses : maxSubClasses[level]; + } + int totalBits = 1; + UInt32 mask = 1; + bitShift[0] = 0; + gClassIDMask[0] = 1; + for(int i = 1; i < 32; ++i) + { + int bitsReq = bitsRequired(maxSubClasses[i-1]); + int bitsReq1 = bitsReq; + while(bitsReq--) + mask = (mask<<1) | 1; + + bitShift[i] = totalBits; + totalBits += bitsReq1; + AssertIf(totalBits > 32 - CLASS_ID_MASK_BITS); //OUT OF BITS + gClassIDMask[i] = mask; + } +#define VERIFY_CLASS_IDS 0 +#if VERIFY_CLASS_IDS + std::set<UInt32> ClassIdSet; +#endif + int lastIndex = 0; + for(int i = 0; i < ms_MaxClassID; ++i) + { + Object::RTTI* klass = info[i].klass; + Object::RTTI* base = info[i].base; + int level = info[i].level; + int parentId = 0; + int parentIndex = -1; + int parentSubIndex = 1; + if(base) + { + for(int j = 0; j < ms_MaxClassID; ++j) + { + if(info[j].klass == base) + { + parentIndex = j; + break; + } + } + AssertIf(parentIndex >= i); + parentId = info[parentIndex].newId & CLASS_ID_MASK_IDS; + AssertIf(-1 == parentId); + parentSubIndex = info[parentIndex].subClassIdAssign++; + } + else + { + parentSubIndex = 1; + if(i != 0) + { + lastIndex = i; + break; + } + } + int shift = bitShift[level]; + int id = (parentSubIndex << shift) | parentId; + int fullId = id | (level<<(32-CLASS_ID_MASK_BITS)); +#if VERIFY_CLASS_IDS + AssertIf(ClassIdSet.find(fullId) != ClassIdSet.end() || ClassIdSet.find(id) != ClassIdSet.end() ); // DUPE CLASSID. should never happen + ClassIdSet.insert(id); + ClassIdSet.insert(fullId); +#endif + info[i].newId = fullId; + ms_ClassIsDerivedFrom[info[i].id] = fullId; + AssertIf(info[i].subClassIdAssign != 0); + info[i].subClassIdAssign = 1; + } +#endif +} + +void Object::CallInitializeClass() +{ + RegisterClassCallbacks& callbacks = *gRegisterClassCallbacks; + + // Call the IntializeClass function for classes that registered for it (IMPLEMENT_CLASS_HAS_INIT) + // This is done after all classes are registered and the rtti setup + // so that the rtti system can be used insie InitializeClass () + for (int i=0;i<callbacks.size ();i++) + { + if (callbacks[i].initClass) + { + callbacks[i].initClass (); + } + } +} + +void Object::CallPostInitializeClass() +{ + RegisterClassCallbacks& callbacks = *gRegisterClassCallbacks; + + // Call the PostIntializeClass function for classes that registered for it (IMPLEMENT_CLASS_HAS_POSTINIT) + // This is done after all classes are registered and the rtti setup + // so that the rtti system can be used inside PostInitializeClass () + for (int i=0;i<callbacks.size ();i++) + { + if (callbacks[i].postInitClass) + { + callbacks[i].postInitClass (); + } + } +} + +void Object::AddEvent (EventCallback* callback, void* userData) +{ + m_EventIndex = GetEventManager().AddEvent(callback, userData, m_EventIndex); +} + +void Object::RemoveEvent (EventCallback* callback, void* userData) +{ + m_EventIndex = GetEventManager().RemoveEvent(m_EventIndex, callback, userData); +} + +bool Object::HasEvent (EventCallback* callback, const void* userData) const +{ + return EventManager::HasEvent(m_EventIndex, callback, userData); +} + +void Object::InvokeEvent (int eventType) +{ + EventManager::InvokeEvent(m_EventIndex, this, eventType); +} + +bool IsObjectAvailable (int instanceID) +{ + Object* temp = Object::IDToPointer (instanceID); + if (temp != NULL) + return true; + + return GetPersistentManager ().IsObjectAvailable (instanceID); +} +#if !USE_NEW_IS_DERIVED_FROM +#if !UNITY_RELEASE + +bool Object::IsDerivedFromClassID (int classID, int compareClass) +{ + if (classID >= ms_MaxClassID || classID < 0) + { + char buffy[512]; + sprintf (buffy, "The class with classID: %d out of bounds", classID); + AssertString (buffy); + return false; + } + if (compareClass >= ms_MaxClassID || compareClass < 0) + { + /* + char buffy[512]; + sprintf (buffy, "The compare class with classID: %d out of bounds", compareClass); + AssertString (buffy); + */ + return false; + } + + AssertIf (classID >= ms_MaxClassID || classID < 0); + AssertIf (compareClass >= ms_MaxClassID || compareClass < 0); + + + if (!(*gRegisteredClassIDs)[classID]) + { + char buffy[512]; + sprintf (buffy, "The class with classID: %d is not registered (see ClassIDs.h)", classID); + AssertString (buffy); + + } + + // When doing classID stripping + #if !ALLOW_CLASS_ID_STRIPPING + if (!(*gRegisteredClassIDs)[compareClass]) + { + char buffy[512]; + sprintf (buffy, "The class with classID: %d is not registered (see ClassIDs.h)", compareClass); + AssertString (buffy); + } + #endif + + int index = classID * ms_MaxClassID + compareClass; + int block = index >> 5; + int bit = index - (block << 5); + return (ms_IsDerivedFromBitMap[block]) & (1 << bit); +} + +#endif +#endif + +INSTANTIATE_TEMPLATE_TRANSFER_WITH_DECL (Object, EXPORTDLL); + +template<class TransferFunction> +void Object::Transfer (TransferFunction& transfer) +{ +#if UNITY_EDITOR + if (!transfer.IsSerializingForGameRelease() && SerializePrefabIgnoreProperties(transfer)) + { + UInt32 flags = m_HideFlags; + transfer.Transfer(flags, "m_ObjectHideFlags", kHideInEditorMask); + m_HideFlags = flags; + } + + if (transfer.GetFlags () & kSerializeDebugProperties) + { + SInt32 instanceID = GetInstanceID (); + transfer.Transfer (instanceID, "m_InstanceID"); + + LocalIdentifierInFileType fileID; + if (IsPersistent ()) + fileID = GetPersistentManager ().GetLocalFileID (instanceID); + else + fileID = GetFileIDHint (); + + transfer.Transfer (fileID, "m_LocalIdentfierInFile"); + } +#endif +} + +#if UNITY_EDITOR + +void Object::RegisterDirtyCallback (ObjectDirtyCallbackFunction* callback) +{ + gSetDirtyCallbackFunc = callback; +} + +Object::ObjectDirtyCallbackFunction* Object::GetDirtyCallback () +{ + return gSetDirtyCallbackFunc; +} + +void Object::SetDirty () +{ + // When we run out of dirty indices, make sure it stays at 1 + m_DirtyIndex++; + if (m_DirtyIndex == 0) + m_DirtyIndex = 1; + + if (gSetDirtyCallbackFunc) + gSetDirtyCallbackFunc (this); + + #if !UNITY_RELEASE + m_DEBUGCLASSID = GetClassID (); + #endif +} + +void Object::ClearPersistentDirty () +{ + m_DirtyIndex = 0; +} + +void Object::SetPersistentDirtyIndex (UInt32 dirtyValue) +{ + m_DirtyIndex = dirtyValue; +} + + +#endif + +#if DEBUGMODE +void AddVerifyClassRegistration (int classID) +{ + if (gVerifyRegisteredClasses == NULL) + { + SET_ALLOC_OWNER(gBaseObjectManagerContainer); + gVerifyRegisteredClasses = UNITY_NEW(RegisteredClassSet, kMemManager)(); + } + gVerifyRegisteredClasses->insert(classID); +} +const RegisteredClassSet& GetVerifyClassRegistration () +{ + if (gVerifyRegisteredClasses == NULL) + { + SET_ALLOC_OWNER(gBaseObjectManagerContainer); + gVerifyRegisteredClasses = UNITY_NEW(RegisteredClassSet, kMemManager)(); + } + return *gVerifyRegisteredClasses; +} + +#endif + + +void RegisterInitializeClassCallback (int classID, + RegisterClassCallback* registerClass, + RegisterClassCallback* initClass, + RegisterClassCallback* postInitClass, + RegisterClassCallback* cleanupClass) +{ + if (gRegisterClassCallbacks == NULL) + { + SET_ALLOC_OWNER(gBaseObjectManagerContainer); + gRegisterClassCallbacks = UNITY_NEW(RegisterClassCallbacks,kMemBaseObject); + } + if (gRegisterClassCallbacks->size () <= classID) + gRegisterClassCallbacks->resize (classID + 1); + + RegisterClassCallbacks& callbacks = *gRegisterClassCallbacks; + if (callbacks[classID].registerClass != NULL || callbacks[classID].initClass != NULL || callbacks[classID].postInitClass != NULL || callbacks[classID].cleanupClass != NULL) + { + char buffer[512]; + sprintf (buffer, "ClassID: %d is already registered. ClassID's have to be unique", classID); + FatalErrorString (buffer); + AssertBreak(false); + } + callbacks[classID].registerClass = registerClass; + callbacks[classID].initClass = initClass; + callbacks[classID].postInitClass = postInitClass; + callbacks[classID].cleanupClass = cleanupClass; +} + +void Object::CleanupAllClasses () +{ + AssertIf(!ms_IDToPointer->empty()); + + if (!gRegisterClassCallbacks) + return; + + RegisterClassCallbacks& callbacks = *gRegisterClassCallbacks; + for (int i=0;i<callbacks.size ();i++) + { + if (callbacks[i].cleanupClass) + callbacks[i].cleanupClass (); + } + + UNITY_DELETE(gRegisterClassCallbacks, kMemBaseObject); +} + +void SetDisableImmediateDestruction (bool disable) +{ + gDisableImmediateDestruction = disable; +} + +bool GetDisableImmediateDestruction () +{ + return gDisableImmediateDestruction; +} + +void delete_object_internal (Object* p) +{ + if (!p) + return; + + delete_object_internal_step1 (p); + delete_object_internal_step2 (p); +} + +// This can be execute on any thread. +void delete_object_internal_step2 (Object* p) +{ + MemLabelId label = p->GetMemoryLabel(); + p->~Object (); + UNITY_FREE(label, p); +} + +void UnloadObject (Object* p) +{ + if (!p) + return; + + LockObjectCreation(); + delete_object_internal(p); + UnlockObjectCreation(); +} + +void Object::DoneLoadingManagers() +{ + // We are done loading managers. Start instance IDs from a high constant value here, + // so new managers and built-in resources can be added without changed instanceIDs + // used by the content. + if (gLowestInstanceID > -10000) + { + gLowestInstanceID = -10000; + } +} + +MemLabelId Object::GetMemoryLabel() const +{ + MemLabelIdentifier id = (MemLabelIdentifier)m_MemLabel; + MemLabelId label(id, NULL); + if(m_IsRootOwner) + label.SetRootHeader(GET_ALLOC_HEADER((void*)this, label)); + return label; +} + +int Object::GetRuntimeMemorySize() const +{ +#if ENABLE_MEM_PROFILER + return GetMemoryProfiler()->GetRelatedMemorySize((void*)this); +#else + return 0; +#endif +} + + +#if !UNITY_RELEASE + +void Object::CheckCorrectAwakeUsage() +{ + // check only if saw that object already to allow delayed awake and immediate destroy + if ( m_AwakeCalled == 0 ) + AssertStringObject(Format("Awake has not been called '%s' (%s). Figure out where the object gets created and call AwakeFromLoad correctly.", GetName(), GetClassName().c_str()), this); + + if ( m_ResetCalled == 0 ) + AssertStringObject(Format("Reset has not been called '%s' (%s). Figure out where the object gets created and call Reset correctly.", GetName(), GetClassName().c_str()), this); + + if ( m_AwakeThreadedCalled && !m_AwakeDidLoadThreadedCalled ) + AssertStringObject(Format("AwakeFromLoadThreaded has not been called '%s' (%s). Figure out where the object gets created and call AwakeFromLoadThreaded correctly.", GetName(), GetClassName().c_str()), this); +} + + +#endif // !UNITY_RELEASE |