Runtime.cs 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. //
  2. // Runtime C# support
  3. //
  4. // Authors:
  5. // Miguel de Icaza ([email protected])
  6. //
  7. // Copyrigh 2015 Xamarin INc
  8. //
  9. using System;
  10. using System.Collections.Generic;
  11. using System.Diagnostics;
  12. using System.Linq;
  13. using System.Reflection;
  14. using System.Runtime.InteropServices;
  15. using Urho.Resources;
  16. namespace Urho
  17. {
  18. internal class Runtime
  19. {
  20. static readonly RefCountedCache RefCountedCache = new RefCountedCache();
  21. static Dictionary<Type, int> hashDict;
  22. static MonoRefCountedCallback monoRefCountedCallback; //keep references to native callbacks (protect from GC)
  23. static MonoComponentCallback monoComponentCallback;
  24. [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
  25. delegate void MonoRefCountedCallback(IntPtr ptr, RefCountedEvent rcEvent);
  26. [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
  27. delegate void MonoComponentCallback(IntPtr componentPtr, IntPtr xmlElementPtr, MonoComponentCallbackType eventType);
  28. [DllImport(Consts.NativeImport, CallingConvention = CallingConvention.Cdecl)]
  29. static extern void RegisterMonoRefCountedCallback(MonoRefCountedCallback callback);
  30. [DllImport(Consts.NativeImport, CallingConvention = CallingConvention.Cdecl)]
  31. static extern void RegisterMonoComponentCallback(MonoComponentCallback callback);
  32. /// <summary>
  33. /// Runtime initialization.
  34. /// </summary>
  35. public static void Initialize()
  36. {
  37. RegisterMonoRefCountedCallback(monoRefCountedCallback = OnRefCountedEvent);
  38. RegisterMonoComponentCallback(monoComponentCallback = OnComponentEvent);
  39. }
  40. /// <summary>
  41. /// This method is called by RefCounted::~RefCounted or RefCounted::AddRef
  42. /// </summary>
  43. [MonoPInvokeCallback(typeof(MonoRefCountedCallback))]
  44. static void OnRefCountedEvent(IntPtr ptr, RefCountedEvent rcEvent)
  45. {
  46. if (rcEvent == RefCountedEvent.Delete)
  47. {
  48. var referenceHolder = RefCountedCache.Get(ptr);
  49. if (referenceHolder == null)
  50. return; //we don't have this object in the cache so let's just skip it
  51. var reference = referenceHolder.Reference;
  52. if (reference == null)
  53. {
  54. // seems like the reference was Weak and GC has removed it - remove item from the dictionary
  55. RefCountedCache.Remove(ptr);
  56. }
  57. else
  58. {
  59. reference.HandleNativeDelete();
  60. }
  61. }
  62. else if (rcEvent == RefCountedEvent.Addref)
  63. {
  64. //if we have an object with this handle and it's reference is weak - then change it to strong.
  65. var referenceHolder = RefCountedCache.Get(ptr);
  66. referenceHolder?.MakeStrong();
  67. }
  68. }
  69. [MonoPInvokeCallback(typeof(MonoComponentCallback))]
  70. static void OnComponentEvent(IntPtr componentPtr, IntPtr xmlElementPtr, MonoComponentCallbackType eventType)
  71. {
  72. const string typeNameKey = "SharpTypeName";
  73. var xmlElement = new XmlElement(xmlElementPtr);
  74. if (eventType == MonoComponentCallbackType.SaveXml)
  75. {
  76. var component = LookupObject<Component>(componentPtr, false);
  77. if (component != null && component.TypeName != component.GetType().Name)
  78. {
  79. xmlElement.SetString(typeNameKey, component.GetType().AssemblyQualifiedName);
  80. component.OnSerialize(new XmlComponentSerializer(xmlElement));
  81. }
  82. }
  83. else if (eventType == MonoComponentCallbackType.LoadXml)
  84. {
  85. var name = xmlElement.GetAttribute(typeNameKey);
  86. if (!string.IsNullOrEmpty(name))
  87. {
  88. Component component;
  89. try
  90. {
  91. component = (Component) Activator.CreateInstance(Type.GetType(name), componentPtr);
  92. }
  93. catch (Exception exc)
  94. {
  95. throw new InvalidOperationException($"{name} doesn't override constructor Component(IntPtr handle).", exc);
  96. }
  97. component.OnDeserialize(new XmlComponentSerializer(xmlElement));
  98. if (component.Node != null)
  99. {
  100. component.OnAttachedToNode(component.Node);
  101. }
  102. }
  103. }
  104. else if (eventType == MonoComponentCallbackType.AttachedToNode)
  105. {
  106. var component = LookupObject<Component>(componentPtr, false);
  107. component?.OnAttachedToNode(component.Node);
  108. }
  109. }
  110. public static T LookupRefCounted<T> (IntPtr ptr, bool createIfNotFound = true) where T:RefCounted
  111. {
  112. if (ptr == IntPtr.Zero)
  113. return null;
  114. var reference = RefCountedCache.Get(ptr)?.Reference;
  115. if (reference is T)
  116. return (T) reference;
  117. if (!createIfNotFound)
  118. return null;
  119. var refCounted = (T)Activator.CreateInstance(typeof(T), ptr);
  120. return refCounted;
  121. }
  122. public static T LookupObject<T>(IntPtr ptr, bool createIfNotFound = true) where T : UrhoObject
  123. {
  124. if (ptr == IntPtr.Zero)
  125. return null;
  126. var referenceHolder = RefCountedCache.Get(ptr);
  127. var reference = referenceHolder?.Reference;
  128. if (reference is T) //possible collisions
  129. return (T)reference;
  130. if (!createIfNotFound)
  131. return null;
  132. var name = Marshal.PtrToStringAnsi(UrhoObject.UrhoObject_GetTypeName(ptr));
  133. var type = FindTypeByName(name);
  134. var typeInfo = type.GetTypeInfo();
  135. if (typeInfo.IsSubclassOf(typeof(Component)) || type == typeof(Component))
  136. {
  137. //TODO: special case, handle managed subclasses
  138. }
  139. var urhoObject = (T)Activator.CreateInstance(type, ptr);
  140. return urhoObject;
  141. }
  142. public static void UnregisterObject (IntPtr handle)
  143. {
  144. RefCountedCache.Remove(handle);
  145. }
  146. public static void RegisterObject (RefCounted refCounted)
  147. {
  148. RefCountedCache.Add(refCounted);
  149. }
  150. public static StringHash LookupStringHash (Type t)
  151. {
  152. if (hashDict == null)
  153. hashDict = new Dictionary<Type, int> ();
  154. int c;
  155. if (hashDict.TryGetValue (t, out c))
  156. return new StringHash (c);
  157. var typeStatic = t.GetRuntimeProperty("TypeStatic");
  158. if (typeStatic == null)
  159. throw new InvalidOperationException("The type doesn't have static TypeStatic property");
  160. var hash = (StringHash)typeStatic.GetValue(null);
  161. hashDict [t] = hash.Code;
  162. return hash;
  163. }
  164. static internal IReadOnlyList<T> CreateVectorSharedPtrProxy<T> (IntPtr handle) where T : UrhoObject
  165. {
  166. return new Vectors.ProxyUrhoObject<T> (handle);
  167. }
  168. static internal IReadOnlyList<T> CreateVectorSharedPtrRefcountedProxy<T>(IntPtr handle) where T : RefCounted
  169. {
  170. return new Vectors.ProxyRefCounted<T>(handle);
  171. }
  172. internal static void Cleanup()
  173. {
  174. RefCountedCache.Clean();
  175. GC.Collect();
  176. GC.WaitForPendingFinalizers();
  177. GC.Collect();
  178. }
  179. // for Debug purposes
  180. static internal int KnownObjectsCount => RefCountedCache.Count;
  181. static Dictionary<string, Type> typesByNativeNames;
  182. // special cases: (TODO: share this code with SharpieBinder somehow)
  183. static Dictionary<string, string> typeNamesMap = new Dictionary<string, string>
  184. {
  185. {nameof(UrhoObject), "Object"},
  186. {nameof(UrhoConsole), "Console"},
  187. {nameof(XmlFile), "XMLFile"},
  188. {nameof(JsonFile), "JSONFile"},
  189. };
  190. static Type FindTypeByName(string name)
  191. {
  192. if (typesByNativeNames == null)
  193. {
  194. typesByNativeNames = new Dictionary<string, Type>(200);
  195. foreach (var type in typeof(Runtime).GetTypeInfo().Assembly.ExportedTypes)
  196. {
  197. if (!type.GetTypeInfo().IsSubclassOf(typeof(RefCounted)))
  198. continue;
  199. string remappedName;
  200. if (!typeNamesMap.TryGetValue(type.Name, out remappedName))
  201. remappedName = type.Name;
  202. typesByNativeNames[remappedName] = type;
  203. }
  204. }
  205. Type result;
  206. if (!typesByNativeNames.TryGetValue(name, out result))
  207. throw new Exception($"Type {name} not found.");
  208. return result;
  209. }
  210. }
  211. }