123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303 |
- using System;
- using System.Linq;
- using System.Reflection;
- using Godot.NativeInterop;
- namespace Godot
- {
- public partial class Object : IDisposable
- {
- private bool _disposed = false;
- private Type _cachedType = typeof(Object);
- internal IntPtr NativePtr;
- private bool _memoryOwn;
- private WeakReference<Object> _weakReferenceToSelf;
- /// <summary>
- /// Constructs a new <see cref="Object"/>.
- /// </summary>
- public Object() : this(false)
- {
- if (NativePtr == IntPtr.Zero)
- {
- unsafe
- {
- NativePtr = NativeCtor();
- }
- InteropUtils.TieManagedToUnmanaged(this, NativePtr,
- NativeName, refCounted: false, GetType(), _cachedType);
- }
- else
- {
- InteropUtils.TieManagedToUnmanagedWithPreSetup(this, NativePtr,
- GetType(), _cachedType);
- }
- _weakReferenceToSelf = DisposablesTracker.RegisterGodotObject(this);
- _InitializeGodotScriptInstanceInternals();
- }
- internal void _InitializeGodotScriptInstanceInternals()
- {
- // Performance is not critical here as this will be replaced with source generators.
- Type top = GetType();
- Type native = InternalGetClassNativeBase(top);
- while (top != null && top != native)
- {
- foreach (var eventSignal in top.GetEvents(
- BindingFlags.DeclaredOnly | BindingFlags.Instance |
- BindingFlags.NonPublic | BindingFlags.Public)
- .Where(ev => ev.GetCustomAttributes().OfType<SignalAttribute>().Any()))
- {
- using var eventSignalName = new StringName(eventSignal.Name);
- var eventSignalNameSelf = (godot_string_name)eventSignalName.NativeValue;
- NativeFuncs.godotsharp_internal_object_connect_event_signal(NativePtr, eventSignalNameSelf);
- }
- top = top.BaseType;
- }
- }
- internal Object(bool memoryOwn)
- {
- _memoryOwn = memoryOwn;
- }
- /// <summary>
- /// The pointer to the native instance of this <see cref="Object"/>.
- /// </summary>
- public IntPtr NativeInstance => NativePtr;
- internal static IntPtr GetPtr(Object instance)
- {
- if (instance == null)
- return IntPtr.Zero;
- // We check if NativePtr is null because this may be called by the debugger.
- // If the debugger puts a breakpoint in one of the base constructors, before
- // NativePtr is assigned, that would result in UB or crashes when calling
- // native functions that receive the pointer, which can happen because the
- // debugger calls ToString() and tries to get the value of properties.
- if (instance._disposed || instance.NativePtr == IntPtr.Zero)
- throw new ObjectDisposedException(instance.GetType().FullName);
- return instance.NativePtr;
- }
- ~Object()
- {
- Dispose(false);
- }
- /// <summary>
- /// Disposes of this <see cref="Object"/>.
- /// </summary>
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
- /// <summary>
- /// Disposes implementation of this <see cref="Object"/>.
- /// </summary>
- protected virtual void Dispose(bool disposing)
- {
- if (_disposed)
- return;
- if (NativePtr != IntPtr.Zero)
- {
- if (_memoryOwn)
- {
- NativeFuncs.godotsharp_internal_refcounted_disposed(NativePtr, (!disposing).ToGodotBool());
- }
- else
- {
- NativeFuncs.godotsharp_internal_object_disposed(NativePtr);
- }
- NativePtr = IntPtr.Zero;
- }
- DisposablesTracker.UnregisterGodotObject(_weakReferenceToSelf);
- _disposed = true;
- }
- /// <summary>
- /// Converts this <see cref="Object"/> to a string.
- /// </summary>
- /// <returns>A string representation of this object.</returns>
- public override string ToString()
- {
- NativeFuncs.godotsharp_object_to_string(GetPtr(this), out godot_string str);
- using (str)
- return Marshaling.ConvertStringToManaged(str);
- }
- /// <summary>
- /// Returns a new <see cref="SignalAwaiter"/> awaiter configured to complete when the instance
- /// <paramref name="source"/> emits the signal specified by the <paramref name="signal"/> parameter.
- /// </summary>
- /// <param name="source">
- /// The instance the awaiter will be listening to.
- /// </param>
- /// <param name="signal">
- /// The signal the awaiter will be waiting for.
- /// </param>
- /// <example>
- /// This sample prints a message once every frame up to 100 times.
- /// <code>
- /// public override void _Ready()
- /// {
- /// for (int i = 0; i < 100; i++)
- /// {
- /// await ToSignal(GetTree(), "process_frame");
- /// GD.Print($"Frame {i}");
- /// }
- /// }
- /// </code>
- /// </example>
- /// <returns>
- /// A <see cref="SignalAwaiter"/> that completes when
- /// <paramref name="source"/> emits the <paramref name="signal"/>.
- /// </returns>
- public SignalAwaiter ToSignal(Object source, StringName signal)
- {
- return new SignalAwaiter(source, signal, this);
- }
- internal static Type InternalGetClassNativeBase(Type t)
- {
- do
- {
- var assemblyName = t.Assembly.GetName();
- if (assemblyName.Name == "GodotSharp")
- return t;
- if (assemblyName.Name == "GodotSharpEditor")
- return t;
- } while ((t = t.BaseType) != null);
- return null;
- }
- internal static bool InternalIsClassNativeBase(Type t)
- {
- var assemblyName = t.Assembly.GetName();
- return assemblyName.Name == "GodotSharp" || assemblyName.Name == "GodotSharpEditor";
- }
- // ReSharper disable once VirtualMemberNeverOverridden.Global
- protected internal virtual bool SetGodotClassPropertyValue(in godot_string_name name, in godot_variant value)
- {
- return false;
- }
- // ReSharper disable once VirtualMemberNeverOverridden.Global
- protected internal virtual bool GetGodotClassPropertyValue(in godot_string_name name, out godot_variant value)
- {
- value = default;
- return false;
- }
- internal void InternalRaiseEventSignal(in godot_string_name eventSignalName, NativeVariantPtrArgs args,
- int argc)
- {
- // Performance is not critical here as this will be replaced with source generators.
- using var stringName = StringName.CreateTakingOwnershipOfDisposableValue(
- NativeFuncs.godotsharp_string_name_new_copy(eventSignalName));
- string eventSignalNameStr = stringName.ToString();
- Type top = GetType();
- Type native = InternalGetClassNativeBase(top);
- while (top != null && top != native)
- {
- var foundEventSignals = top.GetEvents(
- BindingFlags.DeclaredOnly | BindingFlags.Instance |
- BindingFlags.NonPublic | BindingFlags.Public)
- .Where(ev => ev.GetCustomAttributes().OfType<SignalAttribute>().Any())
- .Select(ev => ev.Name);
- var fields = top.GetFields(
- BindingFlags.DeclaredOnly | BindingFlags.Instance |
- BindingFlags.NonPublic | BindingFlags.Public);
- var eventSignalField = fields
- .Where(f => typeof(Delegate).IsAssignableFrom(f.FieldType))
- .Where(f => foundEventSignals.Contains(f.Name))
- .FirstOrDefault(f => f.Name == eventSignalNameStr);
- if (eventSignalField != null)
- {
- var @delegate = (Delegate)eventSignalField.GetValue(this);
- if (@delegate == null)
- continue;
- var delegateType = eventSignalField.FieldType;
- var invokeMethod = delegateType.GetMethod("Invoke");
- if (invokeMethod == null)
- throw new MissingMethodException(delegateType.FullName, "Invoke");
- var parameterInfos = invokeMethod.GetParameters();
- var paramsLength = parameterInfos.Length;
- if (argc != paramsLength)
- {
- throw new InvalidOperationException(
- $"The event delegate expects {paramsLength} arguments, but received {argc}.");
- }
- var managedArgs = new object[argc];
- for (int i = 0; i < argc; i++)
- {
- managedArgs[i] = Marshaling.ConvertVariantToManagedObjectOfType(
- args[i], parameterInfos[i].ParameterType);
- }
- invokeMethod.Invoke(@delegate, managedArgs);
- return;
- }
- top = top.BaseType;
- }
- }
- internal static IntPtr ClassDB_get_method(StringName type, StringName method)
- {
- var typeSelf = (godot_string_name)type.NativeValue;
- var methodSelf = (godot_string_name)method.NativeValue;
- IntPtr methodBind = NativeFuncs.godotsharp_method_bind_get_method(typeSelf, methodSelf);
- if (methodBind == IntPtr.Zero)
- throw new NativeMethodBindNotFoundException(type + "." + method);
- return methodBind;
- }
- internal static unsafe delegate* unmanaged<IntPtr> ClassDB_get_constructor(StringName type)
- {
- // for some reason the '??' operator doesn't support 'delegate*'
- var typeSelf = (godot_string_name)type.NativeValue;
- var nativeConstructor = NativeFuncs.godotsharp_get_class_constructor(typeSelf);
- if (nativeConstructor == null)
- throw new NativeConstructorNotFoundException(type);
- return nativeConstructor;
- }
- }
- }
|