Object.base.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. using System;
  2. using System.Linq;
  3. using System.Reflection;
  4. using System.Runtime.InteropServices;
  5. using Godot.NativeInterop;
  6. namespace Godot
  7. {
  8. public partial class Object : IDisposable
  9. {
  10. private bool _disposed = false;
  11. private static readonly Type CachedType = typeof(Object);
  12. internal IntPtr NativePtr;
  13. private bool _memoryOwn;
  14. private WeakReference<Object> _weakReferenceToSelf;
  15. /// <summary>
  16. /// Constructs a new <see cref="Object"/>.
  17. /// </summary>
  18. public Object() : this(false)
  19. {
  20. unsafe
  21. {
  22. _ConstructAndInitialize(NativeCtor, NativeName, CachedType, refCounted: false);
  23. }
  24. }
  25. internal unsafe void _ConstructAndInitialize(
  26. delegate* unmanaged<IntPtr> nativeCtor,
  27. StringName nativeName,
  28. Type cachedType,
  29. bool refCounted
  30. )
  31. {
  32. if (NativePtr == IntPtr.Zero)
  33. {
  34. NativePtr = nativeCtor();
  35. InteropUtils.TieManagedToUnmanaged(this, NativePtr,
  36. nativeName, refCounted, GetType(), cachedType);
  37. }
  38. else
  39. {
  40. InteropUtils.TieManagedToUnmanagedWithPreSetup(this, NativePtr,
  41. GetType(), cachedType);
  42. }
  43. _weakReferenceToSelf = DisposablesTracker.RegisterGodotObject(this);
  44. _InitializeGodotScriptInstanceInternals();
  45. }
  46. internal void _InitializeGodotScriptInstanceInternals()
  47. {
  48. // Performance is not critical here as this will be replaced with source generators.
  49. Type top = GetType();
  50. Type native = InternalGetClassNativeBase(top);
  51. while (top != null && top != native)
  52. {
  53. foreach (var eventSignal in top.GetEvents(
  54. BindingFlags.DeclaredOnly | BindingFlags.Instance |
  55. BindingFlags.NonPublic | BindingFlags.Public)
  56. .Where(ev => ev.GetCustomAttributes().OfType<SignalAttribute>().Any()))
  57. {
  58. using var eventSignalName = new StringName(eventSignal.Name);
  59. var eventSignalNameSelf = (godot_string_name)eventSignalName.NativeValue;
  60. NativeFuncs.godotsharp_internal_object_connect_event_signal(NativePtr, eventSignalNameSelf);
  61. }
  62. top = top.BaseType;
  63. }
  64. }
  65. internal Object(bool memoryOwn)
  66. {
  67. _memoryOwn = memoryOwn;
  68. }
  69. /// <summary>
  70. /// The pointer to the native instance of this <see cref="Object"/>.
  71. /// </summary>
  72. public IntPtr NativeInstance => NativePtr;
  73. internal static IntPtr GetPtr(Object instance)
  74. {
  75. if (instance == null)
  76. return IntPtr.Zero;
  77. // We check if NativePtr is null because this may be called by the debugger.
  78. // If the debugger puts a breakpoint in one of the base constructors, before
  79. // NativePtr is assigned, that would result in UB or crashes when calling
  80. // native functions that receive the pointer, which can happen because the
  81. // debugger calls ToString() and tries to get the value of properties.
  82. if (instance._disposed || instance.NativePtr == IntPtr.Zero)
  83. throw new ObjectDisposedException(instance.GetType().FullName);
  84. return instance.NativePtr;
  85. }
  86. ~Object()
  87. {
  88. Dispose(false);
  89. }
  90. /// <summary>
  91. /// Disposes of this <see cref="Object"/>.
  92. /// </summary>
  93. public void Dispose()
  94. {
  95. Dispose(true);
  96. GC.SuppressFinalize(this);
  97. }
  98. /// <summary>
  99. /// Disposes implementation of this <see cref="Object"/>.
  100. /// </summary>
  101. protected virtual void Dispose(bool disposing)
  102. {
  103. if (_disposed)
  104. return;
  105. _disposed = true;
  106. if (NativePtr != IntPtr.Zero)
  107. {
  108. IntPtr gcHandleToFree = NativeFuncs.godotsharp_internal_object_get_associated_gchandle(NativePtr);
  109. if (gcHandleToFree != IntPtr.Zero)
  110. {
  111. object target = GCHandle.FromIntPtr(gcHandleToFree).Target;
  112. // The GC handle may have been replaced in another thread. Release it only if
  113. // it's associated to this managed instance, or if the target is no longer alive.
  114. if (target != this && target != null)
  115. gcHandleToFree = IntPtr.Zero;
  116. }
  117. if (_memoryOwn)
  118. {
  119. NativeFuncs.godotsharp_internal_refcounted_disposed(NativePtr, gcHandleToFree,
  120. (!disposing).ToGodotBool());
  121. }
  122. else
  123. {
  124. NativeFuncs.godotsharp_internal_object_disposed(NativePtr, gcHandleToFree);
  125. }
  126. NativePtr = IntPtr.Zero;
  127. }
  128. DisposablesTracker.UnregisterGodotObject(_weakReferenceToSelf);
  129. }
  130. /// <summary>
  131. /// Converts this <see cref="Object"/> to a string.
  132. /// </summary>
  133. /// <returns>A string representation of this object.</returns>
  134. public override string ToString()
  135. {
  136. NativeFuncs.godotsharp_object_to_string(GetPtr(this), out godot_string str);
  137. using (str)
  138. return Marshaling.ConvertStringToManaged(str);
  139. }
  140. /// <summary>
  141. /// Returns a new <see cref="SignalAwaiter"/> awaiter configured to complete when the instance
  142. /// <paramref name="source"/> emits the signal specified by the <paramref name="signal"/> parameter.
  143. /// </summary>
  144. /// <param name="source">
  145. /// The instance the awaiter will be listening to.
  146. /// </param>
  147. /// <param name="signal">
  148. /// The signal the awaiter will be waiting for.
  149. /// </param>
  150. /// <example>
  151. /// This sample prints a message once every frame up to 100 times.
  152. /// <code>
  153. /// public override void _Ready()
  154. /// {
  155. /// for (int i = 0; i &lt; 100; i++)
  156. /// {
  157. /// await ToSignal(GetTree(), "process_frame");
  158. /// GD.Print($"Frame {i}");
  159. /// }
  160. /// }
  161. /// </code>
  162. /// </example>
  163. /// <returns>
  164. /// A <see cref="SignalAwaiter"/> that completes when
  165. /// <paramref name="source"/> emits the <paramref name="signal"/>.
  166. /// </returns>
  167. public SignalAwaiter ToSignal(Object source, StringName signal)
  168. {
  169. return new SignalAwaiter(source, signal, this);
  170. }
  171. internal static Type InternalGetClassNativeBase(Type t)
  172. {
  173. do
  174. {
  175. var assemblyName = t.Assembly.GetName();
  176. if (assemblyName.Name == "GodotSharp")
  177. return t;
  178. if (assemblyName.Name == "GodotSharpEditor")
  179. return t;
  180. } while ((t = t.BaseType) != null);
  181. return null;
  182. }
  183. internal static bool InternalIsClassNativeBase(Type t)
  184. {
  185. // Check whether the type is declared in the GodotSharp or GodotSharpEditor assemblies
  186. var typeAssembly = t.Assembly;
  187. if (typeAssembly == CachedType.Assembly)
  188. return true;
  189. var typeAssemblyName = t.Assembly.GetName();
  190. return typeAssemblyName.Name == "GodotSharpEditor";
  191. }
  192. // ReSharper disable once VirtualMemberNeverOverridden.Global
  193. protected internal virtual bool SetGodotClassPropertyValue(in godot_string_name name, in godot_variant value)
  194. {
  195. return false;
  196. }
  197. // ReSharper disable once VirtualMemberNeverOverridden.Global
  198. protected internal virtual bool GetGodotClassPropertyValue(in godot_string_name name, out godot_variant value)
  199. {
  200. value = default;
  201. return false;
  202. }
  203. internal void InternalRaiseEventSignal(in godot_string_name eventSignalName, NativeVariantPtrArgs args,
  204. int argc)
  205. {
  206. // Performance is not critical here as this will be replaced with source generators.
  207. using var stringName = StringName.CreateTakingOwnershipOfDisposableValue(
  208. NativeFuncs.godotsharp_string_name_new_copy(eventSignalName));
  209. string eventSignalNameStr = stringName.ToString();
  210. Type top = GetType();
  211. Type native = InternalGetClassNativeBase(top);
  212. while (top != null && top != native)
  213. {
  214. var foundEventSignals = top.GetEvents(
  215. BindingFlags.DeclaredOnly | BindingFlags.Instance |
  216. BindingFlags.NonPublic | BindingFlags.Public)
  217. .Where(ev => ev.GetCustomAttributes().OfType<SignalAttribute>().Any())
  218. .Select(ev => ev.Name);
  219. var fields = top.GetFields(
  220. BindingFlags.DeclaredOnly | BindingFlags.Instance |
  221. BindingFlags.NonPublic | BindingFlags.Public);
  222. var eventSignalField = fields
  223. .Where(f => typeof(Delegate).IsAssignableFrom(f.FieldType))
  224. .Where(f => foundEventSignals.Contains(f.Name))
  225. .FirstOrDefault(f => f.Name == eventSignalNameStr);
  226. if (eventSignalField != null)
  227. {
  228. var @delegate = (Delegate)eventSignalField.GetValue(this);
  229. if (@delegate == null)
  230. continue;
  231. var delegateType = eventSignalField.FieldType;
  232. var invokeMethod = delegateType.GetMethod("Invoke");
  233. if (invokeMethod == null)
  234. throw new MissingMethodException(delegateType.FullName, "Invoke");
  235. var parameterInfos = invokeMethod.GetParameters();
  236. var paramsLength = parameterInfos.Length;
  237. if (argc != paramsLength)
  238. {
  239. throw new InvalidOperationException(
  240. $"The event delegate expects {paramsLength} arguments, but received {argc}.");
  241. }
  242. var managedArgs = new object[argc];
  243. for (int i = 0; i < argc; i++)
  244. {
  245. managedArgs[i] = Marshaling.ConvertVariantToManagedObjectOfType(
  246. args[i], parameterInfos[i].ParameterType);
  247. }
  248. invokeMethod.Invoke(@delegate, managedArgs);
  249. return;
  250. }
  251. top = top.BaseType;
  252. }
  253. }
  254. internal static IntPtr ClassDB_get_method(StringName type, StringName method)
  255. {
  256. var typeSelf = (godot_string_name)type.NativeValue;
  257. var methodSelf = (godot_string_name)method.NativeValue;
  258. IntPtr methodBind = NativeFuncs.godotsharp_method_bind_get_method(typeSelf, methodSelf);
  259. if (methodBind == IntPtr.Zero)
  260. throw new NativeMethodBindNotFoundException(type + "." + method);
  261. return methodBind;
  262. }
  263. internal static unsafe delegate* unmanaged<IntPtr> ClassDB_get_constructor(StringName type)
  264. {
  265. // for some reason the '??' operator doesn't support 'delegate*'
  266. var typeSelf = (godot_string_name)type.NativeValue;
  267. var nativeConstructor = NativeFuncs.godotsharp_get_class_constructor(typeSelf);
  268. if (nativeConstructor == null)
  269. throw new NativeConstructorNotFoundException(type);
  270. return nativeConstructor;
  271. }
  272. }
  273. }