From 66a89c7925ef8d80d1a774bc1c818c70f170bf21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Rold=C3=A1n=20Etcheverry?= Date: Sun, 12 Sep 2021 19:49:23 +0200 Subject: [PATCH 1/6] C#: Remove DynamicGodotObject/Object.DynamicObject We are moving in the direction of no dynamic code generation, so this is no longer desired. The feature can still be easily implemented by any project that still want it. --- .../GodotSharp/Core/DynamicObject.cs | 212 ------------------ .../GodotSharp/GodotSharp/Core/Object.base.cs | 5 - .../GodotSharp/GodotSharp/GodotSharp.csproj | 1 - modules/mono/glue/arguments_vector.h | 67 ------ modules/mono/glue/base_object_glue.cpp | 66 ------ 5 files changed, 351 deletions(-) delete mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/DynamicObject.cs delete mode 100644 modules/mono/glue/arguments_vector.h diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DynamicObject.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DynamicObject.cs deleted file mode 100644 index 0c21bcaa3f..0000000000 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DynamicObject.cs +++ /dev/null @@ -1,212 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Dynamic; -using System.Linq.Expressions; -using System.Runtime.CompilerServices; - -namespace Godot -{ - /// - /// Represents an whose members can be dynamically accessed at runtime through the Variant API. - /// - /// - /// - /// The class enables access to the Variant - /// members of a instance at runtime. - /// - /// - /// This allows accessing the class members using their original names in the engine as well as the members from the - /// script attached to the , regardless of the scripting language it was written in. - /// - /// - /// - /// This sample shows how to use to dynamically access the engine members of a . - /// - /// dynamic sprite = GetNode("Sprite2D").DynamicGodotObject; - /// sprite.add_child(this); - /// - /// if ((sprite.hframes * sprite.vframes) > 0) - /// sprite.frame = 0; - /// - /// - /// - /// This sample shows how to use to dynamically access the members of the script attached to a . - /// - /// dynamic childNode = GetNode("ChildNode").DynamicGodotObject; - /// - /// if (childNode.print_allowed) - /// { - /// childNode.message = "Hello from C#"; - /// childNode.print_message(3); - /// } - /// - /// The ChildNode node has the following GDScript script attached: - /// - /// // # ChildNode.gd - /// // var print_allowed = true - /// // var message = "" - /// // - /// // func print_message(times): - /// // for i in times: - /// // print(message) - /// - /// - public class DynamicGodotObject : DynamicObject - { - /// - /// Gets the associated with this . - /// - public Object Value { get; } - - /// - /// Initializes a new instance of the class. - /// - /// - /// The that will be associated with this . - /// - /// - /// Thrown when the parameter is null. - /// - public DynamicGodotObject(Object godotObject) - { - if (godotObject == null) - throw new ArgumentNullException(nameof(godotObject)); - - this.Value = godotObject; - } - - public override IEnumerable GetDynamicMemberNames() - { - return godot_icall_DynamicGodotObject_SetMemberList(Object.GetPtr(Value)); - } - - public override bool TryBinaryOperation(BinaryOperationBinder binder, object arg, out object result) - { - switch (binder.Operation) - { - case ExpressionType.Equal: - case ExpressionType.NotEqual: - if (binder.ReturnType == typeof(bool) || binder.ReturnType.IsAssignableFrom(typeof(bool))) - { - if (arg == null) - { - bool boolResult = Object.IsInstanceValid(Value); - - if (binder.Operation == ExpressionType.Equal) - boolResult = !boolResult; - - result = boolResult; - return true; - } - - if (arg is Object other) - { - bool boolResult = (Value == other); - - if (binder.Operation == ExpressionType.NotEqual) - boolResult = !boolResult; - - result = boolResult; - return true; - } - } - - break; - default: - // We're not implementing operators <, <=, >, and >= (LessThan, LessThanOrEqual, GreaterThan, GreaterThanOrEqual). - // These are used on the actual pointers in variant_op.cpp. It's better to let the user do that explicitly. - break; - } - - return base.TryBinaryOperation(binder, arg, out result); - } - - public override bool TryConvert(ConvertBinder binder, out object result) - { - if (binder.Type == typeof(Object)) - { - result = Value; - return true; - } - - if (typeof(Object).IsAssignableFrom(binder.Type)) - { - // Throws InvalidCastException when the cast fails - result = Convert.ChangeType(Value, binder.Type); - return true; - } - - return base.TryConvert(binder, out result); - } - - public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) - { - if (indexes.Length == 1) - { - if (indexes[0] is string name) - { - return godot_icall_DynamicGodotObject_GetMember(Object.GetPtr(Value), name, out result); - } - } - - return base.TryGetIndex(binder, indexes, out result); - } - - public override bool TryGetMember(GetMemberBinder binder, out object result) - { - return godot_icall_DynamicGodotObject_GetMember(Object.GetPtr(Value), binder.Name, out result); - } - - public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) - { - return godot_icall_DynamicGodotObject_InvokeMember(Object.GetPtr(Value), binder.Name, args, out result); - } - - public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value) - { - if (indexes.Length == 1) - { - if (indexes[0] is string name) - { - return godot_icall_DynamicGodotObject_SetMember(Object.GetPtr(Value), name, value); - } - } - - return base.TrySetIndex(binder, indexes, value); - } - - public override bool TrySetMember(SetMemberBinder binder, object value) - { - return godot_icall_DynamicGodotObject_SetMember(Object.GetPtr(Value), binder.Name, value); - } - - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static string[] godot_icall_DynamicGodotObject_SetMemberList(IntPtr godotObject); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static bool godot_icall_DynamicGodotObject_InvokeMember(IntPtr godotObject, string name, object[] args, out object result); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static bool godot_icall_DynamicGodotObject_GetMember(IntPtr godotObject, string name, out object result); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static bool godot_icall_DynamicGodotObject_SetMember(IntPtr godotObject, string name, object value); - - #region We don't override these methods - - // Looks like this is not usable from C# - //public override bool TryCreateInstance(CreateInstanceBinder binder, object[] args, out object result); - - // Object members cannot be deleted - //public override bool TryDeleteIndex(DeleteIndexBinder binder, object[] indexes); - //public override bool TryDeleteMember(DeleteMemberBinder binder); - - // Invocation on the object itself, e.g.: obj(param) - //public override bool TryInvoke(InvokeBinder binder, object[] args, out object result); - - // No unnary operations to handle - //public override bool TryUnaryOperation(UnaryOperationBinder binder, out object result); - - #endregion - } -} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs index 8587748d03..a52707edfb 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs @@ -118,11 +118,6 @@ namespace Godot return new SignalAwaiter(source, signal, this); } - /// - /// Gets a new associated with this instance. - /// - public dynamic DynamicObject => new DynamicGodotObject(this); - internal static unsafe IntPtr ClassDB_get_method(StringName type, string method) { IntPtr methodBind; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj index 6cd0539ac4..7cafa1ce07 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj @@ -39,7 +39,6 @@ - diff --git a/modules/mono/glue/arguments_vector.h b/modules/mono/glue/arguments_vector.h deleted file mode 100644 index 9ba6a05ac6..0000000000 --- a/modules/mono/glue/arguments_vector.h +++ /dev/null @@ -1,67 +0,0 @@ -/*************************************************************************/ -/* arguments_vector.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef ARGUMENTS_VECTOR_H -#define ARGUMENTS_VECTOR_H - -#include "core/os/memory.h" - -template -struct ArgumentsVector { -private: - T pool[POOL_SIZE]; - T *_ptr; - int size; - - ArgumentsVector() = delete; - ArgumentsVector(const ArgumentsVector &) = delete; - -public: - T *ptr() { return _ptr; } - T &get(int p_idx) { return _ptr[p_idx]; } - void set(int p_idx, const T &p_value) { _ptr[p_idx] = p_value; } - - explicit ArgumentsVector(int p_size) : - size(p_size) { - if (p_size <= POOL_SIZE) { - _ptr = pool; - } else { - _ptr = memnew_arr(T, p_size); - } - } - - ~ArgumentsVector() { - if (size > POOL_SIZE) { - memdelete_arr(_ptr); - } - } -}; - -#endif // ARGUMENTS_VECTOR_H diff --git a/modules/mono/glue/base_object_glue.cpp b/modules/mono/glue/base_object_glue.cpp index 40886ddbea..9d153bdbf8 100644 --- a/modules/mono/glue/base_object_glue.cpp +++ b/modules/mono/glue/base_object_glue.cpp @@ -39,7 +39,6 @@ #include "../mono_gd/gd_mono_marshal.h" #include "../mono_gd/gd_mono_utils.h" #include "../signal_awaiter_utils.h" -#include "arguments_vector.h" void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr) { #ifdef DEBUG_ENABLED @@ -154,67 +153,6 @@ int32_t godot_icall_SignalAwaiter_connect(Object *p_source, StringName *p_signal return (int32_t)gd_mono_connect_signal_awaiter(p_source, signal, p_target, p_awaiter); } -MonoArray *godot_icall_DynamicGodotObject_SetMemberList(Object *p_ptr) { - List property_list; - p_ptr->get_property_list(&property_list); - - MonoArray *result = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(String), property_list.size()); - - int i = 0; - for (const PropertyInfo &E : property_list) { - MonoString *boxed = GDMonoMarshal::mono_string_from_godot(E.name); - mono_array_setref(result, i, boxed); - i++; - } - - return result; -} - -MonoBoolean godot_icall_DynamicGodotObject_InvokeMember(Object *p_ptr, MonoString *p_name, MonoArray *p_args, MonoObject **r_result) { - String name = GDMonoMarshal::mono_string_to_godot(p_name); - - int argc = mono_array_length(p_args); - - ArgumentsVector arg_store(argc); - ArgumentsVector args(argc); - - for (int i = 0; i < argc; i++) { - MonoObject *elem = mono_array_get(p_args, MonoObject *, i); - arg_store.set(i, GDMonoMarshal::mono_object_to_variant(elem)); - args.set(i, &arg_store.get(i)); - } - - Callable::CallError error; - Variant result = p_ptr->call(StringName(name), args.ptr(), argc, error); - - *r_result = GDMonoMarshal::variant_to_mono_object(result); - - return error.error == Callable::CallError::CALL_OK; -} - -MonoBoolean godot_icall_DynamicGodotObject_GetMember(Object *p_ptr, MonoString *p_name, MonoObject **r_result) { - String name = GDMonoMarshal::mono_string_to_godot(p_name); - - bool valid; - Variant value = p_ptr->get(StringName(name), &valid); - - if (valid) { - *r_result = GDMonoMarshal::variant_to_mono_object(value); - } - - return valid; -} - -MonoBoolean godot_icall_DynamicGodotObject_SetMember(Object *p_ptr, MonoString *p_name, MonoObject *p_value) { - String name = GDMonoMarshal::mono_string_to_godot(p_name); - Variant value = GDMonoMarshal::mono_object_to_variant(p_value); - - bool valid; - p_ptr->set(StringName(name), value, &valid); - - return valid; -} - MonoString *godot_icall_Object_ToString(Object *p_ptr) { #ifdef DEBUG_ENABLED // Cannot happen in C#; would get an ObjectDisposedException instead. @@ -232,8 +170,4 @@ void godot_register_object_icalls() { GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_ToString", godot_icall_Object_ToString); GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_weakref", godot_icall_Object_weakref); GDMonoUtils::add_internal_call("Godot.SignalAwaiter::godot_icall_SignalAwaiter_connect", godot_icall_SignalAwaiter_connect); - GDMonoUtils::add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_SetMemberList", godot_icall_DynamicGodotObject_SetMemberList); - GDMonoUtils::add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_InvokeMember", godot_icall_DynamicGodotObject_InvokeMember); - GDMonoUtils::add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_GetMember", godot_icall_DynamicGodotObject_GetMember); - GDMonoUtils::add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_SetMember", godot_icall_DynamicGodotObject_SetMember); } From bbde1b1f09861c2e1ebabb1af8009286fb72b935 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Rold=C3=A1n=20Etcheverry?= Date: Sun, 12 Sep 2021 19:49:39 +0200 Subject: [PATCH 2/6] C#: Re-write Array, Dictionary, NodePath, String icalls as P/Invoke --- modules/mono/csharp_script.cpp | 2 +- .../glue/GodotSharp/GodotSharp/Core/Array.cs | 222 +++++++------ .../GodotSharp/GodotSharp/Core/Dictionary.cs | 233 ++++++++------ .../Core/NativeInterop/InteropStructs.cs | 121 ++++--- .../Core/NativeInterop/Marshaling.cs | 64 ++-- .../Core/NativeInterop/NativeFuncs.cs | 217 +++++++++++-- .../NativeInterop/NativeFuncs.extended.cs | 14 + .../GodotSharp/GodotSharp/Core/NodePath.cs | 47 +-- .../GodotSharp/Core/StringExtensions.cs | 92 +++--- modules/mono/glue/collections_glue.cpp | 300 ------------------ modules/mono/glue/node_path_glue.cpp | 72 ----- modules/mono/glue/runtime_interop.cpp | 208 +++++++++++- modules/mono/glue/string_glue.cpp | 75 ----- 13 files changed, 839 insertions(+), 828 deletions(-) delete mode 100644 modules/mono/glue/collections_glue.cpp delete mode 100644 modules/mono/glue/node_path_glue.cpp delete mode 100644 modules/mono/glue/string_glue.cpp diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 0d9b02b2e2..409680b8fc 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -106,7 +106,7 @@ Error CSharpLanguage::execute_file(const String &p_path) { return OK; } -extern void *godotsharp_pinvoke_funcs[95]; +extern void *godotsharp_pinvoke_funcs[130]; [[maybe_unused]] volatile void **do_not_strip_godotsharp_pinvoke_funcs; void CSharpLanguage::init() { diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs index 92ace4798c..a75a991d36 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.Collections; using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; using Godot.NativeInterop; namespace Godot.Collections @@ -22,7 +21,7 @@ namespace Godot.Collections /// public Array() { - godot_icall_Array_Ctor(out NativeValue); + NativeValue = NativeFuncs.godotsharp_array_new(); } /// @@ -48,11 +47,15 @@ namespace Godot.Collections public Array(params object[] array) : this() { if (array == null) - { throw new NullReferenceException($"Parameter '{nameof(array)} cannot be null.'"); - } - godot_icall_Array_Ctor_MonoArray(array, out NativeValue); + NativeValue = NativeFuncs.godotsharp_array_new(); + int length = array.Length; + + Resize(length); + + for (int i = 0; i < length; i++) + this[i] = array[i]; } private Array(godot_array nativeValueToOwn) @@ -92,7 +95,7 @@ namespace Godot.Collections public Array Duplicate(bool deep = false) { godot_array newArray; - godot_icall_Array_Duplicate(ref NativeValue, deep, out newArray); + NativeFuncs.godotsharp_array_duplicate(ref NativeValue, deep, out newArray); return CreateTakingOwnershipOfDisposableValue(newArray); } @@ -101,18 +104,12 @@ namespace Godot.Collections /// /// The new size of the array. /// if successful, or an error code. - public Error Resize(int newSize) - { - return godot_icall_Array_Resize(ref NativeValue, newSize); - } + public Error Resize(int newSize) => NativeFuncs.godotsharp_array_resize(ref NativeValue, newSize); /// /// Shuffles the contents of this into a random order. /// - public void Shuffle() - { - godot_icall_Array_Shuffle(ref NativeValue); - } + public void Shuffle() => NativeFuncs.godotsharp_array_shuffle(ref NativeValue); /// /// Concatenates these two s. @@ -122,9 +119,16 @@ namespace Godot.Collections /// A new Godot Array with the contents of both arrays. public static Array operator +(Array left, Array right) { - godot_array newArray; - godot_icall_Array_Concatenate(ref left.NativeValue, ref right.NativeValue, out newArray); - return CreateTakingOwnershipOfDisposableValue(newArray); + int leftCount = left.Count; + int rightCount = right.Count; + + Array newArray = left.Duplicate(deep: false); + newArray.Resize(leftCount + rightCount); + + for (int i = 0; i < rightCount; i++) + newArray[i + leftCount] = right[i]; + + return newArray; } // IList @@ -137,18 +141,20 @@ namespace Godot.Collections /// Returns the object at the given index. /// /// The object at the given index. - public object this[int index] + public unsafe object this[int index] { get { - godot_icall_Array_At(ref NativeValue, index, out godot_variant elem); - unsafe - { - using (elem) - return Marshaling.variant_to_mono_object(&elem); - } + GetVariantBorrowElementAt(index, out godot_variant borrowElem); + return Marshaling.variant_to_mono_object(&borrowElem); + } + set + { + if (index < 0 || index >= Count) + throw new IndexOutOfRangeException(); + godot_variant* ptrw = NativeFuncs.godotsharp_array_ptrw(ref NativeValue); + ptrw[index] = Marshaling.mono_object_to_variant(value); } - set => godot_icall_Array_SetAt(ref NativeValue, index, value); } /// @@ -157,19 +163,23 @@ namespace Godot.Collections /// /// The object to add. /// The new size after adding the object. - public int Add(object value) => godot_icall_Array_Add(ref NativeValue, value); + public unsafe int Add(object value) + { + using godot_variant variantValue = Marshaling.mono_object_to_variant(value); + return NativeFuncs.godotsharp_array_add(ref NativeValue, &variantValue); + } /// /// Checks if this contains the given object. /// /// The item to look for. /// Whether or not this array contains the given object. - public bool Contains(object value) => godot_icall_Array_Contains(ref NativeValue, value); + public bool Contains(object value) => IndexOf(value) != -1; /// /// Erases all items from this . /// - public void Clear() => godot_icall_Array_Clear(ref NativeValue); + public void Clear() => Resize(0); /// /// Searches this for an object @@ -177,7 +187,11 @@ namespace Godot.Collections /// /// The object to search for. /// The index of the object, or -1 if not found. - public int IndexOf(object value) => godot_icall_Array_IndexOf(ref NativeValue, value); + public unsafe int IndexOf(object value) + { + using godot_variant variantValue = Marshaling.mono_object_to_variant(value); + return NativeFuncs.godotsharp_array_index_of(ref NativeValue, &variantValue); + } /// /// Inserts a new object at a given position in the array. @@ -187,20 +201,38 @@ namespace Godot.Collections /// /// The index to insert at. /// The object to insert. - public void Insert(int index, object value) => godot_icall_Array_Insert(ref NativeValue, index, value); + public unsafe void Insert(int index, object value) + { + if (index < 0 || index > Count) + throw new IndexOutOfRangeException(); + + using godot_variant variantValue = Marshaling.mono_object_to_variant(value); + NativeFuncs.godotsharp_array_insert(ref NativeValue, index, &variantValue); + } /// /// Removes the first occurrence of the specified value /// from this . /// /// The value to remove. - public void Remove(object value) => godot_icall_Array_Remove(ref NativeValue, value); + public void Remove(object value) + { + int index = IndexOf(value); + if (index >= 0) + RemoveAt(index); + } /// /// Removes an element from this by index. /// /// The index of the element to remove. - public void RemoveAt(int index) => godot_icall_Array_RemoveAt(ref NativeValue, index); + public void RemoveAt(int index) + { + if (index < 0 || index > Count) + throw new IndexOutOfRangeException(); + + NativeFuncs.godotsharp_array_remove_at(ref NativeValue, index); + } // ICollection @@ -209,7 +241,7 @@ namespace Godot.Collections /// This is also known as the size or length of the array. /// /// The number of elements. - public int Count => godot_icall_Array_Count(ref NativeValue); + public int Count => NativeValue.Size; object ICollection.SyncRoot => this; @@ -220,17 +252,35 @@ namespace Godot.Collections /// untyped C# array, starting at the given index. /// /// The array to copy to. - /// The index to start at. - public void CopyTo(System.Array array, int index) + /// The index to start at. + public void CopyTo(System.Array array, int destIndex) { if (array == null) throw new ArgumentNullException(nameof(array), "Value cannot be null."); - if (index < 0) - throw new ArgumentOutOfRangeException(nameof(index), "Number was less than the array's lower bound in the first dimension."); + if (destIndex < 0) + { + throw new ArgumentOutOfRangeException(nameof(destIndex), + "Number was less than the array's lower bound in the first dimension."); + } - // Internal call may throw ArgumentException - godot_icall_Array_CopyTo(ref NativeValue, array, index); + int count = Count; + + if (array.Length < (destIndex + count)) + { + throw new ArgumentException( + "Destination array was not long enough. Check destIndex and length, and the array's lower bounds."); + } + + unsafe + { + for (int i = 0; i < count; i++) + { + object obj = Marshaling.variant_to_mono_object(&(*NativeValue._p)._arrayVector._ptr[i]); + array.SetValue(obj, destIndex); + destIndex++; + } + } } // IEnumerable @@ -253,64 +303,30 @@ namespace Godot.Collections /// Converts this to a string. /// /// A string representation of this array. - public override string ToString() + public override unsafe string ToString() { - return godot_icall_Array_ToString(ref NativeValue); + using godot_string str = default; + NativeFuncs.godotsharp_array_to_string(ref NativeValue, &str); + return Marshaling.mono_string_from_godot(&str); } - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_Ctor(out godot_array dest); + /// + /// The variant returned via the parameter is owned by the Array and must not be disposed. + /// + internal void GetVariantBorrowElementAt(int index, out godot_variant elem) + { + if (index < 0 || index >= Count) + throw new IndexOutOfRangeException(); + GetVariantBorrowElementAtUnchecked(index, out elem); + } - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_Ctor_MonoArray(System.Array array, out godot_array dest); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_At(ref godot_array ptr, int index, out godot_variant elem); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_SetAt(ref godot_array ptr, int index, object value); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern int godot_icall_Array_Count(ref godot_array ptr); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern int godot_icall_Array_Add(ref godot_array ptr, object item); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_Clear(ref godot_array ptr); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_Concatenate(ref godot_array left, ref godot_array right, out godot_array dest); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_Array_Contains(ref godot_array ptr, object item); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_CopyTo(ref godot_array ptr, System.Array array, int arrayIndex); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_Duplicate(ref godot_array ptr, bool deep, out godot_array dest); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern int godot_icall_Array_IndexOf(ref godot_array ptr, object item); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_Insert(ref godot_array ptr, int index, object item); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_Array_Remove(ref godot_array ptr, object item); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_RemoveAt(ref godot_array ptr, int index); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern Error godot_icall_Array_Resize(ref godot_array ptr, int newSize); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern Error godot_icall_Array_Shuffle(ref godot_array ptr); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern string godot_icall_Array_ToString(ref godot_array ptr); + /// + /// The variant returned via the parameter is owned by the Array and must not be disposed. + /// + internal unsafe void GetVariantBorrowElementAtUnchecked(int index, out godot_variant elem) + { + elem = (*NativeValue._p)._arrayVector._ptr[index]; + } } internal interface IGenericGodotArray @@ -449,11 +465,10 @@ namespace Godot.Collections { get { - Array.godot_icall_Array_At(ref _underlyingArray.NativeValue, index, out godot_variant elem); + _underlyingArray.GetVariantBorrowElementAt(index, out godot_variant borrowElem); unsafe { - using (elem) - return (T)Marshaling.variant_to_mono_object_of_type(&elem, TypeOfElements); + return (T)Marshaling.variant_to_mono_object_of_type(&borrowElem, TypeOfElements); } } set => _underlyingArray[index] = value; @@ -544,12 +559,14 @@ namespace Godot.Collections throw new ArgumentNullException(nameof(array), "Value cannot be null."); if (arrayIndex < 0) - throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Number was less than the array's lower bound in the first dimension."); + throw new ArgumentOutOfRangeException(nameof(arrayIndex), + "Number was less than the array's lower bound in the first dimension."); int count = _underlyingArray.Count; if (array.Length < (arrayIndex + count)) - throw new ArgumentException("Destination array was not long enough. Check destIndex and length, and the array's lower bounds."); + throw new ArgumentException( + "Destination array was not long enough. Check destIndex and length, and the array's lower bounds."); for (int i = 0; i < count; i++) { @@ -566,7 +583,14 @@ namespace Godot.Collections /// A bool indicating success or failure. public bool Remove(T item) { - return Array.godot_icall_Array_Remove(ref _underlyingArray.NativeValue, item); + int index = IndexOf(item); + if (index >= 0) + { + RemoveAt(index); + return true; + } + + return false; } // IEnumerable diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs index 344acd65e0..e5887e475c 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Collections; -using System.Runtime.CompilerServices; using Godot.NativeInterop; using System.Diagnostics.CodeAnalysis; @@ -23,7 +22,7 @@ namespace Godot.Collections /// public Dictionary() { - godot_icall_Dictionary_Ctor(out NativeValue); + NativeValue = NativeFuncs.godotsharp_dictionary_new(); } /// @@ -77,7 +76,7 @@ namespace Godot.Collections public Dictionary Duplicate(bool deep = false) { godot_dictionary newDictionary; - godot_icall_Dictionary_Duplicate(ref NativeValue, deep, out newDictionary); + NativeFuncs.godotsharp_dictionary_duplicate(ref NativeValue, deep, out newDictionary); return CreateTakingOwnershipOfDisposableValue(newDictionary); } @@ -91,7 +90,7 @@ namespace Godot.Collections get { godot_array keysArray; - godot_icall_Dictionary_Keys(ref NativeValue, out keysArray); + NativeFuncs.godotsharp_dictionary_keys(ref NativeValue, out keysArray); return Array.CreateTakingOwnershipOfDisposableValue(keysArray); } } @@ -104,7 +103,7 @@ namespace Godot.Collections get { godot_array valuesArray; - godot_icall_Dictionary_Values(ref NativeValue, out valuesArray); + NativeFuncs.godotsharp_dictionary_values(ref NativeValue, out valuesArray); return Array.CreateTakingOwnershipOfDisposableValue(valuesArray); } } @@ -112,10 +111,15 @@ namespace Godot.Collections private (Array keys, Array values, int count) GetKeyValuePairs() { godot_array keysArray; - godot_array valuesArray; - int count = godot_icall_Dictionary_KeyValuePairs(ref NativeValue, out keysArray, out valuesArray); + NativeFuncs.godotsharp_dictionary_keys(ref NativeValue, out keysArray); var keys = Array.CreateTakingOwnershipOfDisposableValue(keysArray); + + godot_array valuesArray; + NativeFuncs.godotsharp_dictionary_keys(ref NativeValue, out valuesArray); var values = Array.CreateTakingOwnershipOfDisposableValue(valuesArray); + + int count = NativeFuncs.godotsharp_dictionary_count(ref NativeValue); + return (keys, values, count); } @@ -127,18 +131,28 @@ namespace Godot.Collections /// Returns the object at the given . /// /// The object at the given . - public object this[object key] + public unsafe object this[object key] { get { - godot_icall_Dictionary_GetValue(ref NativeValue, key, out godot_variant value); - unsafe + using godot_variant variantKey = Marshaling.mono_object_to_variant(key); + if (NativeFuncs.godotsharp_dictionary_try_get_value(ref NativeValue, &variantKey, + out godot_variant value)) { using (value) return Marshaling.variant_to_mono_object(&value); } + else + { + throw new KeyNotFoundException(); + } + } + set + { + using godot_variant variantKey = Marshaling.mono_object_to_variant(key); + using godot_variant variantValue = Marshaling.mono_object_to_variant(value); + NativeFuncs.godotsharp_dictionary_set_value(ref NativeValue, &variantKey, &variantValue); } - set => godot_icall_Dictionary_SetValue(ref NativeValue, key, value); } /// @@ -147,19 +161,32 @@ namespace Godot.Collections /// /// The key at which to add the object. /// The object to add. - public void Add(object key, object value) => godot_icall_Dictionary_Add(ref NativeValue, key, value); + public unsafe void Add(object key, object value) + { + using godot_variant variantKey = Marshaling.mono_object_to_variant(key); + + if (NativeFuncs.godotsharp_dictionary_contains_key(ref NativeValue, &variantKey)) + throw new ArgumentException("An element with the same key already exists", nameof(key)); + + using godot_variant variantValue = Marshaling.mono_object_to_variant(value); + NativeFuncs.godotsharp_dictionary_add(ref NativeValue, &variantKey, &variantValue); + } /// /// Erases all items from this . /// - public void Clear() => godot_icall_Dictionary_Clear(ref NativeValue); + public void Clear() => NativeFuncs.godotsharp_dictionary_clear(ref NativeValue); /// /// Checks if this contains the given key. /// /// The key to look for. /// Whether or not this dictionary contains the given key. - public bool Contains(object key) => godot_icall_Dictionary_ContainsKey(ref NativeValue, key); + public unsafe bool Contains(object key) + { + using godot_variant variantKey = Marshaling.mono_object_to_variant(key); + return NativeFuncs.godotsharp_dictionary_contains_key(ref NativeValue, &variantKey); + } /// /// Gets an enumerator for this . @@ -171,7 +198,11 @@ namespace Godot.Collections /// Removes an element from this by key. /// /// The key of the element to remove. - public void Remove(object key) => godot_icall_Dictionary_RemoveKey(ref NativeValue, key); + public unsafe void Remove(object key) + { + using godot_variant variantKey = Marshaling.mono_object_to_variant(key); + NativeFuncs.godotsharp_dictionary_remove_key(ref NativeValue, &variantKey); + } // ICollection @@ -184,7 +215,7 @@ namespace Godot.Collections /// This is also known as the size or length of the dictionary. /// /// The number of elements. - public int Count => godot_icall_Dictionary_Count(ref NativeValue); + public int Count => NativeFuncs.godotsharp_dictionary_count(ref NativeValue); /// /// Copies the elements of this to the given @@ -198,12 +229,14 @@ namespace Godot.Collections throw new ArgumentNullException(nameof(array), "Value cannot be null."); if (index < 0) - throw new ArgumentOutOfRangeException(nameof(index), "Number was less than the array's lower bound in the first dimension."); + throw new ArgumentOutOfRangeException(nameof(index), + "Number was less than the array's lower bound in the first dimension."); var (keys, values, count) = GetKeyValuePairs(); if (array.Length < (index + count)) - throw new ArgumentException("Destination array was not long enough. Check destIndex and length, and the array's lower bounds."); + throw new ArgumentException( + "Destination array was not long enough. Check destIndex and length, and the array's lower bounds."); for (int i = 0; i < count; i++) { @@ -241,15 +274,23 @@ namespace Godot.Collections { UpdateEntry(); } + return entry; } } - private void UpdateEntry() + private unsafe void UpdateEntry() { dirty = false; - godot_icall_Dictionary_KeyValuePairAt(ref dictionary.NativeValue, index, out object key, out object value); - entry = new DictionaryEntry(key, value); + NativeFuncs.godotsharp_dictionary_key_value_pair_at(ref dictionary.NativeValue, index, + out godot_variant key, + out godot_variant value); + using (key) + using (value) + { + entry = new DictionaryEntry(Marshaling.variant_to_mono_object(&key), + Marshaling.variant_to_mono_object(&value)); + } } public object Key => Entry.Key; @@ -274,61 +315,12 @@ namespace Godot.Collections /// Converts this to a string. /// /// A string representation of this dictionary. - public override string ToString() + public override unsafe string ToString() { - return godot_icall_Dictionary_ToString(ref NativeValue); + using godot_string str = default; + NativeFuncs.godotsharp_dictionary_to_string(ref NativeValue, &str); + return Marshaling.mono_string_from_godot(&str); } - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_Ctor(out godot_dictionary dest); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_GetValue(ref godot_dictionary ptr, object key, out godot_variant value); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_SetValue(ref godot_dictionary ptr, object key, object value); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_Keys(ref godot_dictionary ptr, out godot_array dest); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_Values(ref godot_dictionary ptr, out godot_array dest); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern int godot_icall_Dictionary_KeyValuePairs(ref godot_dictionary ptr, out godot_array keys, out godot_array values); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_KeyValuePairAt(ref godot_dictionary ptr, int index, out object key, out object value); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_Add(ref godot_dictionary ptr, object key, object value); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern int godot_icall_Dictionary_Count(ref godot_dictionary ptr); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_Clear(ref godot_dictionary ptr); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_Dictionary_Contains(ref godot_dictionary ptr, object key, object value); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_Dictionary_ContainsKey(ref godot_dictionary ptr, object key); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_Duplicate(ref godot_dictionary ptr, bool deep, out godot_dictionary dest); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_Dictionary_RemoveKey(ref godot_dictionary ptr, object key); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_Dictionary_Remove(ref godot_dictionary ptr, object key, object value); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_Dictionary_TryGetValue(ref godot_dictionary ptr, object key, out godot_variant value); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern string godot_icall_Dictionary_ToString(ref godot_dictionary ptr); } internal interface IGenericGodotDictionary @@ -401,7 +393,8 @@ namespace Godot.Collections } // Explicit name to make it very clear - internal static Dictionary CreateTakingOwnershipOfDisposableValue(godot_dictionary nativeValueToOwn) + internal static Dictionary CreateTakingOwnershipOfDisposableValue( + godot_dictionary nativeValueToOwn) => new Dictionary(Dictionary.CreateTakingOwnershipOfDisposableValue(nativeValueToOwn)); /// @@ -433,11 +426,19 @@ namespace Godot.Collections { get { - Dictionary.godot_icall_Dictionary_GetValue(ref _underlyingDict.NativeValue, key, out godot_variant value); unsafe { - using (value) - return (TValue)Marshaling.variant_to_mono_object_of_type(&value, TypeOfValues); + using godot_variant variantKey = Marshaling.mono_object_to_variant(key); + if (NativeFuncs.godotsharp_dictionary_try_get_value(ref _underlyingDict.NativeValue, + &variantKey, out godot_variant value)) + { + using (value) + return (TValue)Marshaling.variant_to_mono_object_of_type(&value, TypeOfValues); + } + else + { + throw new KeyNotFoundException(); + } } } set => _underlyingDict[key] = value; @@ -451,7 +452,7 @@ namespace Godot.Collections get { godot_array keyArray; - Dictionary.godot_icall_Dictionary_Keys(ref _underlyingDict.NativeValue, out keyArray); + NativeFuncs.godotsharp_dictionary_keys(ref _underlyingDict.NativeValue, out keyArray); return Array.CreateTakingOwnershipOfDisposableValue(keyArray); } } @@ -464,15 +465,22 @@ namespace Godot.Collections get { godot_array valuesArray; - Dictionary.godot_icall_Dictionary_Values(ref _underlyingDict.NativeValue, out valuesArray); + NativeFuncs.godotsharp_dictionary_values(ref _underlyingDict.NativeValue, out valuesArray); return Array.CreateTakingOwnershipOfDisposableValue(valuesArray); } } - private KeyValuePair GetKeyValuePair(int index) + private unsafe KeyValuePair GetKeyValuePair(int index) { - Dictionary.godot_icall_Dictionary_KeyValuePairAt(ref _underlyingDict.NativeValue, index, out object key, out object value); - return new KeyValuePair((TKey)key, (TValue)value); + NativeFuncs.godotsharp_dictionary_key_value_pair_at(ref _underlyingDict.NativeValue, index, + out godot_variant key, + out godot_variant value); + using (key) + using (value) + { + return new KeyValuePair((TKey)Marshaling.variant_to_mono_object(&key), + (TValue)Marshaling.variant_to_mono_object(&value)); + } } /// @@ -500,9 +508,10 @@ namespace Godot.Collections /// Removes an element from this by key. /// /// The key of the element to remove. - public bool Remove(TKey key) + public unsafe bool Remove(TKey key) { - return Dictionary.godot_icall_Dictionary_RemoveKey(ref _underlyingDict.NativeValue, key); + using godot_variant variantKey = Marshaling.mono_object_to_variant(key); + return NativeFuncs.godotsharp_dictionary_remove_key(ref _underlyingDict.NativeValue, &variantKey); } /// @@ -511,18 +520,17 @@ namespace Godot.Collections /// The key of the element to get. /// The value at the given . /// If an object was found for the given . - public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) + public unsafe bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) { - bool found = Dictionary.godot_icall_Dictionary_TryGetValue(ref _underlyingDict.NativeValue, key, out godot_variant retValue); + using godot_variant variantKey = Marshaling.mono_object_to_variant(key); + bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref _underlyingDict.NativeValue, + &variantKey, out godot_variant retValue); - unsafe + using (retValue) { - using (retValue) - { - value = found ? - (TValue)Marshaling.variant_to_mono_object_of_type(&retValue, TypeOfValues) : - default; - } + value = found ? + (TValue)Marshaling.variant_to_mono_object_of_type(&retValue, TypeOfValues) : + default; } return found; @@ -552,9 +560,19 @@ namespace Godot.Collections _underlyingDict.Clear(); } - bool ICollection>.Contains(KeyValuePair item) + unsafe bool ICollection>.Contains(KeyValuePair item) { - return _underlyingDict.Contains(new KeyValuePair(item.Key, item.Value)); + using godot_variant variantKey = Marshaling.mono_object_to_variant(item.Key); + bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref _underlyingDict.NativeValue, + &variantKey, out godot_variant retValue); + + using (retValue) + { + if (!found) + return false; + + return NativeFuncs.godotsharp_variant_equals(&variantKey, &retValue); + } } /// @@ -569,12 +587,14 @@ namespace Godot.Collections throw new ArgumentNullException(nameof(array), "Value cannot be null."); if (arrayIndex < 0) - throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Number was less than the array's lower bound in the first dimension."); + throw new ArgumentOutOfRangeException(nameof(arrayIndex), + "Number was less than the array's lower bound in the first dimension."); int count = Count; if (array.Length < (arrayIndex + count)) - throw new ArgumentException("Destination array was not long enough. Check destIndex and length, and the array's lower bounds."); + throw new ArgumentException( + "Destination array was not long enough. Check destIndex and length, and the array's lower bounds."); for (int i = 0; i < count; i++) { @@ -583,9 +603,22 @@ namespace Godot.Collections } } - bool ICollection>.Remove(KeyValuePair item) + unsafe bool ICollection>.Remove(KeyValuePair item) { - return Dictionary.godot_icall_Dictionary_Remove(ref _underlyingDict.NativeValue, item.Key, item.Value); + using godot_variant variantKey = Marshaling.mono_object_to_variant(item.Key); + bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref _underlyingDict.NativeValue, + &variantKey, out godot_variant retValue); + + using (retValue) + { + if (!found) + return false; + + if (NativeFuncs.godotsharp_variant_equals(&variantKey, &retValue)) + return NativeFuncs.godotsharp_dictionary_remove_key(ref _underlyingDict.NativeValue, &variantKey); + + return false; + } } // IEnumerable> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs index b36206f2ee..da5e3eccee 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs @@ -161,6 +161,9 @@ namespace Godot.NativeInterop NativeFuncs.godotsharp_string_destroy(ref this); _ptr = IntPtr.Zero; } + + // Size including the null termination character + public unsafe int Size => _ptr != IntPtr.Zero ? *((int*)_ptr - 1) : 0; } [StructLayout(LayoutKind.Sequential)] @@ -252,14 +255,34 @@ namespace Godot.NativeInterop // ReSharper disable once InconsistentNaming internal struct godot_array : IDisposable { - internal IntPtr _p; + internal unsafe ArrayPrivate* _p; - public void Dispose() + [StructLayout(LayoutKind.Sequential)] + internal struct ArrayPrivate { - if (_p == IntPtr.Zero) + private uint _safeRefCount; + + internal VariantVector _arrayVector; + // There's more here, but we don't care as we never store this in C# + } + + [StructLayout(LayoutKind.Sequential)] + internal struct VariantVector + { + internal IntPtr _writeProxy; + internal unsafe godot_variant* _ptr; + + public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0; + } + + public unsafe int Size => _p != null ? _p->_arrayVector.Size : 0; + + public unsafe void Dispose() + { + if (_p == null) return; NativeFuncs.godotsharp_array_destroy(ref this); - _p = IntPtr.Zero; + _p = null; } } @@ -287,15 +310,17 @@ namespace Godot.NativeInterop internal struct godot_packed_byte_array : IDisposable { internal IntPtr _writeProxy; - internal IntPtr _ptr; + internal unsafe byte* _ptr; - public void Dispose() + public unsafe void Dispose() { - if (_ptr == IntPtr.Zero) + if (_ptr == null) return; NativeFuncs.godotsharp_packed_byte_array_destroy(ref this); - _ptr = IntPtr.Zero; + _ptr = null; } + + public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0; } [StructLayout(LayoutKind.Sequential)] @@ -303,15 +328,17 @@ namespace Godot.NativeInterop internal struct godot_packed_int32_array : IDisposable { internal IntPtr _writeProxy; - internal IntPtr _ptr; + internal unsafe int* _ptr; - public void Dispose() + public unsafe void Dispose() { - if (_ptr == IntPtr.Zero) + if (_ptr == null) return; NativeFuncs.godotsharp_packed_int32_array_destroy(ref this); - _ptr = IntPtr.Zero; + _ptr = null; } + + public unsafe int Size => _ptr != null ? *(_ptr - 1) : 0; } [StructLayout(LayoutKind.Sequential)] @@ -319,15 +346,17 @@ namespace Godot.NativeInterop internal struct godot_packed_int64_array : IDisposable { internal IntPtr _writeProxy; - internal IntPtr _ptr; + internal unsafe long* _ptr; - public void Dispose() + public unsafe void Dispose() { - if (_ptr == IntPtr.Zero) + if (_ptr == null) return; NativeFuncs.godotsharp_packed_int64_array_destroy(ref this); - _ptr = IntPtr.Zero; + _ptr = null; } + + public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0; } [StructLayout(LayoutKind.Sequential)] @@ -335,15 +364,17 @@ namespace Godot.NativeInterop internal struct godot_packed_float32_array : IDisposable { internal IntPtr _writeProxy; - internal IntPtr _ptr; + internal unsafe float* _ptr; - public void Dispose() + public unsafe void Dispose() { - if (_ptr == IntPtr.Zero) + if (_ptr == null) return; NativeFuncs.godotsharp_packed_float32_array_destroy(ref this); - _ptr = IntPtr.Zero; + _ptr = null; } + + public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0; } [StructLayout(LayoutKind.Sequential)] @@ -351,15 +382,17 @@ namespace Godot.NativeInterop internal struct godot_packed_float64_array : IDisposable { internal IntPtr _writeProxy; - internal IntPtr _ptr; + internal unsafe double* _ptr; - public void Dispose() + public unsafe void Dispose() { - if (_ptr == IntPtr.Zero) + if (_ptr == null) return; NativeFuncs.godotsharp_packed_float64_array_destroy(ref this); - _ptr = IntPtr.Zero; + _ptr = null; } + + public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0; } [StructLayout(LayoutKind.Sequential)] @@ -367,15 +400,17 @@ namespace Godot.NativeInterop internal struct godot_packed_string_array : IDisposable { internal IntPtr _writeProxy; - internal IntPtr _ptr; + internal unsafe godot_string* _ptr; - public void Dispose() + public unsafe void Dispose() { - if (_ptr == IntPtr.Zero) + if (_ptr == null) return; NativeFuncs.godotsharp_packed_string_array_destroy(ref this); - _ptr = IntPtr.Zero; + _ptr = null; } + + public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0; } [StructLayout(LayoutKind.Sequential)] @@ -383,15 +418,17 @@ namespace Godot.NativeInterop internal struct godot_packed_vector2_array : IDisposable { internal IntPtr _writeProxy; - internal IntPtr _ptr; + internal unsafe Vector2* _ptr; - public void Dispose() + public unsafe void Dispose() { - if (_ptr == IntPtr.Zero) + if (_ptr == null) return; NativeFuncs.godotsharp_packed_vector2_array_destroy(ref this); - _ptr = IntPtr.Zero; + _ptr = null; } + + public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0; } [StructLayout(LayoutKind.Sequential)] @@ -399,15 +436,17 @@ namespace Godot.NativeInterop internal struct godot_packed_vector3_array : IDisposable { internal IntPtr _writeProxy; - internal IntPtr _ptr; + internal unsafe Vector3* _ptr; - public void Dispose() + public unsafe void Dispose() { - if (_ptr == IntPtr.Zero) + if (_ptr == null) return; NativeFuncs.godotsharp_packed_vector3_array_destroy(ref this); - _ptr = IntPtr.Zero; + _ptr = null; } + + public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0; } [StructLayout(LayoutKind.Sequential)] @@ -415,14 +454,16 @@ namespace Godot.NativeInterop internal struct godot_packed_color_array : IDisposable { internal IntPtr _writeProxy; - internal IntPtr _ptr; + internal unsafe Color* _ptr; - public void Dispose() + public unsafe void Dispose() { - if (_ptr == IntPtr.Zero) + if (_ptr == null) return; NativeFuncs.godotsharp_packed_color_array_destroy(ref this); - _ptr = IntPtr.Zero; + _ptr = null; } + + public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0; } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs index 4dfe48fe7b..3a6d455396 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs @@ -152,7 +152,8 @@ namespace Godot.NativeInterop if (genericTypeDefinition == typeof(IDictionary<,>)) return Variant.Type.Dictionary; - if (genericTypeDefinition == typeof(ICollection<>) || genericTypeDefinition == typeof(IEnumerable<>)) + if (genericTypeDefinition == typeof(ICollection<>) || + genericTypeDefinition == typeof(IEnumerable<>)) return Variant.Type.Array; } else if (type == typeof(object)) @@ -415,7 +416,8 @@ namespace Godot.NativeInterop { // TODO: Validate key and value types are compatible with Variant #if NET - Collections.IGenericGodotDictionary genericGodotDictionary = IDictionaryToGenericGodotDictionary((dynamic)p_obj); + Collections.IGenericGodotDictionary genericGodotDictionary = + IDictionaryToGenericGodotDictionary((dynamic)p_obj); #else var genericArguments = type.GetGenericArguments(); @@ -426,7 +428,7 @@ namespace Godot.NativeInterop .MakeGenericMethod(genericArguments[0], genericArguments[1]); var genericGodotDictionary = (Collections.IGenericGodotDictionary)method - .Invoke(null, new[] {p_obj}); + .Invoke(null, new[] { p_obj }); #endif var godotDict = genericGodotDictionary.UnderlyingDictionary; @@ -439,7 +441,8 @@ namespace Godot.NativeInterop { // TODO: Validate element type is compatible with Variant #if NET - var nativeGodotArray = mono_array_to_Array(System.Runtime.InteropServices.CollectionsMarshal.AsSpan((dynamic)p_obj)); + var nativeGodotArray = + mono_array_to_Array(System.Runtime.InteropServices.CollectionsMarshal.AsSpan((dynamic)p_obj)); #else // With .NET Standard we need a package reference for Microsoft.CSharp in order to // use dynamic, so we have this workaround for now until we switch to .NET 5/6. @@ -599,8 +602,9 @@ namespace Godot.NativeInterop return VariantUtils.ConvertToUInt64(p_var); default: { - GD.PushError("Attempted to convert Variant to enum value of unsupported underlying type. Name: " + - type.FullName + " : " + enumUnderlyingType.FullName + "."); + GD.PushError( + "Attempted to convert Variant to enum value of unsupported underlying type. Name: " + + type.FullName + " : " + enumUnderlyingType.FullName + "."); return null; } } @@ -753,7 +757,7 @@ namespace Godot.NativeInterop VariantUtils.ConvertToDictionary(p_var)); return Activator.CreateInstance(fullType, BindingFlags.Public | BindingFlags.Instance, null, - args: new object[] {underlyingDict}, null); + args: new object[] { underlyingDict }, null); } static object variant_to_generic_godot_collections_array(godot_variant* p_var, Type fullType) @@ -762,7 +766,7 @@ namespace Godot.NativeInterop VariantUtils.ConvertToArray(p_var)); return Activator.CreateInstance(fullType, BindingFlags.Public | BindingFlags.Instance, null, - args: new object[] {underlyingArray}, null); + args: new object[] { underlyingArray }, null); } var genericTypeDefinition = type.GetGenericTypeDefinition(); @@ -980,7 +984,7 @@ namespace Godot.NativeInterop const int sizeOfChar32 = 4; byte* bytes = (byte*)(*p_string)._ptr; - int size = *((int*)(*p_string)._ptr - 1); + int size = (*p_string).Size; if (size == 0) return string.Empty; size -= 1; // zero at the end @@ -1118,11 +1122,7 @@ namespace Godot.NativeInterop public static godot_array mono_array_to_Array(Span p_array) { if (p_array.IsEmpty) - { - godot_array ret; - Collections.Array.godot_icall_Array_Ctor(out ret); - return ret; - } + return NativeFuncs.godotsharp_array_new(); using var array = new Collections.Array(); array.Resize(p_array.Length); @@ -1141,8 +1141,8 @@ namespace Godot.NativeInterop public static unsafe byte[] PackedByteArray_to_mono_array(godot_packed_byte_array* p_array) { - byte* buffer = (byte*)(*p_array)._ptr; - int size = *((int*)(*p_array)._ptr - 1); + byte* buffer = (*p_array)._ptr; + int size = (*p_array).Size; var array = new byte[size]; fixed (byte* dest = array) Buffer.MemoryCopy(buffer, dest, size, size); @@ -1161,8 +1161,8 @@ namespace Godot.NativeInterop public static unsafe int[] PackedInt32Array_to_mono_array(godot_packed_int32_array* p_array) { - int* buffer = (int*)(*p_array)._ptr; - int size = *((int*)(*p_array)._ptr - 1); + int* buffer = (*p_array)._ptr; + int size = (*p_array).Size; int sizeInBytes = size * sizeof(int); var array = new int[size]; fixed (int* dest = array) @@ -1182,8 +1182,8 @@ namespace Godot.NativeInterop public static unsafe long[] PackedInt64Array_to_mono_array(godot_packed_int64_array* p_array) { - long* buffer = (long*)(*p_array)._ptr; - int size = *((int*)(*p_array)._ptr - 1); + long* buffer = (*p_array)._ptr; + int size = (*p_array).Size; int sizeInBytes = size * sizeof(long); var array = new long[size]; fixed (long* dest = array) @@ -1203,8 +1203,8 @@ namespace Godot.NativeInterop public static unsafe float[] PackedFloat32Array_to_mono_array(godot_packed_float32_array* p_array) { - float* buffer = (float*)(*p_array)._ptr; - int size = *((int*)(*p_array)._ptr - 1); + float* buffer = (*p_array)._ptr; + int size = (*p_array).Size; int sizeInBytes = size * sizeof(float); var array = new float[size]; fixed (float* dest = array) @@ -1224,8 +1224,8 @@ namespace Godot.NativeInterop public static unsafe double[] PackedFloat64Array_to_mono_array(godot_packed_float64_array* p_array) { - double* buffer = (double*)(*p_array)._ptr; - int size = *((int*)(*p_array)._ptr - 1); + double* buffer = (*p_array)._ptr; + int size = (*p_array).Size; int sizeInBytes = size * sizeof(double); var array = new double[size]; fixed (double* dest = array) @@ -1245,10 +1245,10 @@ namespace Godot.NativeInterop public static unsafe string[] PackedStringArray_to_mono_array(godot_packed_string_array* p_array) { - godot_string* buffer = (godot_string*)(*p_array)._ptr; + godot_string* buffer = (*p_array)._ptr; if (buffer == null) return new string[] { }; - int size = *((int*)(*p_array)._ptr - 1); + int size = (*p_array).Size; var array = new string[size]; for (int i = 0; i < size; i++) array[i] = mono_string_from_godot(&buffer[i]); @@ -1278,8 +1278,8 @@ namespace Godot.NativeInterop public static unsafe Vector2[] PackedVector2Array_to_mono_array(godot_packed_vector2_array* p_array) { - Vector2* buffer = (Vector2*)(*p_array)._ptr; - int size = *((int*)(*p_array)._ptr - 1); + Vector2* buffer = (*p_array)._ptr; + int size = (*p_array).Size; int sizeInBytes = size * sizeof(Vector2); var array = new Vector2[size]; fixed (Vector2* dest = array) @@ -1299,8 +1299,8 @@ namespace Godot.NativeInterop public static unsafe Vector3[] PackedVector3Array_to_mono_array(godot_packed_vector3_array* p_array) { - Vector3* buffer = (Vector3*)(*p_array)._ptr; - int size = *((int*)(*p_array)._ptr - 1); + Vector3* buffer = (*p_array)._ptr; + int size = (*p_array).Size; int sizeInBytes = size * sizeof(Vector3); var array = new Vector3[size]; fixed (Vector3* dest = array) @@ -1320,8 +1320,8 @@ namespace Godot.NativeInterop public static unsafe Color[] PackedColorArray_to_mono_array(godot_packed_color_array* p_array) { - Color* buffer = (Color*)(*p_array)._ptr; - int size = *((int*)(*p_array)._ptr - 1); + Color* buffer = (*p_array)._ptr; + int size = (*p_array).Size; int sizeInBytes = size * sizeof(Color); var array = new Color[size]; fixed (Color* dest = array) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs index 2b0ff62816..1145b265ef 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs @@ -17,7 +17,8 @@ namespace Godot.NativeInterop // Custom functions [DllImport(GodotDllName)] - public static extern IntPtr godotsharp_method_bind_get_method(ref godot_string_name p_classname, char* p_methodname); + public static extern IntPtr godotsharp_method_bind_get_method(ref godot_string_name p_classname, + char* p_methodname); #if NET [DllImport(GodotDllName)] @@ -50,47 +51,60 @@ namespace Godot.NativeInterop public static extern void godotsharp_node_path_as_string(godot_string* r_dest, godot_node_path* p_np); [DllImport(GodotDllName)] - public static extern godot_packed_byte_array godotsharp_packed_byte_array_new_mem_copy(byte* p_src, int p_length); + public static extern godot_packed_byte_array godotsharp_packed_byte_array_new_mem_copy(byte* p_src, + int p_length); [DllImport(GodotDllName)] - public static extern godot_packed_int32_array godotsharp_packed_int32_array_new_mem_copy(int* p_src, int p_length); + public static extern godot_packed_int32_array godotsharp_packed_int32_array_new_mem_copy(int* p_src, + int p_length); [DllImport(GodotDllName)] - public static extern godot_packed_int64_array godotsharp_packed_int64_array_new_mem_copy(long* p_src, int p_length); + public static extern godot_packed_int64_array godotsharp_packed_int64_array_new_mem_copy(long* p_src, + int p_length); [DllImport(GodotDllName)] - public static extern godot_packed_float32_array godotsharp_packed_float32_array_new_mem_copy(float* p_src, int p_length); + public static extern godot_packed_float32_array godotsharp_packed_float32_array_new_mem_copy(float* p_src, + int p_length); [DllImport(GodotDllName)] - public static extern godot_packed_float64_array godotsharp_packed_float64_array_new_mem_copy(double* p_src, int p_length); + public static extern godot_packed_float64_array godotsharp_packed_float64_array_new_mem_copy(double* p_src, + int p_length); [DllImport(GodotDllName)] - public static extern godot_packed_vector2_array godotsharp_packed_vector2_array_new_mem_copy(Vector2* p_src, int p_length); + public static extern godot_packed_vector2_array godotsharp_packed_vector2_array_new_mem_copy(Vector2* p_src, + int p_length); [DllImport(GodotDllName)] - public static extern godot_packed_vector3_array godotsharp_packed_vector3_array_new_mem_copy(Vector3* p_src, int p_length); + public static extern godot_packed_vector3_array godotsharp_packed_vector3_array_new_mem_copy(Vector3* p_src, + int p_length); [DllImport(GodotDllName)] - public static extern godot_packed_color_array godotsharp_packed_color_array_new_mem_copy(Color* p_src, int p_length); + public static extern godot_packed_color_array godotsharp_packed_color_array_new_mem_copy(Color* p_src, + int p_length); [DllImport(GodotDllName)] - public static extern void godotsharp_packed_string_array_add(godot_packed_string_array* r_dest, godot_string* p_element); + public static extern void godotsharp_packed_string_array_add(godot_packed_string_array* r_dest, + godot_string* p_element); [DllImport(GodotDllName)] - public static extern void godotsharp_callable_new_with_delegate(IntPtr p_delegate_handle, godot_callable* r_callable); + public static extern void godotsharp_callable_new_with_delegate(IntPtr p_delegate_handle, + godot_callable* r_callable); [DllImport(GodotDllName)] - public static extern bool godotsharp_callable_get_data_for_marshalling(godot_callable* p_callable, IntPtr* r_delegate_handle, IntPtr* r_object, godot_string_name* r_name); + public static extern godot_bool godotsharp_callable_get_data_for_marshalling(godot_callable* p_callable, + IntPtr* r_delegate_handle, IntPtr* r_object, godot_string_name* r_name); // GDNative functions // gdnative.h [DllImport(GodotDllName)] - public static extern void godotsharp_method_bind_ptrcall(IntPtr p_method_bind, IntPtr p_instance, void** p_args, void* p_ret); + public static extern void godotsharp_method_bind_ptrcall(IntPtr p_method_bind, IntPtr p_instance, void** p_args, + void* p_ret); [DllImport(GodotDllName)] - public static extern godot_variant godotsharp_method_bind_call(IntPtr p_method_bind, IntPtr p_instance, godot_variant** p_args, int p_arg_count, godot_variant_call_error* p_call_error); + public static extern godot_variant godotsharp_method_bind_call(IntPtr p_method_bind, IntPtr p_instance, + godot_variant** p_args, int p_arg_count, godot_variant_call_error* p_call_error); // variant.h @@ -122,34 +136,43 @@ namespace Godot.NativeInterop public static extern void godotsharp_variant_new_array(godot_variant* r_dest, godot_array* p_arr); [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_packed_byte_array(godot_variant* r_dest, godot_packed_byte_array* p_pba); + public static extern void godotsharp_variant_new_packed_byte_array(godot_variant* r_dest, + godot_packed_byte_array* p_pba); [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_packed_int32_array(godot_variant* r_dest, godot_packed_int32_array* p_pia); + public static extern void godotsharp_variant_new_packed_int32_array(godot_variant* r_dest, + godot_packed_int32_array* p_pia); [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_packed_int64_array(godot_variant* r_dest, godot_packed_int64_array* p_pia); + public static extern void godotsharp_variant_new_packed_int64_array(godot_variant* r_dest, + godot_packed_int64_array* p_pia); [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_packed_float32_array(godot_variant* r_dest, godot_packed_float32_array* p_pra); + public static extern void godotsharp_variant_new_packed_float32_array(godot_variant* r_dest, + godot_packed_float32_array* p_pra); [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_packed_float64_array(godot_variant* r_dest, godot_packed_float64_array* p_pra); + public static extern void godotsharp_variant_new_packed_float64_array(godot_variant* r_dest, + godot_packed_float64_array* p_pra); [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_packed_string_array(godot_variant* r_dest, godot_packed_string_array* p_psa); + public static extern void godotsharp_variant_new_packed_string_array(godot_variant* r_dest, + godot_packed_string_array* p_psa); [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_packed_vector2_array(godot_variant* r_dest, godot_packed_vector2_array* p_pv2a); + public static extern void godotsharp_variant_new_packed_vector2_array(godot_variant* r_dest, + godot_packed_vector2_array* p_pv2a); [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_packed_vector3_array(godot_variant* r_dest, godot_packed_vector3_array* p_pv3a); + public static extern void godotsharp_variant_new_packed_vector3_array(godot_variant* r_dest, + godot_packed_vector3_array* p_pv3a); [DllImport(GodotDllName)] - public static extern void godotsharp_variant_new_packed_color_array(godot_variant* r_dest, godot_packed_color_array* p_pca); + public static extern void godotsharp_variant_new_packed_color_array(godot_variant* r_dest, + godot_packed_color_array* p_pca); [DllImport(GodotDllName)] - public static extern bool godotsharp_variant_as_bool(godot_variant* p_self); + public static extern godot_bool godotsharp_variant_as_bool(godot_variant* p_self); [DllImport(GodotDllName)] public static extern Int64 godotsharp_variant_as_int(godot_variant* p_self); @@ -230,23 +253,30 @@ namespace Godot.NativeInterop public static extern godot_packed_int64_array godotsharp_variant_as_packed_int64_array(godot_variant* p_self); [DllImport(GodotDllName)] - public static extern godot_packed_float32_array godotsharp_variant_as_packed_float32_array(godot_variant* p_self); + public static extern godot_packed_float32_array godotsharp_variant_as_packed_float32_array( + godot_variant* p_self); [DllImport(GodotDllName)] - public static extern godot_packed_float64_array godotsharp_variant_as_packed_float64_array(godot_variant* p_self); + public static extern godot_packed_float64_array godotsharp_variant_as_packed_float64_array( + godot_variant* p_self); [DllImport(GodotDllName)] public static extern godot_packed_string_array godotsharp_variant_as_packed_string_array(godot_variant* p_self); [DllImport(GodotDllName)] - public static extern godot_packed_vector2_array godotsharp_variant_as_packed_vector2_array(godot_variant* p_self); + public static extern godot_packed_vector2_array godotsharp_variant_as_packed_vector2_array( + godot_variant* p_self); [DllImport(GodotDllName)] - public static extern godot_packed_vector3_array godotsharp_variant_as_packed_vector3_array(godot_variant* p_self); + public static extern godot_packed_vector3_array godotsharp_variant_as_packed_vector3_array( + godot_variant* p_self); [DllImport(GodotDllName)] public static extern godot_packed_color_array godotsharp_variant_as_packed_color_array(godot_variant* p_self); + [DllImport(GodotDllName)] + public static extern godot_bool godotsharp_variant_equals(godot_variant* p_a, godot_variant* p_b); + // string.h [DllImport(GodotDllName)] @@ -264,11 +294,20 @@ namespace Godot.NativeInterop // array.h + [DllImport(GodotDllName)] + public static extern void godotsharp_array_new(godot_array* p_self); + [DllImport(GodotDllName)] public static extern void godotsharp_array_new_copy(godot_array* r_dest, godot_array* p_src); + [DllImport(GodotDllName)] + public static extern godot_variant* godotsharp_array_ptrw(ref godot_array p_self); + // dictionary.h + [DllImport(GodotDllName)] + public static extern void godotsharp_dictionary_new(godot_dictionary* p_self); + [DllImport(GodotDllName)] public static extern void godotsharp_dictionary_new_copy(godot_dictionary* r_dest, godot_dictionary* p_src); @@ -324,5 +363,127 @@ namespace Godot.NativeInterop [DllImport(GodotDllName)] public static extern void godotsharp_dictionary_destroy(ref godot_dictionary p_self); + + // Array + + [DllImport(GodotDllName)] + public static extern int godotsharp_array_add(ref godot_array p_self, godot_variant* p_item); + + [DllImport(GodotDllName)] + public static extern void + godotsharp_array_duplicate(ref godot_array p_self, godot_bool p_deep, out godot_array r_dest); + + [DllImport(GodotDllName)] + public static extern int godotsharp_array_index_of(ref godot_array p_self, godot_variant* p_item); + + [DllImport(GodotDllName)] + public static extern void godotsharp_array_insert(ref godot_array p_self, int p_index, godot_variant* p_item); + + [DllImport(GodotDllName)] + public static extern void godotsharp_array_remove_at(ref godot_array p_self, int p_index); + + [DllImport(GodotDllName)] + public static extern Error godotsharp_array_resize(ref godot_array p_self, int p_new_size); + + [DllImport(GodotDllName)] + public static extern Error godotsharp_array_shuffle(ref godot_array p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_array_to_string(ref godot_array p_self, godot_string* r_str); + + // Dictionary + + [DllImport(GodotDllName)] + public static extern godot_bool godotsharp_dictionary_try_get_value(ref godot_dictionary p_self, + godot_variant* p_key, + out godot_variant r_value); + + [DllImport(GodotDllName)] + public static extern void godotsharp_dictionary_set_value(ref godot_dictionary p_self, godot_variant* p_key, + godot_variant* p_value); + + [DllImport(GodotDllName)] + public static extern void godotsharp_dictionary_keys(ref godot_dictionary p_self, out godot_array r_dest); + + [DllImport(GodotDllName)] + public static extern void godotsharp_dictionary_values(ref godot_dictionary p_self, out godot_array r_dest); + + [DllImport(GodotDllName)] + public static extern int godotsharp_dictionary_count(ref godot_dictionary p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_dictionary_key_value_pair_at(ref godot_dictionary p_self, int p_index, + out godot_variant r_key, out godot_variant r_value); + + [DllImport(GodotDllName)] + public static extern void godotsharp_dictionary_add(ref godot_dictionary p_self, godot_variant* p_key, + godot_variant* p_value); + + [DllImport(GodotDllName)] + public static extern void godotsharp_dictionary_clear(ref godot_dictionary p_self); + + [DllImport(GodotDllName)] + public static extern godot_bool godotsharp_dictionary_contains_key(ref godot_dictionary p_self, + godot_variant* p_key); + + [DllImport(GodotDllName)] + public static extern void godotsharp_dictionary_duplicate(ref godot_dictionary p_self, godot_bool p_deep, + out godot_dictionary r_dest); + + [DllImport(GodotDllName)] + public static extern godot_bool godotsharp_dictionary_remove_key(ref godot_dictionary p_self, + godot_variant* p_key); + + [DllImport(GodotDllName)] + public static extern void godotsharp_dictionary_to_string(ref godot_dictionary p_self, godot_string* r_str); + + // StringExtensions + + [DllImport(GodotDllName)] + public static extern void godotsharp_string_md5_buffer(godot_string* p_self, + godot_packed_byte_array* r_md5_buffer); + + [DllImport(GodotDllName)] + public static extern void godotsharp_string_md5_text(godot_string* p_self, godot_string* r_md5_text); + + [DllImport(GodotDllName)] + public static extern int godotsharp_string_rfind(godot_string* p_self, godot_string* p_what, int p_from); + + [DllImport(GodotDllName)] + public static extern int godotsharp_string_rfindn(godot_string* p_self, godot_string* p_what, int p_from); + + [DllImport(GodotDllName)] + public static extern void godotsharp_string_sha256_buffer(godot_string* p_self, + godot_packed_byte_array* r_sha256_buffer); + + [DllImport(GodotDllName)] + public static extern void godotsharp_string_sha256_text(godot_string* p_self, godot_string* r_sha256_text); + + // NodePath + + [DllImport(GodotDllName)] + public static extern void godotsharp_node_path_get_as_property_path(ref godot_node_path p_self, + ref godot_node_path r_dest); + + [DllImport(GodotDllName)] + public static extern void godotsharp_node_path_get_concatenated_subnames(ref godot_node_path p_self, + godot_string* r_subnames); + + [DllImport(GodotDllName)] + public static extern void godotsharp_node_path_get_name(ref godot_node_path p_self, int p_idx, + godot_string* r_name); + + [DllImport(GodotDllName)] + public static extern int godotsharp_node_path_get_name_count(ref godot_node_path p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_node_path_get_subname(ref godot_node_path p_self, int p_idx, + godot_string* r_subname); + + [DllImport(GodotDllName)] + public static extern int godotsharp_node_path_get_subname_count(ref godot_node_path p_self); + + [DllImport(GodotDllName)] + public static extern bool godotsharp_node_path_is_absolute(ref godot_node_path p_self); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs index 70df79c1de..6001b3a0de 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs @@ -29,6 +29,13 @@ namespace Godot.NativeInterop public static godot_node_path godotsharp_node_path_new_copy(godot_node_path src) => godotsharp_node_path_new_copy(&src); + public static godot_array godotsharp_array_new() + { + godot_array ret; + godotsharp_array_new(&ret); + return ret; + } + public static godot_array godotsharp_array_new_copy(godot_array* src) { godot_array ret; @@ -40,6 +47,13 @@ namespace Godot.NativeInterop public static godot_array godotsharp_array_new_copy(godot_array src) => godotsharp_array_new_copy(&src); + public static godot_dictionary godotsharp_dictionary_new() + { + godot_dictionary ret; + godotsharp_dictionary_new(&ret); + return ret; + } + public static godot_dictionary godotsharp_dictionary_new_copy(godot_dictionary* src) { godot_dictionary ret; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs index d0d6606936..da318804eb 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs @@ -63,61 +63,46 @@ namespace Godot public NodePath GetAsPropertyPath() { godot_node_path propertyPath = default; - godot_icall_NodePath_get_as_property_path(ref NativeValue, ref propertyPath); + NativeFuncs.godotsharp_node_path_get_as_property_path(ref NativeValue, ref propertyPath); return CreateTakingOwnershipOfDisposableValue(propertyPath); } - public string GetConcatenatedSubNames() + public unsafe string GetConcatenatedSubNames() { - return godot_icall_NodePath_get_concatenated_subnames(ref NativeValue); + using godot_string subNames = default; + NativeFuncs.godotsharp_node_path_get_concatenated_subnames(ref NativeValue, &subNames); + return Marshaling.mono_string_from_godot(&subNames); } - public string GetName(int idx) + public unsafe string GetName(int idx) { - return godot_icall_NodePath_get_name(ref NativeValue, idx); + using godot_string name = default; + NativeFuncs.godotsharp_node_path_get_name(ref NativeValue, idx, &name); + return Marshaling.mono_string_from_godot(&name); } public int GetNameCount() { - return godot_icall_NodePath_get_name_count(ref NativeValue); + return NativeFuncs.godotsharp_node_path_get_name_count(ref NativeValue); } - public string GetSubName(int idx) + public unsafe string GetSubName(int idx) { - return godot_icall_NodePath_get_subname(ref NativeValue, idx); + using godot_string subName = default; + NativeFuncs.godotsharp_node_path_get_subname(ref NativeValue, idx, &subName); + return Marshaling.mono_string_from_godot(&subName); } public int GetSubNameCount() { - return godot_icall_NodePath_get_subname_count(ref NativeValue); + return NativeFuncs.godotsharp_node_path_get_subname_count(ref NativeValue); } public bool IsAbsolute() { - return godot_icall_NodePath_is_absolute(ref NativeValue); + return NativeFuncs.godotsharp_node_path_is_absolute(ref NativeValue); } public bool IsEmpty => godot_node_path.IsEmpty(in NativeValue); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void godot_icall_NodePath_get_as_property_path(ref godot_node_path ptr, ref godot_node_path dest); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string godot_icall_NodePath_get_concatenated_subnames(ref godot_node_path ptr); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string godot_icall_NodePath_get_name(ref godot_node_path ptr, int arg1); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern int godot_icall_NodePath_get_name_count(ref godot_node_path ptr); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string godot_icall_NodePath_get_subname(ref godot_node_path ptr, int arg1); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern int godot_icall_NodePath_get_subname_count(ref godot_node_path ptr); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern bool godot_icall_NodePath_is_absolute(ref godot_node_path ptr); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs index 6009173843..0f97ba9cb6 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs @@ -5,6 +5,7 @@ using System.Runtime.CompilerServices; using System.Security; using System.Text; using System.Text.RegularExpressions; +using Godot.NativeInterop; namespace Godot { @@ -157,6 +158,7 @@ namespace Godot { return 0; } + if (from == 0 && to == len) { str = instance; @@ -355,7 +357,8 @@ namespace Godot /// The starting position of the substring, or -1 if not found. public static int Find(this string instance, string what, int from = 0, bool caseSensitive = true) { - return instance.IndexOf(what, from, caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase); + return instance.IndexOf(what, from, + caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase); } /// @@ -366,7 +369,8 @@ namespace Godot { // TODO: Could be more efficient if we get a char version of `IndexOf`. // See https://github.com/dotnet/runtime/issues/44116 - return instance.IndexOf(what.ToString(), from, caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase); + return instance.IndexOf(what.ToString(), from, + caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase); } /// Find the last occurrence of a substring. @@ -380,7 +384,8 @@ namespace Godot /// The starting position of the substring, or -1 if not found. public static int FindLast(this string instance, string what, int from, bool caseSensitive = true) { - return instance.LastIndexOf(what, from, caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase); + return instance.LastIndexOf(what, from, + caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase); } /// @@ -623,6 +628,7 @@ namespace Godot { match = instance[source] == text[target]; } + if (match) { source++; @@ -682,9 +688,9 @@ namespace Godot } bool validChar = instance[i] >= '0' && - instance[i] <= '9' || instance[i] >= 'a' && - instance[i] <= 'z' || instance[i] >= 'A' && - instance[i] <= 'Z' || instance[i] == '_'; + instance[i] <= '9' || instance[i] >= 'a' && + instance[i] <= 'z' || instance[i] >= 'A' && + instance[i] <= 'Z' || instance[i] == '_'; if (!validChar) return false; @@ -808,15 +814,18 @@ namespace Godot switch (expr[0]) { case '*': - return ExprMatch(instance, expr.Substring(1), caseSensitive) || (instance.Length > 0 && ExprMatch(instance.Substring(1), expr, caseSensitive)); + return ExprMatch(instance, expr.Substring(1), caseSensitive) || (instance.Length > 0 && + ExprMatch(instance.Substring(1), expr, caseSensitive)); case '?': - return instance.Length > 0 && instance[0] != '.' && ExprMatch(instance.Substring(1), expr.Substring(1), caseSensitive); + return instance.Length > 0 && instance[0] != '.' && + ExprMatch(instance.Substring(1), expr.Substring(1), caseSensitive); default: if (instance.Length == 0) return false; if (caseSensitive) return instance[0] == expr[0]; - return (char.ToUpper(instance[0]) == char.ToUpper(expr[0])) && ExprMatch(instance.Substring(1), expr.Substring(1), caseSensitive); + return (char.ToUpper(instance[0]) == char.ToUpper(expr[0])) && + ExprMatch(instance.Substring(1), expr.Substring(1), caseSensitive); } } @@ -847,25 +856,25 @@ namespace Godot /// /// Return the MD5 hash of the string as an array of bytes. /// - public static byte[] MD5Buffer(this string instance) + public static unsafe byte[] MD5Buffer(this string instance) { - return godot_icall_String_md5_buffer(instance); + using godot_string instanceStr = Marshaling.mono_string_to_godot(instance); + using godot_packed_byte_array md5Buffer = default; + NativeFuncs.godotsharp_string_md5_buffer(&instanceStr, &md5Buffer); + return Marshaling.PackedByteArray_to_mono_array(&md5Buffer); } - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static byte[] godot_icall_String_md5_buffer(string str); - /// /// Return the MD5 hash of the string as a string. /// - public static string MD5Text(this string instance) + public static unsafe string MD5Text(this string instance) { - return godot_icall_String_md5_text(instance); + using godot_string instanceStr = Marshaling.mono_string_to_godot(instance); + using godot_string md5Text = default; + NativeFuncs.godotsharp_string_md5_text(&instanceStr, &md5Text); + return Marshaling.mono_string_from_godot(&md5Text); } - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static string godot_icall_String_md5_text(string str); - /// /// Perform a case-insensitive comparison to another string, return -1 if less, 0 if equal and +1 if greater. /// @@ -981,26 +990,24 @@ namespace Godot /// /// Perform a search for a substring, but start from the end of the string instead of the beginning. /// - public static int RFind(this string instance, string what, int from = -1) + public static unsafe int RFind(this string instance, string what, int from = -1) { - return godot_icall_String_rfind(instance, what, from); + using godot_string instanceStr = Marshaling.mono_string_to_godot(instance); + using godot_string whatStr = Marshaling.mono_string_to_godot(instance); + return NativeFuncs.godotsharp_string_rfind(&instanceStr, &whatStr, from); } - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static int godot_icall_String_rfind(string str, string what, int from); - /// /// Perform a search for a substring, but start from the end of the string instead of the beginning. /// Also search case-insensitive. /// - public static int RFindN(this string instance, string what, int from = -1) + public static unsafe int RFindN(this string instance, string what, int from = -1) { - return godot_icall_String_rfindn(instance, what, from); + using godot_string instanceStr = Marshaling.mono_string_to_godot(instance); + using godot_string whatStr = Marshaling.mono_string_to_godot(instance); + return NativeFuncs.godotsharp_string_rfindn(&instanceStr, &whatStr, from); } - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static int godot_icall_String_rfindn(string str, string what, int from); - /// /// Return the right side of the string from a given position. /// @@ -1042,25 +1049,25 @@ namespace Godot return instance.Substr(0, end + 1); } - public static byte[] SHA256Buffer(this string instance) + public static unsafe byte[] SHA256Buffer(this string instance) { - return godot_icall_String_sha256_buffer(instance); + using godot_string instanceStr = Marshaling.mono_string_to_godot(instance); + using godot_packed_byte_array sha256Buffer = default; + NativeFuncs.godotsharp_string_sha256_buffer(&instanceStr, &sha256Buffer); + return Marshaling.PackedByteArray_to_mono_array(&sha256Buffer); } - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static byte[] godot_icall_String_sha256_buffer(string str); - /// /// Return the SHA-256 hash of the string as a string. /// - public static string SHA256Text(this string instance) + public static unsafe string SHA256Text(this string instance) { - return godot_icall_String_sha256_text(instance); + using godot_string instanceStr = Marshaling.mono_string_to_godot(instance); + using godot_string sha256Text = default; + NativeFuncs.godotsharp_string_sha256_text(&instanceStr, &sha256Text); + return Marshaling.mono_string_from_godot(&sha256Text); } - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static string godot_icall_String_sha256_text(string str); - /// /// Return the similarity index of the text compared to this string. /// 1 means totally similar and 0 means totally dissimilar. @@ -1072,6 +1079,7 @@ namespace Godot // Equal strings are totally similar return 1.0f; } + if (instance.Length < 2 || text.Length < 2) { // No way to calculate similarity without a single bigram @@ -1108,7 +1116,8 @@ namespace Godot /// public static string[] Split(this string instance, string divisor, bool allowEmpty = true) { - return instance.Split(new[] { divisor }, allowEmpty ? StringSplitOptions.None : StringSplitOptions.RemoveEmptyEntries); + return instance.Split(new[] { divisor }, + allowEmpty ? StringSplitOptions.None : StringSplitOptions.RemoveEmptyEntries); } /// @@ -1137,7 +1146,8 @@ namespace Godot return ret.ToArray(); } - private static readonly char[] _nonPrintable = { + private static readonly char[] _nonPrintable = + { (char)00, (char)01, (char)02, (char)03, (char)04, (char)05, (char)06, (char)07, (char)08, (char)09, (char)10, (char)11, (char)12, (char)13, (char)14, (char)15, (char)16, (char)17, diff --git a/modules/mono/glue/collections_glue.cpp b/modules/mono/glue/collections_glue.cpp deleted file mode 100644 index 09072cce67..0000000000 --- a/modules/mono/glue/collections_glue.cpp +++ /dev/null @@ -1,300 +0,0 @@ -/*************************************************************************/ -/* collections_glue.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include - -#include "core/variant/array.h" - -#include "../mono_gd/gd_mono_cache.h" -#include "../mono_gd/gd_mono_class.h" -#include "../mono_gd/gd_mono_marshal.h" -#include "../mono_gd/gd_mono_utils.h" - -void godot_icall_Array_Ctor(Array *r_dest) { - memnew_placement(r_dest, Array); -} - -void godot_icall_Array_At(Array *ptr, int32_t index, Variant *r_elem) { - if (index < 0 || index >= ptr->size()) { - GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range()); - *r_elem = Variant(); - return; - } - *r_elem = ptr->operator[](index); -} - -void godot_icall_Array_SetAt(Array *ptr, int32_t index, MonoObject *value) { - if (index < 0 || index >= ptr->size()) { - GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range()); - return; - } - ptr->operator[](index) = GDMonoMarshal::mono_object_to_variant(value); -} - -int32_t godot_icall_Array_Count(Array *ptr) { - return ptr->size(); -} - -int32_t godot_icall_Array_Add(Array *ptr, MonoObject *item) { - ptr->append(GDMonoMarshal::mono_object_to_variant(item)); - return ptr->size(); -} - -void godot_icall_Array_Clear(Array *ptr) { - ptr->clear(); -} - -MonoBoolean godot_icall_Array_Contains(Array *ptr, MonoObject *item) { - return ptr->find(GDMonoMarshal::mono_object_to_variant(item)) != -1; -} - -void godot_icall_Array_CopyTo(Array *ptr, MonoArray *array, int32_t array_index) { - unsigned int count = ptr->size(); - - if (mono_array_length(array) < (array_index + count)) { - MonoException *exc = mono_get_exception_argument("", "Destination array was not long enough. Check destIndex and length, and the array's lower bounds."); - GDMonoUtils::set_pending_exception(exc); - return; - } - - for (unsigned int i = 0; i < count; i++) { - MonoObject *boxed = GDMonoMarshal::variant_to_mono_object(ptr->operator[](i)); - mono_array_setref(array, array_index, boxed); - array_index++; - } -} - -void godot_icall_Array_Ctor_MonoArray(MonoArray *mono_array, Array *r_dest) { - memnew_placement(r_dest, Array); - unsigned int count = mono_array_length(mono_array); - r_dest->resize(count); - for (unsigned int i = 0; i < count; i++) { - MonoObject *item = mono_array_get(mono_array, MonoObject *, i); - godot_icall_Array_SetAt(r_dest, i, item); - } -} - -void godot_icall_Array_Duplicate(Array *ptr, MonoBoolean deep, Array *r_dest) { - memnew_placement(r_dest, Array(ptr->duplicate(deep))); -} - -void godot_icall_Array_Concatenate(Array *left, Array *right, Array *r_dest) { - int count = left->size() + right->size(); - memnew_placement(r_dest, Array(left->duplicate(false))); - r_dest->resize(count); - for (unsigned int i = 0; i < (unsigned int)right->size(); i++) { - r_dest->operator[](i + left->size()) = right->operator[](i); - } -} - -int32_t godot_icall_Array_IndexOf(Array *ptr, MonoObject *item) { - return ptr->find(GDMonoMarshal::mono_object_to_variant(item)); -} - -void godot_icall_Array_Insert(Array *ptr, int32_t index, MonoObject *item) { - if (index < 0 || index > ptr->size()) { - GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range()); - return; - } - ptr->insert(index, GDMonoMarshal::mono_object_to_variant(item)); -} - -MonoBoolean godot_icall_Array_Remove(Array *ptr, MonoObject *item) { - int idx = ptr->find(GDMonoMarshal::mono_object_to_variant(item)); - if (idx >= 0) { - ptr->remove(idx); - return true; - } - return false; -} - -void godot_icall_Array_RemoveAt(Array *ptr, int32_t index) { - if (index < 0 || index >= ptr->size()) { - GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range()); - return; - } - ptr->remove(index); -} - -int32_t godot_icall_Array_Resize(Array *ptr, int32_t new_size) { - return (int32_t)ptr->resize(new_size); -} - -void godot_icall_Array_Shuffle(Array *ptr) { - ptr->shuffle(); -} - -MonoString *godot_icall_Array_ToString(Array *ptr) { - return GDMonoMarshal::mono_string_from_godot(Variant(*ptr).operator String()); -} - -void godot_icall_Dictionary_Ctor(Dictionary *r_dest) { - memnew_placement(r_dest, Dictionary); -} - -void godot_icall_Dictionary_GetValue(Dictionary *ptr, MonoObject *key, Variant *r_value) { - Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key)); - if (ret == nullptr) { - MonoObject *exc = mono_object_new(mono_domain_get(), CACHED_CLASS(KeyNotFoundException)->get_mono_ptr()); -#ifdef DEBUG_ENABLED - CRASH_COND(!exc); -#endif - GDMonoUtils::runtime_object_init(exc, CACHED_CLASS(KeyNotFoundException)); - GDMonoUtils::set_pending_exception((MonoException *)exc); - *r_value = Variant(); - return; - } - *r_value = ret; -} - -void godot_icall_Dictionary_SetValue(Dictionary *ptr, MonoObject *key, MonoObject *value) { - ptr->operator[](GDMonoMarshal::mono_object_to_variant(key)) = GDMonoMarshal::mono_object_to_variant(value); -} - -void godot_icall_Dictionary_Keys(Dictionary *ptr, Array *r_dest) { - memnew_placement(r_dest, Array(ptr->keys())); -} - -void godot_icall_Dictionary_Values(Dictionary *ptr, Array *r_dest) { - memnew_placement(r_dest, Array(ptr->values())); -} - -int32_t godot_icall_Dictionary_Count(Dictionary *ptr) { - return ptr->size(); -} - -int32_t godot_icall_Dictionary_KeyValuePairs(Dictionary *ptr, Array **keys, Array **values) { - memnew_placement(*keys, Array(ptr->keys())); - memnew_placement(*values, Array(ptr->values())); - return ptr->size(); -} - -void godot_icall_Dictionary_KeyValuePairAt(Dictionary *ptr, int index, MonoObject **key, MonoObject **value) { - *key = GDMonoMarshal::variant_to_mono_object(ptr->get_key_at_index(index)); - *value = GDMonoMarshal::variant_to_mono_object(ptr->get_value_at_index(index)); -} - -void godot_icall_Dictionary_Add(Dictionary *ptr, MonoObject *key, MonoObject *value) { - Variant varKey = GDMonoMarshal::mono_object_to_variant(key); - Variant *ret = ptr->getptr(varKey); - if (ret != nullptr) { - GDMonoUtils::set_pending_exception(mono_get_exception_argument("key", "An element with the same key already exists")); - return; - } - ptr->operator[](varKey) = GDMonoMarshal::mono_object_to_variant(value); -} - -void godot_icall_Dictionary_Clear(Dictionary *ptr) { - ptr->clear(); -} - -MonoBoolean godot_icall_Dictionary_Contains(Dictionary *ptr, MonoObject *key, MonoObject *value) { - // no dupes - Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key)); - return ret != nullptr && *ret == GDMonoMarshal::mono_object_to_variant(value); -} - -MonoBoolean godot_icall_Dictionary_ContainsKey(Dictionary *ptr, MonoObject *key) { - return ptr->has(GDMonoMarshal::mono_object_to_variant(key)); -} - -void godot_icall_Dictionary_Duplicate(Dictionary *ptr, MonoBoolean deep, Dictionary *r_dest) { - memnew_placement(r_dest, Dictionary(ptr->duplicate(deep))); -} - -MonoBoolean godot_icall_Dictionary_RemoveKey(Dictionary *ptr, MonoObject *key) { - return ptr->erase(GDMonoMarshal::mono_object_to_variant(key)); -} - -MonoBoolean godot_icall_Dictionary_Remove(Dictionary *ptr, MonoObject *key, MonoObject *value) { - Variant varKey = GDMonoMarshal::mono_object_to_variant(key); - - // no dupes - Variant *ret = ptr->getptr(varKey); - if (ret != nullptr && *ret == GDMonoMarshal::mono_object_to_variant(value)) { - ptr->erase(varKey); - return true; - } - - return false; -} - -MonoBoolean godot_icall_Dictionary_TryGetValue(Dictionary *ptr, MonoObject *key, Variant *value) { - Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key)); - if (ret == nullptr) { - *value = Variant(); - return false; - } - *value = ret; - return true; -} - -MonoString *godot_icall_Dictionary_ToString(Dictionary *ptr) { - return GDMonoMarshal::mono_string_from_godot(Variant(*ptr).operator String()); -} - -void godot_register_collections_icalls() { - GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Ctor", godot_icall_Array_Ctor); - GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Ctor_MonoArray", godot_icall_Array_Ctor_MonoArray); - GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_At", godot_icall_Array_At); - GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_SetAt", godot_icall_Array_SetAt); - GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Count", godot_icall_Array_Count); - GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Add", godot_icall_Array_Add); - GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Clear", godot_icall_Array_Clear); - GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Concatenate", godot_icall_Array_Concatenate); - GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Contains", godot_icall_Array_Contains); - GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_CopyTo", godot_icall_Array_CopyTo); - GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Duplicate", godot_icall_Array_Duplicate); - GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_IndexOf", godot_icall_Array_IndexOf); - GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Insert", godot_icall_Array_Insert); - GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Remove", godot_icall_Array_Remove); - GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_RemoveAt", godot_icall_Array_RemoveAt); - GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Resize", godot_icall_Array_Resize); - GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Shuffle", godot_icall_Array_Shuffle); - GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_ToString", godot_icall_Array_ToString); - - GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Ctor", godot_icall_Dictionary_Ctor); - GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_GetValue", godot_icall_Dictionary_GetValue); - GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_SetValue", godot_icall_Dictionary_SetValue); - GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Keys", godot_icall_Dictionary_Keys); - GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Values", godot_icall_Dictionary_Values); - GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Count", godot_icall_Dictionary_Count); - GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_KeyValuePairs", godot_icall_Dictionary_KeyValuePairs); - GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_KeyValuePairAt", godot_icall_Dictionary_KeyValuePairAt); - GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Add", godot_icall_Dictionary_Add); - GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Clear", godot_icall_Dictionary_Clear); - GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Contains", godot_icall_Dictionary_Contains); - GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_ContainsKey", godot_icall_Dictionary_ContainsKey); - GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Duplicate", godot_icall_Dictionary_Duplicate); - GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_RemoveKey", godot_icall_Dictionary_RemoveKey); - GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Remove", godot_icall_Dictionary_Remove); - GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_TryGetValue", godot_icall_Dictionary_TryGetValue); - GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_ToString", godot_icall_Dictionary_ToString); -} diff --git a/modules/mono/glue/node_path_glue.cpp b/modules/mono/glue/node_path_glue.cpp deleted file mode 100644 index 56962ea9da..0000000000 --- a/modules/mono/glue/node_path_glue.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/*************************************************************************/ -/* node_path_glue.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "core/string/node_path.h" -#include "core/string/ustring.h" - -#include "../mono_gd/gd_mono_marshal.h" - -MonoBoolean godot_icall_NodePath_is_absolute(NodePath *p_ptr) { - return (MonoBoolean)p_ptr->is_absolute(); -} - -int32_t godot_icall_NodePath_get_name_count(NodePath *p_ptr) { - return p_ptr->get_name_count(); -} - -MonoString *godot_icall_NodePath_get_name(NodePath *p_ptr, uint32_t p_idx) { - return GDMonoMarshal::mono_string_from_godot(p_ptr->get_name(p_idx)); -} - -int32_t godot_icall_NodePath_get_subname_count(NodePath *p_ptr) { - return p_ptr->get_subname_count(); -} - -MonoString *godot_icall_NodePath_get_subname(NodePath *p_ptr, uint32_t p_idx) { - return GDMonoMarshal::mono_string_from_godot(p_ptr->get_subname(p_idx)); -} - -MonoString *godot_icall_NodePath_get_concatenated_subnames(NodePath *p_ptr) { - return GDMonoMarshal::mono_string_from_godot(p_ptr->get_concatenated_subnames()); -} - -void godot_icall_NodePath_get_as_property_path(NodePath *p_ptr, NodePath *r_dest) { - *r_dest = p_ptr->get_as_property_path(); -} - -void godot_register_node_path_icalls() { - GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_as_property_path", godot_icall_NodePath_get_as_property_path); - GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_concatenated_subnames", godot_icall_NodePath_get_concatenated_subnames); - GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_name", godot_icall_NodePath_get_name); - GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_name_count", godot_icall_NodePath_get_name_count); - GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_subname", godot_icall_NodePath_get_subname); - GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_subname_count", godot_icall_NodePath_get_subname_count); - GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_is_absolute", godot_icall_NodePath_is_absolute); -} diff --git a/modules/mono/glue/runtime_interop.cpp b/modules/mono/glue/runtime_interop.cpp index aa6f36d898..6c2790a533 100644 --- a/modules/mono/glue/runtime_interop.cpp +++ b/modules/mono/glue/runtime_interop.cpp @@ -56,6 +56,9 @@ extern "C" { #define GD_PINVOKE_EXPORT MAYBE_UNUSED #endif +// For ArrayPrivate and DictionaryPrivate +static_assert(sizeof(SafeRefCount) == sizeof(uint32_t)); + typedef Object *(*godotsharp_class_creation_func)(); GD_PINVOKE_EXPORT MethodBind *godotsharp_method_bind_get_method(const StringName *p_classname, const char16_t *p_methodname) { @@ -185,7 +188,7 @@ GD_PINVOKE_EXPORT void godotsharp_packed_string_array_add(PackedStringArray *r_d GD_PINVOKE_EXPORT void godotsharp_callable_new_with_delegate(void *p_delegate_handle, Callable *r_callable) { // TODO: Use pooling for ManagedCallable instances. CallableCustom *managed_callable = memnew(ManagedCallable(p_delegate_handle)); - *r_callable = Callable(managed_callable); + memnew_placement(r_callable, Callable(managed_callable)); } GD_PINVOKE_EXPORT bool godotsharp_callable_get_data_for_marshalling(const Callable *p_callable, @@ -198,31 +201,31 @@ GD_PINVOKE_EXPORT bool godotsharp_callable_get_data_for_marshalling(const Callab ManagedCallable *managed_callable = static_cast(custom); *r_delegate_handle = managed_callable->get_delegate(); *r_object = nullptr; - *r_name = StringName(); + memnew_placement(r_name, StringName()); return true; } else if (compare_equal_func == SignalAwaiterCallable::compare_equal_func_ptr) { SignalAwaiterCallable *signal_awaiter_callable = static_cast(custom); *r_delegate_handle = nullptr; *r_object = ObjectDB::get_instance(signal_awaiter_callable->get_object()); - *r_name = signal_awaiter_callable->get_signal(); + memnew_placement(r_name, StringName(signal_awaiter_callable->get_signal())); return true; } else if (compare_equal_func == EventSignalCallable::compare_equal_func_ptr) { EventSignalCallable *event_signal_callable = static_cast(custom); *r_delegate_handle = nullptr; *r_object = ObjectDB::get_instance(event_signal_callable->get_object()); - *r_name = event_signal_callable->get_signal(); + memnew_placement(r_name, StringName(event_signal_callable->get_signal())); return true; } // Some other CallableCustom. We only support ManagedCallable. *r_delegate_handle = nullptr; *r_object = nullptr; - *r_name = StringName(); + memnew_placement(r_name, StringName()); return false; } else { *r_delegate_handle = nullptr; *r_object = ObjectDB::get_instance(p_callable->get_object_id()); - *r_name = p_callable->get_method(); + memnew_placement(r_name, StringName(p_callable->get_method())); return true; } } @@ -235,7 +238,7 @@ GD_PINVOKE_EXPORT void godotsharp_method_bind_ptrcall(godot_method_bind *p_metho godot_method_bind_ptrcall(p_method_bind, p_instance, p_args, p_ret); } -GD_PINVOKE_EXPORT godot_variant godotsharp_method_bind_call(godot_method_bind *p_method_bind, godot_object *p_instance, const godot_variant **p_args, const int p_arg_count, godot_variant_call_error *p_call_error) { +GD_PINVOKE_EXPORT godot_variant godotsharp_method_bind_call(godot_method_bind *p_method_bind, godot_object *p_instance, const godot_variant **p_args, const int32_t p_arg_count, godot_variant_call_error *p_call_error) { return godot_method_bind_call(p_method_bind, p_instance, p_args, p_arg_count, p_call_error); } @@ -445,6 +448,10 @@ GD_PINVOKE_EXPORT godot_packed_color_array godotsharp_variant_as_packed_color_ar return godot_variant_as_packed_color_array(p_self); } +GD_PINVOKE_EXPORT bool godotsharp_variant_equals(const godot_variant *p_a, const godot_variant *p_b) { + return *reinterpret_cast(p_a) == *reinterpret_cast(p_b); +} + // string.h GD_PINVOKE_EXPORT void godotsharp_string_new_with_utf16_chars(godot_string *r_dest, const char16_t *p_contents) { @@ -465,12 +472,24 @@ GD_PINVOKE_EXPORT void godotsharp_node_path_new_copy(godot_node_path *r_dest, co // array.h +GD_PINVOKE_EXPORT void godotsharp_array_new(godot_array *r_dest) { + godot_array_new(r_dest); +} + GD_PINVOKE_EXPORT void godotsharp_array_new_copy(godot_array *r_dest, const godot_array *p_src) { godot_array_new_copy(r_dest, p_src); } +GD_PINVOKE_EXPORT godot_variant *godotsharp_array_ptrw(godot_array *p_self) { + return reinterpret_cast(&reinterpret_cast(p_self)->operator[](0)); +} + // dictionary.h +GD_PINVOKE_EXPORT void godotsharp_dictionary_new(godot_dictionary *r_dest) { + godot_dictionary_new(r_dest); +} + GD_PINVOKE_EXPORT void godotsharp_dictionary_new_copy(godot_dictionary *r_dest, const godot_dictionary *p_src) { godot_dictionary_new_copy(r_dest, p_src); } @@ -545,12 +564,148 @@ GD_PINVOKE_EXPORT void godotsharp_dictionary_destroy(godot_dictionary *p_self) { godot_dictionary_destroy(p_self); } +// Array + +int32_t godotsharp_array_add(Array *p_self, const Variant *p_item) { + p_self->append(*p_item); + return p_self->size(); +} + +void godotsharp_array_duplicate(const Array *p_self, bool p_deep, Array *r_dest) { + memnew_placement(r_dest, Array(p_self->duplicate(p_deep))); +} + +int32_t godotsharp_array_index_of(const Array *p_self, const Variant *p_item) { + return p_self->find(*p_item); +} + +void godotsharp_array_insert(Array *p_self, int32_t p_index, const Variant *p_item) { + p_self->insert(p_index, *p_item); +} + +void godotsharp_array_remove_at(Array *p_self, int32_t p_index) { + p_self->remove(p_index); +} + +int32_t godotsharp_array_resize(Array *p_self, int32_t p_new_size) { + return (int32_t)p_self->resize(p_new_size); +} + +void godotsharp_array_shuffle(Array *p_self) { + p_self->shuffle(); +} + +// Dictionary + +bool godotsharp_dictionary_try_get_value(const Dictionary *p_self, const Variant *p_key, Variant *r_value) { + const Variant *ret = p_self->getptr(*p_key); + if (ret == nullptr) { + memnew_placement(r_value, Variant()); + return false; + } + memnew_placement(r_value, Variant(*ret)); + return true; +} + +void godotsharp_dictionary_set_value(Dictionary *p_self, const Variant *p_key, const Variant *p_value) { + p_self->operator[](*p_key) = *p_value; +} + +void godotsharp_dictionary_keys(const Dictionary *p_self, Array *r_dest) { + memnew_placement(r_dest, Array(p_self->keys())); +} + +void godotsharp_dictionary_values(const Dictionary *p_self, Array *r_dest) { + memnew_placement(r_dest, Array(p_self->values())); +} + +int32_t godotsharp_dictionary_count(const Dictionary *p_self) { + return p_self->size(); +} + +void godotsharp_dictionary_key_value_pair_at(const Dictionary *p_self, int32_t p_index, Variant *r_key, Variant *r_value) { + memnew_placement(r_key, Variant(p_self->get_key_at_index(p_index))); + memnew_placement(r_value, Variant(p_self->get_value_at_index(p_index))); +} + +void godotsharp_dictionary_add(Dictionary *p_self, const Variant *p_key, const Variant *p_value) { + p_self->operator[](*p_key) = *p_value; +} + +void godotsharp_dictionary_clear(Dictionary *p_self) { + p_self->clear(); +} + +bool godotsharp_dictionary_contains_key(const Dictionary *p_self, const Variant *p_key) { + return p_self->has(*p_key); +} + +void godotsharp_dictionary_duplicate(const Dictionary *p_self, bool p_deep, Dictionary *r_dest) { + memnew_placement(r_dest, Dictionary(p_self->duplicate(p_deep))); +} + +bool godotsharp_dictionary_remove_key(Dictionary *p_self, const Variant *p_key) { + return p_self->erase(*p_key); +} + +void godotsharp_string_md5_buffer(const String *p_self, PackedByteArray *r_md5_buffer) { + memnew_placement(r_md5_buffer, PackedByteArray(p_self->md5_buffer())); +} + +void godotsharp_string_md5_text(const String *p_self, String *r_md5_text) { + memnew_placement(r_md5_text, String(p_self->md5_text())); +} + +int32_t godotsharp_string_rfind(const String *p_self, const String *p_what, int32_t p_from) { + return p_self->rfind(*p_what, p_from); +} + +int32_t godotsharp_string_rfindn(const String *p_self, const String *p_what, int32_t p_from) { + return p_self->rfindn(*p_what, p_from); +} + +void godotsharp_string_sha256_buffer(const String *p_self, PackedByteArray *r_sha256_buffer) { + memnew_placement(r_sha256_buffer, PackedByteArray(p_self->sha256_buffer())); +} + +void godotsharp_string_sha256_text(const String *p_self, String *r_sha256_text) { + memnew_placement(r_sha256_text, String(p_self->sha256_text())); +} + +void godotsharp_node_path_get_as_property_path(const NodePath *p_ptr, NodePath *r_dest) { + memnew_placement(r_dest, NodePath(p_ptr->get_as_property_path())); +} + +void godotsharp_node_path_get_concatenated_subnames(const NodePath *p_self, String *r_subnames) { + memnew_placement(r_subnames, String(p_self->get_concatenated_subnames())); +} + +void godotsharp_node_path_get_name(const NodePath *p_self, uint32_t p_idx, String *r_name) { + memnew_placement(r_name, String(p_self->get_name(p_idx))); +} + +int32_t godotsharp_node_path_get_name_count(const NodePath *p_self) { + return p_self->get_name_count(); +} + +void godotsharp_node_path_get_subname(const NodePath *p_self, uint32_t p_idx, String *r_subname) { + memnew_placement(r_subname, String(p_self->get_subname(p_idx))); +} + +int32_t godotsharp_node_path_get_subname_count(const NodePath *p_self) { + return p_self->get_subname_count(); +} + +bool godotsharp_node_path_is_absolute(const NodePath *p_self) { + return p_self->is_absolute(); +} + #ifdef __cplusplus } #endif // We need this to prevent the functions from being stripped. -void *godotsharp_pinvoke_funcs[95] = { +void *godotsharp_pinvoke_funcs[130] = { (void *)godotsharp_method_bind_get_method, (void *)godotsharp_get_class_constructor, (void *)godotsharp_invoke_class_constructor, @@ -624,10 +779,14 @@ void *godotsharp_pinvoke_funcs[95] = { (void *)godotsharp_variant_as_packed_vector2_array, (void *)godotsharp_variant_as_packed_vector3_array, (void *)godotsharp_variant_as_packed_color_array, + (void *)godotsharp_variant_equals, (void *)godotsharp_string_new_with_utf16_chars, (void *)godotsharp_string_name_new_copy, (void *)godotsharp_node_path_new_copy, + (void *)godotsharp_array_new, (void *)godotsharp_array_new_copy, + (void *)godotsharp_array_ptrw, + (void *)godotsharp_dictionary_new, (void *)godotsharp_dictionary_new_copy, (void *)godotsharp_packed_byte_array_destroy, (void *)godotsharp_packed_int32_array_destroy, @@ -645,5 +804,36 @@ void *godotsharp_pinvoke_funcs[95] = { (void *)godotsharp_signal_destroy, (void *)godotsharp_callable_destroy, (void *)godotsharp_array_destroy, - (void *)godotsharp_dictionary_destroy + (void *)godotsharp_dictionary_destroy, + (void *)godotsharp_array_add, + (void *)godotsharp_array_duplicate, + (void *)godotsharp_array_index_of, + (void *)godotsharp_array_insert, + (void *)godotsharp_array_remove_at, + (void *)godotsharp_array_resize, + (void *)godotsharp_array_shuffle, + (void *)godotsharp_dictionary_try_get_value, + (void *)godotsharp_dictionary_set_value, + (void *)godotsharp_dictionary_keys, + (void *)godotsharp_dictionary_values, + (void *)godotsharp_dictionary_count, + (void *)godotsharp_dictionary_key_value_pair_at, + (void *)godotsharp_dictionary_add, + (void *)godotsharp_dictionary_clear, + (void *)godotsharp_dictionary_contains_key, + (void *)godotsharp_dictionary_duplicate, + (void *)godotsharp_dictionary_remove_key, + (void *)godotsharp_string_md5_buffer, + (void *)godotsharp_string_md5_text, + (void *)godotsharp_string_rfind, + (void *)godotsharp_string_rfindn, + (void *)godotsharp_string_sha256_buffer, + (void *)godotsharp_string_sha256_text, + (void *)godotsharp_node_path_get_as_property_path, + (void *)godotsharp_node_path_get_concatenated_subnames, + (void *)godotsharp_node_path_get_name, + (void *)godotsharp_node_path_get_name_count, + (void *)godotsharp_node_path_get_subname, + (void *)godotsharp_node_path_get_subname_count, + (void *)godotsharp_node_path_is_absolute }; diff --git a/modules/mono/glue/string_glue.cpp b/modules/mono/glue/string_glue.cpp deleted file mode 100644 index c87084281a..0000000000 --- a/modules/mono/glue/string_glue.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/*************************************************************************/ -/* string_glue.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "core/string/ustring.h" -#include "core/templates/vector.h" -#include "core/variant/variant.h" - -#include "../mono_gd/gd_mono_marshal.h" - -MonoArray *godot_icall_String_md5_buffer(MonoString *p_str) { - Vector ret = GDMonoMarshal::mono_string_to_godot(p_str).md5_buffer(); - // TODO Check possible Array/Vector problem? - return GDMonoMarshal::Array_to_mono_array(Variant(ret)); -} - -MonoString *godot_icall_String_md5_text(MonoString *p_str) { - String ret = GDMonoMarshal::mono_string_to_godot(p_str).md5_text(); - return GDMonoMarshal::mono_string_from_godot(ret); -} - -int godot_icall_String_rfind(MonoString *p_str, MonoString *p_what, int p_from) { - String what = GDMonoMarshal::mono_string_to_godot(p_what); - return GDMonoMarshal::mono_string_to_godot(p_str).rfind(what, p_from); -} - -int godot_icall_String_rfindn(MonoString *p_str, MonoString *p_what, int p_from) { - String what = GDMonoMarshal::mono_string_to_godot(p_what); - return GDMonoMarshal::mono_string_to_godot(p_str).rfindn(what, p_from); -} - -MonoArray *godot_icall_String_sha256_buffer(MonoString *p_str) { - Vector ret = GDMonoMarshal::mono_string_to_godot(p_str).sha256_buffer(); - return GDMonoMarshal::Array_to_mono_array(Variant(ret)); -} - -MonoString *godot_icall_String_sha256_text(MonoString *p_str) { - String ret = GDMonoMarshal::mono_string_to_godot(p_str).sha256_text(); - return GDMonoMarshal::mono_string_from_godot(ret); -} - -void godot_register_string_icalls() { - GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_md5_buffer", godot_icall_String_md5_buffer); - GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_md5_text", godot_icall_String_md5_text); - GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_rfind", godot_icall_String_rfind); - GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_rfindn", godot_icall_String_rfindn); - GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_sha256_buffer", godot_icall_String_sha256_buffer); - GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_sha256_text", godot_icall_String_sha256_text); -} From 3f1a620102f914bf9a01a01bcc05b3994365024b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Rold=C3=A1n=20Etcheverry?= Date: Sun, 12 Sep 2021 19:50:13 +0200 Subject: [PATCH 3/6] C#: Re-write GD and some other icalls as P/Invoke --- modules/mono/csharp_script.cpp | 21 +- .../GodotSharp/GodotSharp/Core/Dispatcher.cs | 13 +- .../Core/Extensions/ObjectExtensions.cs | 20 +- .../glue/GodotSharp/GodotSharp/Core/GD.cs | 182 ++++------ .../Core/NativeInterop/InteropStructs.cs | 2 + .../Core/NativeInterop/Marshaling.cs | 14 +- .../Core/NativeInterop/NativeFuncs.cs | 78 +++++ .../GodotSharp/GodotSharp/Core/Object.base.cs | 10 +- .../GodotSharp/Core/ScriptManager.cs | 10 + .../GodotSharp/GodotSharp/GodotSharp.csproj | 1 + modules/mono/glue/base_object_glue.cpp | 36 -- modules/mono/glue/gd_glue.cpp | 315 ------------------ modules/mono/glue/runtime_interop.cpp | 204 +++++++++++- modules/mono/mono_gd/gd_mono.cpp | 13 +- modules/mono/mono_gd/gd_mono_cache.cpp | 18 +- modules/mono/mono_gd/gd_mono_cache.h | 3 - modules/mono/mono_gd/gd_mono_marshal.cpp | 55 +-- modules/mono/mono_gd/gd_mono_marshal.h | 9 - modules/mono/mono_gd/gd_mono_method.cpp | 10 +- 19 files changed, 422 insertions(+), 592 deletions(-) create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/ScriptManager.cs delete mode 100644 modules/mono/glue/gd_glue.cpp diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 409680b8fc..75e2efa8a4 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -106,7 +106,7 @@ Error CSharpLanguage::execute_file(const String &p_path) { return OK; } -extern void *godotsharp_pinvoke_funcs[130]; +extern void *godotsharp_pinvoke_funcs[154]; [[maybe_unused]] volatile void **do_not_strip_godotsharp_pinvoke_funcs; void CSharpLanguage::init() { @@ -717,19 +717,14 @@ void CSharpLanguage::pre_unsafe_unreference(Object *p_obj) { void CSharpLanguage::frame() { if (gdmono && gdmono->is_runtime_initialized() && gdmono->get_core_api_assembly() != nullptr) { - const Ref &task_scheduler_handle = GDMonoCache::cached_data.task_scheduler_handle; + MonoException *exc = nullptr; + gdmono->get_core_api_assembly() + ->get_class("Godot", "ScriptManager") + ->get_method("FrameCallback") + ->invoke(nullptr, &exc); - if (task_scheduler_handle.is_valid()) { - MonoObject *task_scheduler = task_scheduler_handle->get_target(); - - if (task_scheduler) { - MonoException *exc = nullptr; - CACHED_METHOD_THUNK(GodotTaskScheduler, Activate).invoke(task_scheduler, &exc); - - if (exc) { - GDMonoUtils::debug_unhandled_exception(exc); - } - } + if (exc) { + GDMonoUtils::debug_unhandled_exception(exc); } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs index 072e0f20ff..5f84bb530f 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs @@ -1,13 +1,14 @@ -using System.Runtime.CompilerServices; - namespace Godot { public static class Dispatcher { - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern GodotTaskScheduler godot_icall_DefaultGodotTaskScheduler(); + internal static GodotTaskScheduler DefaultGodotTaskScheduler; - public static GodotSynchronizationContext SynchronizationContext => - godot_icall_DefaultGodotTaskScheduler().Context; + private static void InitializeDefaultGodotTaskScheduler() + { + DefaultGodotTaskScheduler = new GodotTaskScheduler(); + } + + public static GodotSynchronizationContext SynchronizationContext => DefaultGodotTaskScheduler.Context; } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs index 9ef0959750..bbfb200431 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs @@ -1,5 +1,5 @@ using System; -using System.Runtime.CompilerServices; +using Godot.NativeInterop; namespace Godot { @@ -12,10 +12,20 @@ namespace Godot public static WeakRef WeakRef(Object obj) { - return godot_icall_Object_weakref(Object.GetPtr(obj)); - } + if (!IsInstanceValid(obj)) + return null; - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static WeakRef godot_icall_Object_weakref(IntPtr obj); + using godot_ref weakRef = default; + + unsafe + { + NativeFuncs.godotsharp_weakref(GetPtr(obj), &weakRef); + } + + if (weakRef.IsNull) + return null; + + return (WeakRef)InteropUtils.UnmanagedGetManaged(weakRef._reference); + } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs index cd0e0d858a..7d9e6584f0 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs @@ -5,7 +5,6 @@ using real_t = System.Single; #endif using System; using System.Collections.Generic; -using System.Runtime.CompilerServices; using Godot.NativeInterop; // TODO: Add comments describing what this class does. It is not obvious. @@ -17,12 +16,17 @@ namespace Godot public static unsafe object Bytes2Var(byte[] bytes, bool allowObjects = false) { using var varBytes = Marshaling.mono_array_to_PackedByteArray(bytes); - return godot_icall_GD_bytes2var(&varBytes, allowObjects); + using godot_variant ret = default; + NativeFuncs.godotsharp_bytes2var(&varBytes, allowObjects, &ret); + return Marshaling.variant_to_mono_object(&ret); } - public static object Convert(object what, Variant.Type type) + public static unsafe object Convert(object what, Variant.Type type) { - return godot_icall_GD_convert(what, type); + using var whatVariant = Marshaling.mono_object_to_variant(what); + using godot_variant ret = default; + NativeFuncs.godotsharp_convert(&whatVariant, type, &ret); + return Marshaling.variant_to_mono_object(&ret); } public static real_t Db2Linear(real_t db) @@ -30,7 +34,7 @@ namespace Godot return (real_t)Math.Exp(db * 0.11512925464970228420089957273422); } - private static object[] GetPrintParams(object[] parameters) + private static string[] GetPrintParams(object[] parameters) { if (parameters == null) { @@ -40,14 +44,15 @@ namespace Godot return Array.ConvertAll(parameters, x => x?.ToString() ?? "null"); } - public static int Hash(object var) + public static unsafe int Hash(object var) { - return godot_icall_GD_hash(var); + using var variant = Marshaling.mono_object_to_variant(var); + return NativeFuncs.godotsharp_hash(&variant); } public static Object InstanceFromId(ulong instanceId) { - return godot_icall_GD_instance_from_id(instanceId); + return InteropUtils.UnmanagedGetManaged(NativeFuncs.godotsharp_instance_from_id(instanceId)); } public static real_t Linear2Db(real_t linear) @@ -65,19 +70,23 @@ namespace Godot return ResourceLoader.Load(path); } - public static void PushError(string message) + public static unsafe void PushError(string message) { - godot_icall_GD_pusherror(message); + using var godotStr = Marshaling.mono_string_to_godot(message); + NativeFuncs.godotsharp_pusherror(&godotStr); } - public static void PushWarning(string message) + public static unsafe void PushWarning(string message) { - godot_icall_GD_pushwarning(message); + using var godotStr = Marshaling.mono_string_to_godot(message); + NativeFuncs.godotsharp_pushwarning(&godotStr); } - public static void Print(params object[] what) + public static unsafe void Print(params object[] what) { - godot_icall_GD_print(GetPrintParams(what)); + string str = string.Concat(GetPrintParams(what)); + using var godotStr = Marshaling.mono_string_to_godot(str); + NativeFuncs.godotsharp_print(&godotStr); } public static void PrintStack() @@ -85,54 +94,62 @@ namespace Godot Print(System.Environment.StackTrace); } - public static void PrintErr(params object[] what) + public static unsafe void PrintErr(params object[] what) { - godot_icall_GD_printerr(GetPrintParams(what)); + string str = string.Concat(GetPrintParams(what)); + using var godotStr = Marshaling.mono_string_to_godot(str); + NativeFuncs.godotsharp_printerr(&godotStr); } - public static void PrintRaw(params object[] what) + public static unsafe void PrintRaw(params object[] what) { - godot_icall_GD_printraw(GetPrintParams(what)); + string str = string.Concat(GetPrintParams(what)); + using var godotStr = Marshaling.mono_string_to_godot(str); + NativeFuncs.godotsharp_printraw(&godotStr); } - public static void PrintS(params object[] what) + public static unsafe void PrintS(params object[] what) { - godot_icall_GD_prints(GetPrintParams(what)); + string str = string.Join(' ', GetPrintParams(what)); + using var godotStr = Marshaling.mono_string_to_godot(str); + NativeFuncs.godotsharp_prints(&godotStr); } - public static void PrintT(params object[] what) + public static unsafe void PrintT(params object[] what) { - godot_icall_GD_printt(GetPrintParams(what)); + string str = string.Join('\t', GetPrintParams(what)); + using var godotStr = Marshaling.mono_string_to_godot(str); + NativeFuncs.godotsharp_printt(&godotStr); } public static float Randf() { - return godot_icall_GD_randf(); + return NativeFuncs.godotsharp_randf(); } public static uint Randi() { - return godot_icall_GD_randi(); + return NativeFuncs.godotsharp_randi(); } public static void Randomize() { - godot_icall_GD_randomize(); + NativeFuncs.godotsharp_randomize(); } public static double RandRange(double from, double to) { - return godot_icall_GD_randf_range(from, to); + return NativeFuncs.godotsharp_randf_range(from, to); } public static int RandRange(int from, int to) { - return godot_icall_GD_randi_range(from, to); + return NativeFuncs.godotsharp_randi_range(from, to); } public static uint RandFromSeed(ref ulong seed) { - return godot_icall_GD_rand_seed(seed, out seed); + return NativeFuncs.godotsharp_rand_from_seed(seed, out seed); } public static IEnumerable Range(int end) @@ -167,114 +184,45 @@ namespace Godot public static void Seed(ulong seed) { - godot_icall_GD_seed(seed); + NativeFuncs.godotsharp_seed(seed); } - public static string Str(params object[] what) + public static unsafe string Str(params object[] what) { - return godot_icall_GD_str(what); + using var whatGodotArray = Marshaling.mono_array_to_Array(what); + using godot_string ret = default; + NativeFuncs.godotsharp_str(&whatGodotArray, &ret); + return Marshaling.mono_string_from_godot(&ret); } - public static object Str2Var(string str) + public static unsafe object Str2Var(string str) { - return godot_icall_GD_str2var(str); - } - - public static bool TypeExists(StringName type) - { - return godot_icall_GD_type_exists(ref type.NativeValue); + using var godotStr = Marshaling.mono_string_to_godot(str); + using godot_variant ret = default; + NativeFuncs.godotsharp_str2var(&godotStr, &ret); + return Marshaling.variant_to_mono_object(&ret); } public static unsafe byte[] Var2Bytes(object var, bool fullObjects = false) { - godot_packed_byte_array varBytes; - godot_icall_GD_var2bytes(var, fullObjects, &varBytes); + using var variant = Marshaling.mono_object_to_variant(var); + using godot_packed_byte_array varBytes = default; + NativeFuncs.godotsharp_var2bytes(&variant, fullObjects, &varBytes); using (varBytes) - { return Marshaling.PackedByteArray_to_mono_array(&varBytes); - } } - public static string Var2Str(object var) + public static unsafe string Var2Str(object var) { - return godot_icall_GD_var2str(var); + using var variant = Marshaling.mono_object_to_variant(var); + using godot_string ret = default; + NativeFuncs.godotsharp_var2str(&variant, &ret); + return Marshaling.mono_string_from_godot(&ret); } public static Variant.Type TypeToVariantType(Type type) { - return godot_icall_TypeToVariantType(type); + return Marshaling.managed_to_variant_type(type, out bool _); } - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern unsafe object godot_icall_GD_bytes2var(godot_packed_byte_array* bytes, bool allowObjects); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern object godot_icall_GD_convert(object what, Variant.Type type); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern int godot_icall_GD_hash(object var); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern Object godot_icall_GD_instance_from_id(ulong instanceId); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_GD_print(object[] what); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_GD_printerr(object[] what); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_GD_printraw(object[] what); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_GD_prints(object[] what); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_GD_printt(object[] what); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern float godot_icall_GD_randf(); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern uint godot_icall_GD_randi(); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_GD_randomize(); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern double godot_icall_GD_randf_range(double from, double to); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern int godot_icall_GD_randi_range(int from, int to); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern uint godot_icall_GD_rand_seed(ulong seed, out ulong newSeed); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_GD_seed(ulong seed); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern string godot_icall_GD_str(object[] what); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern object godot_icall_GD_str2var(string str); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_GD_type_exists(ref godot_string_name type); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern unsafe void godot_icall_GD_var2bytes(object what, bool fullObjects, godot_packed_byte_array* bytes); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern string godot_icall_GD_var2str(object var); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_GD_pusherror(string type); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_GD_pushwarning(string type); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern Variant.Type godot_icall_TypeToVariantType(Type type); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs index da5e3eccee..21a2e5a1a6 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs @@ -36,6 +36,8 @@ namespace Godot.NativeInterop NativeFuncs.godotsharp_ref_destroy(ref this); _reference = IntPtr.Zero; } + + public bool IsNull => _reference == IntPtr.Zero; } [SuppressMessage("ReSharper", "InconsistentNaming")] diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs index 3a6d455396..d10eb48511 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs @@ -18,8 +18,10 @@ namespace Godot.NativeInterop fieldInfo.SetValue(obj, valueObj); } - public static Variant.Type managed_to_variant_type(Type type, ref bool r_nil_is_variant) + public static Variant.Type managed_to_variant_type(Type type, out bool r_nil_is_variant) { + r_nil_is_variant = false; + switch (Type.GetTypeCode(type)) { case TypeCode.Boolean: @@ -190,8 +192,6 @@ namespace Godot.NativeInterop } } - r_nil_is_variant = false; - // Unknown return Variant.Type.Nil; } @@ -687,7 +687,7 @@ namespace Godot.NativeInterop if (typeof(Godot.Object[]).IsAssignableFrom(type)) { using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var); - return Array_to_mono_array_of_type(&godotArray, type); + return Array_to_mono_array_of_godot_object_type(&godotArray, type); } if (type == typeof(object[])) @@ -1106,7 +1106,7 @@ namespace Godot.NativeInterop return ret; } - public static unsafe object Array_to_mono_array_of_type(godot_array* p_array, Type type) + public static unsafe object Array_to_mono_array_of_godot_object_type(godot_array* p_array, Type type) { var array = Collections.Array.CreateTakingOwnershipOfDisposableValue( NativeFuncs.godotsharp_array_new_copy(p_array)); @@ -1114,7 +1114,9 @@ namespace Godot.NativeInterop int length = array.Count; object ret = Activator.CreateInstance(type, length); - array.CopyTo((object[])ret, 0); // variant_to_mono_object handled by Collections.Array + // variant_to_mono_object handled by Collections.Array + // variant_to_mono_object_of_type is not needed because target element types are Godot.Object (or derived) + array.CopyTo((object[])ret, 0); return ret; } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs index 1145b265ef..991301140e 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs @@ -485,5 +485,83 @@ namespace Godot.NativeInterop [DllImport(GodotDllName)] public static extern bool godotsharp_node_path_is_absolute(ref godot_node_path p_self); + + // GD, etc + + [DllImport(GodotDllName)] + public static extern void godotsharp_bytes2var(godot_packed_byte_array* p_bytes, bool p_allow_objects, + godot_variant* r_ret); + + [DllImport(GodotDllName)] + public static extern void godotsharp_convert(godot_variant* p_what, Variant.Type p_type, godot_variant* r_ret); + + [DllImport(GodotDllName)] + public static extern int godotsharp_hash(godot_variant* var); + + [DllImport(GodotDllName)] + public static extern IntPtr godotsharp_instance_from_id(ulong instanceId); + + [DllImport(GodotDllName)] + public static extern void godotsharp_print(godot_string* p_what); + + [DllImport(GodotDllName)] + public static extern void godotsharp_printerr(godot_string* p_what); + + [DllImport(GodotDllName)] + public static extern void godotsharp_printraw(godot_string* p_what); + + [DllImport(GodotDllName)] + public static extern void godotsharp_prints(godot_string* p_what); + + [DllImport(GodotDllName)] + public static extern void godotsharp_printt(godot_string* p_what); + + [DllImport(GodotDllName)] + public static extern float godotsharp_randf(); + + [DllImport(GodotDllName)] + public static extern uint godotsharp_randi(); + + [DllImport(GodotDllName)] + public static extern void godotsharp_randomize(); + + [DllImport(GodotDllName)] + public static extern double godotsharp_randf_range(double from, double to); + + [DllImport(GodotDllName)] + public static extern int godotsharp_randi_range(int from, int to); + + [DllImport(GodotDllName)] + public static extern uint godotsharp_rand_from_seed(ulong seed, out ulong newSeed); + + [DllImport(GodotDllName)] + public static extern void godotsharp_seed(ulong seed); + + [DllImport(GodotDllName)] + public static extern void godotsharp_weakref(IntPtr obj, godot_ref* r_weak_ref); + + [DllImport(GodotDllName)] + public static extern string godotsharp_str(godot_array* p_what, godot_string* r_ret); + + [DllImport(GodotDllName)] + public static extern void godotsharp_str2var(godot_string* p_str, godot_variant* r_ret); + + [DllImport(GodotDllName)] + public static extern void godotsharp_var2bytes(godot_variant* what, bool fullObjects, + godot_packed_byte_array* bytes); + + [DllImport(GodotDllName)] + public static extern void godotsharp_var2str(godot_variant* var, godot_string* r_ret); + + [DllImport(GodotDllName)] + public static extern void godotsharp_pusherror(godot_string* type); + + [DllImport(GodotDllName)] + public static extern void godotsharp_pushwarning(godot_string* type); + + // Object + + [DllImport(GodotDllName)] + public static extern string godotsharp_object_to_string(IntPtr ptr, godot_string* r_str); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs index a52707edfb..c7394b041e 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.CompilerServices; +using Godot.NativeInterop; namespace Godot { @@ -85,9 +86,11 @@ namespace Godot _disposed = true; } - public override string ToString() + public override unsafe string ToString() { - return godot_icall_Object_ToString(GetPtr(this)); + using godot_string str = default; + NativeFuncs.godotsharp_object_to_string(GetPtr(this), &str); + return Marshaling.mono_string_from_godot(&str); } /// @@ -170,8 +173,5 @@ namespace Godot [MethodImpl(MethodImplOptions.InternalCall)] internal static extern void godot_icall_Object_ConnectEventSignals(IntPtr obj); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern string godot_icall_Object_ToString(IntPtr ptr); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/ScriptManager.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/ScriptManager.cs new file mode 100644 index 0000000000..e92688f5bb --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/ScriptManager.cs @@ -0,0 +1,10 @@ +namespace Godot +{ + internal class ScriptManager + { + internal static void FrameCallback() + { + Dispatcher.DefaultGodotTaskScheduler?.Activate(); + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj index 7cafa1ce07..45e5914938 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj @@ -69,6 +69,7 @@ + diff --git a/modules/mono/glue/base_object_glue.cpp b/modules/mono/glue/base_object_glue.cpp index 9d153bdbf8..d7bcefb9b0 100644 --- a/modules/mono/glue/base_object_glue.cpp +++ b/modules/mono/glue/base_object_glue.cpp @@ -124,50 +124,14 @@ void godot_icall_Object_ConnectEventSignals(Object *p_ptr) { } } -MonoObject *godot_icall_Object_weakref(Object *p_ptr) { - if (!p_ptr) { - return nullptr; - } - - Ref wref; - RefCounted *rc = Object::cast_to(p_ptr); - - if (rc) { - REF r = rc; - if (!r.is_valid()) { - return nullptr; - } - - wref.instantiate(); - wref->set_ref(r); - } else { - wref.instantiate(); - wref->set_obj(p_ptr); - } - - return GDMonoUtils::unmanaged_get_managed(wref.ptr()); -} - int32_t godot_icall_SignalAwaiter_connect(Object *p_source, StringName *p_signal, Object *p_target, MonoObject *p_awaiter) { StringName signal = p_signal ? *p_signal : StringName(); return (int32_t)gd_mono_connect_signal_awaiter(p_source, signal, p_target, p_awaiter); } -MonoString *godot_icall_Object_ToString(Object *p_ptr) { -#ifdef DEBUG_ENABLED - // Cannot happen in C#; would get an ObjectDisposedException instead. - CRASH_COND(p_ptr == nullptr); -#endif - // Can't call 'Object::to_string()' here, as that can end up calling 'ToString' again resulting in an endless circular loop. - String result = "[" + p_ptr->get_class() + ":" + itos(p_ptr->get_instance_id()) + "]"; - return GDMonoMarshal::mono_string_from_godot(result); -} - void godot_register_object_icalls() { GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_Disposed", godot_icall_Object_Disposed); GDMonoUtils::add_internal_call("Godot.Object::godot_icall_RefCounted_Disposed", godot_icall_RefCounted_Disposed); GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_ConnectEventSignals", godot_icall_Object_ConnectEventSignals); - GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_ToString", godot_icall_Object_ToString); - GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_weakref", godot_icall_Object_weakref); GDMonoUtils::add_internal_call("Godot.SignalAwaiter::godot_icall_SignalAwaiter_connect", godot_icall_SignalAwaiter_connect); } diff --git a/modules/mono/glue/gd_glue.cpp b/modules/mono/glue/gd_glue.cpp deleted file mode 100644 index 9cd422ed58..0000000000 --- a/modules/mono/glue/gd_glue.cpp +++ /dev/null @@ -1,315 +0,0 @@ -/*************************************************************************/ -/* gd_glue.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "core/io/marshalls.h" -#include "core/os/os.h" -#include "core/string/ustring.h" -#include "core/variant/array.h" -#include "core/variant/variant.h" -#include "core/variant/variant_parser.h" - -#include "../mono_gd/gd_mono_cache.h" -#include "../mono_gd/gd_mono_marshal.h" -#include "../mono_gd/gd_mono_utils.h" - -MonoObject *godot_icall_GD_bytes2var(PackedByteArray *p_bytes, MonoBoolean p_allow_objects) { - Variant ret; - Error err = decode_variant(ret, p_bytes->ptr(), p_bytes->size(), nullptr, p_allow_objects); - if (err != OK) { - ret = RTR("Not enough bytes for decoding bytes, or invalid format."); - } - return GDMonoMarshal::variant_to_mono_object(ret); -} - -MonoObject *godot_icall_GD_convert(MonoObject *p_what, int32_t p_type) { - Variant what = GDMonoMarshal::mono_object_to_variant(p_what); - const Variant *args[1] = { &what }; - Callable::CallError ce; - Variant ret; - Variant::construct(Variant::Type(p_type), ret, args, 1, ce); - ERR_FAIL_COND_V(ce.error != Callable::CallError::CALL_OK, nullptr); - return GDMonoMarshal::variant_to_mono_object(ret); -} - -int godot_icall_GD_hash(MonoObject *p_var) { - return GDMonoMarshal::mono_object_to_variant(p_var).hash(); -} - -MonoObject *godot_icall_GD_instance_from_id(uint64_t p_instance_id) { - return GDMonoUtils::unmanaged_get_managed(ObjectDB::get_instance(ObjectID(p_instance_id))); -} - -void godot_icall_GD_print(MonoArray *p_what) { - String str; - int length = mono_array_length(p_what); - - for (int i = 0; i < length; i++) { - MonoObject *elem = mono_array_get(p_what, MonoObject *, i); - - MonoException *exc = nullptr; - String elem_str = GDMonoMarshal::mono_object_to_variant_string(elem, &exc); - - if (exc) { - GDMonoUtils::set_pending_exception(exc); - return; - } - - str += elem_str; - } - - print_line(str); -} - -void godot_icall_GD_printerr(MonoArray *p_what) { - String str; - int length = mono_array_length(p_what); - - for (int i = 0; i < length; i++) { - MonoObject *elem = mono_array_get(p_what, MonoObject *, i); - - MonoException *exc = nullptr; - String elem_str = GDMonoMarshal::mono_object_to_variant_string(elem, &exc); - - if (exc) { - GDMonoUtils::set_pending_exception(exc); - return; - } - - str += elem_str; - } - - print_error(str); -} - -void godot_icall_GD_printraw(MonoArray *p_what) { - String str; - int length = mono_array_length(p_what); - - for (int i = 0; i < length; i++) { - MonoObject *elem = mono_array_get(p_what, MonoObject *, i); - - MonoException *exc = nullptr; - String elem_str = GDMonoMarshal::mono_object_to_variant_string(elem, &exc); - - if (exc) { - GDMonoUtils::set_pending_exception(exc); - return; - } - - str += elem_str; - } - - OS::get_singleton()->print("%s", str.utf8().get_data()); -} - -void godot_icall_GD_prints(MonoArray *p_what) { - String str; - int length = mono_array_length(p_what); - - for (int i = 0; i < length; i++) { - MonoObject *elem = mono_array_get(p_what, MonoObject *, i); - - MonoException *exc = nullptr; - String elem_str = GDMonoMarshal::mono_object_to_variant_string(elem, &exc); - - if (exc) { - GDMonoUtils::set_pending_exception(exc); - return; - } - - if (i) { - str += " "; - } - - str += elem_str; - } - - print_line(str); -} - -void godot_icall_GD_printt(MonoArray *p_what) { - String str; - int length = mono_array_length(p_what); - - for (int i = 0; i < length; i++) { - MonoObject *elem = mono_array_get(p_what, MonoObject *, i); - - MonoException *exc = nullptr; - String elem_str = GDMonoMarshal::mono_object_to_variant_string(elem, &exc); - - if (exc) { - GDMonoUtils::set_pending_exception(exc); - return; - } - - if (i) { - str += "\t"; - } - - str += elem_str; - } - - print_line(str); -} - -float godot_icall_GD_randf() { - return Math::randf(); -} - -uint32_t godot_icall_GD_randi() { - return Math::rand(); -} - -void godot_icall_GD_randomize() { - Math::randomize(); -} - -double godot_icall_GD_randf_range(double from, double to) { - return Math::random(from, to); -} - -int32_t godot_icall_GD_randi_range(int32_t from, int32_t to) { - return Math::random(from, to); -} - -uint32_t godot_icall_GD_rand_seed(uint64_t seed, uint64_t *newSeed) { - uint32_t ret = Math::rand_from_seed(&seed); - *newSeed = seed; - return ret; -} - -void godot_icall_GD_seed(uint64_t p_seed) { - Math::seed(p_seed); -} - -MonoString *godot_icall_GD_str(MonoArray *p_what) { - String str; - Array what = GDMonoMarshal::mono_array_to_Array(p_what); - - for (int i = 0; i < what.size(); i++) { - String os = what[i].operator String(); - - if (i == 0) { - str = os; - } else { - str += os; - } - } - - return GDMonoMarshal::mono_string_from_godot(str); -} - -MonoObject *godot_icall_GD_str2var(MonoString *p_str) { - Variant ret; - - VariantParser::StreamString ss; - ss.s = GDMonoMarshal::mono_string_to_godot(p_str); - - String errs; - int line; - Error err = VariantParser::parse(&ss, ret, errs, line); - if (err != OK) { - String err_str = "Parse error at line " + itos(line) + ": " + errs + "."; - ERR_PRINT(err_str); - ret = err_str; - } - - return GDMonoMarshal::variant_to_mono_object(ret); -} - -MonoBoolean godot_icall_GD_type_exists(StringName *p_type) { - StringName type = p_type ? *p_type : StringName(); - return ClassDB::class_exists(type); -} - -void godot_icall_GD_pusherror(MonoString *p_str) { - ERR_PRINT(GDMonoMarshal::mono_string_to_godot(p_str)); -} - -void godot_icall_GD_pushwarning(MonoString *p_str) { - WARN_PRINT(GDMonoMarshal::mono_string_to_godot(p_str)); -} - -void godot_icall_GD_var2bytes(MonoObject *p_var, MonoBoolean p_full_objects, PackedByteArray *r_bytes) { - memnew_placement(r_bytes, PackedByteArray); - - Variant var = GDMonoMarshal::mono_object_to_variant(p_var); - - int len; - Error err = encode_variant(var, nullptr, len, p_full_objects); - ERR_FAIL_COND_MSG(err != OK, "Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID)."); - - r_bytes->resize(len); - encode_variant(var, r_bytes->ptrw(), len, p_full_objects); -} - -MonoString *godot_icall_GD_var2str(MonoObject *p_var) { - String vars; - VariantWriter::write_to_string(GDMonoMarshal::mono_object_to_variant(p_var), vars); - return GDMonoMarshal::mono_string_from_godot(vars); -} - -uint32_t godot_icall_TypeToVariantType(MonoReflectionType *p_refl_type) { - return (uint32_t)GDMonoMarshal::managed_to_variant_type(ManagedType::from_reftype(p_refl_type)); -} - -MonoObject *godot_icall_DefaultGodotTaskScheduler() { - return GDMonoCache::cached_data.task_scheduler_handle->get_target(); -} - -void godot_register_gd_icalls() { - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_bytes2var", godot_icall_GD_bytes2var); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_convert", godot_icall_GD_convert); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_hash", godot_icall_GD_hash); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_instance_from_id", godot_icall_GD_instance_from_id); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_pusherror", godot_icall_GD_pusherror); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_pushwarning", godot_icall_GD_pushwarning); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_print", godot_icall_GD_print); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_printerr", godot_icall_GD_printerr); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_printraw", godot_icall_GD_printraw); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_prints", godot_icall_GD_prints); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_printt", godot_icall_GD_printt); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randf", godot_icall_GD_randf); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randi", godot_icall_GD_randi); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randomize", godot_icall_GD_randomize); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randf_range", godot_icall_GD_randf_range); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randi_range", godot_icall_GD_randi_range); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_rand_seed", godot_icall_GD_rand_seed); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_seed", godot_icall_GD_seed); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_str", godot_icall_GD_str); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_str2var", godot_icall_GD_str2var); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_type_exists", godot_icall_GD_type_exists); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_var2bytes", godot_icall_GD_var2bytes); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_var2str", godot_icall_GD_var2str); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_TypeToVariantType", godot_icall_TypeToVariantType); - - // Dispatcher - GDMonoUtils::add_internal_call("Godot.Dispatcher::godot_icall_DefaultGodotTaskScheduler", godot_icall_DefaultGodotTaskScheduler); -} diff --git a/modules/mono/glue/runtime_interop.cpp b/modules/mono/glue/runtime_interop.cpp index 6c2790a533..da19db0945 100644 --- a/modules/mono/glue/runtime_interop.cpp +++ b/modules/mono/glue/runtime_interop.cpp @@ -29,8 +29,10 @@ /*************************************************************************/ #include "core/config/engine.h" +#include "core/io/marshalls.h" #include "core/object/class_db.h" #include "core/object/method_bind.h" +#include "core/os/os.h" #include "core/string/string_name.h" #include @@ -700,12 +702,186 @@ bool godotsharp_node_path_is_absolute(const NodePath *p_self) { return p_self->is_absolute(); } +void godotsharp_randomize() { + Math::randomize(); +} + +uint32_t godotsharp_randi() { + return Math::rand(); +} + +float godotsharp_randf() { + return Math::randf(); +} + +int32_t godotsharp_randi_range(int32_t p_from, int32_t p_to) { + return Math::random(p_from, p_to); +} + +double godotsharp_randf_range(double p_from, double p_to) { + return Math::random(p_from, p_to); +} + +void godotsharp_seed(uint64_t p_seed) { + Math::seed(p_seed); +} + +uint32_t godotsharp_rand_from_seed(uint64_t p_seed, uint64_t *r_new_seed) { + uint32_t ret = Math::rand_from_seed(&p_seed); + *r_new_seed = p_seed; + return ret; +} + +void godotsharp_weakref(Object *p_ptr, Ref *r_weak_ref) { + if (!p_ptr) { + return; + } + + Ref wref; + RefCounted *rc = Object::cast_to(p_ptr); + + if (rc) { + REF r = rc; + if (!r.is_valid()) { + return; + } + + wref.instantiate(); + wref->set_ref(r); + } else { + wref.instantiate(); + wref->set_obj(p_ptr); + } + + memnew_placement(r_weak_ref, Ref(wref)); +} + +void godotsharp_str(const godot_array *p_what, godot_string *r_ret) { + String &str = *memnew_placement(r_ret, String); + const Array &what = *reinterpret_cast(p_what); + + for (int i = 0; i < what.size(); i++) { + String os = what[i].operator String(); + + if (i == 0) { + str = os; + } else { + str += os; + } + } +} + +void godotsharp_print(const godot_string *p_what) { + print_line(*reinterpret_cast(p_what)); +} + +void godotsharp_printerr(const godot_string *p_what) { + print_error(*reinterpret_cast(p_what)); +} + +void godotsharp_printt(const godot_string *p_what) { + print_line(*reinterpret_cast(p_what)); +} + +void godotsharp_prints(const godot_string *p_what) { + print_line(*reinterpret_cast(p_what)); +} + +void godotsharp_printraw(const godot_string *p_what) { + OS::get_singleton()->print("%s", reinterpret_cast(p_what)->utf8().get_data()); +} + +void godotsharp_pusherror(const godot_string *p_str) { + ERR_PRINT(*reinterpret_cast(p_str)); +} + +void godotsharp_pushwarning(const godot_string *p_str) { + WARN_PRINT(*reinterpret_cast(p_str)); +} + +void godotsharp_var2str(const godot_variant *p_var, godot_string *r_ret) { + const Variant &var = *reinterpret_cast(p_var); + String &vars = *memnew_placement(r_ret, String); + VariantWriter::write_to_string(var, vars); +} + +void godotsharp_str2var(const godot_string *p_str, godot_variant *r_ret) { + Variant ret; + + VariantParser::StreamString ss; + ss.s = *reinterpret_cast(p_str); + + String errs; + int line; + Error err = VariantParser::parse(&ss, ret, errs, line); + if (err != OK) { + String err_str = "Parse error at line " + itos(line) + ": " + errs + "."; + ERR_PRINT(err_str); + ret = err_str; + } + memnew_placement(r_ret, Variant(ret)); +} + +void godotsharp_var2bytes(const godot_variant *p_var, bool p_full_objects, godot_packed_byte_array *r_bytes) { + const Variant &var = *reinterpret_cast(p_var); + PackedByteArray &bytes = *memnew_placement(r_bytes, PackedByteArray); + + int len; + Error err = encode_variant(var, nullptr, len, p_full_objects); + ERR_FAIL_COND_MSG(err != OK, "Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID)."); + + bytes.resize(len); + encode_variant(var, bytes.ptrw(), len, p_full_objects); +} + +void godotsharp_bytes2var(const godot_packed_byte_array *p_bytes, bool p_allow_objects, godot_variant *r_ret) { + const PackedByteArray *bytes = reinterpret_cast(p_bytes); + Variant ret; + Error err = decode_variant(ret, bytes->ptr(), bytes->size(), nullptr, p_allow_objects); + if (err != OK) { + ret = RTR("Not enough bytes for decoding bytes, or invalid format."); + } + memnew_placement(r_ret, Variant(ret)); +} + +int godotsharp_hash(const godot_variant *p_var) { + return reinterpret_cast(p_var)->hash(); +} + +void godotsharp_convert(const godot_variant *p_what, int32_t p_type, godot_variant *r_ret) { + const Variant *args[1] = { reinterpret_cast(p_what) }; + Callable::CallError ce; + Variant ret; + Variant::construct(Variant::Type(p_type), ret, args, 1, ce); + if (ce.error != Callable::CallError::CALL_OK) { + memnew_placement(r_ret, Variant); + ERR_FAIL_MSG("Unable to convert parameter from '" + + Variant::get_type_name(reinterpret_cast(p_what)->get_type()) + + "' to '" + Variant::get_type_name(Variant::Type(p_type)) + "'."); + } + memnew_placement(r_ret, Variant(ret)); +} + +Object *godotsharp_instance_from_id(uint64_t p_instance_id) { + return ObjectDB::get_instance(ObjectID(p_instance_id)); +} + +void godotsharp_object_to_string(Object *p_ptr, godot_string *r_str) { +#ifdef DEBUG_ENABLED + // Cannot happen in C#; would get an ObjectDisposedException instead. + CRASH_COND(p_ptr == nullptr); +#endif + // Can't call 'Object::to_string()' here, as that can end up calling 'ToString' again resulting in an endless circular loop. + memnew_placement(r_str, + String("[" + p_ptr->get_class() + ":" + itos(p_ptr->get_instance_id()) + "]")); +} + #ifdef __cplusplus } #endif // We need this to prevent the functions from being stripped. -void *godotsharp_pinvoke_funcs[130] = { +void *godotsharp_pinvoke_funcs[154] = { (void *)godotsharp_method_bind_get_method, (void *)godotsharp_get_class_constructor, (void *)godotsharp_invoke_class_constructor, @@ -835,5 +1011,29 @@ void *godotsharp_pinvoke_funcs[130] = { (void *)godotsharp_node_path_get_name_count, (void *)godotsharp_node_path_get_subname, (void *)godotsharp_node_path_get_subname_count, - (void *)godotsharp_node_path_is_absolute + (void *)godotsharp_node_path_is_absolute, + (void *)godotsharp_randomize, + (void *)godotsharp_randi, + (void *)godotsharp_randf, + (void *)godotsharp_randi_range, + (void *)godotsharp_randf_range, + (void *)godotsharp_seed, + (void *)godotsharp_rand_from_seed, + (void *)godotsharp_weakref, + (void *)godotsharp_str, + (void *)godotsharp_print, + (void *)godotsharp_printerr, + (void *)godotsharp_printt, + (void *)godotsharp_prints, + (void *)godotsharp_printraw, + (void *)godotsharp_pusherror, + (void *)godotsharp_pushwarning, + (void *)godotsharp_var2str, + (void *)godotsharp_str2var, + (void *)godotsharp_var2bytes, + (void *)godotsharp_bytes2var, + (void *)godotsharp_hash, + (void *)godotsharp_convert, + (void *)godotsharp_instance_from_id, + (void *)godotsharp_object_to_string, }; diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index b2c4cfb7bc..a58f629aa0 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -444,22 +444,13 @@ bool GDMono::_are_api_assemblies_out_of_sync() { return out_of_sync; } -void godot_register_collections_icalls(); -void godot_register_gd_icalls(); -void godot_register_node_path_icalls(); void godot_register_object_icalls(); -void godot_register_rid_icalls(); -void godot_register_string_icalls(); void godot_register_scene_tree_icalls(); void godot_register_placeholder_icalls(); void GDMono::_register_internal_calls() { // Registers internal calls that were not generated. - godot_register_collections_icalls(); - godot_register_gd_icalls(); - godot_register_node_path_icalls(); godot_register_object_icalls(); - godot_register_string_icalls(); godot_register_scene_tree_icalls(); godot_register_placeholder_icalls(); } @@ -1001,6 +992,8 @@ Error GDMono::_load_scripts_domain() { Error GDMono::_unload_scripts_domain() { ERR_FAIL_NULL_V(scripts_domain, ERR_BUG); + CSharpLanguage::get_singleton()->_on_scripts_domain_about_to_unload(); + print_verbose("Mono: Finalizing scripts domain..."); if (mono_domain_get() != root_domain) { @@ -1054,8 +1047,6 @@ Error GDMono::_unload_scripts_domain() { Error GDMono::reload_scripts_domain() { ERR_FAIL_COND_V(!runtime_initialized, ERR_BUG); - CSharpLanguage::get_singleton()->_on_scripts_domain_about_to_unload(); - if (scripts_domain) { Error domain_unload_err = _unload_scripts_domain(); ERR_FAIL_COND_V_MSG(domain_unload_err != OK, domain_unload_err, "Mono: Failed to unload scripts domain."); diff --git a/modules/mono/mono_gd/gd_mono_cache.cpp b/modules/mono/mono_gd/gd_mono_cache.cpp index db2740e431..f902b214f1 100644 --- a/modules/mono/mono_gd/gd_mono_cache.cpp +++ b/modules/mono/mono_gd/gd_mono_cache.cpp @@ -112,7 +112,6 @@ void CachedData::clear_godot_api_cache() { methodthunk_GodotObject_Dispose.nullify(); methodthunk_SignalAwaiter_SignalCallback.nullify(); - methodthunk_GodotTaskScheduler_Activate.nullify(); methodthunk_Delegate_Equals.nullify(); @@ -131,8 +130,6 @@ void CachedData::clear_godot_api_cache() { methodthunk_Marshaling_mono_object_to_variant_out.nullify(); methodthunk_Marshaling_SetFieldValue.nullify(); - - task_scheduler_handle = Ref(); } #define GODOT_API_CLASS(m_class) (GDMono::get_singleton()->get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, #m_class)) @@ -188,7 +185,6 @@ void update_godot_api_cache() { CACHE_METHOD_THUNK_AND_CHECK(GodotObject, Dispose, CACHED_CLASS(GodotObject)->get_method("Dispose", 0)); CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, SignalCallback, GODOT_API_CLASS(SignalAwaiter)->get_method("SignalCallback", 1)); - CACHE_METHOD_THUNK_AND_CHECK(GodotTaskScheduler, Activate, GODOT_API_CLASS(GodotTaskScheduler)->get_method("Activate", 0)); CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, TrySerializeDelegateWithGCHandle, GODOT_API_CLASS(DelegateUtils)->get_method("TrySerializeDelegateWithGCHandle", 2)); CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, TryDeserializeDelegateWithGCHandle, GODOT_API_CLASS(DelegateUtils)->get_method("TryDeserializeDelegateWithGCHandle", 2)); @@ -222,10 +218,16 @@ void update_godot_api_cache() { CACHE_METHOD_THUNK_AND_CHECK(DebuggingUtils, GetStackFrameInfo, GODOT_API_CLASS(DebuggingUtils)->get_method("GetStackFrameInfo", 4)); #endif - // TODO Move to CSharpLanguage::init() and do handle disposal - MonoObject *task_scheduler = mono_object_new(mono_domain_get(), GODOT_API_CLASS(GodotTaskScheduler)->get_mono_ptr()); - GDMonoUtils::runtime_object_init(task_scheduler, GODOT_API_CLASS(GodotTaskScheduler)); - cached_data.task_scheduler_handle = MonoGCHandleRef::create_strong(task_scheduler); + MonoException *exc = nullptr; + GDMono::get_singleton() + ->get_core_api_assembly() + ->get_class("Godot", "Dispatcher") + ->get_method("InitializeDefaultGodotTaskScheduler") + ->invoke(nullptr, &exc); + + if (exc) { + GDMonoUtils::debug_unhandled_exception(exc); + } cached_data.godot_api_cache_updated = true; } diff --git a/modules/mono/mono_gd/gd_mono_cache.h b/modules/mono/mono_gd/gd_mono_cache.h index e08b9d24b8..799b161746 100644 --- a/modules/mono/mono_gd/gd_mono_cache.h +++ b/modules/mono/mono_gd/gd_mono_cache.h @@ -86,7 +86,6 @@ struct CachedData { GDMonoMethodThunk methodthunk_GodotObject_Dispose; GDMonoMethodThunk methodthunk_SignalAwaiter_SignalCallback; - GDMonoMethodThunk methodthunk_GodotTaskScheduler_Activate; GDMonoMethodThunkR methodthunk_Delegate_Equals; @@ -108,8 +107,6 @@ struct CachedData { GDMonoMethodThunk methodthunk_Marshaling_SetFieldValue; - Ref task_scheduler_handle; - bool corlib_cache_updated; bool godot_api_cache_updated; diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp index 143975f22f..8828ec588b 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.cpp +++ b/modules/mono/mono_gd/gd_mono_marshal.cpp @@ -40,6 +40,8 @@ namespace GDMonoMarshal { // TODO: Those are just temporary until the code that needs them is moved to C# Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_variant) { + CRASH_COND(p_type.type_class == nullptr); + MonoReflectionType *refltype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type()); MonoBoolean nil_is_variant = false; @@ -133,59 +135,6 @@ Variant mono_object_to_variant_no_err(MonoObject *p_obj) { return mono_object_to_variant_impl(p_obj, /* fail_with_err: */ false); } -String mono_object_to_variant_string(MonoObject *p_obj, MonoException **r_exc) { - if (p_obj == nullptr) { - return String("null"); - } - - Variant var = GDMonoMarshal::mono_object_to_variant_no_err(p_obj); - - if (var.get_type() == Variant::NIL) { // `&& p_obj != nullptr` but omitted because always true - // Cannot convert MonoObject* to Variant; fallback to 'ToString()'. - MonoException *exc = nullptr; - MonoString *mono_str = GDMonoUtils::object_to_string(p_obj, &exc); - - if (exc) { - if (r_exc) { - *r_exc = exc; - } - return String(); - } - - return GDMonoMarshal::mono_string_to_godot(mono_str); - } else { - return var.operator String(); - } -} - -MonoArray *Array_to_mono_array(const Array &p_array) { - int length = p_array.size(); - MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), length); - - for (int i = 0; i < length; i++) { - MonoObject *boxed = variant_to_mono_object(p_array[i]); - mono_array_setref(ret, i, boxed); - } - - return ret; -} - -Array mono_array_to_Array(MonoArray *p_array) { - Array ret; - if (!p_array) { - return ret; - } - int length = mono_array_length(p_array); - ret.resize(length); - - for (int i = 0; i < length; i++) { - MonoObject *elem = mono_array_get(p_array, MonoObject *, i); - ret[i] = mono_object_to_variant(elem); - } - - return ret; -} - MonoArray *PackedStringArray_to_mono_array(const PackedStringArray &p_array) { const String *r = p_array.ptr(); int length = p_array.size(); diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h index 353d70d0c0..6ed142c755 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.h +++ b/modules/mono/mono_gd/gd_mono_marshal.h @@ -88,15 +88,6 @@ _FORCE_INLINE_ MonoObject *variant_to_mono_object(const Variant *p_var) { Variant mono_object_to_variant(MonoObject *p_obj); Variant mono_object_to_variant_no_err(MonoObject *p_obj); -/// Tries to convert the MonoObject* to Variant and then convert the Variant to String. -/// If the MonoObject* cannot be converted to Variant, then 'ToString()' is called instead. -String mono_object_to_variant_string(MonoObject *p_obj, MonoException **r_exc); - -// Array - -MonoArray *Array_to_mono_array(const Array &p_array); -Array mono_array_to_Array(MonoArray *p_array); - // PackedStringArray MonoArray *PackedStringArray_to_mono_array(const PackedStringArray &p_array); diff --git a/modules/mono/mono_gd/gd_mono_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp index fa5b646387..ba3a1ea853 100644 --- a/modules/mono/mono_gd/gd_mono_method.cpp +++ b/modules/mono/mono_gd/gd_mono_method.cpp @@ -253,9 +253,13 @@ const MethodInfo &GDMonoMethod::get_method_info() { method_info.name = name; bool nil_is_variant = false; - method_info.return_val = PropertyInfo(GDMonoMarshal::managed_to_variant_type(return_type, &nil_is_variant), ""); - if (method_info.return_val.type == Variant::NIL && nil_is_variant) { - method_info.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; + if (return_type.type_encoding == MONO_TYPE_VOID) { + method_info.return_val = PropertyInfo(Variant::NIL, ""); + } else { + method_info.return_val = PropertyInfo(GDMonoMarshal::managed_to_variant_type(return_type, &nil_is_variant), ""); + if (method_info.return_val.type == Variant::NIL && nil_is_variant) { + method_info.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; + } } Vector names; From f744d991795989d7edce7c9e6d66c50518fcbb4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Rold=C3=A1n=20Etcheverry?= Date: Sun, 12 Sep 2021 20:21:15 +0200 Subject: [PATCH 4/6] C#: Restructure code prior move to .NET Core The main focus here was to remove the majority of code that relied on Mono's embedding APIs, specially the reflection APIs. The embedding APIs we still use are the bare minimum we need for things to work. A lot of code was moved to C#. We no longer deal with any managed objects (`MonoObject*`, and such) in native code, and all marshaling is done in C#. The reason for restructuring the code and move away from embedding APIs is that once we move to .NET Core, we will be limited by the much more minimal .NET hosting. PERFORMANCE REGRESSIONS ----------------------- Some parts of the code were written with little to no concern about performance. This includes code that calls into script methods and accesses script fields, properties and events. The reason for this is that all of that will be moved to source generators, so any work prior to that would be a waste of time. DISABLED FEATURES ----------------- Some code was removed as it no longer makes sense (or won't make sense in the future). Other parts were commented out with `#if 0`s and TODO warnings because it doesn't make much sense to work on them yet as those parts will change heavily when we switch to .NET Core but also when we start introducing source generators. As such, the following features were disabled temporarily: - Assembly-reloading (will be done with ALCs in .NET Core). - Properties/fields exports and script method listing (will be handled by source generators in the future). - Exception logging in the editor and stack info for errors. - Exporting games. - Building of C# projects. We no longer copy the Godot API assemblies to the project directory, so MSBuild won't be able to find them. The idea is to turn them into NuGet packages in the future, which could also be obtained from local NuGet sources during development. --- modules/mono/csharp_script.cpp | 1273 +++++++---------- modules/mono/csharp_script.h | 87 +- .../GodotTools/Build/BuildManager.cs | 11 - .../GodotTools/Export/ExportPlugin.cs | 61 +- .../GodotTools/GodotTools/GodotSharpEditor.cs | 23 +- .../GodotTools/GodotTools/GodotTools.csproj | 3 +- .../GodotTools/Internals/EditorProgress.cs | 24 +- .../GodotTools/Internals/Globals.cs | 37 +- .../GodotTools/Internals/GodotSharpDirs.cs | 134 +- .../GodotTools/Internals/Internal.cs | 78 +- .../editor/GodotTools/GodotTools/Utils/OS.cs | 41 +- modules/mono/editor/bindings_generator.cpp | 112 +- modules/mono/editor/bindings_generator.h | 2 + modules/mono/editor/editor_internal_calls.cpp | 231 +-- modules/mono/editor/godotsharp_export.cpp | 4 +- modules/mono/editor/godotsharp_export.h | 4 - .../glue/GodotSharp/GodotSharp/Core/Array.cs | 4 +- .../Attributes/AssemblyHasScriptsAttribute.cs | 17 +- .../Core/Attributes/ScriptPathAttribute.cs | 4 +- .../Core/Bridge/CSharpInstanceBridge.cs | 113 ++ .../GodotSharp/Core/Bridge/GCHandleBridge.cs | 11 + .../Core/Bridge/ScriptManagerBridge.cs | 511 +++++++ .../GodotSharp/Core/DelegateUtils.cs | 14 +- .../GodotSharp/GodotSharp/Core/Dictionary.cs | 4 +- .../Core/Extensions/SceneTreeExtensions.cs | 62 +- .../glue/GodotSharp/GodotSharp/Core/GD.cs | 4 +- .../GodotSharp/Core/GodotTraceListener.cs | 5 +- .../Core/NativeInterop/InteropStructs.cs | 48 +- .../Core/NativeInterop/InteropUtils.cs | 98 +- .../Core/NativeInterop/Marshaling.cs | 30 +- .../Core/NativeInterop/NativeFuncs.cs | 2 +- .../NativeInterop/NativeFuncs.extended.cs | 2 +- .../Core/NativeInterop/VariantUtils.cs | 2 +- .../GodotSharp/GodotSharp/Core/NodePath.cs | 10 +- .../GodotSharp/GodotSharp/Core/Object.base.cs | 269 +++- .../GodotSharp/Core/ScriptManager.cs | 10 - .../GodotSharp/Core/SignalAwaiter.cs | 35 +- .../GodotSharp/Core/StringExtensions.cs | 4 +- .../GodotSharp/GodotSharp/Core/StringName.cs | 4 +- .../GodotSharp/GodotSharp/GodotSharp.csproj | 4 +- modules/mono/glue/base_object_glue.cpp | 24 +- modules/mono/glue/placeholder_glue.cpp | 159 +- modules/mono/glue/runtime_interop.cpp | 12 +- modules/mono/glue/scene_tree_glue.cpp | 81 -- modules/mono/godotsharp_defs.h | 1 + modules/mono/godotsharp_dirs.cpp | 38 +- modules/mono/godotsharp_dirs.h | 6 +- modules/mono/managed_callable.cpp | 19 +- modules/mono/managed_callable.h | 7 +- modules/mono/mono_gc_handle.cpp | 36 +- modules/mono/mono_gc_handle.h | 49 +- modules/mono/mono_gd/gd_mono.cpp | 453 +----- modules/mono/mono_gd/gd_mono.h | 70 +- modules/mono/mono_gd/gd_mono_assembly.cpp | 102 +- modules/mono/mono_gd/gd_mono_assembly.h | 41 - modules/mono/mono_gd/gd_mono_cache.cpp | 221 +-- modules/mono/mono_gd/gd_mono_cache.h | 113 +- modules/mono/mono_gd/gd_mono_class.cpp | 570 -------- modules/mono/mono_gd/gd_mono_class.h | 160 --- modules/mono/mono_gd/gd_mono_field.cpp | 149 -- modules/mono/mono_gd/gd_mono_field.h | 78 - modules/mono/mono_gd/gd_mono_header.h | 52 - modules/mono/mono_gd/gd_mono_internals.cpp | 70 - modules/mono/mono_gd/gd_mono_internals.h | 4 +- modules/mono/mono_gd/gd_mono_marshal.cpp | 151 -- modules/mono/mono_gd/gd_mono_marshal.h | 97 -- modules/mono/mono_gd/gd_mono_method.cpp | 295 ---- modules/mono/mono_gd/gd_mono_method.h | 96 -- modules/mono/mono_gd/gd_mono_method_thunk.h | 264 +--- modules/mono/mono_gd/gd_mono_property.cpp | 204 --- modules/mono/mono_gd/gd_mono_property.h | 79 - modules/mono/mono_gd/gd_mono_utils.cpp | 234 +-- modules/mono/mono_gd/gd_mono_utils.h | 38 - modules/mono/mono_gd/i_mono_class_member.h | 70 - modules/mono/mono_gd/managed_type.cpp | 58 - modules/mono/mono_gd/managed_type.h | 55 - .../mono/mono_gd/support/android_support.cpp | 2 +- modules/mono/signal_awaiter_utils.cpp | 81 +- modules/mono/signal_awaiter_utils.h | 8 +- 79 files changed, 2514 insertions(+), 5125 deletions(-) create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs delete mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/ScriptManager.cs delete mode 100644 modules/mono/glue/scene_tree_glue.cpp delete mode 100644 modules/mono/mono_gd/gd_mono_class.cpp delete mode 100644 modules/mono/mono_gd/gd_mono_class.h delete mode 100644 modules/mono/mono_gd/gd_mono_field.cpp delete mode 100644 modules/mono/mono_gd/gd_mono_field.h delete mode 100644 modules/mono/mono_gd/gd_mono_header.h delete mode 100644 modules/mono/mono_gd/gd_mono_marshal.cpp delete mode 100644 modules/mono/mono_gd/gd_mono_marshal.h delete mode 100644 modules/mono/mono_gd/gd_mono_method.cpp delete mode 100644 modules/mono/mono_gd/gd_mono_method.h delete mode 100644 modules/mono/mono_gd/gd_mono_property.cpp delete mode 100644 modules/mono/mono_gd/gd_mono_property.h delete mode 100644 modules/mono/mono_gd/i_mono_class_member.h delete mode 100644 modules/mono/mono_gd/managed_type.cpp delete mode 100644 modules/mono/mono_gd/managed_type.h diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 75e2efa8a4..5277db86ad 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -56,8 +56,6 @@ #include "godotsharp_dirs.h" #include "managed_callable.h" #include "mono_gd/gd_mono_cache.h" -#include "mono_gd/gd_mono_class.h" -#include "mono_gd/gd_mono_marshal.h" #include "mono_gd/gd_mono_utils.h" #include "signal_awaiter_utils.h" #include "utils/macros.h" @@ -606,6 +604,8 @@ String CSharpLanguage::debug_get_stack_level_source(int p_level) const { return String(); } +#warning TODO +#if 0 Vector CSharpLanguage::debug_get_current_stack_info() { #ifdef DEBUG_ENABLED // Printing an error here will result in endless recursion, so we must be careful @@ -694,6 +694,11 @@ Vector CSharpLanguage::stack_trace_get_info(MonoObjec return si; } #endif +#else +Vector CSharpLanguage::debug_get_current_stack_info() { + return Vector(); +} +#endif void CSharpLanguage::post_unsafe_reference(Object *p_obj) { #ifdef DEBUG_ENABLED @@ -718,37 +723,13 @@ void CSharpLanguage::pre_unsafe_unreference(Object *p_obj) { void CSharpLanguage::frame() { if (gdmono && gdmono->is_runtime_initialized() && gdmono->get_core_api_assembly() != nullptr) { MonoException *exc = nullptr; - gdmono->get_core_api_assembly() - ->get_class("Godot", "ScriptManager") - ->get_method("FrameCallback") - ->invoke(nullptr, &exc); - + GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_FrameCallback.invoke(&exc); if (exc) { GDMonoUtils::debug_unhandled_exception(exc); } } } -struct CSharpScriptDepSort { - // must support sorting so inheritance works properly (parent must be reloaded first) - bool operator()(const Ref &A, const Ref &B) const { - if (A == B) { - return false; // shouldn't happen but.. - } - GDMonoClass *I = B->base; - while (I) { - if (I == A->script_class) { - // A is a base of B - return true; - } - - I = I->get_parent_class(); - } - - return false; // not a base - } -}; - void CSharpLanguage::reload_all_scripts() { #ifdef GD_MONO_HOT_RELOAD if (is_assembly_reloading_needed()) { @@ -819,6 +800,8 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { return; } +#warning TODO ALCs after switching to .NET 6 +#if 0 // There is no soft reloading with Mono. It's always hard reloading. List> scripts; @@ -845,7 +828,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { MonoObject *managed_serialized_data = GDMonoMarshal::variant_to_mono_object(serialized_data); MonoException *exc = nullptr; - bool success = (bool)CACHED_METHOD_THUNK(DelegateUtils, TrySerializeDelegateWithGCHandle) + bool success = (bool)GDMonoCache::cached_data.methodthunk_DelegateUtils_TrySerializeDelegateWithGCHandle .invoke(managed_callable->delegate_handle, managed_serialized_data, &exc); @@ -925,7 +908,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { CSharpInstance *csi = static_cast(obj->get_script_instance()); // Call OnBeforeSerialize - if (csi->script->script_class->implements_interface(CACHED_CLASS(ISerializationListener))) { + if (csi->script->script_class->implements_interface(GDMonoCache::cached_data.class_ISerializationListener)) { obj->get_script_instance()->call(string_names.on_before_serialize); } @@ -993,7 +976,6 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { #ifdef TOOLS_ENABLED script->exports_invalidated = true; #endif - script->signals_invalidated = true; if (!script->get_path().is_empty()) { script->reload(p_soft_reload); @@ -1027,7 +1009,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { continue; } - bool obj_type = CACHED_CLASS(GodotObject)->is_assignable_from(script_class); + bool obj_type = GDMonoCache::cached_data.class_GodotObject->is_assignable_from(script_class); if (!obj_type) { // The class no longer inherits Godot.Object, can't reload script->pending_reload_instances.clear(); @@ -1119,20 +1101,20 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { const StringName &name = G.first; const Array &serialized_data = G.second; - Map::Element *match = script->event_signals.find(name); + Map::Element *match = script->event_signals.find(name); if (!match) { // The event or its signal attribute were removed continue; } - const CSharpScript::EventSignal &event_signal = match->value(); + GDMonoField *event_signal_field = match->value(); MonoObject *managed_serialized_data = GDMonoMarshal::variant_to_mono_object(serialized_data); MonoDelegate *delegate = nullptr; MonoException *exc = nullptr; - bool success = (bool)CACHED_METHOD_THUNK(DelegateUtils, TryDeserializeDelegate).invoke(managed_serialized_data, &delegate, &exc); + bool success = (bool)GDMonoCache::cached_data.methodthunk_DelegateUtils_TryDeserializeDelegate.invoke(managed_serialized_data, &delegate, &exc); if (exc) { GDMonoUtils::debug_print_unhandled_exception(exc); @@ -1141,14 +1123,14 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { if (success) { ERR_CONTINUE(delegate == nullptr); - event_signal.field->set_value(csi->get_mono_object(), (MonoObject *)delegate); + event_signal_field->set_value(csi->get_mono_object(), (MonoObject *)delegate); } else if (OS::get_singleton()->is_stdout_verbose()) { OS::get_singleton()->print("Failed to deserialize event signal delegate\n"); } } // Call OnAfterDeserialization - if (csi->script->script_class->implements_interface(CACHED_CLASS(ISerializationListener))) { + if (csi->script->script_class->implements_interface(GDMonoCache::cached_data.class_ISerializationListener)) { obj->get_script_instance()->call(string_names.on_after_deserialize); } } @@ -1169,7 +1151,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { void *delegate = nullptr; MonoException *exc = nullptr; - bool success = (bool)CACHED_METHOD_THUNK(DelegateUtils, TryDeserializeDelegateWithGCHandle) + bool success = (bool)GDMonoCache::cached_data.methodthunk_DelegateUtils_TryDeserializeDelegateWithGCHandle .invoke(managed_serialized_data, &delegate, &exc); if (exc) { @@ -1195,63 +1177,10 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { NodeDock::singleton->update_lists(); } #endif +#endif } #endif -void CSharpLanguage::lookup_script_for_class(GDMonoClass *p_class) { - if (!p_class->has_attribute(CACHED_CLASS(ScriptPathAttribute))) { - return; - } - - MonoObject *attr = p_class->get_attribute(CACHED_CLASS(ScriptPathAttribute)); - String path = CACHED_FIELD(ScriptPathAttribute, path)->get_string_value(attr); - - dotnet_script_lookup_map[path] = DotNetScriptLookupInfo( - p_class->get_namespace(), p_class->get_name(), p_class); -} - -void CSharpLanguage::lookup_scripts_in_assembly(GDMonoAssembly *p_assembly) { - if (p_assembly->has_attribute(CACHED_CLASS(AssemblyHasScriptsAttribute))) { - MonoObject *attr = p_assembly->get_attribute(CACHED_CLASS(AssemblyHasScriptsAttribute)); - bool requires_lookup = CACHED_FIELD(AssemblyHasScriptsAttribute, requiresLookup)->get_bool_value(attr); - - if (requires_lookup) { - // This is supported for scenarios where specifying all types would be cumbersome, - // such as when disabling C# source generators (for whatever reason) or when using a - // language other than C# that has nothing similar to source generators to automate it. - MonoImage *image = p_assembly->get_image(); - - int rows = mono_image_get_table_rows(image, MONO_TABLE_TYPEDEF); - - for (int i = 1; i < rows; i++) { - // We don't search inner classes, only top-level. - MonoClass *mono_class = mono_class_get(image, (i + 1) | MONO_TOKEN_TYPE_DEF); - - if (!mono_class_is_assignable_from(CACHED_CLASS_RAW(GodotObject), mono_class)) { - continue; - } - - GDMonoClass *current = p_assembly->get_class(mono_class); - if (current) { - lookup_script_for_class(current); - } - } - } else { - // This is the most likely scenario as we use C# source generators - MonoArray *script_types = (MonoArray *)CACHED_FIELD(AssemblyHasScriptsAttribute, scriptTypes)->get_value(attr); - - int length = mono_array_length(script_types); - - for (int i = 0; i < length; i++) { - MonoReflectionType *reftype = mono_array_get(script_types, MonoReflectionType *, i); - ManagedType type = ManagedType::from_reftype(reftype); - ERR_CONTINUE(!type.type_class); - lookup_script_for_class(type.type_class); - } - } - } -} - void CSharpLanguage::get_recognized_extensions(List *p_extensions) const { p_extensions->push_back("cs"); } @@ -1324,8 +1253,6 @@ void CSharpLanguage::_on_scripts_domain_about_to_unload() { } } #endif - - dotnet_script_lookup_map.clear(); } #ifdef TOOLS_ENABLED @@ -1334,18 +1261,20 @@ void CSharpLanguage::_editor_init_callback() { // Initialize GodotSharpEditor - GDMonoClass *editor_klass = GDMono::get_singleton()->get_tools_assembly()->get_class("GodotTools", "GodotSharpEditor"); + MonoClass *editor_klass = mono_class_from_name( + GDMono::get_singleton()->get_tools_assembly()->get_image(), + "GodotTools", "GodotSharpEditor"); CRASH_COND(editor_klass == nullptr); - MonoObject *mono_object = mono_object_new(mono_domain_get(), editor_klass->get_mono_ptr()); - CRASH_COND(mono_object == nullptr); + MonoMethod *create_instance = mono_class_get_method_from_name(editor_klass, "InternalCreateInstance", 1); + CRASH_COND(create_instance == nullptr); MonoException *exc = nullptr; - GDMonoUtils::runtime_object_init(mono_object, editor_klass, &exc); + EditorPlugin *godotsharp_editor = nullptr; + void *args[1] = { &godotsharp_editor }; + mono_runtime_invoke(create_instance, nullptr, args, (MonoObject **)&exc); UNHANDLED_EXCEPTION(exc); - EditorPlugin *godotsharp_editor = Object::cast_to( - GDMonoMarshal::mono_object_to_variant(mono_object).operator Object *()); CRASH_COND(godotsharp_editor == nullptr); // Enable it as a plugin @@ -1368,24 +1297,17 @@ void CSharpLanguage::release_script_gchandle(MonoGCHandleData &p_gchandle) { } } -void CSharpLanguage::release_script_gchandle(MonoObject *p_expected_obj, MonoGCHandleData &p_gchandle) { - uint32_t pinned_gchandle = GDMonoUtils::new_strong_gchandle_pinned(p_expected_obj); // We might lock after this, so pin it +void CSharpLanguage::release_script_gchandle(void *p_expected_mono_obj_unused, MonoGCHandleData &p_gchandle) { +#warning KNOWN BUG. DO NOT USE THIS IN PRODUCTION + // KNOWN BUG: + // I removed the patch from commit e558e1ec09aa27852426bbd24dfa21e9b60cfbfc. + // This may cause data races. Re-implementing it without the Mono embedding API would be + // too painful and would make the code even more of a mess than it already was. + // We will switch from scripts to the new extension system before a release with .NET 6 support. + // The problem the old patch was working around won't be present at all with the new extension system. - if (!p_gchandle.is_released()) { // Do not lock unnecessarily - MutexLock lock(get_singleton()->script_gchandle_release_mutex); - - MonoObject *target = p_gchandle.get_target(); - - // We release the gchandle if it points to the MonoObject* we expect (otherwise it was - // already released and could have been replaced) or if we can't get its target MonoObject* - // (which doesn't necessarily mean it was released, and we want it released in order to - // avoid locking other threads unnecessarily). - if (target == p_expected_obj || target == nullptr) { - p_gchandle.release(); - } - } - - GDMonoUtils::free_gchandle(pinned_gchandle); + (void)p_expected_mono_obj_unused; + return release_script_gchandle(p_gchandle); } CSharpLanguage::CSharpLanguage() { @@ -1417,18 +1339,25 @@ bool CSharpLanguage::setup_csharp_script_binding(CSharpScriptBinding &r_script_b ERR_FAIL_NULL_V(classinfo, false); type_name = classinfo->name; - GDMonoClass *type_class = GDMonoUtils::type_get_proxy_class(type_name); + bool parent_is_object_class = ClassDB::is_parent_class(p_object->get_class_name(), type_name); + ERR_FAIL_COND_V_MSG(!parent_is_object_class, false, + "Type inherits from native type '" + type_name + "', so it can't be instantiated in object of type: '" + p_object->get_class() + "'."); - ERR_FAIL_NULL_V(type_class, false); + MonoException *exc = nullptr; + GCHandleIntPtr strong_gchandle = + GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_CreateManagedForGodotObjectBinding + .invoke(&type_name, p_object, &exc); - MonoObject *mono_object = GDMonoUtils::create_managed_for_godot_object(type_class, type_name, p_object); + if (exc) { + GDMonoUtils::set_pending_exception(exc); + return false; + } - ERR_FAIL_NULL_V(mono_object, false); + ERR_FAIL_NULL_V(strong_gchandle.value, false); r_script_binding.inited = true; r_script_binding.type_name = type_name; - r_script_binding.wrapper_class = type_class; // cache - r_script_binding.gchandle = MonoGCHandleData::new_strong_handle(mono_object); + r_script_binding.gchandle = MonoGCHandleData(strong_gchandle, gdmono::GCHandleType::STRONG_HANDLE); r_script_binding.owner = p_object; // Tie managed to unmanaged @@ -1471,7 +1400,7 @@ void CSharpLanguage::_instance_binding_free_callback(void *, void *, void *p_bin if (GDMono::get_singleton() == nullptr) { #ifdef DEBUG_ENABLED - CRASH_COND(!csharp_lang->script_bindings.is_empty()); + CRASH_COND(csharp_lang && !csharp_lang->script_bindings.is_empty()); #endif // Mono runtime finalized, all the gchandle bindings were already released return; @@ -1493,10 +1422,11 @@ void CSharpLanguage::_instance_binding_free_callback(void *, void *, void *p_bin if (script_binding.inited) { // Set the native instance field to IntPtr.Zero, if not yet garbage collected. // This is done to avoid trying to dispose the native instance from Dispose(bool). - MonoObject *mono_object = script_binding.gchandle.get_target(); - if (mono_object) { - CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, nullptr); - } + MonoException *exc = nullptr; + GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_SetGodotObjectPtr + .invoke(script_binding.gchandle.get_intptr(), nullptr, &exc); + UNHANDLED_EXCEPTION(exc); + script_binding.gchandle.release(); } @@ -1532,15 +1462,23 @@ GDNativeBool CSharpLanguage::_instance_binding_reference_callback(void *p_token, // This means the owner is being referenced again by the unmanaged side, // so the owner must hold the managed side alive again to avoid it from being GCed. - MonoObject *target = gchandle.get_target(); - if (!target) { + // Release the current weak handle and replace it with a strong handle. + + GCHandleIntPtr old_gchandle = gchandle.get_intptr(); + gchandle.handle = GCHandleIntPtr(); // No longer owns the handle (released by swap function) + + GCHandleIntPtr new_gchandle; + bool create_weak = false; + MonoException *exc = nullptr; + bool target_alive = GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_SwapGCHandleForType + .invoke(old_gchandle, &new_gchandle, create_weak, &exc); + UNHANDLED_EXCEPTION(exc); + + if (!target_alive) { return false; // Called after the managed side was collected, so nothing to do here } - // Release the current weak handle and replace it with a strong handle. - MonoGCHandleData strong_gchandle = MonoGCHandleData::new_strong_handle(target); - gchandle.release(); - gchandle = strong_gchandle; + gchandle = MonoGCHandleData(new_gchandle, gdmono::GCHandleType::STRONG_HANDLE); } return false; @@ -1552,15 +1490,23 @@ GDNativeBool CSharpLanguage::_instance_binding_reference_callback(void *p_token, // If owner owner is no longer referenced by the unmanaged side, // the managed instance takes responsibility of deleting the owner when GCed. - MonoObject *target = gchandle.get_target(); - if (!target) { + // Release the current strong handle and replace it with a weak handle. + + GCHandleIntPtr old_gchandle = gchandle.get_intptr(); + gchandle.handle = GCHandleIntPtr(); // No longer owns the handle (released by swap function) + + GCHandleIntPtr new_gchandle; + bool create_weak = true; + MonoException *exc = nullptr; + bool target_alive = GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_SwapGCHandleForType + .invoke(old_gchandle, &new_gchandle, create_weak, &exc); + UNHANDLED_EXCEPTION(exc); + + if (!target_alive) { return refcount == 0; // Called after the managed side was collected, so nothing to do here } - // Release the current strong handle and replace it with a weak handle. - MonoGCHandleData weak_gchandle = MonoGCHandleData::new_weak_handle(target); - gchandle.release(); - gchandle = weak_gchandle; + gchandle = MonoGCHandleData(new_gchandle, gdmono::GCHandleType::WEAK_HANDLE); return false; } @@ -1605,6 +1551,107 @@ void CSharpLanguage::set_instance_binding(Object *p_object, void *p_binding) { bool CSharpLanguage::has_instance_binding(Object *p_object) { return p_object->has_instance_binding(get_singleton()); } +void CSharpLanguage::tie_native_managed_to_unmanaged(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged, const StringName *p_native_name, bool p_ref_counted) { + // This method should not fail + + CRASH_COND(!p_unmanaged); + + // All mono objects created from the managed world (e.g.: 'new Player()') + // need to have a CSharpScript in order for their methods to be callable from the unmanaged side + + RefCounted *rc = Object::cast_to(p_unmanaged); + + CRASH_COND(p_ref_counted != (bool)rc); + + MonoGCHandleData gchandle = MonoGCHandleData(p_gchandle_intptr, + p_ref_counted ? gdmono::GCHandleType::WEAK_HANDLE : gdmono::GCHandleType::STRONG_HANDLE); + + // If it's just a wrapper Godot class and not a custom inheriting class, then attach a + // script binding instead. One of the advantages of this is that if a script is attached + // later and it's not a C# script, then the managed object won't have to be disposed. + // Another reason for doing this is that this instance could outlive CSharpLanguage, which would + // be problematic when using a script. See: https://github.com/godotengine/godot/issues/25621 + + CSharpScriptBinding script_binding; + + script_binding.inited = true; + script_binding.type_name = *p_native_name; + script_binding.gchandle = gchandle; + script_binding.owner = p_unmanaged; + + if (p_ref_counted) { + // Unsafe refcount increment. The managed instance also counts as a reference. + // This way if the unmanaged world has no references to our owner + // but the managed instance is alive, the refcount will be 1 instead of 0. + // See: godot_icall_RefCounted_Dtor(MonoObject *p_obj, Object *p_ptr) + + // May not me referenced yet, so we must use init_ref() instead of reference() + if (rc->init_ref()) { + CSharpLanguage::get_singleton()->post_unsafe_reference(rc); + } + } + + // The object was just created, no script instance binding should have been attached + CRASH_COND(CSharpLanguage::has_instance_binding(p_unmanaged)); + + void *data = (void *)CSharpLanguage::get_singleton()->insert_script_binding(p_unmanaged, script_binding); + + // Should be thread safe because the object was just created and nothing else should be referencing it + CSharpLanguage::set_instance_binding(p_unmanaged, data); +} + +void CSharpLanguage::tie_user_managed_to_unmanaged(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged, CSharpScript *p_script, bool p_ref_counted) { + // This method should not fail + + CRASH_COND(!p_unmanaged); + + // All mono objects created from the managed world (e.g.: 'new Player()') + // need to have a CSharpScript in order for their methods to be callable from the unmanaged side + + RefCounted *rc = Object::cast_to(p_unmanaged); + + CRASH_COND(p_ref_counted != (bool)rc); + + MonoGCHandleData gchandle = MonoGCHandleData(p_gchandle_intptr, + p_ref_counted ? gdmono::GCHandleType::WEAK_HANDLE : gdmono::GCHandleType::STRONG_HANDLE); + + Ref script = p_script; + + CSharpScript::initialize_for_managed_type(script); + + CRASH_COND(script.is_null()); + + CSharpInstance *csharp_instance = CSharpInstance::create_for_managed_type(p_unmanaged, script.ptr(), gchandle); + + p_unmanaged->set_script_and_instance(script, csharp_instance); +} + +void CSharpLanguage::tie_managed_to_unmanaged_with_pre_setup(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged) { + // This method should not fail + + CRASH_COND(!p_unmanaged); + + CSharpInstance *instance = CAST_CSHARP_INSTANCE(p_unmanaged->get_script_instance()); + + if (!instance) { + return; + } + + CRASH_COND(!instance->gchandle.is_released()); + + // Tie managed to unmanaged + instance->gchandle = MonoGCHandleData(p_gchandle_intptr, gdmono::GCHandleType::STRONG_HANDLE); + + if (instance->base_ref_counted) { + instance->_reference_owner_unsafe(); // Here, after assigning the gchandle (for the refcount_incremented callback) + } + + { + MutexLock lock(CSharpLanguage::get_singleton()->get_script_instances_mutex()); + // instances is a set, so it's safe to insert multiple times (e.g.: from _internal_new_managed) + instance->script->instances.insert(instance->owner); + } +} CSharpInstance *CSharpInstance::create_for_managed_type(Object *p_owner, CSharpScript *p_script, const MonoGCHandleData &p_gchandle) { CSharpInstance *instance = memnew(CSharpInstance(Ref(p_script))); @@ -1624,11 +1671,6 @@ CSharpInstance *CSharpInstance::create_for_managed_type(Object *p_owner, CSharpS return instance; } -MonoObject *CSharpInstance::get_mono_object() const { - ERR_FAIL_COND_V(gchandle.is_released(), nullptr); - return gchandle.get_target(); -} - Object *CSharpInstance::get_owner() { return owner; } @@ -1638,50 +1680,14 @@ bool CSharpInstance::set(const StringName &p_name, const Variant &p_value) { GD_MONO_SCOPE_THREAD_ATTACH; - MonoObject *mono_object = get_mono_object(); - ERR_FAIL_NULL_V(mono_object, false); + MonoException *exc = nullptr; + bool ret = GDMonoCache::cached_data.methodthunk_CSharpInstanceBridge_Set.invoke( + gchandle.get_intptr(), &p_name, &p_value, &exc); - GDMonoClass *top = script->script_class; - - while (top && top != script->native) { - GDMonoField *field = top->get_field(p_name); - - if (field) { - field->set_value_from_variant(mono_object, p_value); - return true; - } - - GDMonoProperty *property = top->get_property(p_name); - - if (property) { - property->set_value(mono_object, GDMonoMarshal::variant_to_mono_object_of_type(p_value, property->get_type())); - return true; - } - - top = top->get_parent_class(); - } - - // Call _set - - top = script->script_class; - - while (top && top != script->native) { - GDMonoMethod *method = top->get_method(CACHED_STRING_NAME(_set), 2); - - if (method) { - Variant name = p_name; - const Variant *args[2] = { &name, &p_value }; - - MonoObject *ret = method->invoke(mono_object, args); - - if (ret && GDMonoMarshal::unbox(ret)) { - return true; - } - - break; - } - - top = top->get_parent_class(); + if (exc) { + GDMonoUtils::set_pending_exception(exc); + } else if (ret) { + return true; } return false; @@ -1692,64 +1698,24 @@ bool CSharpInstance::get(const StringName &p_name, Variant &r_ret) const { GD_MONO_SCOPE_THREAD_ATTACH; - MonoObject *mono_object = get_mono_object(); - ERR_FAIL_NULL_V(mono_object, false); + Variant ret_value; - GDMonoClass *top = script->script_class; + MonoException *exc = nullptr; + bool ret = GDMonoCache::cached_data.methodthunk_CSharpInstanceBridge_Get.invoke( + gchandle.get_intptr(), &p_name, &ret_value, &exc); - while (top && top != script->native) { - GDMonoField *field = top->get_field(p_name); - - if (field) { - MonoObject *value = field->get_value(mono_object); - r_ret = GDMonoMarshal::mono_object_to_variant(value); - return true; - } - - GDMonoProperty *property = top->get_property(p_name); - - if (property) { - MonoException *exc = nullptr; - MonoObject *value = property->get_value(mono_object, &exc); - if (exc) { - r_ret = Variant(); - GDMonoUtils::set_pending_exception(exc); - } else { - r_ret = GDMonoMarshal::mono_object_to_variant(value); - } - return true; - } - - top = top->get_parent_class(); - } - - // Call _get - - top = script->script_class; - - while (top && top != script->native) { - GDMonoMethod *method = top->get_method(CACHED_STRING_NAME(_get), 1); - - if (method) { - Variant name = p_name; - const Variant *args[1] = { &name }; - - MonoObject *ret = method->invoke(mono_object, args); - - if (ret) { - r_ret = GDMonoMarshal::mono_object_to_variant(ret); - return true; - } - - break; - } - - top = top->get_parent_class(); + if (exc) { + GDMonoUtils::set_pending_exception(exc); + } else if (ret) { + r_ret = ret_value; + return true; } return false; } +#warning TODO +#if 0 void CSharpInstance::get_properties_state_for_reloading(List> &r_state) { List property_list; get_property_list(&property_list); @@ -1779,10 +1745,10 @@ void CSharpInstance::get_event_signals_state_for_reloading(List &E : script->event_signals) { - const CSharpScript::EventSignal &event_signal = E.value; + for (const KeyValue &E : script->event_signals) { + GDMonoField *event_signal_field = E.value; - MonoDelegate *delegate_field_value = (MonoDelegate *)event_signal.field->get_value(owner_managed); + MonoDelegate *delegate_field_value = (MonoDelegate *)event_signal_field->get_value(owner_managed); if (!delegate_field_value) { continue; // Empty } @@ -1791,7 +1757,7 @@ void CSharpInstance::get_event_signals_state_for_reloading(List(event_signal.field->get_name(), serialized_data)); + r_state.push_back(Pair(event_signal_field->get_name(), serialized_data)); } else if (OS::get_singleton()->is_stdout_verbose()) { OS::get_singleton()->print("Failed to serialize event signal delegate\n"); } } } +#endif void CSharpInstance::get_property_list(List *p_properties) const { for (const KeyValue &E : script->member_info) { @@ -1818,29 +1785,24 @@ void CSharpInstance::get_property_list(List *p_properties) const { GD_MONO_SCOPE_THREAD_ATTACH; - MonoObject *mono_object = get_mono_object(); - ERR_FAIL_NULL(mono_object); + StringName method = SNAME("_get_property_list"); - GDMonoClass *top = script->script_class; + Variant ret; + Callable::CallError call_error; + MonoException *exc = nullptr; + GDMonoCache::cached_data.methodthunk_CSharpInstanceBridge_Call.invoke( + gchandle.get_intptr(), &method, nullptr, 0, &call_error, &ret, &exc); - while (top && top != script->native) { - GDMonoMethod *method = top->get_method(CACHED_STRING_NAME(_get_property_list), 0); + if (exc) { + GDMonoUtils::set_pending_exception(exc); + } - if (method) { - MonoObject *ret = method->invoke(mono_object); + ERR_FAIL_COND_MSG(call_error.error != Callable::CallError::CALL_OK, + "Error calling '_get_property_list': " + Variant::get_call_error_text(method, nullptr, 0, call_error)); - if (ret) { - Array array = Array(GDMonoMarshal::mono_object_to_variant(ret)); - for (int i = 0, size = array.size(); i < size; i++) { - p_properties->push_back(PropertyInfo::from_dict(array.get(i))); - } - return; - } - - break; - } - - top = top->get_parent_class(); + Array array = ret; + for (int i = 0, size = array.size(); i < size; i++) { + p_properties->push_back(PropertyInfo::from_dict(array.get(i))); } } @@ -1866,17 +1828,19 @@ bool CSharpInstance::has_method(const StringName &p_method) const { GD_MONO_SCOPE_THREAD_ATTACH; - GDMonoClass *top = script->script_class; - - while (top && top != script->native) { - if (top->has_fetched_method_unknown_params(p_method)) { - return true; - } - - top = top->get_parent_class(); + if (!GDMonoCache::cached_data.godot_api_cache_updated) { + return false; } - return false; + String method = p_method; + bool deep = true; + + MonoException *exc = nullptr; + bool found = GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_HasMethodUnknownParams + .invoke(script.ptr(), &method, deep, &exc); + UNHANDLED_EXCEPTION(exc); + + return found; } Variant CSharpInstance::call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { @@ -1884,36 +1848,16 @@ Variant CSharpInstance::call(const StringName &p_method, const Variant **p_args, GD_MONO_SCOPE_THREAD_ATTACH; - MonoObject *mono_object = get_mono_object(); + Variant ret; + MonoException *exc = nullptr; + GDMonoCache::cached_data.methodthunk_CSharpInstanceBridge_Call.invoke( + gchandle.get_intptr(), &p_method, p_args, p_argcount, &r_error, &ret, &exc); - if (!mono_object) { - r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL; - ERR_FAIL_V(Variant()); + if (exc) { + GDMonoUtils::set_pending_exception(exc); } - GDMonoClass *top = script->script_class; - - while (top && top != script->native) { - GDMonoMethod *method = top->get_method(p_method, p_argcount); - - if (method) { - MonoObject *return_value = method->invoke(mono_object, p_args); - - r_error.error = Callable::CallError::CALL_OK; - - if (return_value) { - return GDMonoMarshal::mono_object_to_variant(return_value); - } else { - return Variant(); - } - } - - top = top->get_parent_class(); - } - - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - - return Variant(); + return ret; } bool CSharpInstance::_reference_owner_unsafe() { @@ -1959,48 +1903,32 @@ bool CSharpInstance::_unreference_owner_unsafe() { return static_cast(owner)->unreference(); } -MonoObject *CSharpInstance::_internal_new_managed() { - // Search the constructor first, to fail with an error if it's not found before allocating anything else. - GDMonoMethod *ctor = script->script_class->get_method(CACHED_STRING_NAME(dotctor), 0); - ERR_FAIL_NULL_V_MSG(ctor, nullptr, - "Cannot create script instance because the class does not define a parameterless constructor: '" + script->get_path() + "'."); - +bool CSharpInstance::_internal_new_managed() { CSharpLanguage::get_singleton()->release_script_gchandle(gchandle); - ERR_FAIL_NULL_V(owner, nullptr); - ERR_FAIL_COND_V(script.is_null(), nullptr); + ERR_FAIL_NULL_V(owner, false); + ERR_FAIL_COND_V(script.is_null(), false); - MonoObject *mono_object = mono_object_new(mono_domain_get(), script->script_class->get_mono_ptr()); + MonoException *exc = nullptr; + GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance + .invoke(script.ptr(), owner, nullptr, 0, &exc); + + if (exc) { + GDMonoUtils::set_pending_exception(exc); - if (!mono_object) { // Important to clear this before destroying the script instance here script = Ref(); - - bool die = _unreference_owner_unsafe(); - // Not ok for the owner to die here. If there is a situation where this can happen, it will be considered a bug. - CRASH_COND(die); - owner = nullptr; - ERR_FAIL_V_MSG(nullptr, "Failed to allocate memory for the object."); + return false; } - // Tie managed to unmanaged - gchandle = MonoGCHandleData::new_strong_handle(mono_object); + CRASH_COND(gchandle.is_released()); - if (base_ref_counted) { - _reference_owner_unsafe(); // Here, after assigning the gchandle (for the refcount_incremented callback) - } - - CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, owner); - - // Construct - ctor->invoke_raw(mono_object, nullptr); - - return mono_object; + return true; } -void CSharpInstance::mono_object_disposed(MonoObject *p_obj) { +void CSharpInstance::mono_object_disposed() { // Must make sure event signals are not left dangling disconnect_event_signals(); @@ -2008,10 +1936,10 @@ void CSharpInstance::mono_object_disposed(MonoObject *p_obj) { CRASH_COND(base_ref_counted); CRASH_COND(gchandle.is_released()); #endif - CSharpLanguage::get_singleton()->release_script_gchandle(p_obj, gchandle); + CSharpLanguage::get_singleton()->release_script_gchandle(nullptr, gchandle); } -void CSharpInstance::mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_finalizer, bool &r_delete_owner, bool &r_remove_script_instance) { +void CSharpInstance::mono_object_disposed_baseref(bool p_is_finalizer, bool &r_delete_owner, bool &r_remove_script_instance) { #ifdef DEBUG_ENABLED CRASH_COND(!base_ref_counted); CRASH_COND(gchandle.is_released()); @@ -2027,7 +1955,7 @@ void CSharpInstance::mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_f r_delete_owner = true; } else { r_delete_owner = false; - CSharpLanguage::get_singleton()->release_script_gchandle(p_obj, gchandle); + CSharpLanguage::get_singleton()->release_script_gchandle(nullptr, gchandle); if (!p_is_finalizer) { // If the native instance is still alive and Dispose() was called @@ -2039,27 +1967,20 @@ void CSharpInstance::mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_f // unreference and delete it, so we want to keep it. // GC.ReRegisterForFinalize(this) is not safe because the objects referenced by 'this' // could have already been collected. Instead we will create a new managed instance here. - MonoObject *new_managed = _internal_new_managed(); - if (!new_managed) { + if (!_internal_new_managed()) { r_remove_script_instance = true; } } } } -void CSharpInstance::connect_event_signals() { - for (const KeyValue &E : script->event_signals) { - const CSharpScript::EventSignal &event_signal = E.value; +void CSharpInstance::connect_event_signal(const StringName &p_event_signal) { + // TODO: Use pooling for ManagedCallable instances. + EventSignalCallable *event_signal_callable = memnew(EventSignalCallable(owner, p_event_signal)); - StringName signal_name = event_signal.field->get_name(); - - // TODO: Use pooling for ManagedCallable instances. - EventSignalCallable *event_signal_callable = memnew(EventSignalCallable(owner, &event_signal)); - - Callable callable(event_signal_callable); - connected_event_signals.push_back(callable); - owner->connect(signal_name, callable); - } + Callable callable(event_signal_callable); + connected_event_signals.push_back(callable); + owner->connect(p_event_signal, callable); } void CSharpInstance::disconnect_event_signals() { @@ -2087,9 +2008,22 @@ void CSharpInstance::refcount_incremented() { // so the owner must hold the managed side alive again to avoid it from being GCed. // Release the current weak handle and replace it with a strong handle. - MonoGCHandleData strong_gchandle = MonoGCHandleData::new_strong_handle(gchandle.get_target()); - gchandle.release(); - gchandle = strong_gchandle; + + GCHandleIntPtr old_gchandle = gchandle.get_intptr(); + gchandle.handle = GCHandleIntPtr(); // No longer owns the handle (released by swap function) + + GCHandleIntPtr new_gchandle; + bool create_weak = false; + MonoException *exc = nullptr; + bool target_alive = GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_SwapGCHandleForType + .invoke(old_gchandle, &new_gchandle, create_weak, &exc); + UNHANDLED_EXCEPTION(exc); + + if (!target_alive) { + return; // Called after the managed side was collected, so nothing to do here + } + + gchandle = MonoGCHandleData(new_gchandle, gdmono::GCHandleType::STRONG_HANDLE); } } @@ -2110,9 +2044,22 @@ bool CSharpInstance::refcount_decremented() { // the managed instance takes responsibility of deleting the owner when GCed. // Release the current strong handle and replace it with a weak handle. - MonoGCHandleData weak_gchandle = MonoGCHandleData::new_weak_handle(gchandle.get_target()); - gchandle.release(); - gchandle = weak_gchandle; + + GCHandleIntPtr old_gchandle = gchandle.get_intptr(); + gchandle.handle = GCHandleIntPtr(); // No longer owns the handle (released by swap function) + + GCHandleIntPtr new_gchandle; + bool create_weak = true; + MonoException *exc = nullptr; + bool target_alive = GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_SwapGCHandleForType + .invoke(old_gchandle, &new_gchandle, create_weak, &exc); + UNHANDLED_EXCEPTION(exc); + + if (!target_alive) { + return refcount == 0; // Called after the managed side was collected, so nothing to do here + } + + gchandle = MonoGCHandleData(new_gchandle, gdmono::GCHandleType::WEAK_HANDLE); return false; } @@ -2148,11 +2095,9 @@ void CSharpInstance::notification(int p_notification) { _call_notification(p_notification); - MonoObject *mono_object = get_mono_object(); - ERR_FAIL_NULL(mono_object); - MonoException *exc = nullptr; - GDMonoUtils::dispose(mono_object, &exc); + GDMonoCache::cached_data.methodthunk_CSharpInstanceBridge_CallDispose + .invoke(gchandle.get_intptr(), /* okIfNull */ false, &exc); if (exc) { GDMonoUtils::set_pending_exception(exc); @@ -2167,43 +2112,31 @@ void CSharpInstance::notification(int p_notification) { void CSharpInstance::_call_notification(int p_notification) { GD_MONO_ASSERT_THREAD_ATTACHED; - MonoObject *mono_object = get_mono_object(); - ERR_FAIL_NULL(mono_object); + Variant arg = p_notification; + const Variant *args[1] = { &arg }; + StringName method_name = SNAME("_notification"); - // Custom version of _call_multilevel, optimized for _notification + Callable::CallError call_error; - int32_t arg = p_notification; - void *args[1] = { &arg }; - StringName method_name = CACHED_STRING_NAME(_notification); + Variant ret; + MonoException *exc = nullptr; + GDMonoCache::cached_data.methodthunk_CSharpInstanceBridge_Call.invoke( + gchandle.get_intptr(), &method_name, args, 1, &call_error, &ret, &exc); - GDMonoClass *top = script->script_class; - - while (top && top != script->native) { - GDMonoMethod *method = top->get_method(method_name, 1); - - if (method) { - method->invoke_raw(mono_object, args); - return; - } - - top = top->get_parent_class(); + if (exc) { + GDMonoUtils::set_pending_exception(exc); } } String CSharpInstance::to_string(bool *r_valid) { GD_MONO_SCOPE_THREAD_ATTACH; - MonoObject *mono_object = get_mono_object(); - - if (mono_object == nullptr) { - if (r_valid) { - *r_valid = false; - } - return String(); - } + String res; + bool valid; MonoException *exc = nullptr; - MonoString *result = GDMonoUtils::object_to_string(mono_object, &exc); + GDMonoCache::cached_data.methodthunk_CSharpInstanceBridge_CallToString + .invoke(gchandle.get_intptr(), &res, &valid, &exc); if (exc) { GDMonoUtils::set_pending_exception(exc); @@ -2213,14 +2146,11 @@ String CSharpInstance::to_string(bool *r_valid) { return String(); } - if (result == nullptr) { - if (r_valid) { - *r_valid = false; - } - return String(); + if (r_valid) { + *r_valid = valid; } - return GDMonoMarshal::mono_string_to_godot(result); + return res; } Ref