NativeCore.cs 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Runtime.InteropServices;
  4. namespace AtomicEngine
  5. {
  6. public class NativeType
  7. {
  8. public Type Type => type;
  9. public NativeType(IntPtr nativeClassID, Type type, Func<IntPtr, RefCounted> managedConstructor)
  10. {
  11. this.nativeClassID = nativeClassID;
  12. this.type = type;
  13. this.managedConstructor = managedConstructor;
  14. NativeCore.RegisterNativeType(this);
  15. }
  16. internal Type type;
  17. internal IntPtr nativeClassID;
  18. internal Func<IntPtr, RefCounted> managedConstructor;
  19. }
  20. public static class NativeCore
  21. {
  22. static internal void SubscribeToEvent(AObject receiver, uint eventType)
  23. {
  24. List<WeakReference> eventReceivers;
  25. if (!eventReceiverLookup.TryGetValue(eventType, out eventReceivers))
  26. {
  27. eventReceivers = eventReceiverLookup[eventType] = new List<WeakReference>();
  28. }
  29. foreach (WeakReference wr in eventReceivers)
  30. {
  31. // GC'd?
  32. if (!wr.IsAlive)
  33. continue;
  34. // already on list?
  35. if (((AObject)wr.Target) == receiver)
  36. return;
  37. }
  38. WeakReference w = null;
  39. if (!nativeLookup.TryGetValue(receiver.nativeInstance, out w))
  40. {
  41. throw new System.InvalidOperationException("NativeCore.SubscribeToEvent - unable to find native instance");
  42. }
  43. if (!w.IsAlive)
  44. {
  45. throw new System.InvalidOperationException("NativeCore.SubscribeToEvent - attempting to subscribe a GC'd AObject");
  46. }
  47. eventReceivers.Add(w);
  48. }
  49. static ScriptVariantMap[] svm;
  50. static int svmDepth = 0;
  51. const int svmMax = 256;
  52. internal static void Initialize()
  53. {
  54. // preallocate script variant maps
  55. svm = new ScriptVariantMap[svmMax];
  56. for (int i = 0; i < svmMax; i++)
  57. svm[i] = new ScriptVariantMap();
  58. }
  59. public static void EventDispatch(uint eventType, IntPtr eventData)
  60. {
  61. List<WeakReference> eventReceivers;
  62. if (!eventReceiverLookup.TryGetValue(eventType, out eventReceivers))
  63. {
  64. // This should not happen, as event NET objects are subscribed to are filtered
  65. throw new System.InvalidOperationException("NativeCore.EventDispatch - received unregistered event type");
  66. }
  67. ScriptVariantMap scriptMap = null;
  68. foreach (var w in eventReceivers)
  69. {
  70. // GC'd?
  71. if (!w.IsAlive)
  72. continue;
  73. if (scriptMap == null)
  74. {
  75. if (svmDepth == svmMax)
  76. {
  77. throw new System.InvalidOperationException("NativeCore.EventDispatch - exceeded max svm");
  78. }
  79. scriptMap = svm[svmDepth++];
  80. scriptMap.CopyVariantMap(eventData);
  81. }
  82. ((AObject)w.Target).HandleEvent(eventType, scriptMap);
  83. }
  84. if (scriptMap != null)
  85. svmDepth--;
  86. }
  87. // register a newly created native
  88. public static IntPtr RegisterNative(IntPtr native, RefCounted r)
  89. {
  90. if (nativeLookup.ContainsKey(native))
  91. {
  92. throw new System.InvalidOperationException("NativeCore.RegisterNative - Duplicate IntPtr key");
  93. }
  94. r.nativeInstance = native;
  95. var w = new WeakReference(r);
  96. nativeLookup[native] = w;
  97. // keep native side alive
  98. r.AddRef();
  99. return native;
  100. }
  101. // wraps an existing native instance, with downcast support
  102. public static T WrapNative<T>(IntPtr native) where T : RefCounted
  103. {
  104. if (native == IntPtr.Zero)
  105. return null;
  106. WeakReference w;
  107. // first see if we're already available
  108. if (nativeLookup.TryGetValue(native, out w))
  109. {
  110. if (w.IsAlive)
  111. {
  112. // we're alive!
  113. return (T)w.Target;
  114. }
  115. else
  116. {
  117. // we were seen before, but have since been GC'd, remove!
  118. nativeLookup.Remove(native);
  119. csb_AtomicEngine_ReleaseRef(native);
  120. }
  121. }
  122. IntPtr classID = RefCounted.csb_Atomic_RefCounted_GetClassID(native);
  123. // and store, with downcast support for instance Component -> StaticModel
  124. // we never want to hit this path for script inherited natives
  125. NativeType nativeType;
  126. if (!nativeClassIDToNativeType.TryGetValue(classID, out nativeType))
  127. {
  128. throw new System.InvalidOperationException("NativeCore.WrapNative - Attempting to wrap unknown native class id");
  129. }
  130. RefCounted r = nativeType.managedConstructor(native);
  131. w = new WeakReference(r);
  132. NativeCore.nativeLookup[native] = w;
  133. // store a ref, so native side will not be released while we still have a reference in managed code
  134. r.AddRef();
  135. return (T)r;
  136. }
  137. public static void RegisterNativeType(NativeType nativeType)
  138. {
  139. if (nativeClassIDToNativeType.ContainsKey(nativeType.nativeClassID))
  140. {
  141. throw new System.InvalidOperationException("NativeCore.RegisterNativeType - Duplicate NativeType class id registered");
  142. }
  143. if (typeToNativeType.ContainsKey(nativeType.type))
  144. {
  145. throw new System.InvalidOperationException("NativeCore.RegisterNativeType - Duplicate NativeType type registered");
  146. }
  147. nativeClassIDToNativeType[nativeType.nativeClassID] = nativeType;
  148. typeToNativeType[nativeType.type] = nativeType;
  149. }
  150. static public IntPtr NativeContructorOverride
  151. {
  152. get
  153. {
  154. IntPtr value = nativeContructorOverride;
  155. nativeContructorOverride = IntPtr.Zero;
  156. return value;
  157. }
  158. set
  159. {
  160. if (nativeContructorOverride != IntPtr.Zero)
  161. {
  162. throw new System.InvalidOperationException("NativeCore.NativeContructorOverride - Previous nativeContructorOverride not consumed");
  163. }
  164. nativeContructorOverride = value;
  165. }
  166. }
  167. static public void VerifyNativeContructorOverrideConsumed()
  168. {
  169. if (nativeContructorOverride != IntPtr.Zero)
  170. {
  171. throw new System.InvalidOperationException("NativeCore.VerifyNativeContructorOverrideConsumed - NativeContructorOverride not consumed");
  172. }
  173. }
  174. private static IntPtr nativeContructorOverride = IntPtr.Zero;
  175. // weak references here, hold a ref native side
  176. internal static Dictionary<IntPtr, WeakReference> nativeLookup = new Dictionary<IntPtr, WeakReference>();
  177. // Native ClassID to NativeType lookup
  178. internal static Dictionary<IntPtr, NativeType> nativeClassIDToNativeType = new Dictionary<IntPtr, NativeType>();
  179. // weak references here, hold a ref native side
  180. internal static Dictionary<uint, List<WeakReference>> eventReceiverLookup = new Dictionary<uint, List<WeakReference>>();
  181. // Managed Type to NativeType lookup
  182. internal static Dictionary<Type, NativeType> typeToNativeType = new Dictionary<Type, NativeType>();
  183. [DllImport(Constants.LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
  184. private static extern void csb_AtomicEngine_ReleaseRef(IntPtr refCounted);
  185. }
  186. }