Runtime.cs 7.4 KB

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