Runtime.cs 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  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.CompilerServices;
  13. using System.Runtime.InteropServices;
  14. using Urho.IO;
  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. [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. static MonoRefCountedCallback monoRefCountedCallback; //keep references to native callbacks (protect from GC)
  31. static MonoComponentCallback monoComponentCallback;
  32. internal static bool IsClosing { get; private set; }
  33. internal static void Start()
  34. {
  35. IsClosing = false;
  36. RegisterMonoRefCountedCallback(monoRefCountedCallback = OnRefCountedEvent);
  37. RegisterMonoComponentCallback(monoComponentCallback = OnComponentEvent);
  38. }
  39. internal static void Setup()
  40. {
  41. }
  42. /// <summary>
  43. /// This method is called by RefCounted::~RefCounted or RefCounted::AddRef
  44. /// </summary>
  45. [MonoPInvokeCallback(typeof(MonoRefCountedCallback))]
  46. static void OnRefCountedEvent(IntPtr ptr, RefCountedEvent rcEvent)
  47. {
  48. if (rcEvent == RefCountedEvent.Delete)
  49. {
  50. var referenceHolder = RefCountedCache.Get(ptr);
  51. if (referenceHolder == null)
  52. return; //we don't have this object in the cache so let's just skip it
  53. var reference = referenceHolder.Reference;
  54. if (reference == null)
  55. {
  56. // seems like the reference was Weak and GC has removed it - remove item from the dictionary
  57. RefCountedCache.Remove(ptr);
  58. }
  59. else
  60. {
  61. reference.HandleNativeDelete();
  62. }
  63. }
  64. else if (rcEvent == RefCountedEvent.Addref)
  65. {
  66. //if we have an object with this handle and it's reference is weak - then change it to strong.
  67. var referenceHolder = RefCountedCache.Get(ptr);
  68. referenceHolder?.MakeStrong();
  69. }
  70. }
  71. [MonoPInvokeCallback(typeof(MonoComponentCallback))]
  72. static void OnComponentEvent(IntPtr componentPtr, IntPtr xmlElementPtr, MonoComponentCallbackType eventType)
  73. {
  74. const string typeNameKey = "SharpTypeName";
  75. var xmlElement = new XmlElement(xmlElementPtr);
  76. if (eventType == MonoComponentCallbackType.SaveXml)
  77. {
  78. var component = LookupObject<Component>(componentPtr, false);
  79. if (component != null && component.TypeName != component.GetType().Name)
  80. {
  81. xmlElement.SetString(typeNameKey, component.GetType().AssemblyQualifiedName);
  82. component.OnSerialize(new XmlComponentSerializer(xmlElement));
  83. }
  84. }
  85. else if (eventType == MonoComponentCallbackType.LoadXml)
  86. {
  87. var name = xmlElement.GetAttribute(typeNameKey);
  88. if (!string.IsNullOrEmpty(name))
  89. {
  90. Component component;
  91. try
  92. {
  93. component = (Component) Activator.CreateInstance(Type.GetType(name), componentPtr);
  94. }
  95. catch (Exception exc)
  96. {
  97. throw new InvalidOperationException($"{name} doesn't override constructor Component(IntPtr handle).", exc);
  98. }
  99. component.OnDeserialize(new XmlComponentSerializer(xmlElement));
  100. if (component.Node != null)
  101. {
  102. component.OnAttachedToNode(component.Node);
  103. }
  104. }
  105. }
  106. else if (eventType == MonoComponentCallbackType.AttachedToNode)
  107. {
  108. var component = LookupObject<Component>(componentPtr, false);
  109. component?.OnAttachedToNode(component.Node);
  110. }
  111. }
  112. public static T LookupRefCounted<T> (IntPtr ptr, bool createIfNotFound = true) where T:RefCounted
  113. {
  114. if (ptr == IntPtr.Zero)
  115. return null;
  116. var reference = RefCountedCache.Get(ptr)?.Reference;
  117. if (reference is T)
  118. return (T) reference;
  119. if (!createIfNotFound)
  120. return null;
  121. var refCounted = (T)Activator.CreateInstance(typeof(T), ptr);
  122. return refCounted;
  123. }
  124. public static T LookupObject<T>(IntPtr ptr, bool createIfNotFound = true) where T : UrhoObject
  125. {
  126. if (ptr == IntPtr.Zero)
  127. return null;
  128. var referenceHolder = RefCountedCache.Get(ptr);
  129. var reference = referenceHolder?.Reference;
  130. if (reference is T) //possible collisions
  131. return (T)reference;
  132. if (!createIfNotFound)
  133. return null;
  134. var name = Marshal.PtrToStringAnsi(UrhoObject.UrhoObject_GetTypeName(ptr));
  135. var type = FindTypeByName(name);
  136. var typeInfo = type.GetTypeInfo();
  137. if (typeInfo.IsSubclassOf(typeof(Component)) || type == typeof(Component))
  138. {
  139. //TODO: special case, handle managed subclasses
  140. }
  141. var urhoObject = (T)Activator.CreateInstance(type, ptr);
  142. return urhoObject;
  143. }
  144. public static void UnregisterObject (IntPtr handle)
  145. {
  146. RefCountedCache.Remove(handle);
  147. }
  148. public static void RegisterObject (RefCounted refCounted)
  149. {
  150. RefCountedCache.Add(refCounted);
  151. }
  152. public static StringHash LookupStringHash (Type t)
  153. {
  154. if (hashDict == null)
  155. hashDict = new Dictionary<Type, int> ();
  156. int c;
  157. if (hashDict.TryGetValue (t, out c))
  158. return new StringHash (c);
  159. var hash = GetTypeStatic(t);
  160. hashDict [t] = hash.Code;
  161. return hash;
  162. }
  163. static StringHash GetTypeStatic(Type type)
  164. {
  165. var typeStatic = type.GetRuntimeProperty("TypeStatic");
  166. while (typeStatic == null)
  167. {
  168. type = type.GetTypeInfo().BaseType;
  169. if (type == typeof(object))
  170. throw new InvalidOperationException("The type doesn't have static TypeStatic property");
  171. typeStatic = type.GetRuntimeProperty("TypeStatic");
  172. }
  173. return (StringHash)typeStatic.GetValue(null);
  174. }
  175. // for RefCounted, UrhoObjects
  176. internal static void ValidateRefCounted<T>(T obj, [CallerMemberName] string name = "") where T : RefCounted
  177. {
  178. if (IsClosing)
  179. {
  180. var errorText = $"{typeof(T).Name}.{name} (Handle={obj.Handle}) was invoked after Application.Stop";
  181. LogSharp.Error(errorText);
  182. throw new InvalidOperationException(errorText);
  183. }
  184. if (obj.IsDeleted) //IsDeleted is set to True when we receive a native callback from RefCounted::~RefCounted
  185. {
  186. var errorText = $"Underlying native object was deleted for Handle={obj.Handle}. {typeof(T).Name}.{name}";
  187. LogSharp.Error(errorText);
  188. throw new InvalidOperationException(errorText);
  189. }
  190. //if (obj.Handle == IntPtr.Zero)
  191. //{
  192. //}
  193. //TODO: check current thread?
  194. }
  195. // non-RefCounted classes
  196. internal static void ValidateObject<T>(T obj, [CallerMemberName] string name = "") where T : class
  197. {
  198. if (IsClosing)
  199. {
  200. var errorText = $"{typeof(T).Name}.{name} was invoked after Application.Stop";
  201. LogSharp.Error(errorText);
  202. throw new InvalidOperationException(errorText);
  203. }
  204. }
  205. // constructors, static methods, value types
  206. internal static void Validate(Type type, [CallerMemberName] string name = "")
  207. {
  208. if (IsClosing)
  209. {
  210. var errorText = $"{type.Name}.{name} was invoked after Application.Stop";
  211. LogSharp.Error(errorText);
  212. throw new InvalidOperationException(errorText);
  213. }
  214. }
  215. internal static IReadOnlyList<T> CreateVectorSharedPtrProxy<T> (IntPtr handle) where T : UrhoObject
  216. {
  217. return new Vectors.ProxyUrhoObject<T> (handle);
  218. }
  219. internal static IReadOnlyList<T> CreateVectorSharedPtrRefcountedProxy<T>(IntPtr handle) where T : RefCounted
  220. {
  221. return new Vectors.ProxyRefCounted<T>(handle);
  222. }
  223. internal static void Cleanup()
  224. {
  225. IsClosing = true;
  226. RefCountedCache.Clean();
  227. GC.Collect();
  228. GC.WaitForPendingFinalizers();
  229. GC.Collect();
  230. }
  231. // for Debug purposes
  232. static internal int KnownObjectsCount => RefCountedCache.Count;
  233. static Dictionary<string, Type> typesByNativeNames;
  234. // special cases: (TODO: share this code with SharpieBinder somehow)
  235. static Dictionary<string, string> typeNamesMap = new Dictionary<string, string>
  236. {
  237. {nameof(UrhoObject), "Object"},
  238. {nameof(UrhoConsole), "Console"},
  239. {nameof(XmlFile), "XMLFile"},
  240. {nameof(JsonFile), "JSONFile"},
  241. };
  242. static Type FindTypeByName(string name)
  243. {
  244. if (typesByNativeNames == null)
  245. {
  246. typesByNativeNames = new Dictionary<string, Type>(200);
  247. foreach (var type in typeof(Runtime).GetTypeInfo().Assembly.ExportedTypes)
  248. {
  249. if (!type.GetTypeInfo().IsSubclassOf(typeof(RefCounted)))
  250. continue;
  251. string remappedName;
  252. if (!typeNamesMap.TryGetValue(type.Name, out remappedName))
  253. remappedName = type.Name;
  254. typesByNativeNames[remappedName] = type;
  255. }
  256. }
  257. Type result;
  258. if (!typesByNativeNames.TryGetValue(name, out result))
  259. throw new Exception($"Type {name} not found.");
  260. return result;
  261. }
  262. }
  263. }