diff options
79 files changed, 2557 insertions, 5218 deletions
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 4316f38bd2..b088271b18 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -59,8 +59,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" @@ -590,6 +588,8 @@ String CSharpLanguage::debug_get_stack_level_source(int p_level) const { return String(); } +#warning TODO +#if 0 Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info() { #ifdef DEBUG_ENABLED // Printing an error here will result in endless recursion, so we must be careful @@ -682,6 +682,11 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObjec return si; } #endif +#else +Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info() { + return Vector<StackInfo>(); +} +#endif void CSharpLanguage::post_unsafe_reference(Object *p_obj) { #ifdef DEBUG_ENABLED @@ -706,37 +711,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<CSharpScript> &A, const Ref<CSharpScript> &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()) { @@ -803,6 +784,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<Ref<CSharpScript>> scripts; @@ -829,7 +812,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); @@ -909,7 +892,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { CSharpInstance *csi = static_cast<CSharpInstance *>(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); } @@ -977,7 +960,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); @@ -1011,7 +993,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(); @@ -1103,20 +1085,20 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { const StringName &name = G.first; const Array &serialized_data = G.second; - HashMap<StringName, CSharpScript::EventSignal>::Iterator match = script->event_signals.find(name); + HashMap<StringName, GDMonoField *>::Iterator 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); @@ -1125,14 +1107,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); } } @@ -1153,7 +1135,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) { @@ -1179,62 +1161,9 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { NodeDock::get_singleton()->update_lists(); } #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); - } - } - } } +#endif void CSharpLanguage::get_recognized_extensions(List<String> *p_extensions) const { p_extensions->push_back("cs"); @@ -1308,8 +1237,6 @@ void CSharpLanguage::_on_scripts_domain_about_to_unload() { } } #endif - - dotnet_script_lookup_map.clear(); } #ifdef TOOLS_ENABLED @@ -1318,18 +1245,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", 0); + CRASH_COND(create_instance == nullptr); MonoException *exc = nullptr; - GDMonoUtils::runtime_object_init(mono_object, editor_klass, &exc); + MonoObject *ret = mono_runtime_invoke(create_instance, nullptr, nullptr, (MonoObject **)&exc); UNHANDLED_EXCEPTION(exc); - EditorPlugin *godotsharp_editor = Object::cast_to<EditorPlugin>( - GDMonoMarshal::mono_object_to_variant(mono_object).operator Object *()); + EditorPlugin *godotsharp_editor = *(EditorPlugin **)mono_object_unbox(ret); + CRASH_COND(godotsharp_editor == nullptr); // Enable it as a plugin @@ -1353,24 +1282,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() { @@ -1402,18 +1324,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 @@ -1478,11 +1407,13 @@ 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(); + script_binding.inited = false; } csharp_lang->script_bindings.erase(data); @@ -1517,15 +1448,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; @@ -1537,15 +1476,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; } @@ -1590,151 +1537,175 @@ 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 -CSharpInstance *CSharpInstance::create_for_managed_type(Object *p_owner, CSharpScript *p_script, const MonoGCHandleData &p_gchandle) { - CSharpInstance *instance = memnew(CSharpInstance(Ref<CSharpScript>(p_script))); + CRASH_COND(!p_unmanaged); - RefCounted *rc = Object::cast_to<RefCounted>(p_owner); + // 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 - instance->base_ref_counted = rc != nullptr; - instance->owner = p_owner; - instance->gchandle = p_gchandle; + RefCounted *rc = Object::cast_to<RefCounted>(p_unmanaged); - if (instance->base_ref_counted) { - instance->_reference_owner_unsafe(); + 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); + } } - p_script->instances.insert(p_owner); + // The object was just created, no script instance binding should have been attached + CRASH_COND(CSharpLanguage::has_instance_binding(p_unmanaged)); - return instance; -} + void *data; + { + MutexLock lock(CSharpLanguage::get_singleton()->get_language_bind_mutex()); + data = (void *)CSharpLanguage::get_singleton()->insert_script_binding(p_unmanaged, script_binding); + } -MonoObject *CSharpInstance::get_mono_object() const { - ERR_FAIL_COND_V(gchandle.is_released(), nullptr); - return gchandle.get_target(); + // Should be thread safe because the object was just created and nothing else should be referencing it + CSharpLanguage::set_instance_binding(p_unmanaged, data); } -Object *CSharpInstance::get_owner() { - return owner; -} +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 -bool CSharpInstance::set(const StringName &p_name, const Variant &p_value) { - ERR_FAIL_COND_V(!script.is_valid(), false); + CRASH_COND(!p_unmanaged); - GD_MONO_SCOPE_THREAD_ATTACH; + // 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 - MonoObject *mono_object = get_mono_object(); - ERR_FAIL_NULL_V(mono_object, false); + RefCounted *rc = Object::cast_to<RefCounted>(p_unmanaged); - GDMonoClass *top = script->script_class; + CRASH_COND(p_ref_counted != (bool)rc); - while (top && top != script->native) { - GDMonoField *field = top->get_field(p_name); + MonoGCHandleData gchandle = MonoGCHandleData(p_gchandle_intptr, + p_ref_counted ? gdmono::GCHandleType::WEAK_HANDLE : gdmono::GCHandleType::STRONG_HANDLE); - if (field) { - field->set_value_from_variant(mono_object, p_value); - return true; - } + Ref<CSharpScript> script = p_script; - GDMonoProperty *property = top->get_property(p_name); + CSharpScript::initialize_for_managed_type(script); - if (property) { - property->set_value_from_variant(mono_object, p_value); - return true; - } + CRASH_COND(script.is_null()); - top = top->get_parent_class(); - } + CSharpInstance *csharp_instance = CSharpInstance::create_for_managed_type(p_unmanaged, script.ptr(), gchandle); - // Call _set + p_unmanaged->set_script_and_instance(script, csharp_instance); +} - top = script->script_class; +void CSharpLanguage::tie_managed_to_unmanaged_with_pre_setup(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged) { + // This method should not fail - while (top && top != script->native) { - GDMonoMethod *method = top->get_method(CACHED_STRING_NAME(_set), 2); + CRASH_COND(!p_unmanaged); - if (method) { - Variant name = p_name; - const Variant *args[2] = { &name, &p_value }; + CSharpInstance *instance = CAST_CSHARP_INSTANCE(p_unmanaged->get_script_instance()); - MonoObject *ret = method->invoke(mono_object, args); + if (!instance) { + return; + } - if (ret && GDMonoMarshal::unbox<MonoBoolean>(ret)) { - return true; - } + CRASH_COND(!instance->gchandle.is_released()); - break; - } + // Tie managed to unmanaged + instance->gchandle = MonoGCHandleData(p_gchandle_intptr, gdmono::GCHandleType::STRONG_HANDLE); - top = top->get_parent_class(); + if (instance->base_ref_counted) { + instance->_reference_owner_unsafe(); // Here, after assigning the gchandle (for the refcount_incremented callback) } - return false; + { + 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); + } } -bool CSharpInstance::get(const StringName &p_name, Variant &r_ret) const { - ERR_FAIL_COND_V(!script.is_valid(), false); +CSharpInstance *CSharpInstance::create_for_managed_type(Object *p_owner, CSharpScript *p_script, const MonoGCHandleData &p_gchandle) { + CSharpInstance *instance = memnew(CSharpInstance(Ref<CSharpScript>(p_script))); - GD_MONO_SCOPE_THREAD_ATTACH; + RefCounted *rc = Object::cast_to<RefCounted>(p_owner); - MonoObject *mono_object = get_mono_object(); - ERR_FAIL_NULL_V(mono_object, false); + instance->base_ref_counted = rc != nullptr; + instance->owner = p_owner; + instance->gchandle = p_gchandle; - GDMonoClass *top = script->script_class; + if (instance->base_ref_counted) { + instance->_reference_owner_unsafe(); + } - while (top && top != script->native) { - GDMonoField *field = top->get_field(p_name); + p_script->instances.insert(p_owner); - if (field) { - MonoObject *value = field->get_value(mono_object); - r_ret = GDMonoMarshal::mono_object_to_variant(value); - return true; - } + return instance; +} - GDMonoProperty *property = top->get_property(p_name); +Object *CSharpInstance::get_owner() { + return owner; +} - 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; - } +bool CSharpInstance::set(const StringName &p_name, const Variant &p_value) { + ERR_FAIL_COND_V(!script.is_valid(), false); - top = top->get_parent_class(); - } + GD_MONO_SCOPE_THREAD_ATTACH; - // Call _get + MonoException *exc = nullptr; + bool ret = GDMonoCache::cached_data.methodthunk_CSharpInstanceBridge_Set.invoke( + gchandle.get_intptr(), &p_name, &p_value, &exc); - top = script->script_class; + if (exc) { + GDMonoUtils::set_pending_exception(exc); + } else if (ret) { + return true; + } - while (top && top != script->native) { - GDMonoMethod *method = top->get_method(CACHED_STRING_NAME(_get), 1); + return false; +} - if (method) { - Variant name = p_name; - const Variant *args[1] = { &name }; +bool CSharpInstance::get(const StringName &p_name, Variant &r_ret) const { + ERR_FAIL_COND_V(!script.is_valid(), false); - MonoObject *ret = method->invoke(mono_object, args); + GD_MONO_SCOPE_THREAD_ATTACH; - if (ret) { - r_ret = GDMonoMarshal::mono_object_to_variant(ret); - return true; - } + Variant ret_value; - break; - } + MonoException *exc = nullptr; + bool ret = GDMonoCache::cached_data.methodthunk_CSharpInstanceBridge_Get.invoke( + gchandle.get_intptr(), &p_name, &ret_value, &exc); - 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<Pair<StringName, Variant>> &r_state) { List<PropertyInfo> property_list; get_property_list(&property_list); @@ -1773,10 +1744,10 @@ void CSharpInstance::get_event_signals_state_for_reloading(List<Pair<StringName, MonoObject *owner_managed = get_mono_object(); ERR_FAIL_NULL(owner_managed); - for (const KeyValue<StringName, CSharpScript::EventSignal> &E : script->event_signals) { - const CSharpScript::EventSignal &event_signal = E.value; + for (const KeyValue<StringName, GDMonoField *> &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 } @@ -1785,7 +1756,7 @@ void CSharpInstance::get_event_signals_state_for_reloading(List<Pair<StringName, MonoObject *managed_serialized_data = GDMonoMarshal::variant_to_mono_object(serialized_data); MonoException *exc = nullptr; - bool success = (bool)CACHED_METHOD_THUNK(DelegateUtils, TrySerializeDelegate) + bool success = (bool)GDMonoCache::cached_data.methodthunk_DelegateUtils_TrySerializeDelegate .invoke(delegate_field_value, managed_serialized_data, &exc); if (exc) { @@ -1794,12 +1765,13 @@ void CSharpInstance::get_event_signals_state_for_reloading(List<Pair<StringName, } if (success) { - r_state.push_back(Pair<StringName, Array>(event_signal.field->get_name(), serialized_data)); + r_state.push_back(Pair<StringName, Array>(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<PropertyInfo> *p_properties) const { List<PropertyInfo> props; @@ -1811,28 +1783,24 @@ void CSharpInstance::get_property_list(List<PropertyInfo> *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; - - while (top && top != script->native) { - GDMonoMethod *method = top->get_method(CACHED_STRING_NAME(_get_property_list), 0); - - if (method) { - MonoObject *ret = method->invoke(mono_object); + 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); - if (ret) { - Array array = Array(GDMonoMarshal::mono_object_to_variant(ret)); - for (int i = 0, size = array.size(); i < size; i++) { - props.push_back(PropertyInfo::from_dict(array.get(i))); - } - } + if (exc) { + GDMonoUtils::set_pending_exception(exc); + } - break; - } + 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)); - 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))); } for (const PropertyInfo &prop : props) { @@ -1860,34 +1828,26 @@ bool CSharpInstance::property_can_revert(const StringName &p_name) const { GD_MONO_SCOPE_THREAD_ATTACH; - MonoObject *mono_object = get_mono_object(); - ERR_FAIL_NULL_V(mono_object, false); - - GDMonoClass *top = script->script_class; - - while (top && top != script->native) { - GDMonoMethod *method = top->get_method(CACHED_STRING_NAME(_property_can_revert), 1); - - if (method) { - Variant name = p_name; - const Variant *args[1] = { &name }; + Callable::CallError call_error; - MonoObject *ret = method->invoke(mono_object, args); + Variant name_arg = p_name; + const Variant *args[1] = { &name_arg }; - if (ret) { - bool can_revert = GDMonoMarshal::mono_object_to_variant(ret); - if (can_revert) { - return true; - } - } + Variant ret; + MonoException *exc = nullptr; + GDMonoCache::cached_data.methodthunk_CSharpInstanceBridge_Call.invoke( + gchandle.get_intptr(), &CACHED_STRING_NAME(_property_can_revert), args, 1, &call_error, &ret, &exc); - break; - } + if (exc) { + GDMonoUtils::set_pending_exception(exc); + return false; + } - top = top->get_parent_class(); + if (call_error.error != Callable::CallError::CALL_OK) { + return false; } - return false; + return (bool)ret; } bool CSharpInstance::property_get_revert(const StringName &p_name, Variant &r_ret) const { @@ -1895,35 +1855,32 @@ bool CSharpInstance::property_get_revert(const StringName &p_name, Variant &r_re GD_MONO_SCOPE_THREAD_ATTACH; - MonoObject *mono_object = get_mono_object(); - ERR_FAIL_NULL_V(mono_object, false); + Callable::CallError call_error; - GDMonoClass *top = script->script_class; + Variant name_arg = p_name; + const Variant *args[1] = { &name_arg }; - while (top && top != script->native) { - GDMonoMethod *method = top->get_method(CACHED_STRING_NAME(_property_get_revert), 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; - } + Variant ret; + MonoException *exc = nullptr; + GDMonoCache::cached_data.methodthunk_CSharpInstanceBridge_Call.invoke( + gchandle.get_intptr(), &CACHED_STRING_NAME(_property_get_revert), args, 1, &call_error, &ret, &exc); - break; - } + if (exc) { + GDMonoUtils::set_pending_exception(exc); + return false; + } - top = top->get_parent_class(); + if (call_error.error != Callable::CallError::CALL_OK) { + return false; } - return false; + r_ret = ret; + return true; } void CSharpInstance::get_method_list(List<MethodInfo> *p_list) const { +#warning TODO +#if 0 if (!script->is_valid() || !script->script_class) { return; } @@ -1944,6 +1901,7 @@ void CSharpInstance::get_method_list(List<MethodInfo> *p_list) const { top = top->get_parent_class(); } +#endif } bool CSharpInstance::has_method(const StringName &p_method) const { @@ -1953,17 +1911,19 @@ bool CSharpInstance::has_method(const StringName &p_method) const { GD_MONO_SCOPE_THREAD_ATTACH; - GDMonoClass *top = script->script_class; + if (!GDMonoCache::cached_data.godot_api_cache_updated) { + return false; + } - while (top && top != script->native) { - if (top->has_fetched_method_unknown_params(p_method)) { - return true; - } + String method = p_method; + bool deep = true; - top = top->get_parent_class(); - } + MonoException *exc = nullptr; + bool found = GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_HasMethodUnknownParams + .invoke(script.ptr(), &method, deep, &exc); + UNHANDLED_EXCEPTION(exc); - return false; + return found; } Variant CSharpInstance::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { @@ -1971,36 +1931,16 @@ Variant CSharpInstance::callp(const StringName &p_method, const Variant **p_args GD_MONO_SCOPE_THREAD_ATTACH; - MonoObject *mono_object = get_mono_object(); - - if (!mono_object) { - r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL; - ERR_FAIL_V(Variant()); - } - - 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(); - } - } + 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); - top = top->get_parent_class(); + if (exc) { + GDMonoUtils::set_pending_exception(exc); } - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - - return Variant(); + return ret; } bool CSharpInstance::_reference_owner_unsafe() { @@ -2046,48 +1986,32 @@ bool CSharpInstance::_unreference_owner_unsafe() { return static_cast<RefCounted *>(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<CSharpScript>(); - - 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."); - } - - // Tie managed to unmanaged - gchandle = MonoGCHandleData::new_strong_handle(mono_object); - - if (base_ref_counted) { - _reference_owner_unsafe(); // Here, after assigning the gchandle (for the refcount_incremented callback) + return false; } - CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, owner); - - // Construct - ctor->invoke_raw(mono_object, nullptr); + CRASH_COND(gchandle.is_released()); - 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(); @@ -2095,10 +2019,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()); @@ -2114,7 +2038,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 @@ -2126,27 +2050,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<StringName, CSharpScript::EventSignal> &E : script->event_signals) { - const CSharpScript::EventSignal &event_signal = E.value; - - StringName signal_name = event_signal.field->get_name(); - - // TODO: Use pooling for ManagedCallable instances. - EventSignalCallable *event_signal_callable = memnew(EventSignalCallable(owner, &event_signal)); +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)); - 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() { @@ -2174,9 +2091,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); } } @@ -2197,9 +2127,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; } @@ -2235,11 +2178,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); @@ -2254,43 +2195,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); - - // Custom version of _call_multilevel, optimized for _notification + Variant arg = p_notification; + const Variant *args[1] = { &arg }; + StringName method_name = SNAME("_notification"); - int32_t arg = p_notification; - void *args[1] = { &arg }; - StringName method_name = CACHED_STRING_NAME(_notification); - - GDMonoClass *top = script->script_class; + Callable::CallError call_error; - while (top && top != script->native) { - GDMonoMethod *method = top->get_method(method_name, 1); - - if (method) { - method->invoke_raw(mono_object, args); - return; - } + Variant ret; + MonoException *exc = nullptr; + GDMonoCache::cached_data.methodthunk_CSharpInstanceBridge_Call.invoke( + gchandle.get_intptr(), &method_name, args, 1, &call_error, &ret, &exc); - 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); @@ -2300,14 +2229,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<Script> CSharpInstance::get_script() const { @@ -2338,15 +2264,12 @@ CSharpInstance::~CSharpInstance() { // we must call Dispose here, because Dispose calls owner->set_script_instance(nullptr) // and that would mess up with the new script instance if called later. - MonoObject *mono_object = gchandle.get_target(); - - if (mono_object) { - MonoException *exc = nullptr; - GDMonoUtils::dispose(mono_object, &exc); + MonoException *exc = nullptr; + GDMonoCache::cached_data.methodthunk_CSharpInstanceBridge_CallDispose + .invoke(gchandle.get_intptr(), /* okIfNull */ true, &exc); - if (exc) { - GDMonoUtils::set_pending_exception(exc); - } + if (exc) { + GDMonoUtils::set_pending_exception(exc); } } @@ -2424,6 +2347,8 @@ void CSharpScript::_update_member_info_no_exports() { member_info.clear(); +#warning TODO +#if 0 GDMonoClass *top = script_class; List<PropertyInfo> props; @@ -2468,6 +2393,7 @@ void CSharpScript::_update_member_info_no_exports() { top = top->get_parent_class(); } +#endif } } #endif @@ -2489,6 +2415,8 @@ bool CSharpScript::_update_exports(PlaceHolderScriptInstance *p_instance_to_upda if (exports_invalidated) #endif { +#warning TODO +#if 0 GD_MONO_SCOPE_THREAD_ATTACH; changed = true; @@ -2524,7 +2452,7 @@ bool CSharpScript::_update_exports(PlaceHolderScriptInstance *p_instance_to_upda MonoException *ctor_exc = nullptr; ctor->invoke(tmp_object, nullptr, &ctor_exc); - tmp_native = GDMonoMarshal::unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(tmp_object)); + tmp_native = GDMonoMarshal::unbox<Object *>(GDMonoCache::cached_data.field_GodotObject_ptr->get_value(tmp_object)); if (ctor_exc) { // TODO: Should we free 'tmp_native' if the exception was thrown after its creation? @@ -2649,6 +2577,8 @@ bool CSharpScript::_update_exports(PlaceHolderScriptInstance *p_instance_to_upda } } #endif + +#endif // #if 0 } #ifdef TOOLS_ENABLED @@ -2675,111 +2605,8 @@ bool CSharpScript::_update_exports(PlaceHolderScriptInstance *p_instance_to_upda return changed; } -void CSharpScript::load_script_signals(GDMonoClass *p_class, GDMonoClass *p_native_class) { - // no need to load the script's signals more than once - if (!signals_invalidated) { - return; - } - - // make sure this classes signals are empty when loading for the first time - _signals.clear(); - event_signals.clear(); - - GD_MONO_SCOPE_THREAD_ATTACH; - - GDMonoClass *top = p_class; - while (top && top != p_native_class) { - const Vector<GDMonoClass *> &delegates = top->get_all_delegates(); - for (int i = delegates.size() - 1; i >= 0; --i) { - GDMonoClass *delegate = delegates[i]; - - if (!delegate->has_attribute(CACHED_CLASS(SignalAttribute))) { - continue; - } - - // Arguments are accessibles as arguments of .Invoke method - GDMonoMethod *invoke_method = delegate->get_method(mono_get_delegate_invoke(delegate->get_mono_ptr())); - - Vector<SignalParameter> parameters; - if (_get_signal(top, invoke_method, parameters)) { - _signals[delegate->get_name()] = parameters; - } - } - - List<StringName> found_event_signals; - - void *iter = nullptr; - MonoEvent *raw_event = nullptr; - while ((raw_event = mono_class_get_events(top->get_mono_ptr(), &iter)) != nullptr) { - MonoCustomAttrInfo *event_attrs = mono_custom_attrs_from_event(top->get_mono_ptr(), raw_event); - if (event_attrs) { - if (mono_custom_attrs_has_attr(event_attrs, CACHED_CLASS(SignalAttribute)->get_mono_ptr())) { - String event_name = String::utf8(mono_event_get_name(raw_event)); - found_event_signals.push_back(StringName(event_name)); - } - - mono_custom_attrs_free(event_attrs); - } - } - - const Vector<GDMonoField *> &fields = top->get_all_fields(); - for (int i = 0; i < fields.size(); i++) { - GDMonoField *field = fields[i]; - - GDMonoClass *field_class = field->get_type().type_class; - - if (!mono_class_is_delegate(field_class->get_mono_ptr())) { - continue; - } - - if (!found_event_signals.find(field->get_name())) { - continue; - } - - GDMonoMethod *invoke_method = field_class->get_method(mono_get_delegate_invoke(field_class->get_mono_ptr())); - - Vector<SignalParameter> parameters; - if (_get_signal(top, invoke_method, parameters)) { - event_signals[field->get_name()] = { field, invoke_method, parameters }; - } - } - - top = top->get_parent_class(); - } - - signals_invalidated = false; -} - -bool CSharpScript::_get_signal(GDMonoClass *p_class, GDMonoMethod *p_delegate_invoke, Vector<SignalParameter> ¶ms) { - GD_MONO_ASSERT_THREAD_ATTACHED; - - Vector<StringName> names; - Vector<ManagedType> types; - p_delegate_invoke->get_parameter_names(names); - p_delegate_invoke->get_parameter_types(types); - - for (int i = 0; i < names.size(); ++i) { - SignalParameter arg; - arg.name = names[i]; - - bool nil_is_variant = false; - arg.type = GDMonoMarshal::managed_to_variant_type(types[i], &nil_is_variant); - - if (arg.type == Variant::NIL) { - if (nil_is_variant) { - arg.nil_is_variant = true; - } else { - ERR_PRINT("Unknown type of signal parameter: '" + arg.name + "' in '" + p_class->get_full_name() + "'."); - return false; - } - } - - params.push_back(arg); - } - - return true; -} - +#warning TODO +#if 0 /** * Returns false if there was an error, otherwise true. * If there was an error, r_prop_info and r_exported are not assigned any value. @@ -2793,7 +2620,7 @@ bool CSharpScript::_get_member_export(IMonoClassMember *p_member, bool p_inspect if (p_member->is_static()) { #ifdef TOOLS_ENABLED - if (p_member->has_attribute(CACHED_CLASS(ExportAttribute))) { + if (p_member->has_attribute(GDMonoCache::cached_data.class_ExportAttribute)) { ERR_PRINT("Cannot export member because it is static: '" + MEMBER_FULL_QUALIFIED_NAME(p_member) + "'."); } #endif @@ -2814,7 +2641,7 @@ bool CSharpScript::_get_member_export(IMonoClassMember *p_member, bool p_inspect CRASH_NOW(); } - bool exported = p_member->has_attribute(CACHED_CLASS(ExportAttribute)); + bool exported = p_member->has_attribute(GDMonoCache::cached_data.class_ExportAttribute); if (p_member->get_member_type() == IMonoClassMember::MEMBER_TYPE_PROPERTY) { GDMonoProperty *property = static_cast<GDMonoProperty *>(p_member); @@ -2846,7 +2673,7 @@ bool CSharpScript::_get_member_export(IMonoClassMember *p_member, bool p_inspect } #ifdef TOOLS_ENABLED - MonoObject *attr = p_member->get_attribute(CACHED_CLASS(ExportAttribute)); + MonoObject *attr = p_member->get_attribute(GDMonoCache::cached_data.class_ExportAttribute); #endif PropertyHint hint = PROPERTY_HINT_NONE; @@ -2867,8 +2694,8 @@ bool CSharpScript::_get_member_export(IMonoClassMember *p_member, bool p_inspect MEMBER_FULL_QUALIFIED_NAME(p_member) + "'."); if (hint_res == 0) { - hint = PropertyHint(CACHED_FIELD(ExportAttribute, hint)->get_int_value(attr)); - hint_string = CACHED_FIELD(ExportAttribute, hintString)->get_string_value(attr); + hint = PropertyHint(GDMonoCache::cached_data.field_ExportAttribute_hint->get_int_value(attr)); + hint_string = GDMonoCache::cached_data.field_ExportAttribute_hintString->get_string_value(attr); } #endif @@ -2950,7 +2777,7 @@ int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, Manage // This may not be needed in the future if the editor is changed to not display values. r_hint_string = name_only_hint_string; } - } else if (p_variant_type == Variant::OBJECT && CACHED_CLASS(GodotResource)->is_assignable_from(p_type.type_class)) { + } else if (p_variant_type == Variant::OBJECT && GDMonoCache::cached_data.class_GodotResource->is_assignable_from(p_type.type_class)) { GDMonoClass *field_native_class = GDMonoUtils::get_class_native_base(p_type.type_class); CRASH_COND(field_native_class == nullptr); @@ -3007,41 +2834,7 @@ int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, Manage return 1; } #endif - -Variant CSharpScript::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { - if (unlikely(GDMono::get_singleton() == nullptr)) { - // Probably not the best error but eh. - r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL; - return Variant(); - } - - GD_MONO_SCOPE_THREAD_ATTACH; - - GDMonoClass *top = script_class; - - while (top && top != native) { - GDMonoMethod *method = top->get_method(p_method, p_argcount); - - if (method && method->is_static()) { - MonoObject *result = method->invoke(nullptr, p_args); - - if (result) { - return GDMonoMarshal::mono_object_to_variant(result); - } else { - return Variant(); - } - } - - top = top->get_parent_class(); - } - - // No static method found. Try regular instance calls - return Script::callp(p_method, p_args, p_argcount, r_error); -} - -void CSharpScript::_resource_path_changed() { - _update_name(); -} +#endif bool CSharpScript::_get(const StringName &p_name, Variant &r_ret) const { if (p_name == CSharpLanguage::singleton->string_names._script_source) { @@ -3070,30 +2863,13 @@ void CSharpScript::_bind_methods() { ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "new", &CSharpScript::_new, MethodInfo("new")); } -Ref<CSharpScript> CSharpScript::create_for_managed_type(GDMonoClass *p_class, GDMonoClass *p_native) { - // This method should not fail, only assertions allowed - - CRASH_COND(p_class == nullptr); - - // TODO OPTIMIZE: Cache the 'CSharpScript' associated with this 'p_class' instead of allocating a new one every time - Ref<CSharpScript> script = memnew(CSharpScript); - - initialize_for_managed_type(script, p_class, p_native); +void CSharpScript::initialize_for_managed_type(Ref<CSharpScript> p_script) { + // IMPORTANT: + // This method must be called only after the CSharpScript and its associated type + // have been added to the script bridge map in the ScriptManagerBridge C# class. - return script; -} - -void CSharpScript::initialize_for_managed_type(Ref<CSharpScript> p_script, GDMonoClass *p_class, GDMonoClass *p_native) { // This method should not fail, only assertions allowed - CRASH_COND(p_class == nullptr); - - p_script->name = p_class->get_name(); - p_script->script_class = p_class; - p_script->native = p_native; - - CRASH_COND(p_script->native == nullptr); - p_script->valid = true; p_script->reload_invalidated = false; @@ -3106,71 +2882,21 @@ void CSharpScript::initialize_for_managed_type(Ref<CSharpScript> p_script, GDMon // Extract information about the script using the mono class. void CSharpScript::update_script_class_info(Ref<CSharpScript> p_script) { - GDMonoClass *base = p_script->script_class->get_parent_class(); - - // `base` should only be set if the script is a user defined type. - if (base != p_script->native) { - p_script->base = base; - } + bool tool = false; + Dictionary rpc_functions_dict; + // Destructor won't be called from C#, and I don't want to include the GDNative header + // only for this, so need to call the destructor manually before passing this to C#. + rpc_functions_dict.~Dictionary(); - p_script->tool = p_script->script_class->has_attribute(CACHED_CLASS(ToolAttribute)); - - if (!p_script->tool) { - GDMonoClass *nesting_class = p_script->script_class->get_nesting_class(); - p_script->tool = nesting_class && nesting_class->has_attribute(CACHED_CLASS(ToolAttribute)); - } - -#ifdef TOOLS_ENABLED - if (!p_script->tool) { - p_script->tool = p_script->script_class->get_assembly() == GDMono::get_singleton()->get_tools_assembly(); - } -#endif - -#ifdef DEBUG_ENABLED - // For debug builds, we must fetch from all native base methods as well. - // Native base methods must be fetched before the current class. - // Not needed if the script class itself is a native class. - - if (p_script->script_class != p_script->native) { - GDMonoClass *native_top = p_script->native; - while (native_top) { - native_top->fetch_methods_with_godot_api_checks(p_script->native); - - if (native_top == CACHED_CLASS(GodotObject)) { - break; - } - - native_top = native_top->get_parent_class(); - } - } -#endif + MonoException *exc = nullptr; + GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_UpdateScriptClassInfo + .invoke(p_script.ptr(), &tool, &rpc_functions_dict, &exc); + UNHANDLED_EXCEPTION(exc); - p_script->script_class->fetch_methods_with_godot_api_checks(p_script->native); + p_script->tool = tool; p_script->rpc_config.clear(); - - GDMonoClass *top = p_script->script_class; - while (top && top != p_script->native) { - // Fetch methods from base classes as well - top->fetch_methods_with_godot_api_checks(p_script->native); - - // Update RPC info - { - Vector<GDMonoMethod *> methods = top->get_all_methods(); - for (int i = 0; i < methods.size(); i++) { - if (!methods[i]->is_static()) { - const Variant rpc_config = p_script->_member_get_rpc_config(methods[i]); - if (rpc_config.get_type() != Variant::NIL) { - p_script->rpc_config[methods[i]->get_name()] = rpc_config; - } - } - } - } - - top = top->get_parent_class(); - } - - p_script->load_script_signals(p_script->script_class, p_script->native); + p_script->rpc_config = rpc_functions_dict; } bool CSharpScript::can_instantiate() const { @@ -3183,13 +2909,13 @@ bool CSharpScript::can_instantiate() const { // FIXME Need to think this through better. // For tool scripts, this will never fire if the class is not found. That's because we // don't know if it's a tool script if we can't find the class to access the attributes. - if (extra_cond && !script_class) { + if (extra_cond && !valid) { if (GDMono::get_singleton()->get_project_assembly() == nullptr) { // The project assembly is not loaded ERR_FAIL_V_MSG(false, "Cannot instance script because the project assembly is not loaded. Script: '" + get_path() + "'."); } else { // The project assembly is loaded, but the class could not found - ERR_FAIL_V_MSG(false, "Cannot instance script because the class '" + name + "' could not be found. Script: '" + get_path() + "'."); + ERR_FAIL_V_MSG(false, "Cannot instance script because the associated class could not be found. Script: '" + get_path() + "'."); } } @@ -3197,11 +2923,12 @@ bool CSharpScript::can_instantiate() const { } StringName CSharpScript::get_instance_base_type() const { - if (native) { - return native->get_name(); - } else { - return StringName(); - } + StringName native_name; + MonoException *exc = nullptr; + GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_GetScriptNativeName + .invoke(this, &native_name, &exc); + UNHANDLED_EXCEPTION(exc); + return native_name; } CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_is_ref_counted, Callable::CallError &r_error) { @@ -3209,17 +2936,6 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg /* STEP 1, CREATE */ - // Search the constructor first, to fail with an error if it's not found before allocating anything else. - GDMonoMethod *ctor = script_class->get_method(CACHED_STRING_NAME(dotctor), p_argcount); - if (ctor == nullptr) { - ERR_FAIL_COND_V_MSG(p_argcount == 0, nullptr, - "Cannot create script instance. The class '" + script_class->get_full_name() + - "' does not define a parameterless constructor." + - (get_path().is_empty() ? String() : " Path: '" + get_path() + "'.")); - - ERR_FAIL_V_MSG(nullptr, "Constructor not found."); - } - Ref<RefCounted> ref; if (p_is_ref_counted) { // Hold it alive. Important if we have to dispose a script instance binding before creating the CSharpInstance. @@ -3233,14 +2949,12 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->get(); if (script_binding.inited && !script_binding.gchandle.is_released()) { - MonoObject *mono_object = script_binding.gchandle.get_target(); - if (mono_object) { - MonoException *exc = nullptr; - GDMonoUtils::dispose(mono_object, &exc); + MonoException *exc = nullptr; + GDMonoCache::cached_data.methodthunk_CSharpInstanceBridge_CallDispose + .invoke(script_binding.gchandle.get_intptr(), /* okIfNull */ true, &exc); - if (exc) { - GDMonoUtils::set_pending_exception(exc); - } + if (exc) { + GDMonoUtils::set_pending_exception(exc); } script_binding.gchandle.release(); // Just in case @@ -3255,38 +2969,22 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg /* STEP 2, INITIALIZE AND CONSTRUCT */ - MonoObject *mono_object = mono_object_new(mono_domain_get(), script_class->get_mono_ptr()); + MonoException *exc = nullptr; + GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance + .invoke(this, p_owner, p_args, p_argcount, &exc); + + if (exc) { + GDMonoUtils::set_pending_exception(exc); - if (!mono_object) { // Important to clear this before destroying the script instance here instance->script = Ref<CSharpScript>(); instance->owner = nullptr; - - bool die = instance->_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); - p_owner->set_script_instance(nullptr); - r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL; - ERR_FAIL_V_MSG(nullptr, "Failed to allocate memory for the object."); - } - - // Tie managed to unmanaged - instance->gchandle = MonoGCHandleData::new_strong_handle(mono_object); - if (instance->base_ref_counted) { - instance->_reference_owner_unsafe(); // Here, after assigning the gchandle (for the refcount_incremented callback) - } - - { - MutexLock lock(CSharpLanguage::get_singleton()->script_instances_mutex); - instances.insert(instance->owner); + return nullptr; } - CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, instance->owner); - - // Construct - ctor->invoke(mono_object, p_args); + CRASH_COND(instance->gchandle.is_released()); /* STEP 3, PARTY */ @@ -3302,11 +3000,17 @@ Variant CSharpScript::_new(const Variant **p_args, int p_argcount, Callable::Cal r_error.error = Callable::CallError::CALL_OK; - ERR_FAIL_NULL_V(native, Variant()); + StringName native_name; + MonoException *exc = nullptr; + GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_GetScriptNativeName + .invoke(this, &native_name, &exc); + UNHANDLED_EXCEPTION(exc); + + ERR_FAIL_COND_V(native_name == StringName(), Variant()); GD_MONO_SCOPE_THREAD_ATTACH; - Object *owner = ClassDB::instantiate(NATIVE_GDMONOCLASS_NAME(native)); + Object *owner = ClassDB::instantiate(native_name); Ref<RefCounted> ref; RefCounted *r = Object::cast_to<RefCounted>(owner); @@ -3336,16 +3040,21 @@ ScriptInstance *CSharpScript::instance_create(Object *p_this) { GD_MONO_SCOPE_THREAD_ATTACH; - if (native) { - StringName native_name = NATIVE_GDMONOCLASS_NAME(native); - if (!ClassDB::is_parent_class(p_this->get_class_name(), native_name)) { - if (EngineDebugger::is_active()) { - CSharpLanguage::get_singleton()->debug_break_parse(get_path(), 0, - "Script inherits from native type '" + String(native_name) + - "', so it can't be instantiated in object of type: '" + p_this->get_class() + "'"); - } - ERR_FAIL_V_MSG(nullptr, "Script inherits from native type '" + String(native_name) + "', so it can't be instantiated in object of type: '" + p_this->get_class() + "'."); + StringName native_name; + MonoException *exc = nullptr; + GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_GetScriptNativeName + .invoke(this, &native_name, &exc); + UNHANDLED_EXCEPTION(exc); + + ERR_FAIL_COND_V(native_name == StringName(), nullptr); + + if (!ClassDB::is_parent_class(p_this->get_class_name(), native_name)) { + if (EngineDebugger::is_active()) { + CSharpLanguage::get_singleton()->debug_break_parse(get_path(), 0, + "Script inherits from native type '" + String(native_name) + + "', so it can't be instantiated in object of type: '" + p_this->get_class() + "'"); } + ERR_FAIL_V_MSG(nullptr, "Script inherits from native type '" + String(native_name) + "', so it can't be instantiated in object of type: '" + p_this->get_class() + "'."); } Callable::CallError unchecked_error; @@ -3387,12 +3096,14 @@ void CSharpScript::set_source_code(const String &p_code) { } void CSharpScript::get_script_method_list(List<MethodInfo> *p_list) const { - if (!script_class) { + if (!valid) { return; } GD_MONO_SCOPE_THREAD_ATTACH; +#warning TODO +#if 0 // TODO: We're filtering out constructors but there may be other methods unsuitable for explicit calls. GDMonoClass *top = script_class; @@ -3407,35 +3118,51 @@ void CSharpScript::get_script_method_list(List<MethodInfo> *p_list) const { top = top->get_parent_class(); } +#endif } bool CSharpScript::has_method(const StringName &p_method) const { - if (!script_class) { + if (!valid) { return false; } GD_MONO_SCOPE_THREAD_ATTACH; - return script_class->has_fetched_method_unknown_params(p_method); + if (!GDMonoCache::cached_data.godot_api_cache_updated) { + return false; + } + + String method = p_method; + bool deep = false; + + MonoException *exc = nullptr; + bool found = GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_HasMethodUnknownParams + .invoke(this, &method, deep, &exc); + UNHANDLED_EXCEPTION(exc); + + return found; } MethodInfo CSharpScript::get_method_info(const StringName &p_method) const { - if (!script_class) { + if (!valid) { return MethodInfo(); } GD_MONO_SCOPE_THREAD_ATTACH; +#warning TODO +#if 0 GDMonoClass *top = script_class; while (top && top != native) { - GDMonoMethod *params = top->get_fetched_method_unknown_params(p_method); + GDMonoMethod *params = top->get_method_unknown_params(p_method); if (params) { return params->get_method_info(); } top = top->get_parent_class(); } +#endif return MethodInfo(); } @@ -3451,28 +3178,18 @@ Error CSharpScript::reload(bool p_keep_state) { GD_MONO_SCOPE_THREAD_ATTACH; - const DotNetScriptLookupInfo *lookup_info = - CSharpLanguage::get_singleton()->lookup_dotnet_script(get_path()); - - if (lookup_info) { - GDMonoClass *klass = lookup_info->script_class; - if (klass) { - ERR_FAIL_COND_V(!CACHED_CLASS(GodotObject)->is_assignable_from(klass), FAILED); - script_class = klass; - } - } + String script_path = get_path(); - valid = script_class != nullptr; + MonoException *exc = nullptr; + valid = GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_AddScriptBridge + .invoke(this, &script_path, &exc); + UNHANDLED_EXCEPTION(exc); - if (script_class) { + if (valid) { #ifdef DEBUG_ENABLED - print_verbose("Found class " + script_class->get_full_name() + " for script " + get_path()); + print_verbose("Found class for script " + get_path()); #endif - native = GDMonoUtils::get_class_native_base(script_class); - - CRASH_COND(native == nullptr); - update_script_class_info(this); _update_exports(); @@ -3509,43 +3226,59 @@ void CSharpScript::update_exports() { } bool CSharpScript::has_script_signal(const StringName &p_signal) const { - return event_signals.has(p_signal) || _signals.has(p_signal); + if (!valid) { + return false; + } + + if (!GDMonoCache::cached_data.godot_api_cache_updated) { + return false; + } + + String signal = p_signal; + + MonoException *exc = nullptr; + bool res = GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_HasScriptSignal + .invoke(this, &signal, &exc); + UNHANDLED_EXCEPTION(exc); + + return res; } void CSharpScript::get_script_signal_list(List<MethodInfo> *r_signals) const { - for (const KeyValue<StringName, Vector<SignalParameter>> &E : _signals) { - MethodInfo mi; - mi.name = E.key; + if (!valid) { + return; + } - const Vector<SignalParameter> ¶ms = E.value; - for (int i = 0; i < params.size(); i++) { - const SignalParameter ¶m = params[i]; + // Performance is not critical here as this will be replaced with source generators. - PropertyInfo arg_info = PropertyInfo(param.type, param.name); - if (param.type == Variant::NIL && param.nil_is_variant) { - arg_info.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; - } + if (!GDMonoCache::cached_data.godot_api_cache_updated) { + return; + } - mi.arguments.push_back(arg_info); - } + Dictionary signals_dict; + // Destructor won't be called from C#, and I don't want to include the GDNative header + // only for this, so need to call the destructor manually before passing this to C#. + signals_dict.~Dictionary(); - r_signals->push_back(mi); - } + MonoException *exc = nullptr; + GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_GetScriptSignalList + .invoke(this, &signals_dict, &exc); + UNHANDLED_EXCEPTION(exc); - for (const KeyValue<StringName, EventSignal> &E : event_signals) { + for (const Variant *s = signals_dict.next(nullptr); s != nullptr; s = signals_dict.next(s)) { MethodInfo mi; - mi.name = E.key; + mi.name = *s; + + Array params = signals_dict[*s]; - const EventSignal &event_signal = E.value; - const Vector<SignalParameter> ¶ms = event_signal.parameters; for (int i = 0; i < params.size(); i++) { - const SignalParameter ¶m = params[i]; + Dictionary param = params[i]; - PropertyInfo arg_info = PropertyInfo(param.type, param.name); - if (param.type == Variant::NIL && param.nil_is_variant) { + Variant::Type param_type = (Variant::Type)(int)param["type"]; + PropertyInfo arg_info = PropertyInfo(param_type, (String)param["name"]); + if (param_type == Variant::NIL && (bool)param["nil_is_variant"]) { arg_info.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; } - mi.arguments.push_back(arg_info); } @@ -3559,15 +3292,20 @@ bool CSharpScript::inherits_script(const Ref<Script> &p_script) const { return false; } - if (script_class == nullptr || cs->script_class == nullptr) { + if (!valid || !cs->valid) { return false; } - if (script_class == cs->script_class) { - return true; + if (!GDMonoCache::cached_data.godot_api_cache_updated) { + return false; } - return cs->script_class->is_assignable_from(script_class); + MonoException *exc = nullptr; + bool res = GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_ScriptIsOrInherits + .invoke(this, cs.ptr(), &exc); + UNHANDLED_EXCEPTION(exc); + + return res; } Ref<Script> CSharpScript::get_base_script() const { @@ -3598,22 +3336,6 @@ int CSharpScript::get_member_line(const StringName &p_member) const { return -1; } -Variant CSharpScript::_member_get_rpc_config(IMonoClassMember *p_member) const { - Variant out; - - MonoObject *rpc_attribute = p_member->get_attribute(CACHED_CLASS(RPCAttribute)); - if (rpc_attribute != nullptr) { - Dictionary rpc_config; - rpc_config["rpc_mode"] = CACHED_PROPERTY(RPCAttribute, Mode)->get_int_value(rpc_attribute); - rpc_config["call_local"] = CACHED_PROPERTY(RPCAttribute, CallLocal)->get_bool_value(rpc_attribute); - rpc_config["transfer_mode"] = CACHED_PROPERTY(RPCAttribute, TransferMode)->get_int_value(rpc_attribute); - rpc_config["channel"] = CACHED_PROPERTY(RPCAttribute, TransferChannel)->get_int_value(rpc_attribute); - out = rpc_config; - } - - return out; -} - const Variant CSharpScript::get_rpc_config() const { return rpc_config; } @@ -3634,29 +3356,15 @@ Error CSharpScript::load_source_code(const String &p_path) { return OK; } -void CSharpScript::_update_name() { - String path = get_path(); - - if (!path.is_empty()) { - name = get_path().get_file().get_basename(); - } -} - void CSharpScript::_clear() { tool = false; valid = false; reload_invalidated = true; - - base = nullptr; - native = nullptr; - script_class = nullptr; } CSharpScript::CSharpScript() { _clear(); - _update_name(); - #ifdef DEBUG_ENABLED { MutexLock lock(CSharpLanguage::get_singleton()->script_instances_mutex); @@ -3670,6 +3378,12 @@ CSharpScript::~CSharpScript() { MutexLock lock(CSharpLanguage::get_singleton()->script_instances_mutex); CSharpLanguage::get_singleton()->script_list.remove(&this->script_list); #endif + + if (GDMonoCache::cached_data.godot_api_cache_updated) { + MonoException *exc = nullptr; + GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_RemoveScriptBridge.invoke(this, &exc); + UNHANDLED_EXCEPTION(exc); + } } void CSharpScript::get_members(HashSet<StringName> *p_members) { @@ -3771,16 +3485,9 @@ bool ResourceFormatSaverCSharpScript::recognize(const Ref<Resource> &p_resource) } CSharpLanguage::StringNameCache::StringNameCache() { - _signal_callback = StaticCString::create("_signal_callback"); - _set = StaticCString::create("_set"); - _get = StaticCString::create("_get"); - _get_property_list = StaticCString::create("_get_property_list"); _property_can_revert = StaticCString::create("_property_can_revert"); _property_get_revert = StaticCString::create("_property_get_revert"); - _notification = StaticCString::create("_notification"); _script_source = StaticCString::create("script/source"); on_before_serialize = StaticCString::create("OnBeforeSerialize"); on_after_deserialize = StaticCString::create("OnAfterDeserialize"); - dotctor = StaticCString::create(".ctor"); - delegate_invoke_method_name = StaticCString::create("Invoke"); } diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h index cf4f971b88..34b2e7f735 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -39,7 +39,6 @@ #include "mono_gc_handle.h" #include "mono_gd/gd_mono.h" -#include "mono_gd/gd_mono_header.h" #include "mono_gd/gd_mono_internals.h" #ifdef TOOLS_ENABLED @@ -67,18 +66,6 @@ TScriptInstance *cast_script_instance(ScriptInstance *p_inst) { #define CAST_CSHARP_INSTANCE(m_inst) (cast_script_instance<CSharpInstance, CSharpLanguage>(m_inst)) -struct DotNetScriptLookupInfo { - String class_namespace; - String class_name; - GDMonoClass *script_class = nullptr; - - DotNetScriptLookupInfo() {} // Required by HashMap... - - DotNetScriptLookupInfo(const String &p_class_namespace, const String &p_class_name, GDMonoClass *p_script_class) : - class_namespace(p_class_namespace), class_name(p_class_name), script_class(p_script_class) { - } -}; - class CSharpScript : public Script { GDCLASS(CSharpScript, Script); @@ -89,25 +76,14 @@ public: bool nil_is_variant = false; }; - struct EventSignal { - GDMonoField *field = nullptr; - GDMonoMethod *invoke_method = nullptr; - Vector<SignalParameter> parameters; - }; - private: friend class CSharpInstance; friend class CSharpLanguage; - friend struct CSharpScriptDepSort; bool tool = false; bool valid = false; bool reload_invalidated = false; - GDMonoClass *base = nullptr; - GDMonoClass *native = nullptr; - GDMonoClass *script_class = nullptr; - Ref<CSharpScript> base_cache; // TODO what's this for? HashSet<Object *> instances; @@ -128,14 +104,9 @@ private: #endif String source; - StringName name; SelfList<CSharpScript> script_list = this; - HashMap<StringName, Vector<SignalParameter>> _signals; - HashMap<StringName, EventSignal> event_signals; - bool signals_invalidated = true; - Dictionary rpc_config; #ifdef TOOLS_ENABLED @@ -158,34 +129,26 @@ private: void _clear(); - void _update_name(); - - void load_script_signals(GDMonoClass *p_class, GDMonoClass *p_native_class); - bool _get_signal(GDMonoClass *p_class, GDMonoMethod *p_delegate_invoke, Vector<SignalParameter> ¶ms); - bool _update_exports(PlaceHolderScriptInstance *p_instance_to_update = nullptr); +#warning TODO +#if 0 bool _get_member_export(IMonoClassMember *p_member, bool p_inspect_export, PropertyInfo &r_prop_info, bool &r_exported); #ifdef TOOLS_ENABLED static int _try_get_member_export_hint(IMonoClassMember *p_member, ManagedType p_type, Variant::Type p_variant_type, bool p_allow_generics, PropertyHint &r_hint, String &r_hint_string); #endif +#endif CSharpInstance *_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_is_ref_counted, Callable::CallError &r_error); Variant _new(const Variant **p_args, int p_argcount, Callable::CallError &r_error); // Do not use unless you know what you are doing - friend void GDMonoInternals::tie_managed_to_unmanaged(MonoObject *, Object *); - static Ref<CSharpScript> create_for_managed_type(GDMonoClass *p_class, GDMonoClass *p_native); static void update_script_class_info(Ref<CSharpScript> p_script); - static void initialize_for_managed_type(Ref<CSharpScript> p_script, GDMonoClass *p_class, GDMonoClass *p_native); - - Variant _member_get_rpc_config(IMonoClassMember *p_member) const; + static void initialize_for_managed_type(Ref<CSharpScript> p_script); protected: static void _bind_methods(); - Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override; - void _resource_path_changed() override; bool _get(const StringName &p_name, Variant &r_ret) const; bool _set(const StringName &p_name, const Variant &p_value); void _get_property_list(List<PropertyInfo> *p_properties) const; @@ -270,22 +233,21 @@ class CSharpInstance : public ScriptInstance { bool _unreference_owner_unsafe(); /* - * If nullptr is returned, the caller must destroy the script instance by removing it from its owner. + * If false is returned, the caller must destroy the script instance by removing it from its owner. */ - MonoObject *_internal_new_managed(); + bool _internal_new_managed(); // Do not use unless you know what you are doing - friend void GDMonoInternals::tie_managed_to_unmanaged(MonoObject *, Object *); static CSharpInstance *create_for_managed_type(Object *p_owner, CSharpScript *p_script, const MonoGCHandleData &p_gchandle); void get_properties_state_for_reloading(List<Pair<StringName, Variant>> &r_state); void get_event_signals_state_for_reloading(List<Pair<StringName, Array>> &r_state); public: - MonoObject *get_mono_object() const; - _FORCE_INLINE_ bool is_destructing_script_instance() { return destructing_script_instance; } + _FORCE_INLINE_ GCHandleIntPtr get_gchandle_intptr() { return gchandle.get_intptr(); } + Object *get_owner() override; bool set(const StringName &p_name, const Variant &p_value) override; @@ -300,15 +262,15 @@ public: bool has_method(const StringName &p_method) const override; Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override; - void mono_object_disposed(MonoObject *p_obj); + void mono_object_disposed(); /* * If 'r_delete_owner' is set to true, the caller must memdelete the script instance's owner. Otherwise, if * 'r_remove_script_instance' is set to true, the caller must destroy the script instance by removing it from its owner. */ - void mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_finalizer, bool &r_delete_owner, bool &r_remove_script_instance); + void mono_object_disposed_baseref(bool p_is_finalizer, bool &r_delete_owner, bool &r_remove_script_instance); - void connect_event_signals(); + void connect_event_signal(const StringName &p_event_signal); void disconnect_event_signals(); void refcount_incremented() override; @@ -332,7 +294,6 @@ public: struct CSharpScriptBinding { bool inited = false; StringName type_name; - GDMonoClass *wrapper_class = nullptr; MonoGCHandleData gchandle; Object *owner = nullptr; @@ -370,28 +331,17 @@ class CSharpLanguage : public ScriptLanguage { ManagedCallableMiddleman *managed_callable_middleman = memnew(ManagedCallableMiddleman); struct StringNameCache { - StringName _signal_callback; - StringName _set; - StringName _get; - StringName _get_property_list; StringName _property_can_revert; StringName _property_get_revert; - StringName _notification; StringName _script_source; - StringName dotctor; // .ctor StringName on_before_serialize; // OnBeforeSerialize StringName on_after_deserialize; // OnAfterDeserialize - StringName delegate_invoke_method_name; StringNameCache(); }; int lang_idx = -1; - HashMap<String, DotNetScriptLookupInfo> dotnet_script_lookup_map; - - void lookup_script_for_class(GDMonoClass *p_class); - // For debug_break and debug_break_parse int _debug_parse_err_line = -1; String _debug_parse_err_file; @@ -421,6 +371,7 @@ public: StringNameCache string_names; const Mutex &get_language_bind_mutex() { return language_bind_mutex; } + const Mutex &get_script_instances_mutex() { return script_instances_mutex; } _FORCE_INLINE_ int get_language_index() { return lang_idx; } void set_language_index(int p_idx); @@ -434,7 +385,7 @@ public: #endif static void release_script_gchandle(MonoGCHandleData &p_gchandle); - static void release_script_gchandle(MonoObject *p_expected_obj, MonoGCHandleData &p_gchandle); + static void release_script_gchandle(void *p_expected_mono_obj_unused, MonoGCHandleData &p_gchandle); bool debug_break(const String &p_error, bool p_allow_continue = true); bool debug_break_parse(const String &p_file, int p_line, const String &p_error); @@ -446,12 +397,6 @@ public: _FORCE_INLINE_ ManagedCallableMiddleman *get_managed_callable_middleman() const { return managed_callable_middleman; } - void lookup_scripts_in_assembly(GDMonoAssembly *p_assembly); - - const DotNetScriptLookupInfo *lookup_dotnet_script(const String &p_script_path) const { - return dotnet_script_lookup_map.getptr(p_script_path); - } - String get_name() const override; /* LANGUAGE FUNCTIONS */ @@ -527,6 +472,10 @@ public: RBMap<Object *, CSharpScriptBinding>::Element *insert_script_binding(Object *p_object, const CSharpScriptBinding &p_script_binding); bool setup_csharp_script_binding(CSharpScriptBinding &r_script_binding, Object *p_object); + static void tie_native_managed_to_unmanaged(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged, const StringName *p_native_name, bool p_ref_counted); + static void tie_user_managed_to_unmanaged(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged, CSharpScript *p_script, bool p_ref_counted); + static void tie_managed_to_unmanaged_with_pre_setup(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged); + #ifdef DEBUG_ENABLED Vector<StackInfo> stack_trace_get_info(MonoObject *p_stack_trace); #endif diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs index 21bff70b15..33967ffa71 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs @@ -178,17 +178,6 @@ namespace GodotTools.Build if (!File.Exists(buildInfo.Solution)) return true; // No solution to build - // Make sure the API assemblies are up to date before building the project. - // We may not have had the chance to update the release API assemblies, and the debug ones - // may have been deleted by the user at some point after they were loaded by the Godot editor. - string apiAssembliesUpdateError = Internal.UpdateApiAssembliesFromPrebuilt(buildInfo.Configuration == "ExportRelease" ? "Release" : "Debug"); - - if (!string.IsNullOrEmpty(apiAssembliesUpdateError)) - { - ShowBuildErrorDialog("Failed to update the Godot API assemblies"); - return false; - } - using (var pr = new EditorProgress("mono_project_debug_build", "Building project solution...", 1)) { pr.Step("Building project solution", 0); diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs index cca18a2a1f..ee3f5999cd 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs @@ -1,4 +1,5 @@ using Godot; +using Godot.NativeInterop; using System; using System.Collections.Generic; using System.Diagnostics; @@ -100,7 +101,9 @@ namespace GodotTools.Export return; if (Path.GetExtension(path) != Internal.CSharpLanguageExtension) - throw new ArgumentException($"Resource of type {Internal.CSharpLanguageType} has an invalid file extension: {path}", nameof(path)); + throw new ArgumentException( + $"Resource of type {Internal.CSharpLanguageType} has an invalid file extension: {path}", + nameof(path)); // TODO What if the source file is not part of the game's C# project @@ -194,7 +197,8 @@ namespace GodotTools.Export { string thisWasmFrameworkAssemblyPath = Path.Combine(bclDir, thisWasmFrameworkAssemblyName + ".dll"); if (!File.Exists(thisWasmFrameworkAssemblyPath)) - throw new FileNotFoundException($"Assembly not found: '{thisWasmFrameworkAssemblyName}'", thisWasmFrameworkAssemblyPath); + throw new FileNotFoundException($"Assembly not found: '{thisWasmFrameworkAssemblyName}'", + thisWasmFrameworkAssemblyPath); assemblies[thisWasmFrameworkAssemblyName] = thisWasmFrameworkAssemblyPath; } @@ -206,18 +210,21 @@ namespace GodotTools.Export foreach (var thisWasmFrameworkAssemblyName in wasmFrameworkAssembliesOneOf) { - string thisWasmFrameworkAssemblyPath = Path.Combine(bclDir, thisWasmFrameworkAssemblyName.newName + ".dll"); + string thisWasmFrameworkAssemblyPath = + Path.Combine(bclDir, thisWasmFrameworkAssemblyName.newName + ".dll"); if (File.Exists(thisWasmFrameworkAssemblyPath)) { assemblies[thisWasmFrameworkAssemblyName.newName] = thisWasmFrameworkAssemblyPath; } else { - thisWasmFrameworkAssemblyPath = Path.Combine(bclDir, thisWasmFrameworkAssemblyName.oldName + ".dll"); + thisWasmFrameworkAssemblyPath = + Path.Combine(bclDir, thisWasmFrameworkAssemblyName.oldName + ".dll"); if (!File.Exists(thisWasmFrameworkAssemblyPath)) { - throw new FileNotFoundException("Expected one of the following assemblies but none were found: " + - $"'{thisWasmFrameworkAssemblyName.newName}' / '{thisWasmFrameworkAssemblyName.oldName}'", + throw new FileNotFoundException( + "Expected one of the following assemblies but none were found: " + + $"'{thisWasmFrameworkAssemblyName.newName}' / '{thisWasmFrameworkAssemblyName.oldName}'", thisWasmFrameworkAssemblyPath); } @@ -227,7 +234,12 @@ namespace GodotTools.Export } var initialAssemblies = assemblies.Duplicate(); - internal_GetExportedAssemblyDependencies(initialAssemblies, buildConfig, bclDir, assemblies); + godot_dictionary initialAssembliesAux = ((Godot.Collections.Dictionary)initialAssemblies).NativeValue; + using godot_string buildConfigAux = Marshaling.mono_string_to_godot(buildConfig); + using godot_string bclDirAux = Marshaling.mono_string_to_godot(bclDir); + godot_dictionary assembliesAux = ((Godot.Collections.Dictionary)assemblies).NativeValue; + internal_GetExportedAssemblyDependencies(initialAssembliesAux, buildConfigAux, bclDirAux, + ref assembliesAux); AddI18NAssemblies(assemblies, bclDir); @@ -237,9 +249,12 @@ namespace GodotTools.Export outputDataDir = ExportDataDirectory(features, platform, isDebug, outputDir); string apiConfig = isDebug ? "Debug" : "Release"; - string resAssembliesDir = Path.Combine(GodotSharpDirs.ResAssembliesBaseDir, apiConfig); + // TODO + throw new NotImplementedException(); + string resAssembliesDir = null; // Path.Combine(GodotSharpDirs.ResAssembliesBaseDir, apiConfig); - bool assembliesInsidePck = (bool)ProjectSettings.GetSetting("mono/export/export_assemblies_inside_pck") || outputDataDir == null; + bool assembliesInsidePck = (bool)ProjectSettings.GetSetting("mono/export/export_assemblies_inside_pck") || + outputDataDir == null; if (!assembliesInsidePck) { @@ -277,7 +292,8 @@ namespace GodotTools.Export } // AOT compilation - bool aotEnabled = platform == OS.Platforms.iOS || (bool)ProjectSettings.GetSetting("mono/export/aot/enabled"); + bool aotEnabled = platform == OS.Platforms.iOS || + (bool)ProjectSettings.GetSetting("mono/export/aot/enabled"); if (aotEnabled) { @@ -296,14 +312,19 @@ namespace GodotTools.Export LLVMOnly = false, LLVMPath = "", LLVMOutputPath = "", - FullAot = platform == OS.Platforms.iOS || (bool)(ProjectSettings.GetSetting("mono/export/aot/full_aot") ?? false), + FullAot = platform == OS.Platforms.iOS || + (bool)(ProjectSettings.GetSetting("mono/export/aot/full_aot") ?? false), UseInterpreter = (bool)ProjectSettings.GetSetting("mono/export/aot/use_interpreter"), - ExtraAotOptions = (string[])ProjectSettings.GetSetting("mono/export/aot/extra_aot_options") ?? Array.Empty<string>(), - ExtraOptimizerOptions = (string[])ProjectSettings.GetSetting("mono/export/aot/extra_optimizer_options") ?? Array.Empty<string>(), + ExtraAotOptions = (string[])ProjectSettings.GetSetting("mono/export/aot/extra_aot_options") ?? + Array.Empty<string>(), + ExtraOptimizerOptions = + (string[])ProjectSettings.GetSetting("mono/export/aot/extra_optimizer_options") ?? + Array.Empty<string>(), ToolchainPath = aotToolchainPath }; - AotBuilder.CompileAssemblies(this, aotOpts, features, platform, isDebug, bclDir, outputDir, outputDataDir, assemblies); + AotBuilder.CompileAssemblies(this, aotOpts, features, platform, isDebug, bclDir, outputDir, + outputDataDir, assemblies); } } @@ -316,8 +337,10 @@ namespace GodotTools.Export if (Directory.Exists(aotTempDir)) Directory.Delete(aotTempDir, recursive: true); - // TODO: Just a workaround until the export plugins can be made to abort with errors - if (!string.IsNullOrEmpty(_maybeLastExportError)) // Check empty as well, because it's set to empty after hot-reloading + // TODO: The following is just a workaround until the export plugins can be made to abort with errors + + // We check for empty as well, because it's set to empty after hot-reloading + if (!string.IsNullOrEmpty(_maybeLastExportError)) { string lastExportError = _maybeLastExportError; _maybeLastExportError = null; @@ -381,7 +404,8 @@ namespace GodotTools.Export private static bool PlatformHasTemplateDir(string platform) { // macOS export templates are contained in a zip, so we place our custom template inside it and let Godot do the rest. - return !new[] { OS.Platforms.MacOS, OS.Platforms.Android, OS.Platforms.iOS, OS.Platforms.HTML5 }.Contains(platform); + return !new[] { OS.Platforms.MacOS, OS.Platforms.Android, OS.Platforms.iOS, OS.Platforms.HTML5 } + .Contains(platform); } private static bool DeterminePlatformFromFeatures(IEnumerable<string> features, out string platform) @@ -476,7 +500,8 @@ namespace GodotTools.Export } [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void internal_GetExportedAssemblyDependencies(Godot.Collections.Dictionary<string, string> initialAssemblies, - string buildConfig, string customBclDir, Godot.Collections.Dictionary<string, string> dependencyAssemblies); + private static extern void internal_GetExportedAssemblyDependencies( + in godot_dictionary initialAssemblies, in godot_string buildConfig, + in godot_string customBclDir, ref godot_dictionary dependencyAssemblies); } } diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs index b39c3d1c0d..5167333716 100644 --- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs +++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs @@ -53,7 +53,7 @@ namespace GodotTools private bool CreateProjectSolution() { - using (var pr = new EditorProgress("create_csharp_solution", "Generating solution...".TTR(), 3)) + using (var pr = new EditorProgress("create_csharp_solution", "Generating solution...".TTR(), 2)) { pr.Step("Generating C# project...".TTR()); @@ -90,24 +90,6 @@ namespace GodotTools return false; } - pr.Step("Updating Godot API assemblies...".TTR()); - - string debugApiAssembliesError = Internal.UpdateApiAssembliesFromPrebuilt("Debug"); - - if (!string.IsNullOrEmpty(debugApiAssembliesError)) - { - ShowErrorDialog("Failed to update the Godot API assemblies: " + debugApiAssembliesError); - return false; - } - - string releaseApiAssembliesError = Internal.UpdateApiAssembliesFromPrebuilt("Release"); - - if (!string.IsNullOrEmpty(releaseApiAssembliesError)) - { - ShowErrorDialog("Failed to update the Godot API assemblies: " + releaseApiAssembliesError); - return false; - } - pr.Step("Done".TTR()); // Here, after all calls to progress_task_step @@ -533,8 +515,9 @@ namespace GodotTools public static GodotSharpEditor Instance { get; private set; } [UsedImplicitly] - private GodotSharpEditor() + private static IntPtr InternalCreateInstance() { + return new GodotSharpEditor().NativeInstance; } } } diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj index f1d45463c5..d44cd75155 100644 --- a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj +++ b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj @@ -2,12 +2,13 @@ <PropertyGroup> <ProjectGuid>{27B00618-A6F2-4828-B922-05CAEB08C286}</ProjectGuid> <TargetFramework>net472</TargetFramework> - <LangVersion>7.2</LangVersion> + <LangVersion>8</LangVersion> <!-- The Godot editor uses the Debug Godot API assemblies --> <GodotApiConfiguration>Debug</GodotApiConfiguration> <GodotSourceRootPath>$(SolutionDir)/../../../../</GodotSourceRootPath> <GodotOutputDataDir>$(GodotSourceRootPath)/bin/GodotSharp</GodotOutputDataDir> <GodotApiAssembliesDir>$(GodotOutputDataDir)/Api/$(GodotApiConfiguration)</GodotApiAssembliesDir> + <AllowUnsafeBlocks>true</AllowUnsafeBlocks> </PropertyGroup> <PropertyGroup Condition=" Exists('$(GodotApiAssembliesDir)/GodotSharp.dll') "> <!-- The project is part of the Godot source tree --> diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/EditorProgress.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/EditorProgress.cs index 70ba7c733a..b221ae7c5c 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Internals/EditorProgress.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Internals/EditorProgress.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using Godot; +using Godot.NativeInterop; namespace GodotTools.Internals { @@ -9,18 +10,22 @@ namespace GodotTools.Internals public string Task { get; } [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void internal_Create(string task, string label, int amount, bool canCancel); + private static extern void internal_Create(in godot_string task, in godot_string label, int amount, + bool canCancel); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void internal_Dispose(string task); + private static extern void internal_Dispose(in godot_string task); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern bool internal_Step(string task, string state, int step, bool forceRefresh); + private static extern bool internal_Step(in godot_string task, in godot_string state, int step, + bool forceRefresh); public EditorProgress(string task, string label, int amount, bool canCancel = false) { Task = task; - internal_Create(task, label, amount, canCancel); + using godot_string taskIn = Marshaling.mono_string_to_godot(task); + using godot_string labelIn = Marshaling.mono_string_to_godot(label); + internal_Create(taskIn, labelIn, amount, canCancel); } ~EditorProgress() @@ -33,18 +38,23 @@ namespace GodotTools.Internals public void Dispose() { - internal_Dispose(Task); + using godot_string taskIn = Marshaling.mono_string_to_godot(Task); + internal_Dispose(taskIn); GC.SuppressFinalize(this); } public void Step(string state, int step = -1, bool forceRefresh = true) { - internal_Step(Task, state, step, forceRefresh); + using godot_string taskIn = Marshaling.mono_string_to_godot(Task); + using godot_string stateIn = Marshaling.mono_string_to_godot(state); + internal_Step(taskIn, stateIn, step, forceRefresh); } public bool TryStep(string state, int step = -1, bool forceRefresh = true) { - return internal_Step(Task, state, step, forceRefresh); + using godot_string taskIn = Marshaling.mono_string_to_godot(Task); + using godot_string stateIn = Marshaling.mono_string_to_godot(state); + return internal_Step(taskIn, stateIn, step, forceRefresh); } } } diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs index 5c5ced8c29..d79821de3c 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs @@ -1,3 +1,4 @@ +using Godot.NativeInterop; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; @@ -7,17 +8,40 @@ namespace GodotTools.Internals { public static float EditorScale => internal_EditorScale(); - public static object GlobalDef(string setting, object defaultValue, bool restartIfChanged = false) => - internal_GlobalDef(setting, defaultValue, restartIfChanged); + public static unsafe object GlobalDef(string setting, object defaultValue, bool restartIfChanged = false) + { + using godot_string settingIn = Marshaling.mono_string_to_godot(setting); + using godot_variant defaultValueIn = Marshaling.mono_object_to_variant(defaultValue); + internal_GlobalDef(settingIn, defaultValueIn, restartIfChanged, out godot_variant result); + using (result) + return Marshaling.variant_to_mono_object(&result); + } - public static object EditorDef(string setting, object defaultValue, bool restartIfChanged = false) => - internal_EditorDef(setting, defaultValue, restartIfChanged); + public static unsafe object EditorDef(string setting, object defaultValue, bool restartIfChanged = false) + { + using godot_string settingIn = Marshaling.mono_string_to_godot(setting); + using godot_variant defaultValueIn = Marshaling.mono_object_to_variant(defaultValue); + internal_EditorDef(settingIn, defaultValueIn, restartIfChanged, out godot_variant result); + using (result) + return Marshaling.variant_to_mono_object(&result); + } - public static object EditorShortcut(string setting) => - internal_EditorShortcut(setting); + public static unsafe object EditorShortcut(string setting) + { + using godot_string settingIn = Marshaling.mono_string_to_godot(setting); + internal_EditorShortcut(settingIn, out godot_variant result); + using (result) + return Marshaling.variant_to_mono_object(&result); + } [SuppressMessage("ReSharper", "InconsistentNaming")] - public static string TTR(this string text) => internal_TTR(text); + public static string TTR(this string text) + { + using godot_string textIn = Marshaling.mono_string_to_godot(text); + internal_TTR(textIn, out godot_string dest); + using (dest) + return Marshaling.mono_string_from_godot(dest); + } // Internal Calls @@ -25,15 +49,17 @@ namespace GodotTools.Internals private static extern float internal_EditorScale(); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern object internal_GlobalDef(string setting, object defaultValue, bool restartIfChanged); + private static extern void internal_GlobalDef(in godot_string setting, in godot_variant defaultValue, + bool restartIfChanged, out godot_variant result); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern object internal_EditorDef(string setting, object defaultValue, bool restartIfChanged); + private static extern void internal_EditorDef(in godot_string setting, in godot_variant defaultValue, + bool restartIfChanged, out godot_variant result); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern object internal_EditorShortcut(string setting); + private static extern void internal_EditorShortcut(in godot_string setting, out godot_variant result); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string internal_TTR(string text); + private static extern void internal_TTR(in godot_string text, out godot_string dest); } } diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs index 5e70c399b2..b15ebc1ae2 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs @@ -1,102 +1,102 @@ using System.Runtime.CompilerServices; +using Godot.NativeInterop; namespace GodotTools.Internals { public static class GodotSharpDirs { - public static string ResDataDir => internal_ResDataDir(); - public static string ResMetadataDir => internal_ResMetadataDir(); - public static string ResAssembliesBaseDir => internal_ResAssembliesBaseDir(); - public static string ResAssembliesDir => internal_ResAssembliesDir(); - public static string ResConfigDir => internal_ResConfigDir(); - public static string ResTempDir => internal_ResTempDir(); - public static string ResTempAssembliesBaseDir => internal_ResTempAssembliesBaseDir(); - public static string ResTempAssembliesDir => internal_ResTempAssembliesDir(); - - public static string MonoUserDir => internal_MonoUserDir(); - public static string MonoLogsDir => internal_MonoLogsDir(); - - #region Tools-only - public static string MonoSolutionsDir => internal_MonoSolutionsDir(); - public static string BuildLogsDirs => internal_BuildLogsDirs(); - - public static string ProjectSlnPath => internal_ProjectSlnPath(); - public static string ProjectCsProjPath => internal_ProjectCsProjPath(); - - public static string DataEditorToolsDir => internal_DataEditorToolsDir(); - public static string DataEditorPrebuiltApiDir => internal_DataEditorPrebuiltApiDir(); - #endregion - - public static string DataMonoEtcDir => internal_DataMonoEtcDir(); - public static string DataMonoLibDir => internal_DataMonoLibDir(); - - #region Windows-only - public static string DataMonoBinDir => internal_DataMonoBinDir(); - #endregion - + public static string ResMetadataDir + { + get + { + internal_ResMetadataDir(out godot_string dest); + using (dest) + return Marshaling.mono_string_from_godot(dest); + } + } + + public static string ResTempAssembliesBaseDir + { + get + { + internal_ResTempAssembliesBaseDir(out godot_string dest); + using (dest) + return Marshaling.mono_string_from_godot(dest); + } + } + + public static string MonoUserDir + { + get + { + internal_MonoUserDir(out godot_string dest); + using (dest) + return Marshaling.mono_string_from_godot(dest); + } + } + + public static string BuildLogsDirs + { + get + { + internal_BuildLogsDirs(out godot_string dest); + using (dest) + return Marshaling.mono_string_from_godot(dest); + } + } + + public static string ProjectSlnPath + { + get + { + internal_ProjectSlnPath(out godot_string dest); + using (dest) + return Marshaling.mono_string_from_godot(dest); + } + } + + public static string ProjectCsProjPath + { + get + { + internal_ProjectCsProjPath(out godot_string dest); + using (dest) + return Marshaling.mono_string_from_godot(dest); + } + } + + public static string DataEditorToolsDir + { + get + { + internal_DataEditorToolsDir(out godot_string dest); + using (dest) + return Marshaling.mono_string_from_godot(dest); + } + } #region Internal [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string internal_ResDataDir(); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string internal_ResMetadataDir(); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string internal_ResAssembliesBaseDir(); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string internal_ResAssembliesDir(); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string internal_ResConfigDir(); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string internal_ResTempDir(); + private static extern void internal_ResMetadataDir(out godot_string r_dest); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string internal_ResTempAssembliesBaseDir(); + private static extern void internal_ResTempAssembliesBaseDir(out godot_string r_dest); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string internal_ResTempAssembliesDir(); + private static extern void internal_MonoUserDir(out godot_string r_dest); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string internal_MonoUserDir(); + private static extern void internal_BuildLogsDirs(out godot_string r_dest); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string internal_MonoLogsDir(); + private static extern void internal_ProjectSlnPath(out godot_string r_dest); - #region Tools-only [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string internal_MonoSolutionsDir(); + private static extern void internal_ProjectCsProjPath(out godot_string r_dest); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string internal_BuildLogsDirs(); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string internal_ProjectSlnPath(); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string internal_ProjectCsProjPath(); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string internal_DataEditorToolsDir(); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string internal_DataEditorPrebuiltApiDir(); - #endregion - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string internal_DataMonoEtcDir(); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string internal_DataMonoLibDir(); - - #region Windows-only - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string internal_DataMonoBinDir(); - #endregion + private static extern void internal_DataEditorToolsDir(out godot_string r_dest); #endregion } diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs index 12c90178c9..7ba26939fa 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs @@ -1,5 +1,7 @@ +using System; using System.Runtime.CompilerServices; using Godot; +using Godot.NativeInterop; using GodotTools.IdeMessaging.Requests; namespace GodotTools.Internals @@ -9,15 +11,23 @@ namespace GodotTools.Internals public const string CSharpLanguageType = "CSharpScript"; public const string CSharpLanguageExtension = ".cs"; - public static string UpdateApiAssembliesFromPrebuilt(string config) => - internal_UpdateApiAssembliesFromPrebuilt(config); + public static string FullExportTemplatesDir + { + get + { + internal_FullExportTemplatesDir(out godot_string dest); + using (dest) + return Marshaling.mono_string_from_godot(dest); + } + } - public static string FullExportTemplatesDir => - internal_FullExportTemplatesDir(); + public static string SimplifyGodotPath(this string path) => Godot.StringExtensions.SimplifyPath(path); - public static string SimplifyGodotPath(this string path) => internal_SimplifyGodotPath(path); - - public static bool IsMacOSAppBundleInstalled(string bundleId) => internal_IsMacOSAppBundleInstalled(bundleId); + public static bool IsMacOSAppBundleInstalled(string bundleId) + { + using godot_string bundleIdIn = Marshaling.mono_string_to_godot(bundleId); + return internal_IsMacOSAppBundleInstalled(bundleIdIn); + } public static bool GodotIs32Bits() => internal_GodotIs32Bits(); @@ -25,10 +35,6 @@ namespace GodotTools.Internals public static void GodotMainIteration() => internal_GodotMainIteration(); - public static ulong GetCoreApiHash() => internal_GetCoreApiHash(); - - public static ulong GetEditorApiHash() => internal_GetEditorApiHash(); - public static bool IsAssembliesReloadingNeeded() => internal_IsAssembliesReloadingNeeded(); public static void ReloadAssemblies(bool softReload) => internal_ReloadAssemblies(softReload); @@ -36,11 +42,19 @@ namespace GodotTools.Internals public static void EditorDebuggerNodeReloadScripts() => internal_EditorDebuggerNodeReloadScripts(); public static bool ScriptEditorEdit(Resource resource, int line, int col, bool grabFocus = true) => - internal_ScriptEditorEdit(resource, line, col, grabFocus); + internal_ScriptEditorEdit(resource.NativeInstance, line, col, grabFocus); public static void EditorNodeShowScriptScreen() => internal_EditorNodeShowScriptScreen(); - public static string MonoWindowsInstallRoot => internal_MonoWindowsInstallRoot(); + public static string MonoWindowsInstallRoot + { + get + { + internal_MonoWindowsInstallRoot(out godot_string dest); + using (dest) + return Marshaling.mono_string_from_godot(dest); + } + } public static void EditorRunPlay() => internal_EditorRunPlay(); @@ -48,22 +62,22 @@ namespace GodotTools.Internals public static void ScriptEditorDebugger_ReloadScripts() => internal_ScriptEditorDebugger_ReloadScripts(); - public static string[] CodeCompletionRequest(CodeCompletionRequest.CompletionKind kind, string scriptFile) => - internal_CodeCompletionRequest((int)kind, scriptFile); + public static unsafe string[] CodeCompletionRequest(CodeCompletionRequest.CompletionKind kind, + string scriptFile) + { + using godot_string scriptFileIn = Marshaling.mono_string_to_godot(scriptFile); + internal_CodeCompletionRequest((int)kind, scriptFileIn, out godot_packed_string_array res); + using (res) + return Marshaling.PackedStringArray_to_mono_array(&res); + } #region Internal [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string internal_UpdateApiAssembliesFromPrebuilt(string config); + private static extern void internal_FullExportTemplatesDir(out godot_string dest); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string internal_FullExportTemplatesDir(); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string internal_SimplifyGodotPath(this string path); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern bool internal_IsMacOSAppBundleInstalled(string bundleId); + private static extern bool internal_IsMacOSAppBundleInstalled(in godot_string bundleId); [MethodImpl(MethodImplOptions.InternalCall)] private static extern bool internal_GodotIs32Bits(); @@ -75,12 +89,6 @@ namespace GodotTools.Internals private static extern void internal_GodotMainIteration(); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern ulong internal_GetCoreApiHash(); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern ulong internal_GetEditorApiHash(); - - [MethodImpl(MethodImplOptions.InternalCall)] private static extern bool internal_IsAssembliesReloadingNeeded(); [MethodImpl(MethodImplOptions.InternalCall)] @@ -90,13 +98,13 @@ namespace GodotTools.Internals private static extern void internal_EditorDebuggerNodeReloadScripts(); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern bool internal_ScriptEditorEdit(Resource resource, int line, int col, bool grabFocus); + private static extern bool internal_ScriptEditorEdit(IntPtr resource, int line, int col, bool grabFocus); [MethodImpl(MethodImplOptions.InternalCall)] private static extern void internal_EditorNodeShowScriptScreen(); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string internal_MonoWindowsInstallRoot(); + private static extern void internal_MonoWindowsInstallRoot(out godot_string dest); [MethodImpl(MethodImplOptions.InternalCall)] private static extern void internal_EditorRunPlay(); @@ -108,7 +116,8 @@ namespace GodotTools.Internals private static extern void internal_ScriptEditorDebugger_ReloadScripts(); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string[] internal_CodeCompletionRequest(int kind, string scriptFile); + private static extern void internal_CodeCompletionRequest(int kind, in godot_string scriptFile, + out godot_packed_string_array res); #endregion } diff --git a/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs b/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs index 5cef6e5c3c..aa100433c9 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs @@ -1,3 +1,4 @@ +using Godot.NativeInterop; using System; using System.Collections.Generic; using System.Diagnostics; @@ -13,10 +14,10 @@ namespace GodotTools.Utils public static class OS { [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string GetPlatformName(); + private static extern void GetPlatformName(out godot_string dest); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern bool UnixFileHasExecutableAccess(string filePath); + private static extern bool UnixFileHasExecutableAccess(in godot_string filePath); public static class Names { @@ -63,14 +64,24 @@ namespace GodotTools.Utils [Names.HTML5] = Platforms.HTML5 }; - private static bool IsOS(string name) + private static unsafe bool IsOS(string name) { - return name.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase); + GetPlatformName(out godot_string dest); + using (dest) + { + string platformName = Marshaling.mono_string_from_godot(dest); + return name.Equals(platformName, StringComparison.OrdinalIgnoreCase); + } } - private static bool IsAnyOS(IEnumerable<string> names) + private static unsafe bool IsAnyOS(IEnumerable<string> names) { - return names.Any(p => p.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase)); + GetPlatformName(out godot_string dest); + using (dest) + { + string platformName = Marshaling.mono_string_from_godot(dest); + return names.Any(p => p.Equals(platformName, StringComparison.OrdinalIgnoreCase)); + } } private static readonly IEnumerable<string> LinuxBSDPlatforms = @@ -114,7 +125,8 @@ namespace GodotTools.Utils private static string PathWhichWindows([NotNull] string name) { - string[] windowsExts = Environment.GetEnvironmentVariable("PATHEXT")?.Split(PathSep) ?? Array.Empty<string>(); + string[] windowsExts = + Environment.GetEnvironmentVariable("PATHEXT")?.Split(PathSep) ?? Array.Empty<string>(); string[] pathDirs = Environment.GetEnvironmentVariable("PATH")?.Split(PathSep); char[] invalidPathChars = Path.GetInvalidPathChars(); @@ -141,10 +153,10 @@ namespace GodotTools.Utils return searchDirs.Select(dir => Path.Combine(dir, name)).FirstOrDefault(File.Exists); return (from dir in searchDirs - select Path.Combine(dir, name) + select Path.Combine(dir, name) into path - from ext in windowsExts - select path + ext).FirstOrDefault(File.Exists); + from ext in windowsExts + select path + ext).FirstOrDefault(File.Exists); } private static string PathWhichUnix([NotNull] string name) @@ -168,7 +180,11 @@ namespace GodotTools.Utils searchDirs.Add(System.IO.Directory.GetCurrentDirectory()); // last in the list return searchDirs.Select(dir => Path.Combine(dir, name)) - .FirstOrDefault(path => File.Exists(path) && UnixFileHasExecutableAccess(path)); + .FirstOrDefault(path => + { + using godot_string pathIn = Marshaling.mono_string_to_godot(path); + return File.Exists(path) && UnixFileHasExecutableAccess(pathIn); + }); } public static void RunProcess(string command, IEnumerable<string> arguments) diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index 64fe82664d..f70155f4e8 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -96,9 +96,9 @@ StringBuilder &operator<<(StringBuilder &r_sb, const char *p_cstring) { #define C_CLASS_NATIVE_FUNCS "NativeFuncs" #define C_NS_MONOUTILS "InteropUtils" -#define C_METHOD_TIE_MANAGED_TO_UNMANAGED "NativeInterop." C_NS_MONOUTILS ".TieManagedToUnmanaged" +#define C_METHOD_TIE_MANAGED_TO_UNMANAGED C_NS_MONOUTILS ".TieManagedToUnmanaged" #define C_METHOD_UNMANAGED_GET_MANAGED C_NS_MONOUTILS ".UnmanagedGetManaged" -#define C_METHOD_ENGINE_GET_SINGLETON "NativeInterop." C_NS_MONOUTILS ".EngineGetSingleton" +#define C_METHOD_ENGINE_GET_SINGLETON C_NS_MONOUTILS ".EngineGetSingleton" #define C_NS_MONOMARSHAL "Marshaling" #define C_METHOD_MANAGED_TO_VARIANT C_NS_MONOMARSHAL ".mono_object_to_variant" @@ -1376,6 +1376,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str output.append("using System;\n"); // IntPtr output.append("using System.Diagnostics;\n"); // DebuggerBrowsable + output.append("using Godot.NativeInterop;\n"); output.append("\n" "#pragma warning disable CS1591 // Disable warning: " @@ -1408,7 +1409,11 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str if (itype.is_singleton) { output.append("static partial class "); } else { - output.append(itype.is_instantiable ? "partial class " : "abstract partial class "); + // Even if the class is not instantiable, we can't declare it abstract because + // the engine can still instantiate them and return them via the scripting API. + // Example: `SceneTreeTimer` returned from `SceneTree.create_timer`. + // See the reverted commit: ef5672d3f94a7321ed779c922088bb72adbb1521 + output.append("partial class "); } output.append(itype.proxy_name); @@ -1533,6 +1538,10 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str // Add native name static field + if (is_derived_type) { + output << MEMBER_BEGIN "private static readonly System.Type _cachedType = typeof(" << itype.proxy_name << ");\n"; + } + output.append(MEMBER_BEGIN "private static readonly StringName " BINDINGS_NATIVE_NAME_FIELD " = \""); output.append(itype.name); output.append("\";\n"); @@ -1540,27 +1549,19 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str if (itype.is_instantiable) { // Add native constructor static field - String get_constructor_method = ICALL_CLASSDB_GET_CONSTRUCTOR; - - if (itype.is_singleton) { - // Singletons are static classes. They don't derive Godot.Object, - // so we need to specify the type to call the static method. - get_constructor_method = "Object." + get_constructor_method; - } - output << MEMBER_BEGIN << "[DebuggerBrowsable(DebuggerBrowsableState.Never)]\n" << "#if NET\n" << INDENT2 "private static unsafe readonly delegate* unmanaged<IntPtr> " - << CS_STATIC_FIELD_NATIVE_CTOR " = " << get_constructor_method + << CS_STATIC_FIELD_NATIVE_CTOR " = " ICALL_CLASSDB_GET_CONSTRUCTOR << "(" BINDINGS_NATIVE_NAME_FIELD ");\n" << "#else\n" // Get rid of this one once we switch to .NET 5/6 << INDENT2 "private static readonly IntPtr " CS_STATIC_FIELD_NATIVE_CTOR - << " = " << get_constructor_method << "(" BINDINGS_NATIVE_NAME_FIELD ");\n" + << " = " ICALL_CLASSDB_GET_CONSTRUCTOR "(" BINDINGS_NATIVE_NAME_FIELD ");\n" << "#endif\n"; } @@ -1589,8 +1590,12 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str << "#endif\n" - << INDENT4 C_METHOD_TIE_MANAGED_TO_UNMANAGED "(this, " BINDINGS_PTR_FIELD ");\n" - << CLOSE_BLOCK_L3 + << INDENT4 C_METHOD_TIE_MANAGED_TO_UNMANAGED "(this, " BINDINGS_PTR_FIELD ", " + << BINDINGS_NATIVE_NAME_FIELD << ", refCounted: " << (itype.is_ref_counted ? "true" : "false") + << ", ((object)this).GetType(), _cachedType);\n" CLOSE_BLOCK_L3 + << INDENT3 "else\n" INDENT3 OPEN_BLOCK + << INDENT4 "InteropUtils.TieManagedToUnmanagedWithPreSetup(this, " + << BINDINGS_PTR_FIELD ", ((object)this).GetType(), _cachedType);\n" CLOSE_BLOCK_L3 << INDENT3 "_InitializeGodotScriptInstanceInternals();\n" CLOSE_BLOCK_L2; } else { // Hide the constructor @@ -1606,6 +1611,8 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str } } + // Methods + int method_bind_count = 0; for (const MethodInterface &imethod : itype.methods) { Error method_err = _generate_cs_method(itype, imethod, method_bind_count, output); @@ -1613,12 +1620,89 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str "Failed to generate method '" + imethod.name + "' for class '" + itype.name + "'."); } + // Signals + for (const SignalInterface &isignal : itype.signals_) { Error method_err = _generate_cs_signal(itype, isignal, output); ERR_FAIL_COND_V_MSG(method_err != OK, method_err, "Failed to generate signal '" + isignal.name + "' for class '" + itype.name + "'."); } + // Script calls + + if (!itype.is_singleton && (is_derived_type || itype.has_virtual_methods)) { + // TODO: string is ok for now. But should be replaced with StringName in the future for performance. + + output << MEMBER_BEGIN "internal " << (is_derived_type ? "override" : "virtual") + << " unsafe bool InternalGodotScriptCall(string method, godot_variant** args, " + << "int argCount, out godot_variant ret)\n" + << INDENT2 "{\n"; + + for (const MethodInterface &imethod : itype.methods) { + if (!imethod.is_virtual) { + continue; + } + + // TODO: + // Compare with cached StringName. We already have a cached StringName + // field for the proxy name. We need one for the original snake_case name. + output << INDENT3 "if ((method == nameof(" << imethod.proxy_name << ") || method == \"" << imethod.name + << "\") && argCount == " << itos(imethod.arguments.size()) << ")\n" + << INDENT3 "{\n"; + + if (imethod.return_type.cname != name_cache.type_void) { + output << INDENT4 "object retBoxed = "; + } else { + output << INDENT4; + } + + output << imethod.proxy_name << "("; + + for (int i = 0; i < imethod.arguments.size(); i++) { + const ArgumentInterface &iarg = imethod.arguments[i]; + + const TypeInterface *arg_type = _get_type_or_null(iarg.type); + ERR_FAIL_NULL_V(arg_type, ERR_BUG); // Argument type not found + + if (i != 0) { + output << ", "; + } + + // TODO: static marshaling (no reflection, no runtime type checks) + if (arg_type->cname == name_cache.type_Array_generic || arg_type->cname == name_cache.type_Dictionary_generic) { + String arg_cs_type = arg_type->cs_type + _get_generic_type_parameters(*arg_type, iarg.type.generic_type_parameters); + + output << "new " << arg_cs_type << "((" << arg_type->cs_type << ")Marshaling.variant_to_mono_object_of_type(args[" + << itos(i) << "], typeof(" << arg_type->cs_type << ")))"; + } else { + output << "(" << arg_type->cs_type << ")Marshaling.variant_to_mono_object_of_type(args[" + << itos(i) << "], typeof(" << arg_type->cs_type << "))"; + } + } + + output << ");\n"; + + if (imethod.return_type.cname != name_cache.type_void) { + // TODO: static marshaling (no reflection, no runtime type checks) + output << INDENT4 "ret = Marshaling.mono_object_to_variant(retBoxed);\n"; + output << INDENT4 "return true;\n"; + } else { + output << INDENT4 "ret = default;\n"; + output << INDENT4 "return true;\n"; + } + + output << INDENT3 "}\n"; + } + + if (is_derived_type) { + output << INDENT3 "return base.InternalGodotScriptCall(method, args, argCount, out ret);\n"; + } else { + output << INDENT3 "return InternalGodotScriptCallViaReflection(method, args, argCount, out ret);\n"; + } + + output << INDENT2 "}\n"; + } + output.append(INDENT1 CLOSE_BLOCK /* class */ CLOSE_BLOCK /* namespace */); @@ -2726,6 +2810,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() { if (method_info.flags & METHOD_FLAG_VIRTUAL) { imethod.is_virtual = true; + itype.has_virtual_methods = true; } PropertyInfo return_info = method_info.return_val; @@ -2865,7 +2950,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() { ERR_FAIL_COND_V_MSG(itype.find_property_by_name(imethod.cname), false, "Method name conflicts with property: '" + itype.name + "." + imethod.name + "'."); - // Classes starting with an underscore are ignored unless they're used as a property setter or getter + // Methods starting with an underscore are ignored unless they're used as a property setter or getter if (!imethod.is_virtual && imethod.name[0] == '_') { for (const PropertyInterface &iprop : itype.properties) { if (iprop.setter == imethod.name || iprop.getter == imethod.name) { @@ -3335,7 +3420,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.proxy_name = "string"; itype.cs_type = itype.proxy_name; itype.c_in = "%5using %0 %1_in = " C_METHOD_MONOSTR_TO_GODOT "(%1);\n"; - itype.c_out = "%5return " C_METHOD_MONOSTR_FROM_GODOT "(&%1);\n"; + itype.c_out = "%5return " C_METHOD_MONOSTR_FROM_GODOT "(%1);\n"; itype.c_arg_in = "&%s_in"; itype.c_type = "godot_string"; itype.c_type_in = itype.cs_type; diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h index 1e1e8be178..c9eb8e6317 100644 --- a/modules/mono/editor/bindings_generator.h +++ b/modules/mono/editor/bindings_generator.h @@ -380,6 +380,8 @@ class BindingsGenerator { List<MethodInterface> methods; List<SignalInterface> signals_; + bool has_virtual_methods = false; + const MethodInterface *find_method_by_name(const StringName &p_cname) const { for (const MethodInterface &E : methods) { if (E.cname == p_cname) { diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp index 102d27378f..dd0d94121d 100644 --- a/modules/mono/editor/editor_internal_calls.cpp +++ b/modules/mono/editor/editor_internal_calls.cpp @@ -47,177 +47,107 @@ #include "../csharp_script.h" #include "../godotsharp_dirs.h" -#include "../mono_gd/gd_mono_marshal.h" #include "../utils/macos_utils.h" #include "code_completion.h" #include "godotsharp_export.h" -MonoString *godot_icall_GodotSharpDirs_ResDataDir() { - return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_data_dir()); -} - -MonoString *godot_icall_GodotSharpDirs_ResMetadataDir() { - return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_metadata_dir()); -} - -MonoString *godot_icall_GodotSharpDirs_ResAssembliesBaseDir() { - return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_assemblies_base_dir()); -} - -MonoString *godot_icall_GodotSharpDirs_ResAssembliesDir() { - return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_assemblies_dir()); -} - -MonoString *godot_icall_GodotSharpDirs_ResConfigDir() { - return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_config_dir()); -} - -MonoString *godot_icall_GodotSharpDirs_ResTempDir() { - return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_temp_dir()); -} +#include "../interop_types.h" -MonoString *godot_icall_GodotSharpDirs_ResTempAssembliesBaseDir() { - return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_temp_assemblies_base_dir()); +void godot_icall_GodotSharpDirs_ResMetadataDir(godot_string *r_dest) { + memnew_placement(r_dest, String(GodotSharpDirs::get_res_metadata_dir())); } -MonoString *godot_icall_GodotSharpDirs_ResTempAssembliesDir() { - return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_temp_assemblies_dir()); +void godot_icall_GodotSharpDirs_ResTempAssembliesBaseDir(godot_string *r_dest) { + memnew_placement(r_dest, String(GodotSharpDirs::get_res_temp_assemblies_base_dir())); } -MonoString *godot_icall_GodotSharpDirs_MonoUserDir() { - return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_mono_user_dir()); -} - -MonoString *godot_icall_GodotSharpDirs_MonoLogsDir() { - return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_mono_logs_dir()); -} - -MonoString *godot_icall_GodotSharpDirs_MonoSolutionsDir() { -#ifdef TOOLS_ENABLED - return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_mono_solutions_dir()); -#else - return nullptr; -#endif -} - -MonoString *godot_icall_GodotSharpDirs_BuildLogsDirs() { -#ifdef TOOLS_ENABLED - return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_build_logs_dir()); -#else - return nullptr; -#endif +void godot_icall_GodotSharpDirs_MonoUserDir(godot_string *r_dest) { + memnew_placement(r_dest, String(GodotSharpDirs::get_mono_user_dir())); } -MonoString *godot_icall_GodotSharpDirs_ProjectSlnPath() { +void godot_icall_GodotSharpDirs_BuildLogsDirs(godot_string *r_dest) { #ifdef TOOLS_ENABLED - return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_project_sln_path()); + memnew_placement(r_dest, String(GodotSharpDirs::get_build_logs_dir())); #else return nullptr; #endif } -MonoString *godot_icall_GodotSharpDirs_ProjectCsProjPath() { +void godot_icall_GodotSharpDirs_ProjectSlnPath(godot_string *r_dest) { #ifdef TOOLS_ENABLED - return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_project_csproj_path()); + memnew_placement(r_dest, String(GodotSharpDirs::get_project_sln_path())); #else return nullptr; #endif } -MonoString *godot_icall_GodotSharpDirs_DataEditorToolsDir() { +void godot_icall_GodotSharpDirs_ProjectCsProjPath(godot_string *r_dest) { #ifdef TOOLS_ENABLED - return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_editor_tools_dir()); + memnew_placement(r_dest, String(GodotSharpDirs::get_project_csproj_path())); #else return nullptr; #endif } -MonoString *godot_icall_GodotSharpDirs_DataEditorPrebuiltApiDir() { +void godot_icall_GodotSharpDirs_DataEditorToolsDir(godot_string *r_dest) { #ifdef TOOLS_ENABLED - return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_editor_prebuilt_api_dir()); -#else - return nullptr; -#endif -} - -MonoString *godot_icall_GodotSharpDirs_DataMonoEtcDir() { - return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_mono_etc_dir()); -} - -MonoString *godot_icall_GodotSharpDirs_DataMonoLibDir() { - return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_mono_lib_dir()); -} - -MonoString *godot_icall_GodotSharpDirs_DataMonoBinDir() { -#ifdef WINDOWS_ENABLED - return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_mono_bin_dir()); + memnew_placement(r_dest, String(GodotSharpDirs::get_data_editor_tools_dir())); #else return nullptr; #endif } -void godot_icall_EditorProgress_Create(MonoString *p_task, MonoString *p_label, int32_t p_amount, MonoBoolean p_can_cancel) { - String task = GDMonoMarshal::mono_string_to_godot(p_task); - String label = GDMonoMarshal::mono_string_to_godot(p_label); +void godot_icall_EditorProgress_Create(const godot_string *p_task, const godot_string *p_label, int32_t p_amount, bool p_can_cancel) { + String task = *reinterpret_cast<const String *>(p_task); + String label = *reinterpret_cast<const String *>(p_label); EditorNode::progress_add_task(task, label, p_amount, (bool)p_can_cancel); } -void godot_icall_EditorProgress_Dispose(MonoString *p_task) { - String task = GDMonoMarshal::mono_string_to_godot(p_task); +void godot_icall_EditorProgress_Dispose(const godot_string *p_task) { + String task = *reinterpret_cast<const String *>(p_task); EditorNode::progress_end_task(task); } -MonoBoolean godot_icall_EditorProgress_Step(MonoString *p_task, MonoString *p_state, int32_t p_step, MonoBoolean p_force_refresh) { - String task = GDMonoMarshal::mono_string_to_godot(p_task); - String state = GDMonoMarshal::mono_string_to_godot(p_state); +bool godot_icall_EditorProgress_Step(const godot_string *p_task, const godot_string *p_state, int32_t p_step, bool p_force_refresh) { + String task = *reinterpret_cast<const String *>(p_task); + String state = *reinterpret_cast<const String *>(p_state); return EditorNode::progress_task_step(task, state, p_step, (bool)p_force_refresh); } -uint32_t godot_icall_ExportPlugin_GetExportedAssemblyDependencies(MonoObject *p_initial_assemblies, - MonoString *p_build_config, MonoString *p_custom_bcl_dir, MonoObject *r_assembly_dependencies) { - Dictionary initial_dependencies = GDMonoMarshal::mono_object_to_variant(p_initial_assemblies); - String build_config = GDMonoMarshal::mono_string_to_godot(p_build_config); - String custom_bcl_dir = GDMonoMarshal::mono_string_to_godot(p_custom_bcl_dir); - Dictionary assembly_dependencies = GDMonoMarshal::mono_object_to_variant(r_assembly_dependencies); +uint32_t godot_icall_ExportPlugin_GetExportedAssemblyDependencies(const godot_dictionary *p_initial_assemblies, + const godot_string *p_build_config, const godot_string *p_custom_bcl_dir, godot_dictionary *r_assembly_dependencies) { + Dictionary initial_dependencies = *reinterpret_cast<const Dictionary *>(p_initial_assemblies); + String build_config = *reinterpret_cast<const String *>(p_build_config); + String custom_bcl_dir = *reinterpret_cast<const String *>(p_custom_bcl_dir); + Dictionary assembly_dependencies = *reinterpret_cast<Dictionary *>(r_assembly_dependencies); return GodotSharpExport::get_exported_assembly_dependencies(initial_dependencies, build_config, custom_bcl_dir, assembly_dependencies); } -MonoString *godot_icall_Internal_UpdateApiAssembliesFromPrebuilt(MonoString *p_config) { - String config = GDMonoMarshal::mono_string_to_godot(p_config); - String error_str = GDMono::get_singleton()->update_api_assemblies_from_prebuilt(config); - return GDMonoMarshal::mono_string_from_godot(error_str); -} - -MonoString *godot_icall_Internal_FullExportTemplatesDir() { +void godot_icall_Internal_FullExportTemplatesDir(godot_string *r_dest) { String full_templates_dir = EditorPaths::get_singleton()->get_export_templates_dir().plus_file(VERSION_FULL_CONFIG); - return GDMonoMarshal::mono_string_from_godot(full_templates_dir); -} - -MonoString *godot_icall_Internal_SimplifyGodotPath(MonoString *p_path) { - String path = GDMonoMarshal::mono_string_to_godot(p_path); - return GDMonoMarshal::mono_string_from_godot(path.simplify_path()); + memnew_placement(r_dest, String(full_templates_dir)); } -MonoBoolean godot_icall_Internal_IsMacOSAppBundleInstalled(MonoString *p_bundle_id) { +bool godot_icall_Internal_IsMacOSAppBundleInstalled(const godot_string *p_bundle_id) { #ifdef MACOS_ENABLED - String bundle_id = GDMonoMarshal::mono_string_to_godot(p_bundle_id); - return (MonoBoolean)macos_is_app_bundle_installed(bundle_id); + String bundle_id = *reinterpret_cast<const String *>(p_bundle_id); + return (bool)macos_is_app_bundle_installed(bundle_id); #else (void)p_bundle_id; // UNUSED - return (MonoBoolean) false; + return (bool)false; #endif } -MonoBoolean godot_icall_Internal_GodotIs32Bits() { +bool godot_icall_Internal_GodotIs32Bits() { return sizeof(void *) == 4; } -MonoBoolean godot_icall_Internal_GodotIsRealTDouble() { +bool godot_icall_Internal_GodotIsRealTDouble() { #ifdef REAL_T_IS_DOUBLE - return (MonoBoolean) true; + return (bool)true; #else - return (MonoBoolean) false; + return (bool)false; #endif } @@ -225,23 +155,15 @@ void godot_icall_Internal_GodotMainIteration() { Main::iteration(); } -uint64_t godot_icall_Internal_GetCoreApiHash() { - return ClassDB::get_api_hash(ClassDB::API_CORE); -} - -uint64_t godot_icall_Internal_GetEditorApiHash() { - return ClassDB::get_api_hash(ClassDB::API_EDITOR); -} - -MonoBoolean godot_icall_Internal_IsAssembliesReloadingNeeded() { +bool godot_icall_Internal_IsAssembliesReloadingNeeded() { #ifdef GD_MONO_HOT_RELOAD - return (MonoBoolean)CSharpLanguage::get_singleton()->is_assembly_reloading_needed(); + return (bool)CSharpLanguage::get_singleton()->is_assembly_reloading_needed(); #else - return (MonoBoolean) false; + return (bool)false; #endif } -void godot_icall_Internal_ReloadAssemblies(MonoBoolean p_soft_reload) { +void godot_icall_Internal_ReloadAssemblies(bool p_soft_reload) { #ifdef GD_MONO_HOT_RELOAD mono_bind::GodotSharp::get_singleton()->call_deferred(SNAME("_reload_assemblies"), (bool)p_soft_reload); #endif @@ -251,21 +173,22 @@ void godot_icall_Internal_EditorDebuggerNodeReloadScripts() { EditorDebuggerNode::get_singleton()->reload_scripts(); } -MonoBoolean godot_icall_Internal_ScriptEditorEdit(MonoObject *p_resource, int32_t p_line, int32_t p_col, MonoBoolean p_grab_focus) { - Ref<Resource> resource = GDMonoMarshal::mono_object_to_variant(p_resource); - return (MonoBoolean)ScriptEditor::get_singleton()->edit(resource, p_line, p_col, (bool)p_grab_focus); +bool godot_icall_Internal_ScriptEditorEdit(Resource *p_resource, int32_t p_line, int32_t p_col, bool p_grab_focus) { + Ref<Resource> resource = p_resource; + return (bool)ScriptEditor::get_singleton()->edit(resource, p_line, p_col, (bool)p_grab_focus); } void godot_icall_Internal_EditorNodeShowScriptScreen() { EditorNode::get_singleton()->call("_editor_select", EditorNode::EDITOR_SCRIPT); } -MonoString *godot_icall_Internal_MonoWindowsInstallRoot() { +void godot_icall_Internal_MonoWindowsInstallRoot(godot_string *r_dest) { #ifdef WINDOWS_ENABLED String install_root_dir = GDMono::get_singleton()->get_mono_reg_info().install_root_dir; - return GDMonoMarshal::mono_string_from_godot(install_root_dir); + memnew_placement(r_dest, String(install_root_dir)); #else - return nullptr; + memnew_placement(r_dest, String); + return; #endif } @@ -284,49 +207,49 @@ void godot_icall_Internal_ScriptEditorDebugger_ReloadScripts() { } } -MonoArray *godot_icall_Internal_CodeCompletionRequest(int32_t p_kind, MonoString *p_script_file) { - String script_file = GDMonoMarshal::mono_string_to_godot(p_script_file); +void godot_icall_Internal_CodeCompletionRequest(int32_t p_kind, const godot_string *p_script_file, godot_packed_array *r_ret) { + String script_file = *reinterpret_cast<const String *>(p_script_file); PackedStringArray suggestions = gdmono::get_code_completion((gdmono::CompletionKind)p_kind, script_file); - return GDMonoMarshal::PackedStringArray_to_mono_array(suggestions); + memnew_placement(r_ret, PackedStringArray(suggestions)); } float godot_icall_Globals_EditorScale() { return EDSCALE; } -MonoObject *godot_icall_Globals_GlobalDef(MonoString *p_setting, MonoObject *p_default_value, MonoBoolean p_restart_if_changed) { - String setting = GDMonoMarshal::mono_string_to_godot(p_setting); - Variant default_value = GDMonoMarshal::mono_object_to_variant(p_default_value); +void godot_icall_Globals_GlobalDef(const godot_string *p_setting, const godot_variant *p_default_value, bool p_restart_if_changed, godot_variant *r_result) { + String setting = *reinterpret_cast<const String *>(p_setting); + Variant default_value = *reinterpret_cast<const Variant *>(p_default_value); Variant result = _GLOBAL_DEF(setting, default_value, (bool)p_restart_if_changed); - return GDMonoMarshal::variant_to_mono_object(result); + memnew_placement(r_result, Variant(result)); } -MonoObject *godot_icall_Globals_EditorDef(MonoString *p_setting, MonoObject *p_default_value, MonoBoolean p_restart_if_changed) { - String setting = GDMonoMarshal::mono_string_to_godot(p_setting); - Variant default_value = GDMonoMarshal::mono_object_to_variant(p_default_value); +void godot_icall_Globals_EditorDef(const godot_string *p_setting, const godot_variant *p_default_value, bool p_restart_if_changed, godot_variant *r_result) { + String setting = *reinterpret_cast<const String *>(p_setting); + Variant default_value = *reinterpret_cast<const Variant *>(p_default_value); Variant result = _EDITOR_DEF(setting, default_value, (bool)p_restart_if_changed); - return GDMonoMarshal::variant_to_mono_object(result); + memnew_placement(r_result, Variant(result)); } -MonoObject *godot_icall_Globals_EditorShortcut(MonoString *p_setting) { - String setting = GDMonoMarshal::mono_string_to_godot(p_setting); +void godot_icall_Globals_EditorShortcut(const godot_string *p_setting, godot_variant *r_result) { + String setting = *reinterpret_cast<const String *>(p_setting); Ref<Shortcut> result = ED_GET_SHORTCUT(setting); - return GDMonoMarshal::variant_to_mono_object(result); + memnew_placement(r_result, Variant(result)); } -MonoString *godot_icall_Globals_TTR(MonoString *p_text) { - String text = GDMonoMarshal::mono_string_to_godot(p_text); - return GDMonoMarshal::mono_string_from_godot(TTR(text)); +void godot_icall_Globals_TTR(const godot_string *p_text, godot_string *r_dest) { + String text = *reinterpret_cast<const String *>(p_text); + memnew_placement(r_dest, String(TTR(text))); } -MonoString *godot_icall_Utils_OS_GetPlatformName() { +void godot_icall_Utils_OS_GetPlatformName(godot_string *r_dest) { String os_name = OS::get_singleton()->get_name(); - return GDMonoMarshal::mono_string_from_godot(os_name); + memnew_placement(r_dest, String(os_name)); } -MonoBoolean godot_icall_Utils_OS_UnixFileHasExecutableAccess(MonoString *p_file_path) { +bool godot_icall_Utils_OS_UnixFileHasExecutableAccess(const godot_string *p_file_path) { #ifdef UNIX_ENABLED - String file_path = GDMonoMarshal::mono_string_to_godot(p_file_path); + String file_path = *reinterpret_cast<const String *>(p_file_path); return access(file_path.utf8().get_data(), X_OK) == 0; #else ERR_FAIL_V(false); @@ -335,25 +258,13 @@ MonoBoolean godot_icall_Utils_OS_UnixFileHasExecutableAccess(MonoString *p_file_ void register_editor_internal_calls() { // GodotSharpDirs - GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResDataDir", godot_icall_GodotSharpDirs_ResDataDir); GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResMetadataDir", godot_icall_GodotSharpDirs_ResMetadataDir); - GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResAssembliesBaseDir", godot_icall_GodotSharpDirs_ResAssembliesBaseDir); - GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResAssembliesDir", godot_icall_GodotSharpDirs_ResAssembliesDir); - GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResConfigDir", godot_icall_GodotSharpDirs_ResConfigDir); - GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempDir", godot_icall_GodotSharpDirs_ResTempDir); GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempAssembliesBaseDir", godot_icall_GodotSharpDirs_ResTempAssembliesBaseDir); - GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempAssembliesDir", godot_icall_GodotSharpDirs_ResTempAssembliesDir); GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoUserDir", godot_icall_GodotSharpDirs_MonoUserDir); - GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoLogsDir", godot_icall_GodotSharpDirs_MonoLogsDir); - GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoSolutionsDir", godot_icall_GodotSharpDirs_MonoSolutionsDir); GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_BuildLogsDirs", godot_icall_GodotSharpDirs_BuildLogsDirs); GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ProjectSlnPath", godot_icall_GodotSharpDirs_ProjectSlnPath); GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ProjectCsProjPath", godot_icall_GodotSharpDirs_ProjectCsProjPath); GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataEditorToolsDir", godot_icall_GodotSharpDirs_DataEditorToolsDir); - GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataEditorPrebuiltApiDir", godot_icall_GodotSharpDirs_DataEditorPrebuiltApiDir); - GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoEtcDir", godot_icall_GodotSharpDirs_DataMonoEtcDir); - GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoLibDir", godot_icall_GodotSharpDirs_DataMonoLibDir); - GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoBinDir", godot_icall_GodotSharpDirs_DataMonoBinDir); // EditorProgress GDMonoUtils::add_internal_call("GodotTools.Internals.EditorProgress::internal_Create", godot_icall_EditorProgress_Create); @@ -364,15 +275,11 @@ void register_editor_internal_calls() { GDMonoUtils::add_internal_call("GodotTools.Export.ExportPlugin::internal_GetExportedAssemblyDependencies", godot_icall_ExportPlugin_GetExportedAssemblyDependencies); // Internals - GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_UpdateApiAssembliesFromPrebuilt", godot_icall_Internal_UpdateApiAssembliesFromPrebuilt); GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_FullExportTemplatesDir", godot_icall_Internal_FullExportTemplatesDir); - GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_SimplifyGodotPath", godot_icall_Internal_SimplifyGodotPath); GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_IsMacOSAppBundleInstalled", godot_icall_Internal_IsMacOSAppBundleInstalled); GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GodotIs32Bits", godot_icall_Internal_GodotIs32Bits); GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GodotIsRealTDouble", godot_icall_Internal_GodotIsRealTDouble); GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GodotMainIteration", godot_icall_Internal_GodotMainIteration); - GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GetCoreApiHash", godot_icall_Internal_GetCoreApiHash); - GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GetEditorApiHash", godot_icall_Internal_GetEditorApiHash); GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_IsAssembliesReloadingNeeded", godot_icall_Internal_IsAssembliesReloadingNeeded); GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_ReloadAssemblies", godot_icall_Internal_ReloadAssemblies); GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_EditorDebuggerNodeReloadScripts", godot_icall_Internal_EditorDebuggerNodeReloadScripts); diff --git a/modules/mono/editor/godotsharp_export.cpp b/modules/mono/editor/godotsharp_export.cpp index f9ea403334..d065aa30e5 100644 --- a/modules/mono/editor/godotsharp_export.cpp +++ b/modules/mono/editor/godotsharp_export.cpp @@ -92,7 +92,7 @@ Error get_assembly_dependencies(GDMonoAssembly *p_assembly, MonoAssemblyName *re mono_assembly_get_assemblyref(image, i, reusable_aname); GDMonoAssembly *ref_assembly = nullptr; - if (!GDMono::get_singleton()->load_assembly(ref_name, reusable_aname, &ref_assembly, /* refonly: */ true, p_search_dirs)) { + if (!GDMono::get_singleton()->load_assembly(ref_name, reusable_aname, &ref_assembly, p_search_dirs)) { ERR_FAIL_V_MSG(ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + ref_name + "'."); } @@ -126,7 +126,7 @@ Error get_exported_assembly_dependencies(const Dictionary &p_initial_assemblies, String assembly_path = p_initial_assemblies[*key]; GDMonoAssembly *assembly = nullptr; - bool load_success = GDMono::get_singleton()->load_assembly_from(assembly_name, assembly_path, &assembly, /* refonly: */ true); + bool load_success = GDMono::get_singleton()->load_assembly_from(assembly_name, assembly_path, &assembly); ERR_FAIL_COND_V_MSG(!load_success, ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + assembly_name + "'."); diff --git a/modules/mono/editor/godotsharp_export.h b/modules/mono/editor/godotsharp_export.h index 60620b5f4d..20d6f6fe73 100644 --- a/modules/mono/editor/godotsharp_export.h +++ b/modules/mono/editor/godotsharp_export.h @@ -35,12 +35,8 @@ #include "core/string/ustring.h" #include "core/variant/dictionary.h" -#include "../mono_gd/gd_mono_header.h" - namespace GodotSharpExport { -Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_dependencies); - Error get_exported_assembly_dependencies(const Dictionary &p_initial_assemblies, const String &p_build_config, const String &p_custom_lib_dir, Dictionary &r_assembly_dependencies); } // namespace GodotSharpExport diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs index 893ab81e5d..a2a97e0a3e 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs @@ -14,7 +14,7 @@ namespace Godot.Collections /// </summary> public sealed class Array : IList, IDisposable { - internal godot_array NativeValue; + public godot_array NativeValue; /// <summary> /// Constructs a new empty <see cref="Array"/>. @@ -307,7 +307,7 @@ namespace Godot.Collections { using godot_string str = default; NativeFuncs.godotsharp_array_to_string(ref NativeValue, &str); - return Marshaling.mono_string_from_godot(&str); + return Marshaling.mono_string_from_godot(str); } /// <summary> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs index 2febf37f05..b7d633517a 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs @@ -1,5 +1,7 @@ using System; +#nullable enable + namespace Godot { /// <summary> @@ -8,25 +10,28 @@ namespace Godot [AttributeUsage(AttributeTargets.Assembly)] public class AssemblyHasScriptsAttribute : Attribute { - private readonly bool requiresLookup; - private readonly System.Type[] scriptTypes; + public bool RequiresLookup { get; } + public Type[]? ScriptTypes { get; } /// <summary> /// Constructs a new AssemblyHasScriptsAttribute instance. /// </summary> public AssemblyHasScriptsAttribute() { - requiresLookup = true; + RequiresLookup = true; + ScriptTypes = null; } /// <summary> /// Constructs a new AssemblyHasScriptsAttribute instance. /// </summary> /// <param name="scriptTypes">The specified type(s) of scripts.</param> - public AssemblyHasScriptsAttribute(System.Type[] scriptTypes) + public AssemblyHasScriptsAttribute(Type[] scriptTypes) { - requiresLookup = false; - this.scriptTypes = scriptTypes; + RequiresLookup = false; + ScriptTypes = scriptTypes; } } } + +#nullable restore diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs index 3ebb6612de..2c8a53ae1c 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs @@ -8,7 +8,7 @@ namespace Godot [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] public class ScriptPathAttribute : Attribute { - private string path; + public string Path { get; } /// <summary> /// Constructs a new ScriptPathAttribute instance. @@ -16,7 +16,7 @@ namespace Godot /// <param name="path">The file path to the script</param> public ScriptPathAttribute(string path) { - this.path = path; + Path = path; } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs new file mode 100644 index 0000000000..16fde2a900 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs @@ -0,0 +1,123 @@ +using System; +using System.Runtime.InteropServices; +using Godot.NativeInterop; + +namespace Godot.Bridge +{ + internal static class CSharpInstanceBridge + { + private static unsafe void Call(IntPtr godotObjectGCHandle, godot_string_name* method, + godot_variant** args, int argCount, godot_variant_call_error* ref_callError, godot_variant* r_ret) + { + // Performance is not critical here as this will be replaced with source generators. + var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target; + + if (godotObject == null) + { + *r_ret = default; + (*ref_callError).error = godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INSTANCE_IS_NULL; + return; + } + + using godot_string dest = default; + NativeFuncs.godotsharp_string_name_as_string(&dest, method); + string methodStr = Marshaling.mono_string_from_godot(dest); + + bool methodInvoked = godotObject.InternalGodotScriptCall(methodStr, args, argCount, out godot_variant outRet); + + if (!methodInvoked) + { + *r_ret = default; + // This is important, as it tells Object::call that no method was called. + // Otherwise, it would prevent Object::call from calling native methods. + (*ref_callError).error = godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INVALID_METHOD; + return; + } + + *r_ret = outRet; + } + + private static unsafe bool Set(IntPtr godotObjectGCHandle, godot_string_name* name, godot_variant* value) + { + // Performance is not critical here as this will be replaced with source generators. + var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target; + + if (godotObject == null) + throw new InvalidOperationException(); + + var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue( + NativeFuncs.godotsharp_string_name_new_copy(name)); + + if (godotObject.InternalGodotScriptSetFieldOrPropViaReflection(nameManaged.ToString(), value)) + return true; + + object valueManaged = Marshaling.variant_to_mono_object(value); + + return godotObject._Set(nameManaged, valueManaged); + } + + private static unsafe bool Get(IntPtr godotObjectGCHandle, godot_string_name* name, godot_variant* r_retValue) + { + // Performance is not critical here as this will be replaced with source generators. + var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target; + + if (godotObject == null) + throw new InvalidOperationException(); + + var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue( + NativeFuncs.godotsharp_string_name_new_copy(name)); + + if (godotObject.InternalGodotScriptGetFieldOrPropViaReflection(nameManaged.ToString(), + out godot_variant outRet)) + { + *r_retValue = outRet; + return true; + } + + object ret = godotObject._Get(nameManaged); + + if (ret == null) + { + *r_retValue = default; + return false; + } + + *r_retValue = Marshaling.mono_object_to_variant(ret); + return true; + } + + private static void CallDispose(IntPtr godotObjectGCHandle, bool okIfNull) + { + var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target; + + if (okIfNull) + godotObject?.Dispose(); + else + godotObject!.Dispose(); + } + + private static unsafe void CallToString(IntPtr godotObjectGCHandle, godot_string* r_res, bool* r_valid) + { + var self = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target; + + if (self == null) + { + *r_res = default; + *r_valid = false; + return; + } + + var resultStr = self.ToString(); + + if (resultStr == null) + { + *r_res = default; + *r_valid = false; + return; + } + + *r_res = Marshaling.mono_string_to_godot(resultStr); + *r_valid = true; + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs new file mode 100644 index 0000000000..aa9e434b07 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs @@ -0,0 +1,11 @@ +using System; +using System.Runtime.InteropServices; + +namespace Godot.Bridge +{ + internal static class GCHandleBridge + { + private static void FreeGCHandle(IntPtr gcHandlePtr) + => GCHandle.FromIntPtr(gcHandlePtr).Free(); + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs new file mode 100644 index 0000000000..a39da68a02 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs @@ -0,0 +1,501 @@ +using System; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; +using Godot.Collections; +using Godot.NativeInterop; + +namespace Godot.Bridge +{ + internal static class ScriptManagerBridge + { + private static System.Collections.Generic.Dictionary<string, ScriptLookupInfo> _scriptLookupMap = new(); + private static System.Collections.Generic.Dictionary<IntPtr, Type> _scriptBridgeMap = new(); + + private struct ScriptLookupInfo + { + public string ClassNamespace { get; private set; } + public string ClassName { get; private set; } + public Type ScriptType { get; private set; } + + public ScriptLookupInfo(string classNamespace, string className, Type scriptType) + { + ClassNamespace = classNamespace; + ClassName = className; + ScriptType = scriptType; + } + }; + + internal static void FrameCallback() + { + Dispatcher.DefaultGodotTaskScheduler?.Activate(); + } + + internal static unsafe IntPtr CreateManagedForGodotObjectBinding(godot_string_name* nativeTypeName, + IntPtr godotObject) + { + Type nativeType = TypeGetProxyClass(nativeTypeName); + var obj = (Object)FormatterServices.GetUninitializedObject(nativeType); + + obj.NativePtr = godotObject; + + var ctor = nativeType.GetConstructor( + BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, + null, Type.EmptyTypes, null); + _ = ctor!.Invoke(obj, null); + + return GCHandle.ToIntPtr(GCHandle.Alloc(obj)); + } + + internal static unsafe void CreateManagedForGodotObjectScriptInstance(IntPtr scriptPtr, IntPtr godotObject, + godot_variant** args, int argCount) + { + // Performance is not critical here as this will be replaced with source generators. + Type scriptType = _scriptBridgeMap[scriptPtr]; + var obj = (Object)FormatterServices.GetUninitializedObject(scriptType); + + obj.NativePtr = godotObject; + + var ctor = scriptType + .GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) + .Where(c => c.GetParameters().Length == argCount) + .FirstOrDefault(); + + if (ctor == null) + { + if (argCount == 0) + { + throw new MissingMemberException( + $"Cannot create script instance. The class '{scriptType.FullName}' does not define a parameterless constructor."); + } + else + { + throw new MissingMemberException( + $"The class '{scriptType.FullName}' does not define a constructor that takes x parameters."); + } + } + + var parameters = ctor.GetParameters(); + int paramCount = parameters.Length; + + object[] invokeParams = new object[paramCount]; + + for (int i = 0; i < paramCount; i++) + { + invokeParams[i] = Marshaling.variant_to_mono_object_of_type( + args[i], parameters[i].ParameterType); + } + + ctor.Invoke(obj, invokeParams); + } + + private static unsafe void GetScriptNativeName(IntPtr scriptPtr, godot_string_name* r_res) + { + // Performance is not critical here as this will be replaced with source generators. + if (!_scriptBridgeMap.TryGetValue(scriptPtr, out var scriptType)) + { + *r_res = default; + return; + } + + var native = Object.InternalGetClassNativeBase(scriptType); + + var field = native?.GetField("NativeName", BindingFlags.DeclaredOnly | BindingFlags.Static | + BindingFlags.Public | BindingFlags.NonPublic); + + if (field == null) + { + *r_res = default; + return; + } + + var nativeName = (StringName)field.GetValue(null); + + *r_res = NativeFuncs.godotsharp_string_name_new_copy(nativeName.NativeValue); + } + + private static void SetGodotObjectPtr(IntPtr gcHandlePtr, IntPtr newPtr) + { + var target = (Object)GCHandle.FromIntPtr(gcHandlePtr).Target; + if (target != null) + target.NativePtr = newPtr; + } + + private static unsafe Type TypeGetProxyClass(godot_string_name* nativeTypeName) + { + // Performance is not critical here as this will be replaced with a generated dictionary. + using var stringName = StringName.CreateTakingOwnershipOfDisposableValue( + NativeFuncs.godotsharp_string_name_new_copy(nativeTypeName)); + string nativeTypeNameStr = stringName.ToString(); + + if (nativeTypeNameStr[0] == '_') + nativeTypeNameStr = nativeTypeNameStr.Substring(1); + + Type wrapperType = typeof(Object).Assembly.GetType("Godot." + nativeTypeNameStr); + + if (wrapperType == null) + { + wrapperType = AppDomain.CurrentDomain.GetAssemblies() + .First(a => a.GetName().Name == "GodotSharpEditor") + .GetType("Godot." + nativeTypeNameStr); + } + + static bool IsStatic(Type type) => type.IsAbstract && type.IsSealed; + + if (wrapperType != null && IsStatic(wrapperType)) + { + // A static class means this is a Godot singleton class. If an instance is needed we use Godot.Object. + return typeof(Object); + } + + return wrapperType; + } + + internal static void LookupScriptsInAssembly(Assembly assembly) + { + static void LookupScriptForClass(Type type) + { + var scriptPathAttr = type.GetCustomAttributes(inherit: false) + .OfType<ScriptPathAttribute>() + .FirstOrDefault(); + + if (scriptPathAttr == null) + return; + + _scriptLookupMap[scriptPathAttr.Path] = new ScriptLookupInfo(type.Namespace, type.Name, type); + } + + var assemblyHasScriptsAttr = assembly.GetCustomAttributes(inherit: false) + .OfType<AssemblyHasScriptsAttribute>() + .FirstOrDefault(); + + if (assemblyHasScriptsAttr == null) + return; + + if (assemblyHasScriptsAttr.RequiresLookup) + { + // 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. + + var typeOfGodotObject = typeof(Object); + + foreach (var type in assembly.GetTypes()) + { + if (type.IsNested) + continue; + + if (!typeOfGodotObject.IsAssignableFrom(type)) + continue; + + LookupScriptForClass(type); + } + } + else + { + // This is the most likely scenario as we use C# source generators + + var scriptTypes = assemblyHasScriptsAttr.ScriptTypes; + + if (scriptTypes != null) + { + for (int i = 0; i < scriptTypes.Length; i++) + { + LookupScriptForClass(scriptTypes[i]); + } + } + } + } + + internal static unsafe void RaiseEventSignal(IntPtr ownerGCHandlePtr, + godot_string_name* eventSignalName, godot_variant** args, int argCount, bool* r_ownerIsNull) + { + var owner = (Object)GCHandle.FromIntPtr(ownerGCHandlePtr).Target; + + if (owner == null) + { + *r_ownerIsNull = true; + return; + } + + *r_ownerIsNull = false; + + owner.InternalRaiseEventSignal(eventSignalName, args, argCount); + } + + internal static unsafe void GetScriptSignalList(IntPtr scriptPtr, godot_dictionary* r_retSignals) + { + // Performance is not critical here as this will be replaced with source generators. + using var signals = new Dictionary(); + + Type top = _scriptBridgeMap[scriptPtr]; + Type native = Object.InternalGetClassNativeBase(top); + + while (top != null && top != native) + { + // Legacy signals + + foreach (var signalDelegate in top + .GetNestedTypes(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public) + .Where(nestedType => typeof(Delegate).IsAssignableFrom(nestedType)) + .Where(@delegate => @delegate.GetCustomAttributes().OfType<SignalAttribute>().Any())) + { + var invokeMethod = signalDelegate.GetMethod("Invoke"); + + if (invokeMethod == null) + throw new MissingMethodException(signalDelegate.FullName, "Invoke"); + + var signalParams = new Collections.Array(); + + foreach (var parameters in invokeMethod.GetParameters()) + { + var paramType = Marshaling.managed_to_variant_type( + parameters.ParameterType, out bool nilIsVariant); + signalParams.Add(new Dictionary() + { + { "name", parameters.Name }, + { "type", paramType }, + { "nil_is_variant", nilIsVariant } + }); + } + + signals.Add(signalDelegate.Name, signalParams); + } + + // Event signals + + var foundEventSignals = top.GetEvents( + BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public) + .Where(ev => ev.GetCustomAttributes().OfType<SignalAttribute>().Any()) + .Select(ev => ev.Name); + + var fields = top.GetFields( + BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public); + + foreach (var eventSignalField in fields + .Where(f => typeof(Delegate).IsAssignableFrom(f.FieldType)) + .Where(f => foundEventSignals.Contains(f.Name))) + { + var delegateType = eventSignalField.FieldType; + var invokeMethod = delegateType.GetMethod("Invoke"); + + if (invokeMethod == null) + throw new MissingMethodException(delegateType.FullName, "Invoke"); + + var signalParams = new Collections.Array(); + + foreach (var parameters in invokeMethod.GetParameters()) + { + var paramType = Marshaling.managed_to_variant_type( + parameters.ParameterType, out bool nilIsVariant); + signalParams.Add(new Dictionary() + { + { "name", parameters.Name }, + { "type", paramType }, + { "nil_is_variant", nilIsVariant } + }); + } + + signals.Add(eventSignalField.Name, signalParams); + } + + top = top.BaseType; + } + + *r_retSignals = NativeFuncs.godotsharp_dictionary_new_copy(signals.NativeValue); + } + + internal static unsafe bool HasScriptSignal(IntPtr scriptPtr, godot_string* signalName) + { + // Performance is not critical here as this will be replaced with source generators. + using var signals = new Dictionary(); + + string signalNameStr = Marshaling.mono_string_from_godot(*signalName); + + Type top = _scriptBridgeMap[scriptPtr]; + Type native = Object.InternalGetClassNativeBase(top); + + while (top != null && top != native) + { + // Legacy signals + + if (top + .GetNestedTypes(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public) + .Where(nestedType => typeof(Delegate).IsAssignableFrom(nestedType)) + .Where(@delegate => @delegate.GetCustomAttributes().OfType<SignalAttribute>().Any()) + .Any(signalDelegate => signalDelegate.Name == signalNameStr) + ) + { + return true; + } + + // Event signals + + if (top.GetEvents( + BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public) + .Where(ev => ev.GetCustomAttributes().OfType<SignalAttribute>().Any()) + .Any(eventSignal => eventSignal.Name == signalNameStr) + ) + { + return true; + } + + top = top.BaseType; + } + + return false; + } + + internal static unsafe bool HasMethodUnknownParams(IntPtr scriptPtr, godot_string* method, bool deep) + { + // Performance is not critical here as this will be replaced with source generators. + if (!_scriptBridgeMap.TryGetValue(scriptPtr, out var scriptType)) + return false; + + string methodStr = Marshaling.mono_string_from_godot(*method); + + if (deep) + { + Type top = scriptType; + Type native = Object.InternalGetClassNativeBase(scriptType); + + while (top != null && top != native) + { + var methodInfo = top.GetMethod(methodStr, + BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public); + + if (methodInfo != null) + return true; + + top = top.BaseType; + } + + return false; + } + else + { + var methodInfo = scriptType.GetMethod(methodStr, BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public); + return methodInfo != null; + } + } + + internal static bool ScriptIsOrInherits(IntPtr scriptPtr, IntPtr scriptPtrMaybeBase) + { + if (!_scriptBridgeMap.TryGetValue(scriptPtr, out var scriptType)) + return false; + + if (!_scriptBridgeMap.TryGetValue(scriptPtrMaybeBase, out var maybeBaseType)) + return false; + + return scriptType == maybeBaseType || maybeBaseType.IsAssignableFrom(scriptType); + } + + internal static unsafe bool AddScriptBridge(IntPtr scriptPtr, godot_string* scriptPath) + { + string scriptPathStr = Marshaling.mono_string_from_godot(*scriptPath); + + if (!_scriptLookupMap.TryGetValue(scriptPathStr, out var lookupInfo)) + return false; + + _scriptBridgeMap.Add(scriptPtr, lookupInfo.ScriptType); + + return true; + } + + internal static void AddScriptBridgeWithType(IntPtr scriptPtr, Type scriptType) + => _scriptBridgeMap.Add(scriptPtr, scriptType); + + internal static void RemoveScriptBridge(IntPtr scriptPtr) + => _scriptBridgeMap.Remove(scriptPtr); + + internal static unsafe void UpdateScriptClassInfo(IntPtr scriptPtr, bool* r_tool, + godot_dictionary* r_rpcFunctionsDest) + { + // Performance is not critical here as this will be replaced with source generators. + var scriptType = _scriptBridgeMap[scriptPtr]; + + *r_tool = scriptType.GetCustomAttributes(inherit: false) + .OfType<ToolAttribute>() + .Any(); + + if (!*r_tool && scriptType.IsNested) + { + *r_tool = scriptType.DeclaringType?.GetCustomAttributes(inherit: false) + .OfType<ToolAttribute>() + .Any() ?? false; + } + + if (!*r_tool && scriptType.Assembly.GetName().Name == "GodotTools") + *r_tool = true; + + // RPC functions + + Dictionary<string, Dictionary> rpcFunctions = new(); + + Type top = _scriptBridgeMap[scriptPtr]; + Type native = Object.InternalGetClassNativeBase(top); + + while (top != null && top != native) + { + foreach (var method in top.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public)) + { + if (method.IsStatic) + continue; + + string methodName = method.Name; + + if (rpcFunctions.ContainsKey(methodName)) + continue; + + var rpcAttr = method.GetCustomAttributes(inherit: false) + .OfType<RPCAttribute>().FirstOrDefault(); + + if (rpcAttr == null) + continue; + + var rpcConfig = new Dictionary(); + + rpcConfig["rpc_mode"] = (long)rpcAttr.Mode; + rpcConfig["call_local"] = rpcAttr.CallLocal; + rpcConfig["transfer_mode"] = (long)rpcAttr.TransferMode; + rpcConfig["channel"] = rpcAttr.TransferChannel; + + rpcFunctions.Add(methodName, rpcConfig); + } + + top = top.BaseType; + } + + *r_rpcFunctionsDest = NativeFuncs.godotsharp_dictionary_new_copy(((Dictionary)rpcFunctions).NativeValue); + } + + internal static unsafe bool SwapGCHandleForType(IntPtr oldGCHandlePtr, IntPtr* r_newGCHandlePtr, + bool createWeak) + { + var oldGCHandle = GCHandle.FromIntPtr(oldGCHandlePtr); + + object target = oldGCHandle.Target; + + if (target == null) + { + oldGCHandle.Free(); + *r_newGCHandlePtr = IntPtr.Zero; + 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. + var newGCHandle = GCHandle.Alloc(target, createWeak ? GCHandleType.Weak : GCHandleType.Normal); + + oldGCHandle.Free(); + *r_newGCHandlePtr = GCHandle.ToIntPtr(newGCHandle); + return true; + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs index 187d910f9f..2a562d4d48 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs @@ -11,10 +11,6 @@ namespace Godot { internal static class DelegateUtils { - // TODO: Move somewhere else once we need to for things other than delegates - internal static void FreeGCHandle(IntPtr delegateGCHandle) - => GCHandle.FromIntPtr(delegateGCHandle).Free(); - internal static bool DelegateEquals(IntPtr delegateGCHandleA, IntPtr delegateGCHandleB) { var @delegateA = (Delegate)GCHandle.FromIntPtr(delegateGCHandleA).Target; @@ -22,14 +18,14 @@ namespace Godot return @delegateA == @delegateB; } - internal static unsafe void InvokeWithVariantArgs(IntPtr delegateGCHandle, godot_variant** args, uint argc, godot_variant* ret) + internal static unsafe void InvokeWithVariantArgs(IntPtr delegateGCHandle, godot_variant** args, uint argc, + godot_variant* ret) { // TODO: Optimize var @delegate = (Delegate)GCHandle.FromIntPtr(delegateGCHandle).Target; var managedArgs = new object[argc]; var parameterInfos = @delegate.Method.GetParameters(); - var paramsLength = parameterInfos.Length; if (argc != paramsLength) @@ -260,7 +256,8 @@ namespace Godot } } - private static bool TryDeserializeDelegateWithGCHandle(Collections.Array serializedData, out IntPtr delegateGCHandle) + private static bool TryDeserializeDelegateWithGCHandle(Collections.Array serializedData, + out IntPtr delegateGCHandle) { bool res = TryDeserializeDelegate(serializedData, out Delegate @delegate); delegateGCHandle = GCHandle.ToIntPtr(GCHandle.Alloc(@delegate)); @@ -368,7 +365,8 @@ namespace Godot int valueBufferLength = reader.ReadInt32(); byte[] valueBuffer = reader.ReadBytes(valueBufferLength); - FieldInfo fieldInfo = targetType.GetField(name, BindingFlags.Instance | BindingFlags.Public); + FieldInfo fieldInfo = + targetType.GetField(name, BindingFlags.Instance | BindingFlags.Public); fieldInfo?.SetValue(recreatedTarget, GD.Bytes2Var(valueBuffer)); } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs index 932ee33fe3..8bc33837e6 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs @@ -15,7 +15,7 @@ namespace Godot.Collections IDictionary, IDisposable { - internal godot_dictionary NativeValue; + public godot_dictionary NativeValue; /// <summary> /// Constructs a new empty <see cref="Dictionary"/>. @@ -319,7 +319,7 @@ namespace Godot.Collections { using godot_string str = default; NativeFuncs.godotsharp_dictionary_to_string(ref NativeValue, &str); - return Marshaling.mono_string_from_godot(&str); + return Marshaling.mono_string_from_godot(str); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs index 7922f38ac5..b939da8778 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs @@ -1,4 +1,4 @@ -using System; +using System.Reflection; using System.Runtime.CompilerServices; using Godot.Collections; using Godot.NativeInterop; @@ -11,14 +11,64 @@ namespace Godot /// Returns a list of all nodes assigned to the given <paramref name="group"/>. /// </summary> /// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam> - public Array<T> GetNodesInGroup<T>(StringName group) where T : class + public unsafe Array<T> GetNodesInGroup<T>(StringName group) where T : class { - godot_array array; - godot_icall_SceneTree_get_nodes_in_group_Generic(GetPtr(this), ref group.NativeValue, typeof(T), out array); - return Array<T>.CreateTakingOwnershipOfDisposableValue(array); + var array = GetNodesInGroup(group); + + if (array.Count == 0) + return new Array<T>(array); + + var typeOfT = typeof(T); + bool nativeBase = InternalIsClassNativeBase(typeOfT); + + if (nativeBase) + { + // Native type + var field = typeOfT.GetField("NativeName", BindingFlags.DeclaredOnly | BindingFlags.Static | + BindingFlags.Public | BindingFlags.NonPublic); + + var nativeName = (StringName)field!.GetValue(null); + godot_string_name nativeNameAux = nativeName.NativeValue; + godot_array inputAux = array.NativeValue; + godot_array filteredArray; + godotsharp_array_filter_godot_objects_by_native(&nativeNameAux, &inputAux, &filteredArray); + return Array<T>.CreateTakingOwnershipOfDisposableValue(filteredArray); + } + else + { + // Custom derived type + godot_array inputAux = array.NativeValue; + godot_array filteredArray; + godotsharp_array_filter_godot_objects_by_non_native(&inputAux, &filteredArray); + + var filteredArrayWrapped = Array.CreateTakingOwnershipOfDisposableValue(filteredArray); + + // Re-use first array as its size is the same or greater than the filtered one + var resWrapped = new Array<T>(array); + + int j = 0; + for (int i = 0; i < filteredArrayWrapped.Count; i++) + { + if (filteredArrayWrapped[i] is T t) + { + resWrapped[j] = t; + j++; + } + } + + // Remove trailing elements, since this was re-used + resWrapped.Resize(j); + + return resWrapped; + } } [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_SceneTree_get_nodes_in_group_Generic(IntPtr obj, ref godot_string_name group, Type elemType, out godot_array dest); + internal extern unsafe void godotsharp_array_filter_godot_objects_by_native(godot_string_name* p_native_name, + godot_array* p_input, godot_array* r_output); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern unsafe void godotsharp_array_filter_godot_objects_by_non_native(godot_array* p_input, + godot_array* r_output); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs index 9d237b8d93..f428100ff7 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs @@ -525,7 +525,7 @@ namespace Godot 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); + return Marshaling.mono_string_from_godot(ret); } /// <summary> @@ -588,7 +588,7 @@ namespace Godot 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); + return Marshaling.mono_string_from_godot(ret); } /// <summary> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTraceListener.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTraceListener.cs index 9ccac1faaf..78a9d0fe9d 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTraceListener.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTraceListener.cs @@ -17,10 +17,7 @@ namespace Godot public override void Fail(string message, string detailMessage) { GD.PrintErr("Assertion failed: ", message); - if (detailMessage != null) - { - GD.PrintErr(" Details: ", detailMessage); - } + GD.PrintErr(" Details: ", detailMessage); try { diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs index 865863cd3e..d8931f8348 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs @@ -13,7 +13,7 @@ namespace Godot.NativeInterop { [StructLayout(LayoutKind.Sequential)] // ReSharper disable once InconsistentNaming - internal struct godot_bool + public struct godot_bool { public byte _value; @@ -25,7 +25,7 @@ namespace Godot.NativeInterop [StructLayout(LayoutKind.Sequential)] // ReSharper disable once InconsistentNaming - internal struct godot_ref : IDisposable + public struct godot_ref : IDisposable { internal IntPtr _reference; @@ -41,7 +41,7 @@ namespace Godot.NativeInterop } [SuppressMessage("ReSharper", "InconsistentNaming")] - internal enum godot_variant_call_error_error + public enum godot_variant_call_error_error { GODOT_CALL_ERROR_CALL_OK = 0, GODOT_CALL_ERROR_CALL_ERROR_INVALID_METHOD, @@ -53,16 +53,16 @@ namespace Godot.NativeInterop [StructLayout(LayoutKind.Sequential)] // ReSharper disable once InconsistentNaming - internal struct godot_variant_call_error + public struct godot_variant_call_error { - godot_variant_call_error_error error; - int argument; - int expected; + public godot_variant_call_error_error error; + public int argument; + public int expected; } [StructLayout(LayoutKind.Explicit)] // ReSharper disable once InconsistentNaming - internal struct godot_variant : IDisposable + public struct godot_variant : IDisposable { // Variant.Type is generated as an enum of type long, so we can't use for the field as it must only take 32-bits. [FieldOffset(0)] private int _typeField; @@ -162,7 +162,7 @@ namespace Godot.NativeInterop [StructLayout(LayoutKind.Sequential)] // ReSharper disable once InconsistentNaming - internal struct godot_string : IDisposable + public struct godot_string : IDisposable { internal IntPtr _ptr; @@ -180,7 +180,7 @@ namespace Godot.NativeInterop [StructLayout(LayoutKind.Sequential)] // ReSharper disable once InconsistentNaming - internal struct godot_string_name : IDisposable + public struct godot_string_name : IDisposable { internal IntPtr _data; @@ -201,7 +201,7 @@ namespace Godot.NativeInterop [StructLayout(LayoutKind.Sequential)] // ReSharper disable once InconsistentNaming - internal struct godot_node_path : IDisposable + public struct godot_node_path : IDisposable { internal IntPtr _data; @@ -222,7 +222,7 @@ namespace Godot.NativeInterop [StructLayout(LayoutKind.Explicit)] // ReSharper disable once InconsistentNaming - internal struct godot_signal : IDisposable + public struct godot_signal : IDisposable { [FieldOffset(0)] public godot_string_name _name; @@ -241,7 +241,7 @@ namespace Godot.NativeInterop [StructLayout(LayoutKind.Explicit)] // ReSharper disable once InconsistentNaming - internal struct godot_callable : IDisposable + public struct godot_callable : IDisposable { [FieldOffset(0)] public godot_string_name _method; @@ -265,7 +265,7 @@ namespace Godot.NativeInterop // be re-assigned a new value (the copy constructor checks if `_p` is null so that's fine). [StructLayout(LayoutKind.Sequential)] // ReSharper disable once InconsistentNaming - internal struct godot_array : IDisposable + public struct godot_array : IDisposable { internal unsafe ArrayPrivate* _p; @@ -304,7 +304,7 @@ namespace Godot.NativeInterop // be re-assigned a new value (the copy constructor checks if `_p` is null so that's fine). [StructLayout(LayoutKind.Sequential)] // ReSharper disable once InconsistentNaming - internal struct godot_dictionary : IDisposable + public struct godot_dictionary : IDisposable { internal IntPtr _p; @@ -319,7 +319,7 @@ namespace Godot.NativeInterop [StructLayout(LayoutKind.Sequential)] // ReSharper disable once InconsistentNaming - internal struct godot_packed_byte_array : IDisposable + public struct godot_packed_byte_array : IDisposable { internal IntPtr _writeProxy; internal unsafe byte* _ptr; @@ -337,7 +337,7 @@ namespace Godot.NativeInterop [StructLayout(LayoutKind.Sequential)] // ReSharper disable once InconsistentNaming - internal struct godot_packed_int32_array : IDisposable + public struct godot_packed_int32_array : IDisposable { internal IntPtr _writeProxy; internal unsafe int* _ptr; @@ -355,7 +355,7 @@ namespace Godot.NativeInterop [StructLayout(LayoutKind.Sequential)] // ReSharper disable once InconsistentNaming - internal struct godot_packed_int64_array : IDisposable + public struct godot_packed_int64_array : IDisposable { internal IntPtr _writeProxy; internal unsafe long* _ptr; @@ -373,7 +373,7 @@ namespace Godot.NativeInterop [StructLayout(LayoutKind.Sequential)] // ReSharper disable once InconsistentNaming - internal struct godot_packed_float32_array : IDisposable + public struct godot_packed_float32_array : IDisposable { internal IntPtr _writeProxy; internal unsafe float* _ptr; @@ -391,7 +391,7 @@ namespace Godot.NativeInterop [StructLayout(LayoutKind.Sequential)] // ReSharper disable once InconsistentNaming - internal struct godot_packed_float64_array : IDisposable + public struct godot_packed_float64_array : IDisposable { internal IntPtr _writeProxy; internal unsafe double* _ptr; @@ -409,7 +409,7 @@ namespace Godot.NativeInterop [StructLayout(LayoutKind.Sequential)] // ReSharper disable once InconsistentNaming - internal struct godot_packed_string_array : IDisposable + public struct godot_packed_string_array : IDisposable { internal IntPtr _writeProxy; internal unsafe godot_string* _ptr; @@ -427,7 +427,7 @@ namespace Godot.NativeInterop [StructLayout(LayoutKind.Sequential)] // ReSharper disable once InconsistentNaming - internal struct godot_packed_vector2_array : IDisposable + public struct godot_packed_vector2_array : IDisposable { internal IntPtr _writeProxy; internal unsafe Vector2* _ptr; @@ -445,7 +445,7 @@ namespace Godot.NativeInterop [StructLayout(LayoutKind.Sequential)] // ReSharper disable once InconsistentNaming - internal struct godot_packed_vector3_array : IDisposable + public struct godot_packed_vector3_array : IDisposable { internal IntPtr _writeProxy; internal unsafe Vector3* _ptr; @@ -463,7 +463,7 @@ namespace Godot.NativeInterop [StructLayout(LayoutKind.Sequential)] // ReSharper disable once InconsistentNaming - internal struct godot_packed_color_array : IDisposable + public struct godot_packed_color_array : IDisposable { internal IntPtr _writeProxy; internal unsafe Color* _ptr; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs index 08d49bb937..5d53006140 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs @@ -1,5 +1,9 @@ using System; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Godot.Bridge; + +// ReSharper disable InconsistentNaming namespace Godot.NativeInterop { @@ -7,21 +11,105 @@ namespace Godot.NativeInterop { public static Object UnmanagedGetManaged(IntPtr unmanaged) { - // TODO: Move to C# - return internal_unmanaged_get_managed(unmanaged); + // The native pointer may be null + if (unmanaged == IntPtr.Zero) + return null; + + IntPtr gcHandlePtr; + bool has_cs_script_instance = false; + + // First try to get the tied managed instance from a CSharpInstance script instance + + unsafe + { + gcHandlePtr = unmanaged_get_script_instance_managed(unmanaged, &has_cs_script_instance); + } + + if (gcHandlePtr != IntPtr.Zero) + return (Object)GCHandle.FromIntPtr(gcHandlePtr).Target; + + // Otherwise, if the object has a CSharpInstance script instance, return null + + if (has_cs_script_instance) + return null; + + // If it doesn't have a CSharpInstance script instance, try with native instance bindings + + gcHandlePtr = unmanaged_get_instance_binding_managed(unmanaged); + + object target = gcHandlePtr != IntPtr.Zero ? GCHandle.FromIntPtr(gcHandlePtr).Target : null; + + if (target != null) + return (Object)target; + + // If the native instance binding GC handle target was collected, create a new one + + gcHandlePtr = unmanaged_instance_binding_create_managed(unmanaged, gcHandlePtr); + + return gcHandlePtr != IntPtr.Zero ? (Object)GCHandle.FromIntPtr(gcHandlePtr).Target : null; } [MethodImpl(MethodImplOptions.InternalCall)] - private static extern Object internal_unmanaged_get_managed(IntPtr unmanaged); + private static extern unsafe IntPtr unmanaged_get_script_instance_managed(IntPtr p_unmanaged, + bool* r_has_cs_script_instance); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern IntPtr unmanaged_get_instance_binding_managed(IntPtr p_unmanaged); - public static void TieManagedToUnmanaged(Object managed, IntPtr unmanaged) + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern IntPtr unmanaged_instance_binding_create_managed(IntPtr p_unmanaged, + IntPtr oldGCHandlePtr); + + public static void TieManagedToUnmanaged(Object managed, IntPtr unmanaged, + StringName nativeName, bool refCounted, Type type, Type nativeType) { - // TODO: Move to C# - internal_tie_managed_to_unmanaged(managed, unmanaged); + var gcHandle = GCHandle.Alloc(managed, refCounted ? GCHandleType.Weak : GCHandleType.Normal); + + if (type == nativeType) + { + unsafe + { + godot_string_name nativeNameAux = nativeName.NativeValue; + internal_tie_native_managed_to_unmanaged(GCHandle.ToIntPtr(gcHandle), unmanaged, + &nativeNameAux, refCounted); + } + } + else + { + IntPtr scriptPtr = internal_new_csharp_script(); + + ScriptManagerBridge.AddScriptBridgeWithType(scriptPtr, type); + + // IMPORTANT: This must be called after AddScriptWithTypeBridge + internal_tie_user_managed_to_unmanaged(GCHandle.ToIntPtr(gcHandle), unmanaged, + scriptPtr, refCounted); + } } [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void internal_tie_managed_to_unmanaged(Object managed, IntPtr unmanaged); + private static extern unsafe void internal_tie_native_managed_to_unmanaged(IntPtr gcHandleIntPtr, + IntPtr unmanaged, godot_string_name* nativeName, bool refCounted); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern void internal_tie_user_managed_to_unmanaged(IntPtr gcHandleIntPtr, + IntPtr unmanaged, IntPtr scriptPtr, bool refCounted); + + public static void TieManagedToUnmanagedWithPreSetup(Object managed, IntPtr unmanaged, + Type type, Type nativeType) + { + if (type == nativeType) + return; + + var strongGCHandle = GCHandle.Alloc(managed, GCHandleType.Normal); + internal_tie_managed_to_unmanaged_with_pre_setup(GCHandle.ToIntPtr(strongGCHandle), unmanaged); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern void internal_tie_managed_to_unmanaged_with_pre_setup( + IntPtr gcHandleIntPtr, IntPtr unmanaged); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern IntPtr internal_new_csharp_script(); public static unsafe Object EngineGetSingleton(string name) { diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs index e0819b2857..eae644af85 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs @@ -10,14 +10,8 @@ namespace Godot.NativeInterop { // We want to use full name qualifiers here even if redundant for clarity [SuppressMessage("ReSharper", "RedundantNameQualifier")] - internal static class Marshaling + public static class Marshaling { - public static unsafe void SetFieldValue(FieldInfo fieldInfo, object obj, godot_variant* value) - { - var valueObj = variant_to_mono_object_of_type(value, fieldInfo.FieldType); - fieldInfo.SetValue(obj, valueObj); - } - public static Variant.Type managed_to_variant_type(Type type, out bool r_nil_is_variant) { r_nil_is_variant = false; @@ -242,10 +236,6 @@ namespace Godot.NativeInterop return mono_object_to_variant_impl(p_obj); } - // TODO: Only called from C++. Remove once no longer needed. - private static unsafe void mono_object_to_variant_out(object p_obj, bool p_fail_with_err, godot_variant* r_ret) - => *r_ret = mono_object_to_variant_impl(p_obj, p_fail_with_err); - private static unsafe godot_variant mono_object_to_variant_impl(object p_obj, bool p_fail_with_err = true) { if (p_obj == null) @@ -457,7 +447,7 @@ 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)); + (godot_array)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. @@ -500,12 +490,12 @@ namespace Godot.NativeInterop case Variant.Type.String: { // We avoid the internal call if the stored type is the same we want. - return mono_string_from_godot(&(*p_var)._data._m_string); + return mono_string_from_godot((*p_var)._data._m_string); } default: { using godot_string godotString = NativeFuncs.godotsharp_variant_as_string(p_var); - return mono_string_from_godot(&godotString); + return mono_string_from_godot(godotString); } } } @@ -877,7 +867,7 @@ namespace Godot.NativeInterop #endif } case Variant.Type.String: - return mono_string_from_godot(&(*p_var)._data._m_string); + return mono_string_from_godot((*p_var)._data._m_string); case Variant.Type.Vector2: return (*p_var)._data._m_vector2; case Variant.Type.Vector2i: @@ -1007,14 +997,14 @@ namespace Godot.NativeInterop } } - public static unsafe string mono_string_from_godot(godot_string* p_string) + public static unsafe string mono_string_from_godot(in godot_string p_string) { - if ((*p_string)._ptr == IntPtr.Zero) + if (p_string._ptr == IntPtr.Zero) return string.Empty; const int sizeOfChar32 = 4; - byte* bytes = (byte*)(*p_string)._ptr; - int size = (*p_string).Size; + byte* bytes = (byte*)p_string._ptr; + int size = p_string.Size; if (size == 0) return string.Empty; size -= 1; // zero at the end @@ -1283,7 +1273,7 @@ namespace Godot.NativeInterop 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]); + array[i] = mono_string_from_godot(buffer[i]); return array; } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs index adbf5eb9b6..73ac837fe1 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs @@ -10,7 +10,7 @@ namespace Godot.NativeInterop // The attribute is not available with .NET Core and it's not needed there. [System.Security.SuppressUnmanagedCodeSecurity] #endif - internal static unsafe partial class NativeFuncs + public static unsafe partial class NativeFuncs { private const string GodotDllName = "__Internal"; 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 6001b3a0de..089883c7e8 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs @@ -5,7 +5,7 @@ using System.Runtime.CompilerServices; namespace Godot.NativeInterop { - internal static unsafe partial class NativeFuncs + public static unsafe partial class NativeFuncs { public static godot_string_name godotsharp_string_name_new_copy(godot_string_name* src) { diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs index 67f9e23893..91ba864687 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs @@ -5,7 +5,7 @@ using System.Runtime.CompilerServices; namespace Godot.NativeInterop { - internal static class VariantUtils + public static class VariantUtils { public static godot_variant CreateFromRID(RID from) => new() {_type = Variant.Type.Rid, _data = {_m_rid = from}}; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs index 541364b281..824f29558f 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs @@ -42,7 +42,7 @@ namespace Godot /// </example> public sealed class NodePath : IDisposable { - internal godot_node_path NativeValue; + public godot_node_path NativeValue; ~NodePath() { @@ -140,7 +140,7 @@ namespace Godot godot_node_path src = NativeValue; NativeFuncs.godotsharp_node_path_as_string(&dest, &src); using (dest) - return Marshaling.mono_string_from_godot(&dest); + return Marshaling.mono_string_from_godot(dest); } /// <summary> @@ -179,7 +179,7 @@ namespace Godot { using godot_string names = default; NativeFuncs.godotsharp_node_path_get_concatenated_names(ref NativeValue, &names); - return Marshaling.mono_string_from_godot(&names); + return Marshaling.mono_string_from_godot(names); } /// <summary> @@ -197,7 +197,7 @@ namespace Godot { using godot_string subNames = default; NativeFuncs.godotsharp_node_path_get_concatenated_subnames(ref NativeValue, &subNames); - return Marshaling.mono_string_from_godot(&subNames); + return Marshaling.mono_string_from_godot(subNames); } /// <summary> @@ -217,7 +217,7 @@ namespace Godot { using godot_string name = default; NativeFuncs.godotsharp_node_path_get_name(ref NativeValue, idx, &name); - return Marshaling.mono_string_from_godot(&name); + return Marshaling.mono_string_from_godot(name); } /// <summary> @@ -240,7 +240,7 @@ namespace Godot { using godot_string subName = default; NativeFuncs.godotsharp_node_path_get_subname(ref NativeValue, idx, &subName); - return Marshaling.mono_string_from_godot(&subName); + return Marshaling.mono_string_from_godot(subName); } /// <summary> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs index 7bbaef62fa..763483a11f 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs @@ -1,4 +1,6 @@ using System; +using System.Linq; +using System.Reflection; using System.Runtime.CompilerServices; using Godot.NativeInterop; @@ -7,6 +9,7 @@ namespace Godot public partial class Object : IDisposable { private bool _disposed = false; + private Type _cachedType = typeof(Object); internal IntPtr NativePtr; internal bool MemoryOwn; @@ -21,12 +24,18 @@ namespace Godot #if NET unsafe { - ptr = NativeCtor(); + NativePtr = NativeCtor(); } #else NativePtr = _gd__invoke_class_constructor(NativeCtor); #endif - NativeInterop.InteropUtils.TieManagedToUnmanaged(this, NativePtr); + InteropUtils.TieManagedToUnmanaged(this, NativePtr, + NativeName, refCounted: false, GetType(), _cachedType); + } + else + { + InteropUtils.TieManagedToUnmanagedWithPreSetup(this, NativePtr, + GetType(), _cachedType); } _InitializeGodotScriptInstanceInternals(); @@ -34,12 +43,32 @@ namespace Godot internal void _InitializeGodotScriptInstanceInternals() { - godot_icall_Object_ConnectEventSignals(NativePtr); + // Performance is not critical here as this will be replaced with source generators. + Type top = GetType(); + Type native = InternalGetClassNativeBase(top); + + while (top != null && top != native) + { + foreach (var eventSignal in top.GetEvents( + BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public) + .Where(ev => ev.GetCustomAttributes().OfType<SignalAttribute>().Any())) + { + unsafe + { + using var eventSignalName = new StringName(eventSignal.Name); + godot_string_name eventSignalNameAux = eventSignalName.NativeValue; + godot_icall_Object_ConnectEventSignal(NativePtr, &eventSignalNameAux); + } + } + + top = top.BaseType; + } } internal Object(bool memoryOwn) { - this.MemoryOwn = memoryOwn; + MemoryOwn = memoryOwn; } /// <summary> @@ -85,11 +114,11 @@ namespace Godot if (MemoryOwn) { MemoryOwn = false; - godot_icall_RefCounted_Disposed(this, NativePtr, !disposing); + godot_icall_RefCounted_Disposed(NativePtr, !disposing); } else { - godot_icall_Object_Disposed(this, NativePtr); + godot_icall_Object_Disposed(NativePtr); } this.NativePtr = IntPtr.Zero; @@ -106,7 +135,7 @@ namespace Godot { using godot_string str = default; NativeFuncs.godotsharp_object_to_string(GetPtr(this), &str); - return Marshaling.mono_string_from_godot(&str); + return Marshaling.mono_string_from_godot(str); } /// <summary> @@ -141,13 +170,219 @@ namespace Godot return new SignalAwaiter(source, signal, this); } + internal static Type InternalGetClassNativeBase(Type t) + { + do + { + var assemblyName = t.Assembly.GetName(); + + if (assemblyName.Name == "GodotSharp") + return t; + + if (assemblyName.Name == "GodotSharpEditor") + return t; + } while ((t = t.BaseType) != null); + + return null; + } + + internal static bool InternalIsClassNativeBase(Type t) + { + var assemblyName = t.Assembly.GetName(); + return assemblyName.Name == "GodotSharp" || assemblyName.Name == "GodotSharpEditor"; + } + + internal unsafe bool InternalGodotScriptCallViaReflection(string method, godot_variant** args, int argCount, + out godot_variant ret) + { + // Performance is not critical here as this will be replaced with source generators. + Type top = GetType(); + Type native = InternalGetClassNativeBase(top); + + while (top != null && top != native) + { + var methodInfo = top.GetMethod(method, + BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public); + + if (methodInfo != null) + { + var parameters = methodInfo.GetParameters(); + int paramCount = parameters.Length; + + if (argCount == paramCount) + { + object[] invokeParams = new object[paramCount]; + + for (int i = 0; i < paramCount; i++) + { + invokeParams[i] = Marshaling.variant_to_mono_object_of_type( + args[i], parameters[i].ParameterType); + } + + object retObj = methodInfo.Invoke(this, invokeParams); + + ret = Marshaling.mono_object_to_variant(retObj); + return true; + } + } + + top = top.BaseType; + } + + ret = default; + return false; + } + + internal unsafe bool InternalGodotScriptSetFieldOrPropViaReflection(string name, godot_variant* value) + { + // Performance is not critical here as this will be replaced with source generators. + Type top = GetType(); + Type native = InternalGetClassNativeBase(top); + + while (top != null && top != native) + { + var fieldInfo = top.GetField(name, + BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public); + + if (fieldInfo != null) + { + object valueManaged = Marshaling.variant_to_mono_object_of_type(value, fieldInfo.FieldType); + fieldInfo.SetValue(this, valueManaged); + + return true; + } + + var propertyInfo = top.GetProperty(name, + BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public); + + if (propertyInfo != null) + { + object valueManaged = Marshaling.variant_to_mono_object_of_type(value, propertyInfo.PropertyType); + propertyInfo.SetValue(this, valueManaged); + + return true; + } + + top = top.BaseType; + } + + return false; + } + + internal bool InternalGodotScriptGetFieldOrPropViaReflection(string name, out godot_variant value) + { + // Performance is not critical here as this will be replaced with source generators. + Type top = GetType(); + Type native = InternalGetClassNativeBase(top); + + while (top != null && top != native) + { + var fieldInfo = top.GetField(name, + BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public); + + if (fieldInfo != null) + { + object valueManaged = fieldInfo.GetValue(this); + value = Marshaling.mono_object_to_variant(valueManaged); + return true; + } + + var propertyInfo = top.GetProperty(name, + BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public); + + if (propertyInfo != null) + { + object valueManaged = propertyInfo.GetValue(this); + value = Marshaling.mono_object_to_variant(valueManaged); + return true; + } + + top = top.BaseType; + } + + value = default; + return false; + } + + internal unsafe void InternalRaiseEventSignal(godot_string_name* eventSignalName, godot_variant** args, + int argc) + { + // Performance is not critical here as this will be replaced with source generators. + + using var stringName = StringName.CreateTakingOwnershipOfDisposableValue( + NativeFuncs.godotsharp_string_name_new_copy(eventSignalName)); + string eventSignalNameStr = stringName.ToString(); + + Type top = GetType(); + Type native = InternalGetClassNativeBase(top); + + while (top != null && top != native) + { + var foundEventSignals = top.GetEvents( + BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public) + .Where(ev => ev.GetCustomAttributes().OfType<SignalAttribute>().Any()) + .Select(ev => ev.Name); + + var fields = top.GetFields( + BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public); + + var eventSignalField = fields + .Where(f => typeof(Delegate).IsAssignableFrom(f.FieldType)) + .Where(f => foundEventSignals.Contains(f.Name)) + .FirstOrDefault(f => f.Name == eventSignalNameStr); + + if (eventSignalField != null) + { + var @delegate = (Delegate)eventSignalField.GetValue(this); + + if (@delegate == null) + continue; + + var delegateType = eventSignalField.FieldType; + + var invokeMethod = delegateType.GetMethod("Invoke"); + + if (invokeMethod == null) + throw new MissingMethodException(delegateType.FullName, "Invoke"); + + var parameterInfos = invokeMethod.GetParameters(); + var paramsLength = parameterInfos.Length; + + if (argc != paramsLength) + { + throw new InvalidOperationException( + $"The event delegate expects {paramsLength} arguments, but received {argc}."); + } + + var managedArgs = new object[argc]; + + for (uint i = 0; i < argc; i++) + { + managedArgs[i] = Marshaling.variant_to_mono_object_of_type( + args[i], parameterInfos[i].ParameterType); + } + + invokeMethod.Invoke(@delegate, managedArgs); + return; + } + + top = top.BaseType; + } + } + internal static unsafe IntPtr ClassDB_get_method(StringName type, string method) { IntPtr methodBind; fixed (char* methodChars = method) { - methodBind = NativeInterop.NativeFuncs - .godotsharp_method_bind_get_method(ref type.NativeValue, methodChars); + methodBind = NativeFuncs.godotsharp_method_bind_get_method(ref type.NativeValue, methodChars); } if (methodBind == IntPtr.Zero) @@ -157,11 +392,10 @@ namespace Godot } #if NET - internal static unsafe delegate* unmanaged<IntPtr> _gd__ClassDB_get_constructor(StringName type) + internal static unsafe delegate* unmanaged<IntPtr> ClassDB_get_constructor(StringName type) { // for some reason the '??' operator doesn't support 'delegate*' - var nativeConstructor = NativeInterop.NativeFuncs - .godotsharp_get_class_constructor(ref type.NativeValue); + var nativeConstructor = NativeFuncs.godotsharp_get_class_constructor(ref type.NativeValue); if (nativeConstructor == null) throw new NativeConstructorNotFoundException(type); @@ -172,8 +406,7 @@ namespace Godot internal static IntPtr ClassDB_get_constructor(StringName type) { // for some reason the '??' operator doesn't support 'delegate*' - var nativeConstructor = NativeInterop.NativeFuncs - .godotsharp_get_class_constructor(ref type.NativeValue); + var nativeConstructor = NativeFuncs.godotsharp_get_class_constructor(ref type.NativeValue); if (nativeConstructor == IntPtr.Zero) throw new NativeConstructorNotFoundException(type); @@ -182,16 +415,17 @@ namespace Godot } internal static IntPtr _gd__invoke_class_constructor(IntPtr ctorFuncPtr) - => NativeInterop.NativeFuncs.godotsharp_invoke_class_constructor(ctorFuncPtr); + => NativeFuncs.godotsharp_invoke_class_constructor(ctorFuncPtr); #endif [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Object_Disposed(Object obj, IntPtr ptr); + internal static extern void godot_icall_Object_Disposed(IntPtr ptr); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_RefCounted_Disposed(Object obj, IntPtr ptr, bool isFinalizer); + internal static extern void godot_icall_RefCounted_Disposed(IntPtr ptr, bool isFinalizer); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Object_ConnectEventSignals(IntPtr obj); + internal static extern unsafe void godot_icall_Object_ConnectEventSignal(IntPtr obj, + godot_string_name* eventSignal); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/ScriptManager.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/ScriptManager.cs deleted file mode 100644 index e92688f5bb..0000000000 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/ScriptManager.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Godot -{ - internal class ScriptManager - { - internal static void FrameCallback() - { - Dispatcher.DefaultGodotTaskScheduler?.Activate(); - } - } -} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs index e485207fb4..fd6636e410 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using Godot.NativeInterop; namespace Godot @@ -12,11 +13,13 @@ namespace Godot public SignalAwaiter(Object source, StringName signal, Object target) { - godot_icall_SignalAwaiter_connect(Object.GetPtr(source), ref signal.NativeValue, Object.GetPtr(target), this); + godot_icall_SignalAwaiter_connect(Object.GetPtr(source), ref signal.NativeValue, + Object.GetPtr(target), GCHandle.ToIntPtr(GCHandle.Alloc(this))); } [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern Error godot_icall_SignalAwaiter_connect(IntPtr source, ref godot_string_name signal, IntPtr target, SignalAwaiter awaiter); + internal static extern Error godot_icall_SignalAwaiter_connect(IntPtr source, ref godot_string_name signal, + IntPtr target, IntPtr awaiterHandlePtr); public bool IsCompleted => _completed; @@ -29,11 +32,30 @@ namespace Godot public IAwaiter<object[]> GetAwaiter() => this; - internal void SignalCallback(object[] args) + internal static unsafe void SignalCallback(IntPtr awaiterGCHandlePtr, + godot_variant** args, int argCount, + bool* r_awaiterIsNull) { - _completed = true; - _result = args; - _action?.Invoke(); + var awaiter = (SignalAwaiter)GCHandle.FromIntPtr(awaiterGCHandlePtr).Target; + + if (awaiter == null) + { + *r_awaiterIsNull = true; + return; + } + + *r_awaiterIsNull = false; + + awaiter._completed = true; + + object[] signalArgs = new object[argCount]; + + for (int i = 0; i < argCount; i++) + signalArgs[i] = Marshaling.variant_to_mono_object(args[i]); + + awaiter._result = signalArgs; + + awaiter._action?.Invoke(); } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs index 6c3d673fdc..dfdef81f9e 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs @@ -1096,7 +1096,7 @@ namespace Godot 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); + return Marshaling.mono_string_from_godot(md5Text); } /// <summary> @@ -1345,7 +1345,7 @@ namespace Godot 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); + return Marshaling.mono_string_from_godot(sha256Text); } /// <summary> @@ -1401,7 +1401,7 @@ namespace Godot using godot_string instanceStr = Marshaling.mono_string_to_godot(instance); using godot_string simplifiedPath = default; NativeFuncs.godotsharp_string_simplify_path(&instanceStr, &simplifiedPath); - return Marshaling.mono_string_from_godot(&simplifiedPath); + return Marshaling.mono_string_from_godot(simplifiedPath); } /// <summary> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs index 40d282eab4..84b0ab623c 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs @@ -13,7 +13,7 @@ namespace Godot /// </summary> public sealed class StringName : IDisposable { - internal godot_string_name NativeValue; + public godot_string_name NativeValue; ~StringName() { @@ -86,7 +86,7 @@ namespace Godot godot_string_name src = NativeValue; NativeFuncs.godotsharp_string_name_as_string(&dest, &src); using (dest) - return Marshaling.mono_string_from_godot(&dest); + return Marshaling.mono_string_from_godot(dest); } /// <summary> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj index 36faf92144..6a529de99b 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj @@ -32,6 +32,9 @@ <Compile Include="Core\Attributes\SignalAttribute.cs" /> <Compile Include="Core\Attributes\ToolAttribute.cs" /> <Compile Include="Core\Basis.cs" /> + <Compile Include="Core\Bridge\CSharpInstanceBridge.cs" /> + <Compile Include="Core\Bridge\GCHandleBridge.cs" /> + <Compile Include="Core\Bridge\ScriptManagerBridge.cs" /> <Compile Include="Core\Callable.cs" /> <Compile Include="Core\Color.cs" /> <Compile Include="Core\Colors.cs" /> @@ -71,7 +74,6 @@ <Compile Include="Core\NativeInterop\NativeFuncs.cs" /> <Compile Include="Core\NativeInterop\InteropStructs.cs" /> <Compile Include="Core\NativeInterop\Marshaling.cs" /> - <Compile Include="Core\ScriptManager.cs" /> <Compile Include="Core\SignalInfo.cs" /> <Compile Include="Core\SignalAwaiter.cs" /> <Compile Include="Core\StringExtensions.cs" /> diff --git a/modules/mono/glue/base_object_glue.cpp b/modules/mono/glue/base_object_glue.cpp index d56b70e739..dbf2ae84aa 100644 --- a/modules/mono/glue/base_object_glue.cpp +++ b/modules/mono/glue/base_object_glue.cpp @@ -34,13 +34,11 @@ #include "../csharp_script.h" #include "../mono_gd/gd_mono_cache.h" -#include "../mono_gd/gd_mono_class.h" #include "../mono_gd/gd_mono_internals.h" -#include "../mono_gd/gd_mono_marshal.h" #include "../mono_gd/gd_mono_utils.h" #include "../signal_awaiter_utils.h" -void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr) { +void godot_icall_Object_Disposed(Object *p_ptr) { #ifdef DEBUG_ENABLED CRASH_COND(p_ptr == nullptr); #endif @@ -49,7 +47,7 @@ void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr) { CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(p_ptr->get_script_instance()); if (cs_instance) { if (!cs_instance->is_destructing_script_instance()) { - cs_instance->mono_object_disposed(p_obj); + cs_instance->mono_object_disposed(); p_ptr->set_script_instance(nullptr); } return; @@ -63,13 +61,14 @@ void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr) { if (script_binding.inited) { MonoGCHandleData &gchandle = script_binding.gchandle; if (!gchandle.is_released()) { - CSharpLanguage::release_script_gchandle(p_obj, gchandle); + CSharpLanguage::release_script_gchandle(nullptr, gchandle); + script_binding.inited = false; } } } } -void godot_icall_RefCounted_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoolean p_is_finalizer) { +void godot_icall_RefCounted_Disposed(Object *p_ptr, MonoBoolean p_is_finalizer) { #ifdef DEBUG_ENABLED CRASH_COND(p_ptr == nullptr); // This is only called with RefCounted derived classes @@ -85,7 +84,7 @@ void godot_icall_RefCounted_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoole bool delete_owner; bool remove_script_instance; - cs_instance->mono_object_disposed_baseref(p_obj, p_is_finalizer, delete_owner, remove_script_instance); + cs_instance->mono_object_disposed_baseref(p_is_finalizer, delete_owner, remove_script_instance); if (delete_owner) { memdelete(rc); @@ -110,28 +109,29 @@ void godot_icall_RefCounted_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoole if (script_binding.inited) { MonoGCHandleData &gchandle = script_binding.gchandle; if (!gchandle.is_released()) { - CSharpLanguage::release_script_gchandle(p_obj, gchandle); + CSharpLanguage::release_script_gchandle(nullptr, gchandle); + script_binding.inited = false; } } } } } -void godot_icall_Object_ConnectEventSignals(Object *p_ptr) { +void godot_icall_Object_ConnectEventSignal(Object *p_ptr, const StringName *p_event_signal) { CSharpInstance *csharp_instance = CAST_CSHARP_INSTANCE(p_ptr->get_script_instance()); if (csharp_instance) { - csharp_instance->connect_event_signals(); + csharp_instance->connect_event_signal(*p_event_signal); } } -int32_t godot_icall_SignalAwaiter_connect(Object *p_source, StringName *p_signal, Object *p_target, MonoObject *p_awaiter) { +int32_t godot_icall_SignalAwaiter_connect(Object *p_source, StringName *p_signal, Object *p_target, GCHandleIntPtr p_awaiter_handle_ptr) { StringName signal = p_signal ? *p_signal : StringName(); - return (int32_t)gd_mono_connect_signal_awaiter(p_source, signal, p_target, p_awaiter); + return (int32_t)gd_mono_connect_signal_awaiter(p_source, signal, p_target, p_awaiter_handle_ptr); } 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_ConnectEventSignal", godot_icall_Object_ConnectEventSignal); GDMonoUtils::add_internal_call("Godot.SignalAwaiter::godot_icall_SignalAwaiter_connect", godot_icall_SignalAwaiter_connect); } diff --git a/modules/mono/glue/placeholder_glue.cpp b/modules/mono/glue/placeholder_glue.cpp index edac231bb4..0dd904373e 100644 --- a/modules/mono/glue/placeholder_glue.cpp +++ b/modules/mono/glue/placeholder_glue.cpp @@ -33,24 +33,169 @@ #include "core/object/object.h" +#include "../csharp_script.h" +#include "../mono_gd/gd_mono_cache.h" #include "../mono_gd/gd_mono_internals.h" #include "../mono_gd/gd_mono_utils.h" -MonoObject *godot_icall_InteropUtils_unmanaged_get_managed(Object *unmanaged) { - return GDMonoUtils::unmanaged_get_managed(unmanaged); +GCHandleIntPtr unmanaged_get_script_instance_managed(Object *p_unmanaged, bool *r_has_cs_script_instance) { +#ifdef DEBUG_ENABLED + CRASH_COND(!p_unmanaged); + CRASH_COND(!r_has_cs_script_instance); +#endif + + if (p_unmanaged->get_script_instance()) { + CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(p_unmanaged->get_script_instance()); + + if (cs_instance) { + *r_has_cs_script_instance = true; + return cs_instance->get_gchandle_intptr(); + } + } + + *r_has_cs_script_instance = false; + return GCHandleIntPtr(); } -void godot_icall_InteropUtils_tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) { - GDMonoInternals::tie_managed_to_unmanaged(managed, unmanaged); +GCHandleIntPtr unmanaged_get_instance_binding_managed(Object *p_unmanaged) { +#ifdef DEBUG_ENABLED + CRASH_COND(!p_unmanaged); +#endif + + void *data = CSharpLanguage::get_instance_binding(p_unmanaged); + ERR_FAIL_NULL_V(data, GCHandleIntPtr()); + CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->value(); + ERR_FAIL_COND_V(!script_binding.inited, GCHandleIntPtr()); + + return script_binding.gchandle.get_intptr(); +} + +GCHandleIntPtr unmanaged_instance_binding_create_managed(Object *p_unmanaged, GCHandleIntPtr p_old_gchandle) { +#ifdef DEBUG_ENABLED + CRASH_COND(!p_unmanaged); +#endif + + void *data = CSharpLanguage::get_instance_binding(p_unmanaged); + ERR_FAIL_NULL_V(data, GCHandleIntPtr()); + CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->value(); + ERR_FAIL_COND_V(!script_binding.inited, GCHandleIntPtr()); + + MonoGCHandleData &gchandle = script_binding.gchandle; + + // TODO: Possible data race? + CRASH_COND(gchandle.get_intptr().value != p_old_gchandle.value); + + CSharpLanguage::get_singleton()->release_script_gchandle(gchandle); + script_binding.inited = false; + + // Create a new one + +#ifdef DEBUG_ENABLED + CRASH_COND(script_binding.type_name == StringName()); +#endif + + bool parent_is_object_class = ClassDB::is_parent_class(p_unmanaged->get_class_name(), script_binding.type_name); + ERR_FAIL_COND_V_MSG(!parent_is_object_class, GCHandleIntPtr(), + "Type inherits from native type '" + script_binding.type_name + "', so it can't be instantiated in object of type: '" + p_unmanaged->get_class() + "'."); + + MonoException *exc = nullptr; + GCHandleIntPtr strong_gchandle = + GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_CreateManagedForGodotObjectBinding + .invoke(&script_binding.type_name, p_unmanaged, &exc); + + if (exc) { + GDMonoUtils::set_pending_exception(exc); + return GCHandleIntPtr(); + } + + ERR_FAIL_NULL_V(strong_gchandle.value, GCHandleIntPtr()); + + gchandle = MonoGCHandleData(strong_gchandle, gdmono::GCHandleType::STRONG_HANDLE); + script_binding.inited = true; + + // Tie managed to unmanaged + RefCounted *rc = Object::cast_to<RefCounted>(p_unmanaged); + + if (rc) { + // 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) + rc->reference(); + CSharpLanguage::get_singleton()->post_unsafe_reference(rc); + } + + return gchandle.get_intptr(); +} + +void godot_icall_InteropUtils_tie_native_managed_to_unmanaged(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged, const StringName *p_native_name, bool p_ref_counted) { + CSharpLanguage::tie_native_managed_to_unmanaged(p_gchandle_intptr, p_unmanaged, p_native_name, p_ref_counted); +} + +void godot_icall_InteropUtils_tie_user_managed_to_unmanaged(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged, CSharpScript *p_script, bool p_ref_counted) { + CSharpLanguage::tie_user_managed_to_unmanaged(p_gchandle_intptr, p_unmanaged, p_script, p_ref_counted); +} + +void godot_icall_InteropUtils_tie_managed_to_unmanaged_with_pre_setup(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged) { + CSharpLanguage::tie_managed_to_unmanaged_with_pre_setup(p_gchandle_intptr, p_unmanaged); +} + +CSharpScript *godot_icall_InteropUtils_internal_new_csharp_script() { + CSharpScript *script = memnew(CSharpScript); + CRASH_COND(!script); + return script; +} + +void godotsharp_array_filter_godot_objects_by_native(StringName *p_native_name, const Array *p_input, Array *r_output) { + memnew_placement(r_output, Array); + + for (int i = 0; i < p_input->size(); ++i) { + if (ClassDB::is_parent_class(((Object *)(*p_input)[i])->get_class(), *p_native_name)) { + r_output->push_back(p_input[i]); + } + } +} + +void godotsharp_array_filter_godot_objects_by_non_native(const Array *p_input, Array *r_output) { + memnew_placement(r_output, Array); + + for (int i = 0; i < p_input->size(); ++i) { + CSharpInstance *si = CAST_CSHARP_INSTANCE(((Object *)(*p_input)[i])->get_script_instance()); + + if (si != nullptr) { + r_output->push_back(p_input[i]); + } + } } void godot_register_placeholder_icalls() { GDMonoUtils::add_internal_call( - "Godot.NativeInterop.InteropUtils::internal_unmanaged_get_managed", - godot_icall_InteropUtils_unmanaged_get_managed); + "Godot.NativeInterop.InteropUtils::unmanaged_get_script_instance_managed", + unmanaged_get_script_instance_managed); + GDMonoUtils::add_internal_call( + "Godot.NativeInterop.InteropUtils::unmanaged_get_instance_binding_managed", + unmanaged_get_instance_binding_managed); + GDMonoUtils::add_internal_call( + "Godot.NativeInterop.InteropUtils::unmanaged_instance_binding_create_managed", + unmanaged_instance_binding_create_managed); + GDMonoUtils::add_internal_call( + "Godot.NativeInterop.InteropUtils::internal_tie_native_managed_to_unmanaged", + godot_icall_InteropUtils_tie_native_managed_to_unmanaged); + GDMonoUtils::add_internal_call( + "Godot.NativeInterop.InteropUtils::internal_tie_user_managed_to_unmanaged", + godot_icall_InteropUtils_tie_user_managed_to_unmanaged); + GDMonoUtils::add_internal_call( + "Godot.NativeInterop.InteropUtils::internal_tie_managed_to_unmanaged_with_pre_setup", + godot_icall_InteropUtils_tie_managed_to_unmanaged_with_pre_setup); + GDMonoUtils::add_internal_call( + "Godot.NativeInterop.InteropUtils::internal_new_csharp_script", + godot_icall_InteropUtils_internal_new_csharp_script); + GDMonoUtils::add_internal_call( + "Godot.NativeInterop.SceneTree::godotsharp_array_filter_godot_objects_by_native", + godotsharp_array_filter_godot_objects_by_native); GDMonoUtils::add_internal_call( - "Godot.NativeInterop.InteropUtils::internal_tie_managed_to_unmanaged", - godot_icall_InteropUtils_tie_managed_to_unmanaged); + "Godot.NativeInterop.SceneTree::godotsharp_array_filter_godot_objects_by_non_native", + godotsharp_array_filter_godot_objects_by_non_native); } #endif // GLUE_HEADER_H diff --git a/modules/mono/glue/runtime_interop.cpp b/modules/mono/glue/runtime_interop.cpp index 2860386127..29d373e885 100644 --- a/modules/mono/glue/runtime_interop.cpp +++ b/modules/mono/glue/runtime_interop.cpp @@ -187,14 +187,14 @@ GD_PINVOKE_EXPORT void godotsharp_packed_string_array_add(PackedStringArray *r_d r_dest->append(*p_element); } -GD_PINVOKE_EXPORT void godotsharp_callable_new_with_delegate(void *p_delegate_handle, Callable *r_callable) { +GD_PINVOKE_EXPORT void godotsharp_callable_new_with_delegate(GCHandleIntPtr p_delegate_handle, Callable *r_callable) { // TODO: Use pooling for ManagedCallable instances. CallableCustom *managed_callable = memnew(ManagedCallable(p_delegate_handle)); memnew_placement(r_callable, Callable(managed_callable)); } GD_PINVOKE_EXPORT bool godotsharp_callable_get_data_for_marshalling(const Callable *p_callable, - void **r_delegate_handle, Object **r_object, StringName *r_name) { + GCHandleIntPtr *r_delegate_handle, Object **r_object, StringName *r_name) { if (p_callable->is_custom()) { CallableCustom *custom = p_callable->get_custom(); CallableCustom::CompareEqualFunc compare_equal_func = custom->get_compare_equal_func(); @@ -207,25 +207,25 @@ GD_PINVOKE_EXPORT bool godotsharp_callable_get_data_for_marshalling(const Callab return true; } else if (compare_equal_func == SignalAwaiterCallable::compare_equal_func_ptr) { SignalAwaiterCallable *signal_awaiter_callable = static_cast<SignalAwaiterCallable *>(custom); - *r_delegate_handle = nullptr; + *r_delegate_handle = GCHandleIntPtr(); *r_object = ObjectDB::get_instance(signal_awaiter_callable->get_object()); 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<EventSignalCallable *>(custom); - *r_delegate_handle = nullptr; + *r_delegate_handle = GCHandleIntPtr(); *r_object = ObjectDB::get_instance(event_signal_callable->get_object()); memnew_placement(r_name, StringName(event_signal_callable->get_signal())); return true; } // Some other CallableCustom. We only support ManagedCallable. - *r_delegate_handle = nullptr; + *r_delegate_handle = GCHandleIntPtr(); *r_object = nullptr; memnew_placement(r_name, StringName()); return false; } else { - *r_delegate_handle = nullptr; + *r_delegate_handle = GCHandleIntPtr(); *r_object = ObjectDB::get_instance(p_callable->get_object_id()); memnew_placement(r_name, StringName(p_callable->get_method())); return true; diff --git a/modules/mono/glue/scene_tree_glue.cpp b/modules/mono/glue/scene_tree_glue.cpp deleted file mode 100644 index 55a46ad368..0000000000 --- a/modules/mono/glue/scene_tree_glue.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/*************************************************************************/ -/* scene_tree_glue.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 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/object/class_db.h" -#include "core/string/string_name.h" -#include "core/variant/array.h" -#include "scene/main/node.h" -#include "scene/main/scene_tree.h" - -#include "../csharp_script.h" -#include "../mono_gd/gd_mono_marshal.h" -#include "../mono_gd/gd_mono_utils.h" - -void godot_icall_SceneTree_get_nodes_in_group_Generic(SceneTree *ptr, StringName *group, MonoReflectionType *refltype, Array *r_dest) { - memnew_placement(r_dest, Array); - - List<Node *> nodes; - - // Retrieve all the nodes in the group - ptr->get_nodes_in_group(*group, &nodes); - - // No need to bother if the group is empty - if (!nodes.is_empty()) { - MonoType *elem_type = mono_reflection_type_get_type(refltype); - MonoClass *mono_class = mono_class_from_mono_type(elem_type); - GDMonoClass *klass = GDMono::get_singleton()->get_class(mono_class); - - if (klass == GDMonoUtils::get_class_native_base(klass)) { - // If we're trying to get native objects, just check the inheritance list - StringName native_class_name = GDMonoUtils::get_native_godot_class_name(klass); - for (int i = 0; i < nodes.size(); ++i) { - if (ClassDB::is_parent_class(nodes[i]->get_class(), native_class_name)) { - r_dest->push_back(nodes[i]); - } - } - } else { - // If we're trying to get csharpscript instances, get the mono object and compare the classes - for (int i = 0; i < nodes.size(); ++i) { - CSharpInstance *si = CAST_CSHARP_INSTANCE(nodes[i]->get_script_instance()); - - if (si != nullptr) { - MonoObject *obj = si->get_mono_object(); - if (obj != nullptr && mono_object_get_class(obj) == mono_class) { - r_dest->push_back(nodes[i]); - } - } - } - } - } -} - -void godot_register_scene_tree_icalls() { - GDMonoUtils::add_internal_call("Godot.SceneTree::godot_icall_SceneTree_get_nodes_in_group_Generic", godot_icall_SceneTree_get_nodes_in_group_Generic); -} diff --git a/modules/mono/godotsharp_defs.h b/modules/mono/godotsharp_defs.h index a637e0be9b..872c875cbe 100644 --- a/modules/mono/godotsharp_defs.h +++ b/modules/mono/godotsharp_defs.h @@ -33,6 +33,7 @@ #define BINDINGS_NAMESPACE "Godot" #define BINDINGS_NAMESPACE_COLLECTIONS BINDINGS_NAMESPACE ".Collections" +#define BINDINGS_NAMESPACE_BRIDGE BINDINGS_NAMESPACE ".Bridge" #define BINDINGS_GLOBAL_SCOPE_CLASS "GD" #define BINDINGS_PTR_FIELD "NativePtr" #define BINDINGS_NATIVE_NAME_FIELD "NativeName" diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp index f17b24e399..26ae7246ad 100644 --- a/modules/mono/godotsharp_dirs.cpp +++ b/modules/mono/godotsharp_dirs.cpp @@ -96,8 +96,6 @@ class _GodotSharpDirs { public: String res_data_dir; String res_metadata_dir; - String res_assemblies_base_dir; - String res_assemblies_dir; String res_config_dir; String res_temp_dir; String res_temp_assemblies_base_dir; @@ -105,6 +103,9 @@ public: String mono_user_dir; String mono_logs_dir; + String api_assemblies_base_dir; + String api_assemblies_dir; + #ifdef TOOLS_ENABLED String mono_solutions_dir; String build_logs_dir; @@ -113,7 +114,6 @@ public: String csproj_filepath; String data_editor_tools_dir; - String data_editor_prebuilt_api_dir; #else // Equivalent of res_assemblies_dir, but in the data directory rather than in 'res://'. // Only defined on export templates. Used when exporting assemblies outside of PCKs. @@ -131,8 +131,6 @@ private: _GodotSharpDirs() { res_data_dir = ProjectSettings::get_singleton()->get_project_data_path().plus_file("mono"); res_metadata_dir = res_data_dir.plus_file("metadata"); - res_assemblies_base_dir = res_data_dir.plus_file("assemblies"); - res_assemblies_dir = res_assemblies_base_dir.plus_file(GDMono::get_expected_api_build_config()); res_config_dir = res_data_dir.plus_file("etc").plus_file("mono"); // TODO use paths from csproj @@ -140,6 +138,8 @@ private: res_temp_assemblies_base_dir = res_temp_dir.plus_file("bin"); res_temp_assemblies_dir = res_temp_assemblies_base_dir.plus_file(_get_expected_build_config()); + api_assemblies_base_dir = res_data_dir.plus_file("assemblies"); + #ifdef JAVASCRIPT_ENABLED mono_user_dir = "user://"; #else @@ -169,7 +169,7 @@ private: String data_dir_root = exe_dir.plus_file("GodotSharp"); data_editor_tools_dir = data_dir_root.plus_file("Tools"); - data_editor_prebuilt_api_dir = data_dir_root.plus_file("Api"); + api_assemblies_base_dir = data_dir_root.plus_file("Api"); String data_mono_root_dir = data_dir_root.plus_file("Mono"); data_mono_etc_dir = data_mono_root_dir.plus_file("etc"); @@ -189,8 +189,8 @@ private: data_editor_tools_dir = exe_dir.plus_file("../Resources/GodotSharp/Tools"); } - if (!DirAccess::exists(data_editor_prebuilt_api_dir)) { - data_editor_prebuilt_api_dir = exe_dir.plus_file("../Resources/GodotSharp/Api"); + if (!DirAccess::exists(api_assemblies_base_dir)) { + api_assemblies_base_dir = exe_dir.plus_file("../Resources/GodotSharp/Api"); } if (!DirAccess::exists(data_mono_root_dir)) { @@ -234,6 +234,8 @@ private: #endif #endif + + api_assemblies_dir = api_assemblies_base_dir.plus_file(GDMono::get_expected_api_build_config()); } public: @@ -251,14 +253,6 @@ String get_res_metadata_dir() { return _GodotSharpDirs::get_singleton().res_metadata_dir; } -String get_res_assemblies_base_dir() { - return _GodotSharpDirs::get_singleton().res_assemblies_base_dir; -} - -String get_res_assemblies_dir() { - return _GodotSharpDirs::get_singleton().res_assemblies_dir; -} - String get_res_config_dir() { return _GodotSharpDirs::get_singleton().res_config_dir; } @@ -275,6 +269,14 @@ String get_res_temp_assemblies_dir() { return _GodotSharpDirs::get_singleton().res_temp_assemblies_dir; } +String get_api_assemblies_dir() { + return _GodotSharpDirs::get_singleton().api_assemblies_dir; +} + +String get_api_assemblies_base_dir() { + return _GodotSharpDirs::get_singleton().api_assemblies_base_dir; +} + String get_mono_user_dir() { return _GodotSharpDirs::get_singleton().mono_user_dir; } @@ -303,10 +305,6 @@ String get_project_csproj_path() { String get_data_editor_tools_dir() { return _GodotSharpDirs::get_singleton().data_editor_tools_dir; } - -String get_data_editor_prebuilt_api_dir() { - return _GodotSharpDirs::get_singleton().data_editor_prebuilt_api_dir; -} #else String get_data_game_assemblies_dir() { return _GodotSharpDirs::get_singleton().data_game_assemblies_dir; diff --git a/modules/mono/godotsharp_dirs.h b/modules/mono/godotsharp_dirs.h index da25e0778f..869991bb35 100644 --- a/modules/mono/godotsharp_dirs.h +++ b/modules/mono/godotsharp_dirs.h @@ -37,13 +37,14 @@ namespace GodotSharpDirs { String get_res_data_dir(); String get_res_metadata_dir(); -String get_res_assemblies_base_dir(); -String get_res_assemblies_dir(); String get_res_config_dir(); String get_res_temp_dir(); String get_res_temp_assemblies_base_dir(); String get_res_temp_assemblies_dir(); +String get_api_assemblies_dir(); +String get_api_assemblies_base_dir(); + String get_mono_user_dir(); String get_mono_logs_dir(); @@ -55,7 +56,6 @@ String get_project_sln_path(); String get_project_csproj_path(); String get_data_editor_tools_dir(); -String get_data_editor_prebuilt_api_dir(); #else String get_data_game_assemblies_dir(); #endif diff --git a/modules/mono/managed_callable.cpp b/modules/mono/managed_callable.cpp index 72b353d8c1..e01d0c6e18 100644 --- a/modules/mono/managed_callable.cpp +++ b/modules/mono/managed_callable.cpp @@ -32,7 +32,6 @@ #include "csharp_script.h" #include "mono_gd/gd_mono_cache.h" -#include "mono_gd/gd_mono_marshal.h" #include "mono_gd/gd_mono_utils.h" #ifdef GD_MONO_HOT_RELOAD @@ -45,8 +44,8 @@ bool ManagedCallable::compare_equal(const CallableCustom *p_a, const CallableCus const ManagedCallable *a = static_cast<const ManagedCallable *>(p_a); const ManagedCallable *b = static_cast<const ManagedCallable *>(p_b); - if (!a->delegate_handle || !b->delegate_handle) { - if (!a->delegate_handle && !b->delegate_handle) { + if (!a->delegate_handle.value || !b->delegate_handle.value) { + if (!a->delegate_handle.value && !b->delegate_handle.value) { return true; } return false; @@ -54,7 +53,7 @@ bool ManagedCallable::compare_equal(const CallableCustom *p_a, const CallableCus // Call Delegate's 'Equals' MonoException *exc = nullptr; - MonoBoolean res = CACHED_METHOD_THUNK(DelegateUtils, DelegateEquals) + MonoBoolean res = GDMonoCache::cached_data.methodthunk_DelegateUtils_DelegateEquals .invoke(a->delegate_handle, b->delegate_handle, &exc); UNHANDLED_EXCEPTION(exc); @@ -69,7 +68,7 @@ bool ManagedCallable::compare_less(const CallableCustom *p_a, const CallableCust } uint32_t ManagedCallable::hash() const { - return hash_murmur3_one_64((uint64_t)delegate_handle); + return hash_murmur3_one_64((uint64_t)delegate_handle.value); } String ManagedCallable::get_as_text() const { @@ -94,7 +93,7 @@ void ManagedCallable::call(const Variant **p_arguments, int p_argcount, Variant r_return_value = Variant(); MonoException *exc = nullptr; - CACHED_METHOD_THUNK(DelegateUtils, InvokeWithVariantArgs) + GDMonoCache::cached_data.methodthunk_DelegateUtils_InvokeWithVariantArgs .invoke(delegate_handle, p_arguments, p_argcount, &r_return_value, &exc); @@ -106,21 +105,21 @@ void ManagedCallable::call(const Variant **p_arguments, int p_argcount, Variant } void ManagedCallable::release_delegate_handle() { - if (delegate_handle) { + if (delegate_handle.value) { MonoException *exc = nullptr; - CACHED_METHOD_THUNK(DelegateUtils, FreeGCHandle).invoke(delegate_handle, &exc); + GDMonoCache::cached_data.methodthunk_GCHandleBridge_FreeGCHandle.invoke(delegate_handle, &exc); if (exc) { GDMonoUtils::debug_print_unhandled_exception(exc); } - delegate_handle = nullptr; + delegate_handle = GCHandleIntPtr(); } } // Why you do this clang-format... /* clang-format off */ -ManagedCallable::ManagedCallable(void *p_delegate_handle) : delegate_handle(p_delegate_handle) { +ManagedCallable::ManagedCallable(GCHandleIntPtr p_delegate_handle) : delegate_handle(p_delegate_handle) { #ifdef GD_MONO_HOT_RELOAD { MutexLock lock(instances_mutex); diff --git a/modules/mono/managed_callable.h b/modules/mono/managed_callable.h index 56c11e7e45..d0f6362d42 100644 --- a/modules/mono/managed_callable.h +++ b/modules/mono/managed_callable.h @@ -38,11 +38,10 @@ #include "core/variant/callable.h" #include "mono_gc_handle.h" -#include "mono_gd/gd_mono_method.h" class ManagedCallable : public CallableCustom { friend class CSharpLanguage; - void *delegate_handle = nullptr; + GCHandleIntPtr delegate_handle; #ifdef GD_MONO_HOT_RELOAD SelfList<ManagedCallable> self_instance = this; @@ -59,7 +58,7 @@ public: ObjectID get_object() const override; void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override; - _FORCE_INLINE_ void *get_delegate() const { return delegate_handle; } + _FORCE_INLINE_ GCHandleIntPtr get_delegate() const { return delegate_handle; } static bool compare_equal(const CallableCustom *p_a, const CallableCustom *p_b); static bool compare_less(const CallableCustom *p_a, const CallableCustom *p_b); @@ -69,7 +68,7 @@ public: void release_delegate_handle(); - ManagedCallable(void *p_delegate_handle); + ManagedCallable(GCHandleIntPtr p_delegate_handle); ~ManagedCallable(); }; diff --git a/modules/mono/mono_gc_handle.cpp b/modules/mono/mono_gc_handle.cpp index f3dafa6ecf..f0a48483f6 100644 --- a/modules/mono/mono_gc_handle.cpp +++ b/modules/mono/mono_gc_handle.cpp @@ -31,34 +31,24 @@ #include "mono_gc_handle.h" #include "mono_gd/gd_mono.h" +#include "mono_gd/gd_mono_cache.h" void MonoGCHandleData::release() { #ifdef DEBUG_ENABLED - CRASH_COND(handle && GDMono::get_singleton() == nullptr); + CRASH_COND(handle.value && GDMono::get_singleton() == nullptr); #endif - if (handle && GDMono::get_singleton()->is_runtime_initialized()) { - GDMonoUtils::free_gchandle(handle); - handle = 0; + if (handle.value && !GDMonoCache::cached_data.methodthunk_GCHandleBridge_FreeGCHandle.is_null() && + GDMono::get_singleton()->is_runtime_initialized()) { + free_gchandle(handle); + handle.value = nullptr; } } - -MonoGCHandleData MonoGCHandleData::new_strong_handle(MonoObject *p_object) { - return MonoGCHandleData(GDMonoUtils::new_strong_gchandle(p_object), gdmono::GCHandleType::STRONG_HANDLE); -} - -MonoGCHandleData MonoGCHandleData::new_strong_handle_pinned(MonoObject *p_object) { - return MonoGCHandleData(GDMonoUtils::new_strong_gchandle_pinned(p_object), gdmono::GCHandleType::STRONG_HANDLE); -} - -MonoGCHandleData MonoGCHandleData::new_weak_handle(MonoObject *p_object) { - return MonoGCHandleData(GDMonoUtils::new_weak_gchandle(p_object), gdmono::GCHandleType::WEAK_HANDLE); -} - -Ref<MonoGCHandleRef> MonoGCHandleRef::create_strong(MonoObject *p_object) { - return memnew(MonoGCHandleRef(MonoGCHandleData::new_strong_handle(p_object))); -} - -Ref<MonoGCHandleRef> MonoGCHandleRef::create_weak(MonoObject *p_object) { - return memnew(MonoGCHandleRef(MonoGCHandleData::new_weak_handle(p_object))); +void MonoGCHandleData::free_gchandle(GCHandleIntPtr p_gchandle) { + CRASH_COND(GDMonoCache::cached_data.methodthunk_GCHandleBridge_FreeGCHandle.is_null()); + MonoException *exc = nullptr; + GDMonoCache::cached_data.methodthunk_GCHandleBridge_FreeGCHandle.invoke(p_gchandle, &exc); + if (exc) { + GDMonoUtils::debug_unhandled_exception(exc); + } } diff --git a/modules/mono/mono_gc_handle.h b/modules/mono/mono_gc_handle.h index e2aff1d19d..96553cf4f4 100644 --- a/modules/mono/mono_gc_handle.h +++ b/modules/mono/mono_gc_handle.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -31,8 +31,6 @@ #ifndef MONO_GC_HANDLE_H #define MONO_GC_HANDLE_H -#include <mono/jit/jit.h> - #include "core/object/ref_counted.h" namespace gdmono { @@ -44,18 +42,25 @@ enum class GCHandleType : char { }; } +struct GCHandleIntPtr { + void *value = nullptr; +}; + +static_assert(sizeof(GCHandleIntPtr) == sizeof(void *)); + // Manual release of the GC handle must be done when using this struct struct MonoGCHandleData { - uint32_t handle = 0; + GCHandleIntPtr handle; gdmono::GCHandleType type = gdmono::GCHandleType::NIL; - _FORCE_INLINE_ bool is_released() const { return !handle; } + _FORCE_INLINE_ bool is_released() const { return !handle.value; } _FORCE_INLINE_ bool is_weak() const { return type == gdmono::GCHandleType::WEAK_HANDLE; } - - _FORCE_INLINE_ MonoObject *get_target() const { return handle ? mono_gchandle_get_target(handle) : nullptr; } + _FORCE_INLINE_ GCHandleIntPtr get_intptr() const { return handle; } void release(); + static void free_gchandle(GCHandleIntPtr p_gchandle); + void operator=(const MonoGCHandleData &p_other) { #ifdef DEBUG_ENABLED CRASH_COND(!is_released()); @@ -68,40 +73,10 @@ struct MonoGCHandleData { MonoGCHandleData() {} - MonoGCHandleData(uint32_t p_handle, gdmono::GCHandleType p_type) : + MonoGCHandleData(GCHandleIntPtr p_handle, gdmono::GCHandleType p_type) : handle(p_handle), type(p_type) { } - - static MonoGCHandleData new_strong_handle(MonoObject *p_object); - static MonoGCHandleData new_strong_handle_pinned(MonoObject *p_object); - static MonoGCHandleData new_weak_handle(MonoObject *p_object); -}; - -class MonoGCHandleRef : public RefCounted { - GDCLASS(MonoGCHandleRef, RefCounted); - - MonoGCHandleData data; - -public: - static Ref<MonoGCHandleRef> create_strong(MonoObject *p_object); - static Ref<MonoGCHandleRef> create_weak(MonoObject *p_object); - - _FORCE_INLINE_ bool is_released() const { return data.is_released(); } - _FORCE_INLINE_ bool is_weak() const { return data.is_weak(); } - - _FORCE_INLINE_ MonoObject *get_target() const { return data.get_target(); } - - void release() { data.release(); } - - _FORCE_INLINE_ void set_handle(uint32_t p_handle, gdmono::GCHandleType p_handle_type) { - data = MonoGCHandleData(p_handle, p_handle_type); - } - - MonoGCHandleRef(const MonoGCHandleData &p_gc_handle_data) : - data(p_gc_handle_data) { - } - ~MonoGCHandleRef() { release(); } }; #endif // MONO_GC_HANDLE_H diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index bf387be4c8..ce4fc0c5a0 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -48,8 +48,6 @@ #include "../godotsharp_dirs.h" #include "../utils/path_utils.h" #include "gd_mono_cache.h" -#include "gd_mono_class.h" -#include "gd_mono_marshal.h" #include "gd_mono_utils.h" #ifdef ANDROID_ENABLED @@ -411,7 +409,9 @@ void GDMono::initialize_load_assemblies() { // Load assemblies. The API and tools assemblies are required, // the application is aborted if these assemblies cannot be loaded. - _load_api_assemblies(); + if (!_try_load_api_assemblies()) { + CRASH_NOW_MSG("Failed to load one of the API assemblies."); + } #if defined(TOOLS_ENABLED) bool tool_assemblies_loaded = _load_tools_assemblies(); @@ -432,24 +432,12 @@ void GDMono::initialize_load_assemblies() { } } -bool GDMono::_are_api_assemblies_out_of_sync() { - bool out_of_sync = core_api_assembly.assembly && (core_api_assembly.out_of_sync || !GDMonoCache::cached_data.godot_api_cache_updated); -#ifdef TOOLS_ENABLED - if (!out_of_sync) { - out_of_sync = editor_api_assembly.assembly && editor_api_assembly.out_of_sync; - } -#endif - return out_of_sync; -} - void godot_register_object_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_object_icalls(); - godot_register_scene_tree_icalls(); godot_register_placeholder_icalls(); } @@ -490,35 +478,35 @@ GDMonoAssembly *GDMono::get_loaded_assembly(const String &p_name) { return result ? *result : nullptr; } -bool GDMono::load_assembly(const String &p_name, GDMonoAssembly **r_assembly, bool p_refonly) { +bool GDMono::load_assembly(const String &p_name, GDMonoAssembly **r_assembly) { #ifdef DEBUG_ENABLED CRASH_COND(!r_assembly); #endif MonoAssemblyName *aname = mono_assembly_name_new(p_name.utf8()); - bool result = load_assembly(p_name, aname, r_assembly, p_refonly); + bool result = load_assembly(p_name, aname, r_assembly); mono_assembly_name_free(aname); mono_free(aname); return result; } -bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly) { +bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly) { #ifdef DEBUG_ENABLED CRASH_COND(!r_assembly); #endif - return load_assembly(p_name, p_aname, r_assembly, p_refonly, GDMonoAssembly::get_default_search_dirs()); + return load_assembly(p_name, p_aname, r_assembly, GDMonoAssembly::get_default_search_dirs()); } -bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly, const Vector<String> &p_search_dirs) { +bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, const Vector<String> &p_search_dirs) { #ifdef DEBUG_ENABLED CRASH_COND(!r_assembly); #endif - print_verbose("Mono: Loading assembly " + p_name + (p_refonly ? " (refonly)" : "") + "..."); + print_verbose("Mono: Loading assembly " + p_name + "..."); - GDMonoAssembly *assembly = GDMonoAssembly::load(p_name, p_aname, p_refonly, p_search_dirs); + GDMonoAssembly *assembly = GDMonoAssembly::load(p_name, p_aname, /* refonly: */ false, p_search_dirs); if (!assembly) { return false; @@ -526,17 +514,17 @@ bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMo *r_assembly = assembly; - print_verbose("Mono: Assembly " + p_name + (p_refonly ? " (refonly)" : "") + " loaded from path: " + (*r_assembly)->get_path()); + print_verbose("Mono: Assembly " + p_name + " loaded from path: " + (*r_assembly)->get_path()); return true; } -bool GDMono::load_assembly_from(const String &p_name, const String &p_path, GDMonoAssembly **r_assembly, bool p_refonly) { +bool GDMono::load_assembly_from(const String &p_name, const String &p_path, GDMonoAssembly **r_assembly) { CRASH_COND(!r_assembly); - print_verbose("Mono: Loading assembly " + p_name + (p_refonly ? " (refonly)" : "") + "..."); + print_verbose("Mono: Loading assembly " + p_name + "..."); - GDMonoAssembly *assembly = GDMonoAssembly::load_from(p_name, p_path, p_refonly); + GDMonoAssembly *assembly = GDMonoAssembly::load_from(p_name, p_path, /* refonly: */ false); if (!assembly) { return false; @@ -544,279 +532,41 @@ bool GDMono::load_assembly_from(const String &p_name, const String &p_path, GDMo *r_assembly = assembly; - print_verbose("Mono: Assembly " + p_name + (p_refonly ? " (refonly)" : "") + " loaded from path: " + (*r_assembly)->get_path()); + print_verbose("Mono: Assembly " + p_name + " loaded from path: " + (*r_assembly)->get_path()); return true; } -ApiAssemblyInfo::Version ApiAssemblyInfo::Version::get_from_loaded_assembly(GDMonoAssembly *p_api_assembly, ApiAssemblyInfo::Type p_api_type) { - ApiAssemblyInfo::Version api_assembly_version; - - const char *nativecalls_name = p_api_type == ApiAssemblyInfo::API_CORE - ? BINDINGS_CLASS_NATIVECALLS - : BINDINGS_CLASS_NATIVECALLS_EDITOR; - - GDMonoClass *nativecalls_klass = p_api_assembly->get_class(BINDINGS_NAMESPACE, nativecalls_name); - - if (nativecalls_klass) { - GDMonoField *api_hash_field = nativecalls_klass->get_field("godot_api_hash"); - if (api_hash_field) { - api_assembly_version.godot_api_hash = GDMonoMarshal::unbox<uint64_t>(api_hash_field->get_value(nullptr)); - } - } - - return api_assembly_version; -} - -String ApiAssemblyInfo::to_string(ApiAssemblyInfo::Type p_type) { - return p_type == ApiAssemblyInfo::API_CORE ? "API_CORE" : "API_EDITOR"; -} - bool GDMono::_load_corlib_assembly() { if (corlib_assembly) { return true; } - bool success = load_assembly("mscorlib", &corlib_assembly); - - if (success) { - GDMonoCache::update_corlib_cache(); - } - - return success; -} - -#ifdef TOOLS_ENABLED -bool GDMono::copy_prebuilt_api_assembly(ApiAssemblyInfo::Type p_api_type, const String &p_config) { - String src_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file(p_config); - String dst_dir = GodotSharpDirs::get_res_assemblies_base_dir().plus_file(p_config); - - String assembly_name = p_api_type == ApiAssemblyInfo::API_CORE ? CORE_API_ASSEMBLY_NAME : EDITOR_API_ASSEMBLY_NAME; - - // Create destination directory if needed - if (!DirAccess::exists(dst_dir)) { - Ref<DirAccess> da = DirAccess::create_for_path(dst_dir); - Error err = da->make_dir_recursive(dst_dir); - - if (err != OK) { - ERR_PRINT("Failed to create destination directory for the API assemblies. Error: " + itos(err) + "."); - return false; - } - } - - Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - - String xml_file = assembly_name + ".xml"; - if (da->copy(src_dir.plus_file(xml_file), dst_dir.plus_file(xml_file)) != OK) { - WARN_PRINT("Failed to copy '" + xml_file + "'."); - } - - String pdb_file = assembly_name + ".pdb"; - if (da->copy(src_dir.plus_file(pdb_file), dst_dir.plus_file(pdb_file)) != OK) { - WARN_PRINT("Failed to copy '" + pdb_file + "'."); - } - - String assembly_file = assembly_name + ".dll"; - if (da->copy(src_dir.plus_file(assembly_file), dst_dir.plus_file(assembly_file)) != OK) { - ERR_PRINT("Failed to copy '" + assembly_file + "'."); - return false; - } - - return true; -} - -static bool try_get_cached_api_hash_for(const String &p_api_assemblies_dir, bool &r_out_of_sync) { - String core_api_assembly_path = p_api_assemblies_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll"); - String editor_api_assembly_path = p_api_assemblies_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll"); - - if (!FileAccess::exists(core_api_assembly_path) || !FileAccess::exists(editor_api_assembly_path)) { - return false; - } - - String cached_api_hash_path = p_api_assemblies_dir.plus_file("api_hash_cache.cfg"); - - if (!FileAccess::exists(cached_api_hash_path)) { - return false; - } - - Ref<ConfigFile> cfg; - cfg.instantiate(); - Error cfg_err = cfg->load(cached_api_hash_path); - ERR_FAIL_COND_V(cfg_err != OK, false); - - // Checking the modified time is good enough - if (FileAccess::get_modified_time(core_api_assembly_path) != (uint64_t)cfg->get_value("core", "modified_time") || - FileAccess::get_modified_time(editor_api_assembly_path) != (uint64_t)cfg->get_value("editor", "modified_time")) { - return false; - } - - r_out_of_sync = GDMono::get_singleton()->get_api_core_hash() != (uint64_t)cfg->get_value("core", "api_hash") || - GDMono::get_singleton()->get_api_editor_hash() != (uint64_t)cfg->get_value("editor", "api_hash"); - - return true; -} - -static void create_cached_api_hash_for(const String &p_api_assemblies_dir) { - String core_api_assembly_path = p_api_assemblies_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll"); - String editor_api_assembly_path = p_api_assemblies_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll"); - String cached_api_hash_path = p_api_assemblies_dir.plus_file("api_hash_cache.cfg"); - - Ref<ConfigFile> cfg; - cfg.instantiate(); - - cfg->set_value("core", "modified_time", FileAccess::get_modified_time(core_api_assembly_path)); - cfg->set_value("editor", "modified_time", FileAccess::get_modified_time(editor_api_assembly_path)); - - // This assumes the prebuilt api assemblies we copied to the project are not out of sync - cfg->set_value("core", "api_hash", GDMono::get_singleton()->get_api_core_hash()); - cfg->set_value("editor", "api_hash", GDMono::get_singleton()->get_api_editor_hash()); - - Error err = cfg->save(cached_api_hash_path); - ERR_FAIL_COND(err != OK); -} - -bool GDMono::_temp_domain_load_are_assemblies_out_of_sync(const String &p_config) { - MonoDomain *temp_domain = GDMonoUtils::create_domain("GodotEngine.Domain.CheckApiAssemblies"); - ERR_FAIL_NULL_V(temp_domain, "Failed to create temporary domain to check API assemblies"); - _GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(temp_domain); - - _GDMONO_SCOPE_DOMAIN_(temp_domain); - - GDMono::LoadedApiAssembly temp_core_api_assembly; - GDMono::LoadedApiAssembly temp_editor_api_assembly; - - if (!_try_load_api_assemblies(temp_core_api_assembly, temp_editor_api_assembly, - p_config, /* refonly: */ true, /* loaded_callback: */ nullptr)) { - return temp_core_api_assembly.out_of_sync || temp_editor_api_assembly.out_of_sync; - } - - return true; // Failed to load, assume they're outdated assemblies -} - -String GDMono::update_api_assemblies_from_prebuilt(const String &p_config, const bool *p_core_api_out_of_sync, const bool *p_editor_api_out_of_sync) { -#define FAIL_REASON(m_out_of_sync, m_prebuilt_exists) \ - ( \ - (m_out_of_sync ? String("The assembly is invalidated ") : String("The assembly was not found ")) + \ - (m_prebuilt_exists ? String("and the prebuilt assemblies are missing.") : String("and we failed to copy the prebuilt assemblies."))) - - String dst_assemblies_dir = GodotSharpDirs::get_res_assemblies_base_dir().plus_file(p_config); - - String core_assembly_path = dst_assemblies_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll"); - String editor_assembly_path = dst_assemblies_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll"); - - bool api_assemblies_out_of_sync = false; - - if (p_core_api_out_of_sync && p_editor_api_out_of_sync) { - api_assemblies_out_of_sync = *p_core_api_out_of_sync || *p_editor_api_out_of_sync; - } else if (FileAccess::exists(core_assembly_path) && FileAccess::exists(editor_assembly_path)) { - // Determine if they're out of sync - if (!try_get_cached_api_hash_for(dst_assemblies_dir, api_assemblies_out_of_sync)) { - api_assemblies_out_of_sync = _temp_domain_load_are_assemblies_out_of_sync(p_config); - } - } - - // Note: Even if only one of the assemblies if missing or out of sync, we update both - - if (!api_assemblies_out_of_sync && FileAccess::exists(core_assembly_path) && FileAccess::exists(editor_assembly_path)) { - return String(); // No update needed - } - - print_verbose("Updating '" + p_config + "' API assemblies"); - - String prebuilt_api_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file(p_config); - String prebuilt_core_dll_path = prebuilt_api_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll"); - String prebuilt_editor_dll_path = prebuilt_api_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll"); - - if (!FileAccess::exists(prebuilt_core_dll_path) || !FileAccess::exists(prebuilt_editor_dll_path)) { - return FAIL_REASON(api_assemblies_out_of_sync, /* prebuilt_exists: */ false); - } - - // Copy the prebuilt Api - if (!copy_prebuilt_api_assembly(ApiAssemblyInfo::API_CORE, p_config) || - !copy_prebuilt_api_assembly(ApiAssemblyInfo::API_EDITOR, p_config)) { - return FAIL_REASON(api_assemblies_out_of_sync, /* prebuilt_exists: */ true); - } - - // Cache the api hash of the assemblies we just copied - create_cached_api_hash_for(dst_assemblies_dir); - - return String(); // Updated successfully - -#undef FAIL_REASON + return load_assembly("mscorlib", &corlib_assembly); } -#endif -bool GDMono::_load_core_api_assembly(LoadedApiAssembly &r_loaded_api_assembly, const String &p_config, bool p_refonly) { - if (r_loaded_api_assembly.assembly) { +bool GDMono::_load_core_api_assembly(GDMonoAssembly **r_loaded_api_assembly, const String &p_config) { + if (*r_loaded_api_assembly) { return true; } -#ifdef TOOLS_ENABLED - // For the editor and the editor player we want to load it from a specific path to make sure we can keep it up to date - - // If running the project manager, load it from the prebuilt API directory - String assembly_dir = !Engine::get_singleton()->is_project_manager_hint() - ? GodotSharpDirs::get_res_assemblies_base_dir().plus_file(p_config) - : GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file(p_config); - - String assembly_path = assembly_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll"); - - bool success = FileAccess::exists(assembly_path) && - load_assembly_from(CORE_API_ASSEMBLY_NAME, assembly_path, &r_loaded_api_assembly.assembly, p_refonly); -#else - bool success = load_assembly(CORE_API_ASSEMBLY_NAME, &r_loaded_api_assembly.assembly, p_refonly); -#endif - -#ifdef DEBUG_METHODS_ENABLED - if (success) { - ApiAssemblyInfo::Version api_assembly_ver = ApiAssemblyInfo::Version::get_from_loaded_assembly(r_loaded_api_assembly.assembly, ApiAssemblyInfo::API_CORE); - r_loaded_api_assembly.out_of_sync = get_api_core_hash() != api_assembly_ver.godot_api_hash; - } else { - r_loaded_api_assembly.out_of_sync = false; - } -#else - r_loaded_api_assembly.out_of_sync = false; -#endif - - return success; + return load_assembly(CORE_API_ASSEMBLY_NAME, r_loaded_api_assembly); } #ifdef TOOLS_ENABLED -bool GDMono::_load_editor_api_assembly(LoadedApiAssembly &r_loaded_api_assembly, const String &p_config, bool p_refonly) { - if (r_loaded_api_assembly.assembly) { +bool GDMono::_load_editor_api_assembly(GDMonoAssembly **r_loaded_api_assembly, const String &p_config) { + if (*r_loaded_api_assembly) { return true; } - // For the editor and the editor player we want to load it from a specific path to make sure we can keep it up to date - - // If running the project manager, load it from the prebuilt API directory - String assembly_dir = !Engine::get_singleton()->is_project_manager_hint() - ? GodotSharpDirs::get_res_assemblies_base_dir().plus_file(p_config) - : GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file(p_config); - - String assembly_path = assembly_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll"); - - bool success = FileAccess::exists(assembly_path) && - load_assembly_from(EDITOR_API_ASSEMBLY_NAME, assembly_path, &r_loaded_api_assembly.assembly, p_refonly); - -#ifdef DEBUG_METHODS_ENABLED - if (success) { - ApiAssemblyInfo::Version api_assembly_ver = ApiAssemblyInfo::Version::get_from_loaded_assembly(r_loaded_api_assembly.assembly, ApiAssemblyInfo::API_EDITOR); - r_loaded_api_assembly.out_of_sync = get_api_editor_hash() != api_assembly_ver.godot_api_hash; - } else { - r_loaded_api_assembly.out_of_sync = false; - } -#else - r_loaded_api_assembly.out_of_sync = false; -#endif - - return success; + return load_assembly(EDITOR_API_ASSEMBLY_NAME, r_loaded_api_assembly); } #endif -bool GDMono::_try_load_api_assemblies(LoadedApiAssembly &r_core_api_assembly, LoadedApiAssembly &r_editor_api_assembly, - const String &p_config, bool p_refonly, CoreApiAssemblyLoadedCallback p_callback) { - if (!_load_core_api_assembly(r_core_api_assembly, p_config, p_refonly)) { +bool GDMono::_try_load_api_assemblies() { + String config = get_expected_api_build_config(); + + if (!_load_core_api_assembly(&core_api_assembly, config)) { if (OS::get_singleton()->is_stdout_verbose()) { print_error("Mono: Failed to load Core API assembly"); } @@ -824,30 +574,15 @@ bool GDMono::_try_load_api_assemblies(LoadedApiAssembly &r_core_api_assembly, Lo } #ifdef TOOLS_ENABLED - if (!_load_editor_api_assembly(r_editor_api_assembly, p_config, p_refonly)) { + if (!_load_editor_api_assembly(&editor_api_assembly, config)) { if (OS::get_singleton()->is_stdout_verbose()) { print_error("Mono: Failed to load Editor API assembly"); } return false; } - - if (r_editor_api_assembly.out_of_sync) { - return false; - } #endif - // Check if the core API assembly is out of sync only after trying to load the - // editor API assembly. Otherwise, if both assemblies are out of sync, we would - // only update the former as we won't know the latter also needs to be updated. - if (r_core_api_assembly.out_of_sync) { - return false; - } - - if (p_callback) { - return p_callback(); - } - - return true; + return _on_core_api_assembly_loaded(); } bool GDMono::_on_core_api_assembly_loaded() { @@ -862,72 +597,14 @@ bool GDMono::_on_core_api_assembly_loaded() { return true; } -bool GDMono::_try_load_api_assemblies_preset() { - return _try_load_api_assemblies(core_api_assembly, editor_api_assembly, - get_expected_api_build_config(), /* refonly: */ false, _on_core_api_assembly_loaded); -} - -void GDMono::_load_api_assemblies() { - bool api_assemblies_loaded = _try_load_api_assemblies_preset(); - -#if defined(TOOLS_ENABLED) && !defined(GD_MONO_SINGLE_APPDOMAIN) - if (!api_assemblies_loaded) { - // The API assemblies are out of sync or some other error happened. Fine, try one more time, but - // this time update them from the prebuilt assemblies directory before trying to load them again. - - // Shouldn't happen. The project manager loads the prebuilt API assemblies - CRASH_COND_MSG(Engine::get_singleton()->is_project_manager_hint(), "Failed to load one of the prebuilt API assemblies."); - - // 1. Unload the scripts domain - Error domain_unload_err = _unload_scripts_domain(); - CRASH_COND_MSG(domain_unload_err != OK, "Mono: Failed to unload scripts domain."); - - // 2. Update the API assemblies - String update_error = update_api_assemblies_from_prebuilt("Debug", &core_api_assembly.out_of_sync, &editor_api_assembly.out_of_sync); - CRASH_COND_MSG(!update_error.is_empty(), update_error); - - // 3. Load the scripts domain again - Error domain_load_err = _load_scripts_domain(); - CRASH_COND_MSG(domain_load_err != OK, "Mono: Failed to load scripts domain."); - - // 4. Try loading the updated assemblies - api_assemblies_loaded = _try_load_api_assemblies_preset(); - } -#endif - - if (!api_assemblies_loaded) { - // welp... too bad - - if (_are_api_assemblies_out_of_sync()) { - if (core_api_assembly.out_of_sync) { - ERR_PRINT("The assembly '" CORE_API_ASSEMBLY_NAME "' is out of sync."); - } else if (!GDMonoCache::cached_data.godot_api_cache_updated) { - ERR_PRINT("The loaded assembly '" CORE_API_ASSEMBLY_NAME "' is in sync, but the cache update failed."); - } - -#ifdef TOOLS_ENABLED - if (editor_api_assembly.out_of_sync) { - ERR_PRINT("The assembly '" EDITOR_API_ASSEMBLY_NAME "' is out of sync."); - } -#endif - - CRASH_NOW(); - } else { - CRASH_NOW_MSG("Failed to load one of the API assemblies."); - } - } -} - #ifdef TOOLS_ENABLED bool GDMono::_load_tools_assemblies() { if (tools_assembly && tools_project_editor_assembly) { return true; } - bool success = load_assembly(TOOLS_ASM_NAME, &tools_assembly) && - load_assembly(TOOLS_PROJECT_EDITOR_ASM_NAME, &tools_project_editor_assembly); - - return success; + return load_assembly(TOOLS_ASM_NAME, &tools_assembly) && + load_assembly(TOOLS_PROJECT_EDITOR_ASM_NAME, &tools_project_editor_assembly); } #endif @@ -946,7 +623,12 @@ bool GDMono::_load_project_assembly() { if (success) { mono_assembly_set_main(project_assembly->get_assembly()); - CSharpLanguage::get_singleton()->lookup_scripts_in_assembly(project_assembly); + MonoException *exc = nullptr; + GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_LookupScriptsInAssembly.invoke( + mono_assembly_get_object(mono_domain_get(), project_assembly->get_assembly()), &exc); + if (exc) { + GDMonoUtils::debug_print_unhandled_exception(exc); + } } return success; @@ -955,11 +637,8 @@ bool GDMono::_load_project_assembly() { void GDMono::_install_trace_listener() { #ifdef DEBUG_ENABLED // Install the trace listener now before the project assembly is loaded - GDMonoClass *debug_utils = get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, "DebuggingUtils"); - GDMonoMethod *install_func = debug_utils->get_method("InstallTraceListener"); - MonoException *exc = nullptr; - install_func->invoke_raw(nullptr, nullptr, &exc); + GDMonoCache::cached_data.methodthunk_DebuggingUtils_InstallTraceListener.invoke(&exc); if (exc) { GDMonoUtils::debug_print_unhandled_exception(exc); ERR_PRINT("Failed to install 'System.Diagnostics.Trace' listener."); @@ -1007,9 +686,9 @@ Error GDMono::_unload_scripts_domain() { _domain_assemblies_cleanup(mono_domain_get_id(scripts_domain)); - core_api_assembly.assembly = nullptr; + core_api_assembly = nullptr; #ifdef TOOLS_ENABLED - editor_api_assembly.assembly = nullptr; + editor_api_assembly = nullptr; #endif project_assembly = nullptr; @@ -1051,7 +730,9 @@ Error GDMono::reload_scripts_domain() { // Load assemblies. The API and tools assemblies are required, // the application is aborted if these assemblies cannot be loaded. - _load_api_assemblies(); + if (!_try_load_api_assemblies()) { + CRASH_NOW_MSG("Failed to load one of the API assemblies."); + } #if defined(TOOLS_ENABLED) bool tools_assemblies_loaded = _load_tools_assemblies(); @@ -1104,49 +785,6 @@ Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) { } #endif -GDMonoClass *GDMono::get_class(MonoClass *p_raw_class) { - MonoImage *image = mono_class_get_image(p_raw_class); - - if (image == corlib_assembly->get_image()) { - return corlib_assembly->get_class(p_raw_class); - } - - int32_t domain_id = mono_domain_get_id(mono_domain_get()); - HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[domain_id]; - - for (const KeyValue<String, GDMonoAssembly *> &E : domain_assemblies) { - GDMonoAssembly *assembly = E.value; - if (assembly->get_image() == image) { - GDMonoClass *klass = assembly->get_class(p_raw_class); - if (klass) { - return klass; - } - } - } - - return nullptr; -} - -GDMonoClass *GDMono::get_class(const StringName &p_namespace, const StringName &p_name) { - GDMonoClass *klass = corlib_assembly->get_class(p_namespace, p_name); - if (klass) { - return klass; - } - - int32_t domain_id = mono_domain_get_id(mono_domain_get()); - HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[domain_id]; - - for (const KeyValue<String, GDMonoAssembly *> &E : domain_assemblies) { - GDMonoAssembly *assembly = E.value; - klass = assembly->get_class(p_namespace, p_name); - if (klass) { - return klass; - } - } - - return nullptr; -} - void GDMono::_domain_assemblies_cleanup(int32_t p_domain_id) { HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[p_domain_id]; diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h index bc871fe750..ac99aecabd 100644 --- a/modules/mono/mono_gd/gd_mono.h +++ b/modules/mono/mono_gd/gd_mono.h @@ -41,31 +41,6 @@ #include "../utils/mono_reg_utils.h" #endif -namespace ApiAssemblyInfo { -enum Type { - API_CORE, - API_EDITOR -}; - -struct Version { - uint64_t godot_api_hash = 0; - - bool operator==(const Version &p_other) const { - return godot_api_hash == p_other.godot_api_hash; - } - - Version() {} - - Version(uint64_t p_godot_api_hash) : - godot_api_hash(p_godot_api_hash) { - } - - static Version get_from_loaded_assembly(GDMonoAssembly *p_api_assembly, Type p_api_type); -}; - -String to_string(Type p_type); -} // namespace ApiAssemblyInfo - class GDMono { public: enum UnhandledExceptionPolicy { @@ -73,13 +48,6 @@ public: POLICY_LOG_ERROR }; - struct LoadedApiAssembly { - GDMonoAssembly *assembly = nullptr; - bool out_of_sync = false; - - LoadedApiAssembly() {} - }; - private: bool runtime_initialized; bool finalizing_scripts_domain; @@ -98,17 +66,12 @@ private: GDMonoAssembly *tools_project_editor_assembly = nullptr; #endif - LoadedApiAssembly core_api_assembly; - LoadedApiAssembly editor_api_assembly; - - typedef bool (*CoreApiAssemblyLoadedCallback)(); + GDMonoAssembly *core_api_assembly; + GDMonoAssembly *editor_api_assembly; - bool _are_api_assemblies_out_of_sync(); - bool _temp_domain_load_are_assemblies_out_of_sync(const String &p_config); - - bool _load_core_api_assembly(LoadedApiAssembly &r_loaded_api_assembly, const String &p_config, bool p_refonly); + bool _load_core_api_assembly(GDMonoAssembly **r_loaded_api_assembly, const String &p_config); #ifdef TOOLS_ENABLED - bool _load_editor_api_assembly(LoadedApiAssembly &r_loaded_api_assembly, const String &p_config, bool p_refonly); + bool _load_editor_api_assembly(GDMonoAssembly **r_loaded_api_assembly, const String &p_config); #endif static bool _on_core_api_assembly_loaded(); @@ -119,10 +82,7 @@ private: #endif bool _load_project_assembly(); - bool _try_load_api_assemblies(LoadedApiAssembly &r_core_api_assembly, LoadedApiAssembly &r_editor_api_assembly, - const String &p_config, bool p_refonly, CoreApiAssemblyLoadedCallback p_callback); - bool _try_load_api_assemblies_preset(); - void _load_api_assemblies(); + bool _try_load_api_assemblies(); void _install_trace_listener(); @@ -184,11 +144,6 @@ public: #endif } -#ifdef TOOLS_ENABLED - bool copy_prebuilt_api_assembly(ApiAssemblyInfo::Type p_api_type, const String &p_config); - String update_api_assemblies_from_prebuilt(const String &p_config, const bool *p_core_api_out_of_sync = nullptr, const bool *p_editor_api_out_of_sync = nullptr); -#endif - static GDMono *get_singleton() { return singleton; } [[noreturn]] static void unhandled_exception_hook(MonoObject *p_exc, void *p_user_data); @@ -206,29 +161,24 @@ public: _FORCE_INLINE_ MonoDomain *get_scripts_domain() { return scripts_domain; } _FORCE_INLINE_ GDMonoAssembly *get_corlib_assembly() const { return corlib_assembly; } - _FORCE_INLINE_ GDMonoAssembly *get_core_api_assembly() const { return core_api_assembly.assembly; } + _FORCE_INLINE_ GDMonoAssembly *get_core_api_assembly() const { return core_api_assembly; } _FORCE_INLINE_ GDMonoAssembly *get_project_assembly() const { return project_assembly; } #ifdef TOOLS_ENABLED - _FORCE_INLINE_ GDMonoAssembly *get_editor_api_assembly() const { return editor_api_assembly.assembly; } _FORCE_INLINE_ GDMonoAssembly *get_tools_assembly() const { return tools_assembly; } - _FORCE_INLINE_ GDMonoAssembly *get_tools_project_editor_assembly() const { return tools_project_editor_assembly; } #endif #if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED) const MonoRegInfo &get_mono_reg_info() { return mono_reg_info; } #endif - GDMonoClass *get_class(MonoClass *p_raw_class); - GDMonoClass *get_class(const StringName &p_namespace, const StringName &p_name); - #ifdef GD_MONO_HOT_RELOAD Error reload_scripts_domain(); #endif - bool load_assembly(const String &p_name, GDMonoAssembly **r_assembly, bool p_refonly = false); - bool load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly = false); - bool load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly, const Vector<String> &p_search_dirs); - bool load_assembly_from(const String &p_name, const String &p_path, GDMonoAssembly **r_assembly, bool p_refonly = false); + bool load_assembly(const String &p_name, GDMonoAssembly **r_assembly); + bool load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly); + bool load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, const Vector<String> &p_search_dirs); + bool load_assembly_from(const String &p_name, const String &p_path, GDMonoAssembly **r_assembly); Error finalize_and_unload_domain(MonoDomain *p_domain); diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp index 42c6b6305f..605216d331 100644 --- a/modules/mono/mono_gd/gd_mono_assembly.cpp +++ b/modules/mono/mono_gd/gd_mono_assembly.cpp @@ -40,8 +40,8 @@ #include "core/templates/list.h" #include "../godotsharp_dirs.h" +#include "gd_mono.h" #include "gd_mono_cache.h" -#include "gd_mono_class.h" Vector<String> GDMonoAssembly::search_dirs; @@ -73,21 +73,18 @@ void GDMonoAssembly::fill_search_dirs(Vector<String> &r_search_dirs, const Strin } if (p_custom_config.is_empty()) { - r_search_dirs.push_back(GodotSharpDirs::get_res_assemblies_dir()); + r_search_dirs.push_back(GodotSharpDirs::get_api_assemblies_dir()); } else { String api_config = p_custom_config == "ExportRelease" ? "Release" : "Debug"; - r_search_dirs.push_back(GodotSharpDirs::get_res_assemblies_base_dir().plus_file(api_config)); + r_search_dirs.push_back(GodotSharpDirs::get_api_assemblies_base_dir().plus_file(api_config)); } - r_search_dirs.push_back(GodotSharpDirs::get_res_assemblies_base_dir()); + r_search_dirs.push_back(GodotSharpDirs::get_api_assemblies_base_dir()); r_search_dirs.push_back(OS::get_singleton()->get_resource_dir()); r_search_dirs.push_back(OS::get_singleton()->get_executable_path().get_base_dir()); #ifdef TOOLS_ENABLED r_search_dirs.push_back(GodotSharpDirs::get_data_editor_tools_dir()); - - // For GodotTools to find the api assemblies - r_search_dirs.push_back(GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file("Debug")); #endif } @@ -330,13 +327,6 @@ no_pdb: void GDMonoAssembly::unload() { ERR_FAIL_NULL(image); // Should not be called if already unloaded - for (const KeyValue<MonoClass *, GDMonoClass *> &E : cached_raw) { - memdelete(E.value); - } - - cached_classes.clear(); - cached_raw.clear(); - assembly = nullptr; image = nullptr; } @@ -345,90 +335,6 @@ String GDMonoAssembly::get_path() const { return String::utf8(mono_image_get_filename(image)); } -bool GDMonoAssembly::has_attribute(GDMonoClass *p_attr_class) { -#ifdef DEBUG_ENABLED - ERR_FAIL_NULL_V(p_attr_class, false); -#endif - - if (!attrs_fetched) { - fetch_attributes(); - } - - if (!attributes) { - return false; - } - - return mono_custom_attrs_has_attr(attributes, p_attr_class->get_mono_ptr()); -} - -MonoObject *GDMonoAssembly::get_attribute(GDMonoClass *p_attr_class) { -#ifdef DEBUG_ENABLED - ERR_FAIL_NULL_V(p_attr_class, nullptr); -#endif - - if (!attrs_fetched) { - fetch_attributes(); - } - - if (!attributes) { - return nullptr; - } - - return mono_custom_attrs_get_attr(attributes, p_attr_class->get_mono_ptr()); -} - -void GDMonoAssembly::fetch_attributes() { - ERR_FAIL_COND(attributes != nullptr); - - attributes = mono_custom_attrs_from_assembly(assembly); - attrs_fetched = true; -} - -GDMonoClass *GDMonoAssembly::get_class(const StringName &p_namespace, const StringName &p_name) { - ERR_FAIL_NULL_V(image, nullptr); - - ClassKey key(p_namespace, p_name); - - GDMonoClass **match = cached_classes.getptr(key); - - if (match) { - return *match; - } - - MonoClass *mono_class = mono_class_from_name(image, String(p_namespace).utf8(), String(p_name).utf8()); - - if (!mono_class) { - return nullptr; - } - - GDMonoClass *wrapped_class = memnew(GDMonoClass(p_namespace, p_name, mono_class, this)); - - cached_classes[key] = wrapped_class; - cached_raw[mono_class] = wrapped_class; - - return wrapped_class; -} - -GDMonoClass *GDMonoAssembly::get_class(MonoClass *p_mono_class) { - ERR_FAIL_NULL_V(image, nullptr); - - HashMap<MonoClass *, GDMonoClass *>::Iterator match = cached_raw.find(p_mono_class); - - if (match) { - return match->value; - } - - StringName namespace_name = String::utf8(mono_class_get_namespace(p_mono_class)); - StringName class_name = String::utf8(mono_class_get_name(p_mono_class)); - - GDMonoClass *wrapped_class = memnew(GDMonoClass(namespace_name, class_name, p_mono_class, this)); - - cached_classes[ClassKey(namespace_name, class_name)] = wrapped_class; - cached_raw[p_mono_class] = wrapped_class; - - return wrapped_class; -} - GDMonoAssembly *GDMonoAssembly::load(const String &p_name, MonoAssemblyName *p_aname, bool p_refonly, const Vector<String> &p_search_dirs) { if (GDMono::get_singleton()->get_corlib_assembly() && (p_name == "mscorlib" || p_name == "mscorlib.dll")) { return GDMono::get_singleton()->get_corlib_assembly(); diff --git a/modules/mono/mono_gd/gd_mono_assembly.h b/modules/mono/mono_gd/gd_mono_assembly.h index 0a3ae6c4fe..f67a3e0f25 100644 --- a/modules/mono/mono_gd/gd_mono_assembly.h +++ b/modules/mono/mono_gd/gd_mono_assembly.h @@ -40,47 +40,14 @@ #include "gd_mono_utils.h" class GDMonoAssembly { - struct ClassKey { - struct Hasher { - static _FORCE_INLINE_ uint32_t hash(const ClassKey &p_key) { - uint32_t hash = 0; - - GDMonoUtils::hash_combine(hash, p_key.namespace_name.hash()); - GDMonoUtils::hash_combine(hash, p_key.class_name.hash()); - - return hash; - } - }; - - _FORCE_INLINE_ bool operator==(const ClassKey &p_a) const { - return p_a.class_name == class_name && p_a.namespace_name == namespace_name; - } - - ClassKey() {} - - ClassKey(const StringName &p_namespace_name, const StringName &p_class_name) { - namespace_name = p_namespace_name; - class_name = p_class_name; - } - - StringName namespace_name; - StringName class_name; - }; - String name; MonoImage *image = nullptr; MonoAssembly *assembly = nullptr; - bool attrs_fetched = false; - MonoCustomAttrInfo *attributes = nullptr; - #ifdef GD_MONO_HOT_RELOAD uint64_t modified_time = 0; #endif - HashMap<ClassKey, GDMonoClass *, ClassKey::Hasher> cached_classes; - HashMap<MonoClass *, GDMonoClass *> cached_raw; - static Vector<String> search_dirs; static void assembly_load_hook(MonoAssembly *assembly, void *user_data); @@ -111,14 +78,6 @@ public: String get_path() const; - bool has_attribute(GDMonoClass *p_attr_class); - MonoObject *get_attribute(GDMonoClass *p_attr_class); - - void fetch_attributes(); - - GDMonoClass *get_class(const StringName &p_namespace, const StringName &p_name); - GDMonoClass *get_class(MonoClass *p_mono_class); - static String find_assembly(const String &p_name); static void fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config = String(), const String &p_custom_bcl_dir = String()); diff --git a/modules/mono/mono_gd/gd_mono_cache.cpp b/modules/mono/mono_gd/gd_mono_cache.cpp index 54e49cf5f5..37c8e7d021 100644 --- a/modules/mono/mono_gd/gd_mono_cache.cpp +++ b/modules/mono/mono_gd/gd_mono_cache.cpp @@ -31,210 +31,76 @@ #include "gd_mono_cache.h" #include "gd_mono.h" -#include "gd_mono_class.h" -#include "gd_mono_marshal.h" -#include "gd_mono_method.h" #include "gd_mono_utils.h" namespace GDMonoCache { CachedData cached_data; -#define CACHE_AND_CHECK(m_var, m_val) \ - { \ - CRASH_COND(m_var != nullptr); \ - m_var = m_val; \ - ERR_FAIL_COND_MSG(m_var == nullptr, "Mono Cache: Member " #m_var " is null."); \ - } +static MonoMethod *get_mono_method(MonoClass *p_mono_class, const char *p_method_name, int p_param_count) { + ERR_FAIL_NULL_V(p_mono_class, nullptr); + return mono_class_get_method_from_name(p_mono_class, p_method_name, p_param_count); +} + +static MonoClass *get_mono_class(GDMonoAssembly *p_assembly, const char *p_namespace, const char *p_name) { + ERR_FAIL_NULL_V(p_assembly->get_image(), nullptr); + return mono_class_from_name(p_assembly->get_image(), p_namespace, p_name); +} -#define CACHE_CLASS_AND_CHECK(m_class, m_val) CACHE_AND_CHECK(cached_data.class_##m_class, m_val) -#define CACHE_FIELD_AND_CHECK(m_class, m_field, m_val) CACHE_AND_CHECK(cached_data.field_##m_class##_##m_field, m_val) -#define CACHE_METHOD_AND_CHECK(m_class, m_method, m_val) CACHE_AND_CHECK(cached_data.method_##m_class##_##m_method, m_val) -#define CACHE_PROPERTY_AND_CHECK(m_class, m_property, m_val) CACHE_AND_CHECK(cached_data.property_##m_class##_##m_property, m_val) - -#define CACHE_METHOD_THUNK_AND_CHECK_IMPL(m_var, m_val) \ - { \ - CRASH_COND(!m_var.is_null()); \ - ERR_FAIL_COND_MSG(m_val == nullptr, "Mono Cache: Method for member " #m_var " is null."); \ - m_var.set_from_method(m_val); \ - ERR_FAIL_COND_MSG(m_var.is_null(), "Mono Cache: Member " #m_var " is null."); \ +void update_godot_api_cache() { +#define CACHE_METHOD_THUNK_AND_CHECK_IMPL(m_var, m_val) \ + { \ + CRASH_COND(!m_var.is_null()); \ + val = m_val; \ + ERR_FAIL_COND_MSG(val == nullptr, "Mono Cache: Method for member " #m_var " is null."); \ + m_var.set_from_method(val); \ + ERR_FAIL_COND_MSG(m_var.is_null(), "Mono Cache: Member " #m_var " is null."); \ } #define CACHE_METHOD_THUNK_AND_CHECK(m_class, m_method, m_val) CACHE_METHOD_THUNK_AND_CHECK_IMPL(cached_data.methodthunk_##m_class##_##m_method, m_val) -void CachedData::clear_corlib_cache() { - corlib_cache_updated = false; +#define GODOT_API_CLASS(m_class) (get_mono_class(GDMono::get_singleton()->get_core_api_assembly(), BINDINGS_NAMESPACE, #m_class)) +#define GODOT_API_BRIDGE_CLASS(m_class) (get_mono_class(GDMono::get_singleton()->get_core_api_assembly(), BINDINGS_NAMESPACE_BRIDGE, #m_class)) - class_MonoObject = nullptr; - class_String = nullptr; + MonoMethod *val = nullptr; -#ifdef DEBUG_ENABLED - class_System_Diagnostics_StackTrace = nullptr; - methodthunk_System_Diagnostics_StackTrace_GetFrames.nullify(); - method_System_Diagnostics_StackTrace_ctor_bool = nullptr; - method_System_Diagnostics_StackTrace_ctor_Exception_bool = nullptr; -#endif + CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, SignalCallback, get_mono_method(GODOT_API_CLASS(SignalAwaiter), "SignalCallback", 4)); - class_KeyNotFoundException = nullptr; -} + CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, InvokeWithVariantArgs, get_mono_method(GODOT_API_CLASS(DelegateUtils), "InvokeWithVariantArgs", 4)); + CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, DelegateEquals, get_mono_method(GODOT_API_CLASS(DelegateUtils), "DelegateEquals", 2)); -void CachedData::clear_godot_api_cache() { - godot_api_cache_updated = false; - - class_GodotObject = nullptr; - class_GodotResource = nullptr; - class_Node = nullptr; - class_Control = nullptr; - class_Callable = nullptr; - class_SignalInfo = nullptr; - class_ISerializationListener = nullptr; - -#ifdef DEBUG_ENABLED - class_DebuggingUtils = nullptr; - methodthunk_DebuggingUtils_GetStackFrameInfo.nullify(); -#endif - - class_ExportAttribute = nullptr; - field_ExportAttribute_hint = nullptr; - field_ExportAttribute_hintString = nullptr; - class_SignalAttribute = nullptr; - class_ToolAttribute = nullptr; - class_RPCAttribute = nullptr; - property_RPCAttribute_Mode = nullptr; - property_RPCAttribute_CallLocal = nullptr; - property_RPCAttribute_TransferMode = nullptr; - property_RPCAttribute_TransferChannel = nullptr; - class_GodotMethodAttribute = nullptr; - field_GodotMethodAttribute_methodName = nullptr; - class_ScriptPathAttribute = nullptr; - field_ScriptPathAttribute_path = nullptr; - class_AssemblyHasScriptsAttribute = nullptr; - field_AssemblyHasScriptsAttribute_requiresLookup = nullptr; - field_AssemblyHasScriptsAttribute_scriptTypes = nullptr; - - field_GodotObject_ptr = nullptr; - - methodthunk_GodotObject_Dispose.nullify(); - methodthunk_SignalAwaiter_SignalCallback.nullify(); - - methodthunk_Delegate_Equals.nullify(); - - methodthunk_DelegateUtils_TrySerializeDelegateWithGCHandle.nullify(); - methodthunk_DelegateUtils_TryDeserializeDelegateWithGCHandle.nullify(); - methodthunk_DelegateUtils_TrySerializeDelegate.nullify(); - methodthunk_DelegateUtils_TryDeserializeDelegate.nullify(); - methodthunk_DelegateUtils_InvokeWithVariantArgs.nullify(); - methodthunk_DelegateUtils_DelegateEquals.nullify(); - methodthunk_DelegateUtils_FreeGCHandle.nullify(); - - methodthunk_Marshaling_managed_to_variant_type.nullify(); - methodthunk_Marshaling_try_get_array_element_type.nullify(); - methodthunk_Marshaling_variant_to_mono_object_of_type.nullify(); - methodthunk_Marshaling_variant_to_mono_object.nullify(); - methodthunk_Marshaling_mono_object_to_variant_out.nullify(); - - methodthunk_Marshaling_SetFieldValue.nullify(); - - methodthunk_MarshalUtils_TypeHasFlagsAttribute.nullify(); -} + CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, FrameCallback, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "FrameCallback", 0)); + CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, CreateManagedForGodotObjectBinding, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "CreateManagedForGodotObjectBinding", 2)); + CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, CreateManagedForGodotObjectScriptInstance, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "CreateManagedForGodotObjectScriptInstance", 4)); + CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, GetScriptNativeName, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "GetScriptNativeName", 2)); + CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, LookupScriptsInAssembly, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "LookupScriptsInAssembly", 1)); + CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, SetGodotObjectPtr, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "SetGodotObjectPtr", 2)); + CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, RaiseEventSignal, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "RaiseEventSignal", 5)); + CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, GetScriptSignalList, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "GetScriptSignalList", 2)); + CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, HasScriptSignal, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "HasScriptSignal", 2)); + CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, HasMethodUnknownParams, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "HasMethodUnknownParams", 3)); + CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, ScriptIsOrInherits, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "ScriptIsOrInherits", 2)); + CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, AddScriptBridge, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "AddScriptBridge", 2)); + CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, RemoveScriptBridge, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "RemoveScriptBridge", 1)); + CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, UpdateScriptClassInfo, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "UpdateScriptClassInfo", 3)); + CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, SwapGCHandleForType, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "SwapGCHandleForType", 3)); -#define GODOT_API_CLASS(m_class) (GDMono::get_singleton()->get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, #m_class)) + CACHE_METHOD_THUNK_AND_CHECK(CSharpInstanceBridge, Call, get_mono_method(GODOT_API_BRIDGE_CLASS(CSharpInstanceBridge), "Call", 6)); + CACHE_METHOD_THUNK_AND_CHECK(CSharpInstanceBridge, Set, get_mono_method(GODOT_API_BRIDGE_CLASS(CSharpInstanceBridge), "Set", 3)); + CACHE_METHOD_THUNK_AND_CHECK(CSharpInstanceBridge, Get, get_mono_method(GODOT_API_BRIDGE_CLASS(CSharpInstanceBridge), "Get", 3)); + CACHE_METHOD_THUNK_AND_CHECK(CSharpInstanceBridge, CallDispose, get_mono_method(GODOT_API_BRIDGE_CLASS(CSharpInstanceBridge), "CallDispose", 2)); + CACHE_METHOD_THUNK_AND_CHECK(CSharpInstanceBridge, CallToString, get_mono_method(GODOT_API_BRIDGE_CLASS(CSharpInstanceBridge), "CallToString", 3)); -void update_corlib_cache() { - CACHE_CLASS_AND_CHECK(MonoObject, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_object_class())); - CACHE_CLASS_AND_CHECK(String, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_string_class())); + CACHE_METHOD_THUNK_AND_CHECK(GCHandleBridge, FreeGCHandle, get_mono_method(GODOT_API_BRIDGE_CLASS(GCHandleBridge), "FreeGCHandle", 1)); -#ifdef DEBUG_ENABLED - CACHE_CLASS_AND_CHECK(System_Diagnostics_StackTrace, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Diagnostics", "StackTrace")); - CACHE_METHOD_THUNK_AND_CHECK(System_Diagnostics_StackTrace, GetFrames, CACHED_CLASS(System_Diagnostics_StackTrace)->get_method("GetFrames")); - CACHE_METHOD_AND_CHECK(System_Diagnostics_StackTrace, ctor_bool, CACHED_CLASS(System_Diagnostics_StackTrace)->get_method_with_desc("System.Diagnostics.StackTrace:.ctor(bool)", true)); - CACHE_METHOD_AND_CHECK(System_Diagnostics_StackTrace, ctor_Exception_bool, CACHED_CLASS(System_Diagnostics_StackTrace)->get_method_with_desc("System.Diagnostics.StackTrace:.ctor(System.Exception,bool)", true)); -#endif - - CACHE_METHOD_THUNK_AND_CHECK(Delegate, Equals, GDMono::get_singleton()->get_corlib_assembly()->get_class("System", "Delegate")->get_method_with_desc("System.Delegate:Equals(object)", true)); - - CACHE_CLASS_AND_CHECK(KeyNotFoundException, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections.Generic", "KeyNotFoundException")); - - cached_data.corlib_cache_updated = true; -} - -void update_godot_api_cache() { - CACHE_CLASS_AND_CHECK(GodotObject, GODOT_API_CLASS(Object)); - CACHE_CLASS_AND_CHECK(GodotResource, GODOT_API_CLASS(Resource)); - CACHE_CLASS_AND_CHECK(Node, GODOT_API_CLASS(Node)); - CACHE_CLASS_AND_CHECK(Control, GODOT_API_CLASS(Control)); - CACHE_CLASS_AND_CHECK(Callable, GODOT_API_CLASS(Callable)); - CACHE_CLASS_AND_CHECK(SignalInfo, GODOT_API_CLASS(SignalInfo)); - CACHE_CLASS_AND_CHECK(ISerializationListener, GODOT_API_CLASS(ISerializationListener)); - -#ifdef DEBUG_ENABLED - CACHE_CLASS_AND_CHECK(DebuggingUtils, GODOT_API_CLASS(DebuggingUtils)); -#endif - - // Attributes - CACHE_CLASS_AND_CHECK(ExportAttribute, GODOT_API_CLASS(ExportAttribute)); - CACHE_FIELD_AND_CHECK(ExportAttribute, hint, CACHED_CLASS(ExportAttribute)->get_field("hint")); - CACHE_FIELD_AND_CHECK(ExportAttribute, hintString, CACHED_CLASS(ExportAttribute)->get_field("hintString")); - CACHE_CLASS_AND_CHECK(SignalAttribute, GODOT_API_CLASS(SignalAttribute)); - CACHE_CLASS_AND_CHECK(ToolAttribute, GODOT_API_CLASS(ToolAttribute)); - CACHE_CLASS_AND_CHECK(RPCAttribute, GODOT_API_CLASS(RPCAttribute)); - CACHE_PROPERTY_AND_CHECK(RPCAttribute, Mode, CACHED_CLASS(RPCAttribute)->get_property("Mode")); - CACHE_PROPERTY_AND_CHECK(RPCAttribute, CallLocal, CACHED_CLASS(RPCAttribute)->get_property("CallLocal")); - CACHE_PROPERTY_AND_CHECK(RPCAttribute, TransferMode, CACHED_CLASS(RPCAttribute)->get_property("TransferMode")); - CACHE_PROPERTY_AND_CHECK(RPCAttribute, TransferChannel, CACHED_CLASS(RPCAttribute)->get_property("TransferChannel")); - CACHE_CLASS_AND_CHECK(GodotMethodAttribute, GODOT_API_CLASS(GodotMethodAttribute)); - CACHE_FIELD_AND_CHECK(GodotMethodAttribute, methodName, CACHED_CLASS(GodotMethodAttribute)->get_field("methodName")); - CACHE_CLASS_AND_CHECK(ScriptPathAttribute, GODOT_API_CLASS(ScriptPathAttribute)); - CACHE_FIELD_AND_CHECK(ScriptPathAttribute, path, CACHED_CLASS(ScriptPathAttribute)->get_field("path")); - CACHE_CLASS_AND_CHECK(AssemblyHasScriptsAttribute, GODOT_API_CLASS(AssemblyHasScriptsAttribute)); - CACHE_FIELD_AND_CHECK(AssemblyHasScriptsAttribute, requiresLookup, CACHED_CLASS(AssemblyHasScriptsAttribute)->get_field("requiresLookup")); - CACHE_FIELD_AND_CHECK(AssemblyHasScriptsAttribute, scriptTypes, CACHED_CLASS(AssemblyHasScriptsAttribute)->get_field("scriptTypes")); - - CACHE_FIELD_AND_CHECK(GodotObject, ptr, CACHED_CLASS(GodotObject)->get_field(BINDINGS_PTR_FIELD)); - - 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(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)); - CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, TrySerializeDelegate, GODOT_API_CLASS(DelegateUtils)->get_method("TrySerializeDelegate", 2)); - CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, TryDeserializeDelegate, GODOT_API_CLASS(DelegateUtils)->get_method("TryDeserializeDelegate", 2)); - CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, InvokeWithVariantArgs, GODOT_API_CLASS(DelegateUtils)->get_method("InvokeWithVariantArgs", 4)); - CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, DelegateEquals, GODOT_API_CLASS(DelegateUtils)->get_method("DelegateEquals", 2)); - CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, FreeGCHandle, GODOT_API_CLASS(DelegateUtils)->get_method("FreeGCHandle", 1)); - - GDMonoClass *gd_mono_marshal_class = GDMono::get_singleton()->get_core_api_assembly()->get_class( - "Godot.NativeInterop", "Marshaling"); - - ERR_FAIL_COND_MSG(gd_mono_marshal_class == nullptr, - "Mono Cache: Class `Godot.NativeInterop.Marshaling` not found."); - - CACHE_METHOD_THUNK_AND_CHECK(Marshaling, managed_to_variant_type, - gd_mono_marshal_class->get_method("managed_to_variant_type", 2)); - CACHE_METHOD_THUNK_AND_CHECK(Marshaling, try_get_array_element_type, - gd_mono_marshal_class->get_method("try_get_array_element_type", 2)); - CACHE_METHOD_THUNK_AND_CHECK(Marshaling, variant_to_mono_object_of_type, - gd_mono_marshal_class->get_method("variant_to_mono_object_of_type", 2)); - CACHE_METHOD_THUNK_AND_CHECK(Marshaling, variant_to_mono_object, - gd_mono_marshal_class->get_method("variant_to_mono_object", 1)); - CACHE_METHOD_THUNK_AND_CHECK(Marshaling, mono_object_to_variant_out, - gd_mono_marshal_class->get_method("mono_object_to_variant_out", 3)); - - CACHE_METHOD_THUNK_AND_CHECK(Marshaling, SetFieldValue, - gd_mono_marshal_class->get_method("SetFieldValue", 3)); - - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeHasFlagsAttribute, GODOT_API_CLASS(MarshalUtils)->get_method("TypeHasFlagsAttribute", 1)); - -#ifdef DEBUG_ENABLED - CACHE_METHOD_THUNK_AND_CHECK(DebuggingUtils, GetStackFrameInfo, GODOT_API_CLASS(DebuggingUtils)->get_method("GetStackFrameInfo", 4)); -#endif + CACHE_METHOD_THUNK_AND_CHECK(DebuggingUtils, InstallTraceListener, get_mono_method(GODOT_API_CLASS(DebuggingUtils), "InstallTraceListener", 0)); MonoException *exc = nullptr; - GDMono::get_singleton() - ->get_core_api_assembly() - ->get_class("Godot", "Dispatcher") - ->get_method("InitializeDefaultGodotTaskScheduler") - ->invoke(nullptr, &exc); + MonoMethod *init_default_godot_task_scheduler = + get_mono_method(GODOT_API_CLASS(Dispatcher), "InitializeDefaultGodotTaskScheduler", 0); + ERR_FAIL_COND_MSG(init_default_godot_task_scheduler == nullptr, + "Mono Cache: InitializeDefaultGodotTaskScheduler is null."); + mono_runtime_invoke(init_default_godot_task_scheduler, nullptr, nullptr, (MonoObject **)&exc); if (exc) { GDMonoUtils::debug_unhandled_exception(exc); diff --git a/modules/mono/mono_gd/gd_mono_cache.h b/modules/mono/mono_gd/gd_mono_cache.h index 5876583260..5785347938 100644 --- a/modules/mono/mono_gd/gd_mono_cache.h +++ b/modules/mono/mono_gd/gd_mono_cache.h @@ -31,115 +31,57 @@ #ifndef GD_MONO_CACHE_H #define GD_MONO_CACHE_H -#include "gd_mono_header.h" #include "gd_mono_method_thunk.h" +class CSharpScript; + namespace GDMonoCache { struct CachedData { - // ----------------------------------------------- - // corlib classes - - // Let's use the no-namespace format for these too - GDMonoClass *class_MonoObject = nullptr; // object - GDMonoClass *class_String = nullptr; // string - -#ifdef DEBUG_ENABLED - GDMonoClass *class_System_Diagnostics_StackTrace = nullptr; - GDMonoMethodThunkR<MonoArray *, MonoObject *> methodthunk_System_Diagnostics_StackTrace_GetFrames; - GDMonoMethod *method_System_Diagnostics_StackTrace_ctor_bool = nullptr; - GDMonoMethod *method_System_Diagnostics_StackTrace_ctor_Exception_bool = nullptr; -#endif - - GDMonoClass *class_KeyNotFoundException = nullptr; - // ----------------------------------------------- - - GDMonoClass *class_GodotObject = nullptr; - GDMonoClass *class_GodotResource = nullptr; - GDMonoClass *class_Node = nullptr; - GDMonoClass *class_Control = nullptr; - GDMonoClass *class_Callable = nullptr; - GDMonoClass *class_SignalInfo = nullptr; - GDMonoClass *class_ISerializationListener = nullptr; - -#ifdef DEBUG_ENABLED - GDMonoClass *class_DebuggingUtils = nullptr; - GDMonoMethodThunk<MonoObject *, MonoString **, int *, MonoString **> methodthunk_DebuggingUtils_GetStackFrameInfo; -#endif - - GDMonoClass *class_ExportAttribute = nullptr; - GDMonoField *field_ExportAttribute_hint = nullptr; - GDMonoField *field_ExportAttribute_hintString = nullptr; - GDMonoClass *class_SignalAttribute = nullptr; - GDMonoClass *class_ToolAttribute = nullptr; - GDMonoClass *class_RPCAttribute = nullptr; - GDMonoProperty *property_RPCAttribute_Mode = nullptr; - GDMonoProperty *property_RPCAttribute_CallLocal = nullptr; - GDMonoProperty *property_RPCAttribute_TransferMode = nullptr; - GDMonoProperty *property_RPCAttribute_TransferChannel = nullptr; - GDMonoClass *class_GodotMethodAttribute = nullptr; - GDMonoField *field_GodotMethodAttribute_methodName = nullptr; - GDMonoClass *class_ScriptPathAttribute = nullptr; - GDMonoField *field_ScriptPathAttribute_path = nullptr; - GDMonoClass *class_AssemblyHasScriptsAttribute = nullptr; - GDMonoField *field_AssemblyHasScriptsAttribute_requiresLookup = nullptr; - GDMonoField *field_AssemblyHasScriptsAttribute_scriptTypes = nullptr; - - - GDMonoField *field_GodotObject_ptr = nullptr; - - GDMonoMethodThunk<MonoObject *> methodthunk_GodotObject_Dispose; - GDMonoMethodThunk<MonoObject *, MonoArray *> methodthunk_SignalAwaiter_SignalCallback; - - GDMonoMethodThunkR<MonoBoolean, MonoObject *, MonoObject *> methodthunk_Delegate_Equals; - - GDMonoMethodThunkR<MonoBoolean, void *, MonoObject *> methodthunk_DelegateUtils_TrySerializeDelegateWithGCHandle; - GDMonoMethodThunkR<MonoBoolean, MonoObject *, void **> methodthunk_DelegateUtils_TryDeserializeDelegateWithGCHandle; - - GDMonoMethodThunkR<MonoBoolean, MonoDelegate *, MonoObject *> methodthunk_DelegateUtils_TrySerializeDelegate; - GDMonoMethodThunkR<MonoBoolean, MonoObject *, MonoDelegate **> methodthunk_DelegateUtils_TryDeserializeDelegate; - - GDMonoMethodThunk<void *, const Variant **, uint32_t, const Variant *> methodthunk_DelegateUtils_InvokeWithVariantArgs; - GDMonoMethodThunkR<MonoBoolean, void *, void *> methodthunk_DelegateUtils_DelegateEquals; - GDMonoMethodThunk<void *> methodthunk_DelegateUtils_FreeGCHandle; - - GDMonoMethodThunkR<int32_t, MonoReflectionType *, MonoBoolean *> methodthunk_Marshaling_managed_to_variant_type; - GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *, MonoReflectionType **> methodthunk_Marshaling_try_get_array_element_type; - GDMonoMethodThunkR<MonoObject *, const Variant *, MonoReflectionType *> methodthunk_Marshaling_variant_to_mono_object_of_type; - GDMonoMethodThunkR<MonoObject *, const Variant *> methodthunk_Marshaling_variant_to_mono_object; - GDMonoMethodThunk<MonoObject *, MonoBoolean, Variant *> methodthunk_Marshaling_mono_object_to_variant_out; - - GDMonoMethodThunk<MonoReflectionField *, MonoObject *, const Variant *> methodthunk_Marshaling_SetFieldValue; - - GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeHasFlagsAttribute; - - bool corlib_cache_updated; - bool godot_api_cache_updated; - - void clear_corlib_cache(); - void clear_godot_api_cache(); - - CachedData() { - clear_corlib_cache(); - clear_godot_api_cache(); - } + // Mono method thunks require structs to be boxed, even if passed by ref (out, ref, in). + // As such we need to use pointers instead for now, instead of by ref parameters. + + GDMonoMethodThunk<GCHandleIntPtr, const Variant **, int, bool *> methodthunk_SignalAwaiter_SignalCallback; + + GDMonoMethodThunk<GCHandleIntPtr, const Variant **, uint32_t, const Variant *> methodthunk_DelegateUtils_InvokeWithVariantArgs; + GDMonoMethodThunkR<bool, GCHandleIntPtr, GCHandleIntPtr> methodthunk_DelegateUtils_DelegateEquals; + + GDMonoMethodThunk<> methodthunk_ScriptManagerBridge_FrameCallback; + GDMonoMethodThunkR<GCHandleIntPtr, const StringName *, Object *> methodthunk_ScriptManagerBridge_CreateManagedForGodotObjectBinding; + GDMonoMethodThunk<const CSharpScript *, Object *, const Variant **, int> methodthunk_ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance; + GDMonoMethodThunk<const CSharpScript *, StringName *> methodthunk_ScriptManagerBridge_GetScriptNativeName; + GDMonoMethodThunk<MonoReflectionAssembly *> methodthunk_ScriptManagerBridge_LookupScriptsInAssembly; + GDMonoMethodThunk<GCHandleIntPtr, Object *> methodthunk_ScriptManagerBridge_SetGodotObjectPtr; + GDMonoMethodThunk<GCHandleIntPtr, const StringName *, const Variant **, int, bool *> methodthunk_ScriptManagerBridge_RaiseEventSignal; + GDMonoMethodThunk<const CSharpScript *, Dictionary *> methodthunk_ScriptManagerBridge_GetScriptSignalList; + GDMonoMethodThunkR<bool, const CSharpScript *, const String *> methodthunk_ScriptManagerBridge_HasScriptSignal; + GDMonoMethodThunkR<bool, const CSharpScript *, const String *, bool> methodthunk_ScriptManagerBridge_HasMethodUnknownParams; + GDMonoMethodThunkR<bool, const CSharpScript *, const CSharpScript *> methodthunk_ScriptManagerBridge_ScriptIsOrInherits; + GDMonoMethodThunkR<bool, const CSharpScript *, const String *> methodthunk_ScriptManagerBridge_AddScriptBridge; + GDMonoMethodThunk<const CSharpScript *> methodthunk_ScriptManagerBridge_RemoveScriptBridge; + GDMonoMethodThunk<const CSharpScript *, bool *, Dictionary *> methodthunk_ScriptManagerBridge_UpdateScriptClassInfo; + GDMonoMethodThunkR<bool, GCHandleIntPtr, GCHandleIntPtr *, bool> methodthunk_ScriptManagerBridge_SwapGCHandleForType; + + GDMonoMethodThunk<GCHandleIntPtr, const StringName *, const Variant **, int, Callable::CallError *, Variant *> methodthunk_CSharpInstanceBridge_Call; + GDMonoMethodThunkR<bool, GCHandleIntPtr, const StringName *, const Variant *> methodthunk_CSharpInstanceBridge_Set; + GDMonoMethodThunkR<bool, GCHandleIntPtr, const StringName *, Variant *> methodthunk_CSharpInstanceBridge_Get; + GDMonoMethodThunk<GCHandleIntPtr, bool> methodthunk_CSharpInstanceBridge_CallDispose; + GDMonoMethodThunk<GCHandleIntPtr, String *, bool *> methodthunk_CSharpInstanceBridge_CallToString; + + GDMonoMethodThunk<GCHandleIntPtr> methodthunk_GCHandleBridge_FreeGCHandle; + + GDMonoMethodThunk<> methodthunk_DebuggingUtils_InstallTraceListener; + + bool godot_api_cache_updated = false; }; extern CachedData cached_data; -void update_corlib_cache(); void update_godot_api_cache(); inline void clear_godot_api_cache() { - cached_data.clear_godot_api_cache(); + cached_data = CachedData(); } } // namespace GDMonoCache -#define CACHED_CLASS(m_class) (GDMonoCache::cached_data.class_##m_class) -#define CACHED_CLASS_RAW(m_class) (GDMonoCache::cached_data.class_##m_class->get_mono_ptr()) -#define CACHED_FIELD(m_class, m_field) (GDMonoCache::cached_data.field_##m_class##_##m_field) -#define CACHED_METHOD(m_class, m_method) (GDMonoCache::cached_data.method_##m_class##_##m_method) -#define CACHED_METHOD_THUNK(m_class, m_method) (GDMonoCache::cached_data.methodthunk_##m_class##_##m_method) -#define CACHED_PROPERTY(m_class, m_property) (GDMonoCache::cached_data.property_##m_class##_##m_property) - #endif // GD_MONO_CACHE_H diff --git a/modules/mono/mono_gd/gd_mono_class.cpp b/modules/mono/mono_gd/gd_mono_class.cpp deleted file mode 100644 index 24b46d2ce8..0000000000 --- a/modules/mono/mono_gd/gd_mono_class.cpp +++ /dev/null @@ -1,569 +0,0 @@ -/*************************************************************************/ -/* gd_mono_class.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 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 "gd_mono_class.h" - -#include <mono/metadata/attrdefs.h> -#include <mono/metadata/debug-helpers.h> - -#include "gd_mono_assembly.h" -#include "gd_mono_cache.h" -#include "gd_mono_marshal.h" - -String GDMonoClass::get_full_name(MonoClass *p_mono_class) { - // mono_type_get_full_name is not exposed to embedders, but this seems to do the job - MonoReflectionType *type_obj = mono_type_get_object(mono_domain_get(), get_mono_type(p_mono_class)); - - MonoException *exc = nullptr; - MonoString *str = GDMonoUtils::object_to_string((MonoObject *)type_obj, &exc); - UNHANDLED_EXCEPTION(exc); - - return GDMonoMarshal::mono_string_to_godot(str); -} - -MonoType *GDMonoClass::get_mono_type(MonoClass *p_mono_class) { - return mono_class_get_type(p_mono_class); -} - -String GDMonoClass::get_full_name() const { - return get_full_name(mono_class); -} - -String GDMonoClass::get_type_desc() const { - return GDMonoUtils::get_type_desc(get_mono_type()); -} - -MonoType *GDMonoClass::get_mono_type() const { - // Careful, you cannot compare two MonoType*. - // There is mono_metadata_type_equal, how is this different from comparing two MonoClass*? - return get_mono_type(mono_class); -} - -uint32_t GDMonoClass::get_flags() const { - return mono_class_get_flags(mono_class); -} - -bool GDMonoClass::is_static() const { - uint32_t static_class_flags = MONO_TYPE_ATTR_ABSTRACT | MONO_TYPE_ATTR_SEALED; - return (get_flags() & static_class_flags) == static_class_flags; -} - -bool GDMonoClass::is_assignable_from(GDMonoClass *p_from) const { - return mono_class_is_assignable_from(mono_class, p_from->mono_class); -} - -StringName GDMonoClass::get_namespace() const { - GDMonoClass *nesting_class = get_nesting_class(); - if (!nesting_class) { - return namespace_name; - } - return nesting_class->get_namespace(); -} - -String GDMonoClass::get_name_for_lookup() const { - GDMonoClass *nesting_class = get_nesting_class(); - if (!nesting_class) { - return class_name; - } - return nesting_class->get_name_for_lookup() + "/" + class_name; -} - -GDMonoClass *GDMonoClass::get_parent_class() const { - MonoClass *parent_mono_class = mono_class_get_parent(mono_class); - return parent_mono_class ? GDMono::get_singleton()->get_class(parent_mono_class) : nullptr; -} - -GDMonoClass *GDMonoClass::get_nesting_class() const { - MonoClass *nesting_type = mono_class_get_nesting_type(mono_class); - return nesting_type ? GDMono::get_singleton()->get_class(nesting_type) : nullptr; -} - -#ifdef TOOLS_ENABLED -Vector<MonoClassField *> GDMonoClass::get_enum_fields() { - bool class_is_enum = mono_class_is_enum(mono_class); - ERR_FAIL_COND_V(!class_is_enum, Vector<MonoClassField *>()); - - Vector<MonoClassField *> enum_fields; - - void *iter = nullptr; - MonoClassField *raw_field = nullptr; - while ((raw_field = mono_class_get_fields(get_mono_ptr(), &iter)) != nullptr) { - uint32_t field_flags = mono_field_get_flags(raw_field); - - // Enums have an instance field named value__ which holds the value of the enum. - // Enum constants are static, so we will use this to ignore the value__ field. - if (field_flags & MONO_FIELD_ATTR_PUBLIC && field_flags & MONO_FIELD_ATTR_STATIC) { - enum_fields.push_back(raw_field); - } - } - - return enum_fields; -} -#endif - -bool GDMonoClass::has_attribute(GDMonoClass *p_attr_class) { -#ifdef DEBUG_ENABLED - ERR_FAIL_NULL_V(p_attr_class, false); -#endif - - if (!attrs_fetched) { - fetch_attributes(); - } - - if (!attributes) { - return false; - } - - return mono_custom_attrs_has_attr(attributes, p_attr_class->get_mono_ptr()); -} - -MonoObject *GDMonoClass::get_attribute(GDMonoClass *p_attr_class) { -#ifdef DEBUG_ENABLED - ERR_FAIL_NULL_V(p_attr_class, nullptr); -#endif - - if (!attrs_fetched) { - fetch_attributes(); - } - - if (!attributes) { - return nullptr; - } - - return mono_custom_attrs_get_attr(attributes, p_attr_class->get_mono_ptr()); -} - -void GDMonoClass::fetch_attributes() { - ERR_FAIL_COND(attributes != nullptr); - - attributes = mono_custom_attrs_from_class(get_mono_ptr()); - attrs_fetched = true; -} - -void GDMonoClass::fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base) { - CRASH_COND(!CACHED_CLASS(GodotObject)->is_assignable_from(this)); - - if (methods_fetched) { - return; - } - - void *iter = nullptr; - MonoMethod *raw_method = nullptr; - while ((raw_method = mono_class_get_methods(get_mono_ptr(), &iter)) != nullptr) { - StringName name = String::utf8(mono_method_get_name(raw_method)); - - // get_method implicitly fetches methods and adds them to this->methods - GDMonoMethod *method = get_method(raw_method, name); - ERR_CONTINUE(!method); - - if (method->get_name() != name) { -#ifdef DEBUG_ENABLED - String fullname = method->get_ret_type_full_name() + " " + name + "(" + method->get_signature_desc(true) + ")"; - WARN_PRINT("Method '" + fullname + "' is hidden by Godot API method. Should be '" + - method->get_full_name_no_class() + "'. In class '" + namespace_name + "." + class_name + "'."); -#endif - continue; - } - -#ifdef DEBUG_ENABLED - // For debug builds, we also fetched from native base classes as well before if this is not a native base class. - // This allows us to warn the user here if they are using snake_case by mistake. - - if (p_native_base != this) { - GDMonoClass *native_top = p_native_base; - while (native_top) { - GDMonoMethod *m = native_top->get_method(name, method->get_parameters_count()); - - if (m && m->get_name() != name) { - // found - String fullname = m->get_ret_type_full_name() + " " + name + "(" + m->get_signature_desc(true) + ")"; - WARN_PRINT("Method '" + fullname + "' should be '" + m->get_full_name_no_class() + - "'. In class '" + namespace_name + "." + class_name + "'."); - break; - } - - if (native_top == CACHED_CLASS(GodotObject)) { - break; - } - - native_top = native_top->get_parent_class(); - } - } -#endif - - uint32_t flags = mono_method_get_flags(method->mono_method, nullptr); - - if (!(flags & MONO_METHOD_ATTR_VIRTUAL)) { - continue; - } - - // Virtual method of Godot Object derived type, let's try to find GodotMethod attribute - - GDMonoClass *top = p_native_base; - - while (top) { - GDMonoMethod *base_method = top->get_method(name, method->get_parameters_count()); - - if (base_method && base_method->has_attribute(CACHED_CLASS(GodotMethodAttribute))) { - // Found base method with GodotMethod attribute. - // We get the original API method name from this attribute. - // This name must point to the virtual method. - - MonoObject *attr = base_method->get_attribute(CACHED_CLASS(GodotMethodAttribute)); - - StringName godot_method_name = CACHED_FIELD(GodotMethodAttribute, methodName)->get_string_value(attr); -#ifdef DEBUG_ENABLED - CRASH_COND(godot_method_name == StringName()); -#endif - MethodKey key = MethodKey(godot_method_name, method->get_parameters_count()); - GDMonoMethod **existing_method = methods.getptr(key); - if (existing_method) { - memdelete(*existing_method); // Must delete old one - } - methods.insert(key, method); - - break; - } - - if (top == CACHED_CLASS(GodotObject)) { - break; - } - - top = top->get_parent_class(); - } - } - - methods_fetched = true; -} - -GDMonoMethod *GDMonoClass::get_fetched_method_unknown_params(const StringName &p_name) { - ERR_FAIL_COND_V(!methods_fetched, nullptr); - - for (const KeyValue<MethodKey, GDMonoMethod *> &E : methods) { - if (E.key.name == p_name) { - return E.value; - } - } - - return nullptr; -} - -bool GDMonoClass::has_fetched_method_unknown_params(const StringName &p_name) { - return get_fetched_method_unknown_params(p_name) != nullptr; -} - -bool GDMonoClass::implements_interface(GDMonoClass *p_interface) { - return mono_class_implements_interface(mono_class, p_interface->get_mono_ptr()); -} - -bool GDMonoClass::has_public_parameterless_ctor() { - GDMonoMethod *ctor = get_method(".ctor", 0); - return ctor && ctor->get_visibility() == IMonoClassMember::PUBLIC; -} - -GDMonoMethod *GDMonoClass::get_method(const StringName &p_name, uint16_t p_params_count) { - MethodKey key = MethodKey(p_name, p_params_count); - - GDMonoMethod **match = methods.getptr(key); - - if (match) { - return *match; - } - - if (methods_fetched) { - return nullptr; - } - - MonoMethod *raw_method = mono_class_get_method_from_name(mono_class, String(p_name).utf8().get_data(), p_params_count); - - if (raw_method) { - GDMonoMethod *method = memnew(GDMonoMethod(p_name, raw_method)); - methods.insert(key, method); - - return method; - } - - return nullptr; -} - -GDMonoMethod *GDMonoClass::get_method(MonoMethod *p_raw_method) { - MonoMethodSignature *sig = mono_method_signature(p_raw_method); - - int params_count = mono_signature_get_param_count(sig); - StringName method_name = String::utf8(mono_method_get_name(p_raw_method)); - - return get_method(p_raw_method, method_name, params_count); -} - -GDMonoMethod *GDMonoClass::get_method(MonoMethod *p_raw_method, const StringName &p_name) { - MonoMethodSignature *sig = mono_method_signature(p_raw_method); - int params_count = mono_signature_get_param_count(sig); - return get_method(p_raw_method, p_name, params_count); -} - -GDMonoMethod *GDMonoClass::get_method(MonoMethod *p_raw_method, const StringName &p_name, uint16_t p_params_count) { - ERR_FAIL_NULL_V(p_raw_method, nullptr); - - MethodKey key = MethodKey(p_name, p_params_count); - - GDMonoMethod **match = methods.getptr(key); - - if (match) { - return *match; - } - - GDMonoMethod *method = memnew(GDMonoMethod(p_name, p_raw_method)); - methods.insert(key, method); - - return method; -} - -GDMonoMethod *GDMonoClass::get_method_with_desc(const String &p_description, bool p_include_namespace) { - MonoMethodDesc *desc = mono_method_desc_new(p_description.utf8().get_data(), p_include_namespace); - MonoMethod *method = mono_method_desc_search_in_class(desc, mono_class); - mono_method_desc_free(desc); - - if (!method) { - return nullptr; - } - - ERR_FAIL_COND_V(mono_method_get_class(method) != mono_class, nullptr); - - return get_method(method); -} - -GDMonoField *GDMonoClass::get_field(const StringName &p_name) { - HashMap<StringName, GDMonoField *>::Iterator result = fields.find(p_name); - - if (result) { - return result->value; - } - - if (fields_fetched) { - return nullptr; - } - - MonoClassField *raw_field = mono_class_get_field_from_name(mono_class, String(p_name).utf8().get_data()); - - if (raw_field) { - GDMonoField *field = memnew(GDMonoField(raw_field, this)); - fields.insert(p_name, field); - - return field; - } - - return nullptr; -} - -const Vector<GDMonoField *> &GDMonoClass::get_all_fields() { - if (fields_fetched) { - return fields_list; - } - - void *iter = nullptr; - MonoClassField *raw_field = nullptr; - while ((raw_field = mono_class_get_fields(mono_class, &iter)) != nullptr) { - StringName name = String::utf8(mono_field_get_name(raw_field)); - - HashMap<StringName, GDMonoField *>::Iterator match = fields.find(name); - - if (match) { - fields_list.push_back(match->value); - } else { - GDMonoField *field = memnew(GDMonoField(raw_field, this)); - fields.insert(name, field); - fields_list.push_back(field); - } - } - - fields_fetched = true; - - return fields_list; -} - -GDMonoProperty *GDMonoClass::get_property(const StringName &p_name) { - HashMap<StringName, GDMonoProperty *>::Iterator result = properties.find(p_name); - - if (result) { - return result->value; - } - - if (properties_fetched) { - return nullptr; - } - - MonoProperty *raw_property = mono_class_get_property_from_name(mono_class, String(p_name).utf8().get_data()); - - if (raw_property) { - GDMonoProperty *property = memnew(GDMonoProperty(raw_property, this)); - properties.insert(p_name, property); - - return property; - } - - return nullptr; -} - -const Vector<GDMonoProperty *> &GDMonoClass::get_all_properties() { - if (properties_fetched) { - return properties_list; - } - - void *iter = nullptr; - MonoProperty *raw_property = nullptr; - while ((raw_property = mono_class_get_properties(mono_class, &iter)) != nullptr) { - StringName name = String::utf8(mono_property_get_name(raw_property)); - - HashMap<StringName, GDMonoProperty *>::Iterator match = properties.find(name); - - if (match) { - properties_list.push_back(match->value); - } else { - GDMonoProperty *property = memnew(GDMonoProperty(raw_property, this)); - properties.insert(name, property); - properties_list.push_back(property); - } - } - - properties_fetched = true; - - return properties_list; -} - -const Vector<GDMonoClass *> &GDMonoClass::get_all_delegates() { - if (delegates_fetched) { - return delegates_list; - } - - // NOTE: Temporarily reverted d28be4d5808947606b8189ae1b2900b8fd2925cf, while we move code to C# - - void *iter = nullptr; - MonoClass *raw_class = nullptr; - while ((raw_class = mono_class_get_nested_types(mono_class, &iter)) != nullptr) { - if (mono_class_is_delegate(raw_class)) { - StringName name = String::utf8(mono_class_get_name(raw_class)); - - HashMap<StringName, GDMonoClass *>::Iterator match = delegates.find(name); - - if (match) { - delegates_list.push_back(match->value); - } else { - GDMonoClass *delegate = memnew(GDMonoClass(String::utf8(mono_class_get_namespace(raw_class)), String::utf8(mono_class_get_name(raw_class)), raw_class, assembly)); - delegates.insert(name, delegate); - delegates_list.push_back(delegate); - } - } - } - - delegates_fetched = true; - - return delegates_list; -} - -const Vector<GDMonoMethod *> &GDMonoClass::get_all_methods() { - if (!method_list_fetched) { - void *iter = nullptr; - MonoMethod *raw_method = nullptr; - while ((raw_method = mono_class_get_methods(get_mono_ptr(), &iter)) != nullptr) { - method_list.push_back(memnew(GDMonoMethod(String::utf8(mono_method_get_name(raw_method)), raw_method))); - } - - method_list_fetched = true; - } - - return method_list; -} - -GDMonoClass::GDMonoClass(const StringName &p_namespace, const StringName &p_name, MonoClass *p_class, GDMonoAssembly *p_assembly) { - namespace_name = p_namespace; - class_name = p_name; - mono_class = p_class; - assembly = p_assembly; - - attrs_fetched = false; - attributes = nullptr; - - methods_fetched = false; - method_list_fetched = false; - fields_fetched = false; - properties_fetched = false; - delegates_fetched = false; -} - -GDMonoClass::~GDMonoClass() { - if (attributes) { - mono_custom_attrs_free(attributes); - } - - for (const KeyValue<StringName, GDMonoField *> &E : fields) { - memdelete(E.value); - } - - for (const KeyValue<StringName, GDMonoProperty *> &E : properties) { - memdelete(E.value); - } - - { - // Ugly workaround... - // We may have duplicated values, because we redirect snake_case methods to PascalCasel (only Godot API methods). - // This way, we end with both the snake_case name and the PascalCasel name paired with the same method. - // Therefore, we must avoid deleting the same pointer twice. - - int offset = 0; - Vector<GDMonoMethod *> deleted_methods; - deleted_methods.resize(methods.size()); - - for (const KeyValue<MethodKey, GDMonoMethod *> &E : methods) { - GDMonoMethod *method = E.value; - - if (method) { - for (int i = 0; i < offset; i++) { - if (deleted_methods[i] == method) { - // Already deleted - goto already_deleted; - } - } - - deleted_methods.write[offset] = method; - ++offset; - - memdelete(method); - } - - already_deleted:; - } - - methods.clear(); - } - - for (int i = 0; i < method_list.size(); ++i) { - memdelete(method_list[i]); - } -} diff --git a/modules/mono/mono_gd/gd_mono_class.h b/modules/mono/mono_gd/gd_mono_class.h deleted file mode 100644 index 6b35da30f9..0000000000 --- a/modules/mono/mono_gd/gd_mono_class.h +++ /dev/null @@ -1,160 +0,0 @@ -/*************************************************************************/ -/* gd_mono_class.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 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 GD_MONO_CLASS_H -#define GD_MONO_CLASS_H - -#include "core/string/ustring.h" -#include "core/templates/rb_map.h" - -#include "gd_mono_field.h" -#include "gd_mono_header.h" -#include "gd_mono_method.h" -#include "gd_mono_property.h" -#include "gd_mono_utils.h" - -class GDMonoClass { - struct MethodKey { - struct Hasher { - static _FORCE_INLINE_ uint32_t hash(const MethodKey &p_key) { - uint32_t hash = 0; - - GDMonoUtils::hash_combine(hash, p_key.name.hash()); - GDMonoUtils::hash_combine(hash, HashMapHasherDefault::hash(p_key.params_count)); - - return hash; - } - }; - - _FORCE_INLINE_ bool operator==(const MethodKey &p_a) const { - return p_a.params_count == params_count && p_a.name == name; - } - - MethodKey() {} - - MethodKey(const StringName &p_name, uint16_t p_params_count) : - name(p_name), params_count(p_params_count) { - } - - StringName name; - uint16_t params_count = 0; - }; - - StringName namespace_name; - StringName class_name; - - MonoClass *mono_class = nullptr; - GDMonoAssembly *assembly = nullptr; - - bool attrs_fetched; - MonoCustomAttrInfo *attributes = nullptr; - - // This contains both the original method names and remapped method names from the native Godot identifiers to the C# functions. - // Most method-related functions refer to this and it's possible this is unintuitive for outside users; this may be a prime location for refactoring or renaming. - bool methods_fetched; - HashMap<MethodKey, GDMonoMethod *, MethodKey::Hasher> methods; - - bool method_list_fetched; - Vector<GDMonoMethod *> method_list; - - bool fields_fetched; - HashMap<StringName, GDMonoField *> fields; - Vector<GDMonoField *> fields_list; - - bool properties_fetched; - HashMap<StringName, GDMonoProperty *> properties; - Vector<GDMonoProperty *> properties_list; - - bool delegates_fetched; - HashMap<StringName, GDMonoClass *> delegates; - Vector<GDMonoClass *> delegates_list; - - friend class GDMonoAssembly; - GDMonoClass(const StringName &p_namespace, const StringName &p_name, MonoClass *p_class, GDMonoAssembly *p_assembly); - -public: - static String get_full_name(MonoClass *p_mono_class); - static MonoType *get_mono_type(MonoClass *p_mono_class); - - String get_full_name() const; - String get_type_desc() const; - MonoType *get_mono_type() const; - - uint32_t get_flags() const; - bool is_static() const; - - bool is_assignable_from(GDMonoClass *p_from) const; - - StringName get_namespace() const; - _FORCE_INLINE_ StringName get_name() const { return class_name; } - String get_name_for_lookup() const; - - _FORCE_INLINE_ MonoClass *get_mono_ptr() const { return mono_class; } - _FORCE_INLINE_ const GDMonoAssembly *get_assembly() const { return assembly; } - - GDMonoClass *get_parent_class() const; - GDMonoClass *get_nesting_class() const; - -#ifdef TOOLS_ENABLED - Vector<MonoClassField *> get_enum_fields(); -#endif - - GDMonoMethod *get_fetched_method_unknown_params(const StringName &p_name); - bool has_fetched_method_unknown_params(const StringName &p_name); - - bool has_attribute(GDMonoClass *p_attr_class); - MonoObject *get_attribute(GDMonoClass *p_attr_class); - - void fetch_attributes(); - void fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base); - - bool implements_interface(GDMonoClass *p_interface); - bool has_public_parameterless_ctor(); - - GDMonoMethod *get_method(const StringName &p_name, uint16_t p_params_count = 0); - GDMonoMethod *get_method(MonoMethod *p_raw_method); - GDMonoMethod *get_method(MonoMethod *p_raw_method, const StringName &p_name); - GDMonoMethod *get_method(MonoMethod *p_raw_method, const StringName &p_name, uint16_t p_params_count); - GDMonoMethod *get_method_with_desc(const String &p_description, bool p_include_namespace); - - GDMonoField *get_field(const StringName &p_name); - const Vector<GDMonoField *> &get_all_fields(); - - GDMonoProperty *get_property(const StringName &p_name); - const Vector<GDMonoProperty *> &get_all_properties(); - - const Vector<GDMonoClass *> &get_all_delegates(); - - const Vector<GDMonoMethod *> &get_all_methods(); - - ~GDMonoClass(); -}; - -#endif // GD_MONO_CLASS_H diff --git a/modules/mono/mono_gd/gd_mono_field.cpp b/modules/mono/mono_gd/gd_mono_field.cpp deleted file mode 100644 index 6beeca79c3..0000000000 --- a/modules/mono/mono_gd/gd_mono_field.cpp +++ /dev/null @@ -1,149 +0,0 @@ -/*************************************************************************/ -/* gd_mono_field.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 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 "gd_mono_field.h" - -#include <mono/metadata/attrdefs.h> - -#include "gd_mono_cache.h" -#include "gd_mono_class.h" -#include "gd_mono_marshal.h" -#include "gd_mono_utils.h" - -void GDMonoField::set_value(MonoObject *p_object, MonoObject *p_value) { - mono_field_set_value(p_object, mono_field, p_value); -} - -void GDMonoField::set_value_raw(MonoObject *p_object, void *p_ptr) { - mono_field_set_value(p_object, mono_field, &p_ptr); -} - -void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_value) { - MonoReflectionField *reflfield = mono_field_get_object(mono_domain_get(), owner->get_mono_ptr(), mono_field); - - MonoException *exc = nullptr; - CACHED_METHOD_THUNK(Marshaling, SetFieldValue) - .invoke(reflfield, p_object, &p_value, &exc); - - if (exc) { - GDMonoUtils::debug_print_unhandled_exception(exc); - } -} - -MonoObject *GDMonoField::get_value(MonoObject *p_object) { - return mono_field_get_value_object(mono_domain_get(), mono_field, p_object); -} - -bool GDMonoField::get_bool_value(MonoObject *p_object) { - return (bool)GDMonoMarshal::unbox<MonoBoolean>(get_value(p_object)); -} - -int GDMonoField::get_int_value(MonoObject *p_object) { - return GDMonoMarshal::unbox<int32_t>(get_value(p_object)); -} - -String GDMonoField::get_string_value(MonoObject *p_object) { - MonoObject *val = get_value(p_object); - return GDMonoMarshal::mono_string_to_godot((MonoString *)val); -} - -bool GDMonoField::has_attribute(GDMonoClass *p_attr_class) { - ERR_FAIL_NULL_V(p_attr_class, false); - - if (!attrs_fetched) { - fetch_attributes(); - } - - if (!attributes) { - return false; - } - - return mono_custom_attrs_has_attr(attributes, p_attr_class->get_mono_ptr()); -} - -MonoObject *GDMonoField::get_attribute(GDMonoClass *p_attr_class) { - ERR_FAIL_NULL_V(p_attr_class, nullptr); - - if (!attrs_fetched) { - fetch_attributes(); - } - - if (!attributes) { - return nullptr; - } - - return mono_custom_attrs_get_attr(attributes, p_attr_class->get_mono_ptr()); -} - -void GDMonoField::fetch_attributes() { - ERR_FAIL_COND(attributes != nullptr); - attributes = mono_custom_attrs_from_field(owner->get_mono_ptr(), mono_field); - attrs_fetched = true; -} - -bool GDMonoField::is_static() { - return mono_field_get_flags(mono_field) & MONO_FIELD_ATTR_STATIC; -} - -IMonoClassMember::Visibility GDMonoField::get_visibility() { - switch (mono_field_get_flags(mono_field) & MONO_FIELD_ATTR_FIELD_ACCESS_MASK) { - case MONO_FIELD_ATTR_PRIVATE: - return IMonoClassMember::PRIVATE; - case MONO_FIELD_ATTR_FAM_AND_ASSEM: - return IMonoClassMember::PROTECTED_AND_INTERNAL; - case MONO_FIELD_ATTR_ASSEMBLY: - return IMonoClassMember::INTERNAL; - case MONO_FIELD_ATTR_FAMILY: - return IMonoClassMember::PROTECTED; - case MONO_FIELD_ATTR_PUBLIC: - return IMonoClassMember::PUBLIC; - default: - ERR_FAIL_V(IMonoClassMember::PRIVATE); - } -} - -GDMonoField::GDMonoField(MonoClassField *p_mono_field, GDMonoClass *p_owner) { - owner = p_owner; - mono_field = p_mono_field; - name = String::utf8(mono_field_get_name(mono_field)); - MonoType *field_type = mono_field_get_type(mono_field); - type.type_encoding = mono_type_get_type(field_type); - MonoClass *field_type_class = mono_class_from_mono_type(field_type); - type.type_class = GDMono::get_singleton()->get_class(field_type_class); - - attrs_fetched = false; - attributes = nullptr; -} - -GDMonoField::~GDMonoField() { - if (attributes) { - mono_custom_attrs_free(attributes); - } -} diff --git a/modules/mono/mono_gd/gd_mono_field.h b/modules/mono/mono_gd/gd_mono_field.h deleted file mode 100644 index 1d30f7a369..0000000000 --- a/modules/mono/mono_gd/gd_mono_field.h +++ /dev/null @@ -1,78 +0,0 @@ -/*************************************************************************/ -/* gd_mono_field.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 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 GD_MONO_FIELD_H -#define GD_MONO_FIELD_H - -#include "gd_mono.h" -#include "gd_mono_header.h" -#include "i_mono_class_member.h" - -class GDMonoField : public IMonoClassMember { - GDMonoClass *owner = nullptr; - MonoClassField *mono_field = nullptr; - - StringName name; - ManagedType type; - - bool attrs_fetched; - MonoCustomAttrInfo *attributes = nullptr; - -public: - virtual GDMonoClass *get_enclosing_class() const final { return owner; } - - virtual MemberType get_member_type() const final { return MEMBER_TYPE_FIELD; } - - virtual StringName get_name() const final { return name; } - - virtual bool is_static() final; - virtual Visibility get_visibility() final; - - virtual bool has_attribute(GDMonoClass *p_attr_class) final; - virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) final; - void fetch_attributes(); - - _FORCE_INLINE_ ManagedType get_type() const { return type; } - - void set_value(MonoObject *p_object, MonoObject *p_value); - void set_value_raw(MonoObject *p_object, void *p_ptr); - void set_value_from_variant(MonoObject *p_object, const Variant &p_value); - - MonoObject *get_value(MonoObject *p_object); - - bool get_bool_value(MonoObject *p_object); - int get_int_value(MonoObject *p_object); - String get_string_value(MonoObject *p_object); - - GDMonoField(MonoClassField *p_mono_field, GDMonoClass *p_owner); - ~GDMonoField(); -}; - -#endif // GD_MONO_FIELD_H diff --git a/modules/mono/mono_gd/gd_mono_header.h b/modules/mono/mono_gd/gd_mono_header.h deleted file mode 100644 index bf21283080..0000000000 --- a/modules/mono/mono_gd/gd_mono_header.h +++ /dev/null @@ -1,52 +0,0 @@ -/*************************************************************************/ -/* gd_mono_header.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 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 GD_MONO_HEADER_H -#define GD_MONO_HEADER_H - -#include <cstdint> - -#ifdef WIN32 -#define GD_MONO_STDCALL __stdcall -#else -#define GD_MONO_STDCALL -#endif - -class GDMonoAssembly; -class GDMonoClass; -class GDMonoField; -class GDMonoMethod; -class GDMonoProperty; - -class IMonoClassMember; - -#include "managed_type.h" - -#endif // GD_MONO_HEADER_H diff --git a/modules/mono/mono_gd/gd_mono_internals.cpp b/modules/mono/mono_gd/gd_mono_internals.cpp index d206b0dfc3..7b5fdef8a3 100644 --- a/modules/mono/mono_gd/gd_mono_internals.cpp +++ b/modules/mono/mono_gd/gd_mono_internals.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -31,10 +31,7 @@ #include "gd_mono_internals.h" #include "../csharp_script.h" -#include "../mono_gc_handle.h" #include "../utils/macros.h" -#include "gd_mono_class.h" -#include "gd_mono_marshal.h" #include "gd_mono_utils.h" #include "core/debugger/engine_debugger.h" @@ -43,77 +40,6 @@ #include <mono/metadata/exception.h> namespace GDMonoInternals { -void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) { - // This method should not fail - - CRASH_COND(!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<RefCounted>(unmanaged); - - GDMonoClass *klass = GDMonoUtils::get_object_class(managed); - - CRASH_COND(!klass); - - GDMonoClass *native = GDMonoUtils::get_class_native_base(klass); - - CRASH_COND(native == nullptr); - - if (native == klass) { - // 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 = NATIVE_GDMONOCLASS_NAME(klass); - script_binding.wrapper_class = klass; - script_binding.gchandle = rc ? MonoGCHandleData::new_weak_handle(managed) : MonoGCHandleData::new_strong_handle(managed); - script_binding.owner = unmanaged; - - if (rc) { - // 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(unmanaged)); - - void *data; - { - MutexLock lock(CSharpLanguage::get_singleton()->get_language_bind_mutex()); - data = (void *)CSharpLanguage::get_singleton()->insert_script_binding(unmanaged, script_binding); - } - - // Should be thread safe because the object was just created and nothing else should be referencing it - CSharpLanguage::set_instance_binding(unmanaged, data); - - return; - } - - MonoGCHandleData gchandle = rc ? MonoGCHandleData::new_weak_handle(managed) : MonoGCHandleData::new_strong_handle(managed); - - Ref<CSharpScript> script = CSharpScript::create_for_managed_type(klass, native); - - CRASH_COND(script.is_null()); - - CSharpInstance *csharp_instance = CSharpInstance::create_for_managed_type(unmanaged, script.ptr(), gchandle); - - unmanaged->set_script_and_instance(script, csharp_instance); -} - void unhandled_exception(MonoException *p_exc) { mono_print_unhandled_exception((MonoObject *)p_exc); gd_unhandled_exception_event(p_exc); @@ -137,7 +63,7 @@ void gd_unhandled_exception_event(MonoException *p_exc) { MonoImage *mono_image = GDMono::get_singleton()->get_core_api_assembly()->get_image(); MonoClass *gd_klass = mono_class_from_name(mono_image, "Godot", "GD"); - MonoMethod *unhandled_exception_method = mono_class_get_method_from_name(gd_klass, "OnUnhandledException", -1); + MonoMethod *unhandled_exception_method = mono_class_get_method_from_name(gd_klass, "OnUnhandledException", 1); void *args[1]; args[0] = p_exc; mono_runtime_invoke(unhandled_exception_method, nullptr, (void **)args, nullptr); diff --git a/modules/mono/mono_gd/gd_mono_internals.h b/modules/mono/mono_gd/gd_mono_internals.h index a8f9cfa3ca..858c395b78 100644 --- a/modules/mono/mono_gd/gd_mono_internals.h +++ b/modules/mono/mono_gd/gd_mono_internals.h @@ -37,9 +37,9 @@ #include "core/object/class_db.h" -namespace GDMonoInternals { -void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged); +class CSharpScript; +namespace GDMonoInternals { /** * Do not call this function directly. * Use GDMonoUtils::debug_unhandled_exception(MonoException *) instead. diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp deleted file mode 100644 index 8828ec588b..0000000000 --- a/modules/mono/mono_gd/gd_mono_marshal.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/*************************************************************************/ -/* gd_mono_marshal.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 "gd_mono_marshal.h" - -#include "../signal_awaiter_utils.h" -#include "gd_mono.h" -#include "gd_mono_cache.h" -#include "gd_mono_class.h" - -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; - - MonoException *exc = nullptr; - int32_t ret = CACHED_METHOD_THUNK(Marshaling, managed_to_variant_type) - .invoke(refltype, &nil_is_variant, &exc); - - if (exc) { - GDMonoUtils::debug_print_unhandled_exception(exc); - return Variant::NIL; - } - - if (r_nil_is_variant) { - *r_nil_is_variant = (bool)nil_is_variant; - } - - return (Variant::Type)ret; -} - -bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_elem_type) { - MonoReflectionType *array_refltype = mono_type_get_object(mono_domain_get(), p_array_type.type_class->get_mono_type()); - MonoReflectionType *elem_refltype = nullptr; - - MonoException *exc = nullptr; - MonoBoolean ret = CACHED_METHOD_THUNK(Marshaling, try_get_array_element_type) - .invoke(array_refltype, &elem_refltype, &exc); - - if (exc) { - GDMonoUtils::debug_print_unhandled_exception(exc); - return Variant::NIL; - } - - r_elem_type = ManagedType::from_reftype(elem_refltype); - return ret; -} - -MonoObject *variant_to_mono_object_of_type(const Variant &p_var, const ManagedType &p_type) { - MonoReflectionType *refltype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type()); - - MonoException *exc = nullptr; - MonoObject *ret = CACHED_METHOD_THUNK(Marshaling, variant_to_mono_object_of_type) - .invoke(&p_var, refltype, &exc); - - if (exc) { - GDMonoUtils::debug_print_unhandled_exception(exc); - return nullptr; - } - - return ret; -} - -MonoObject *variant_to_mono_object(const Variant &p_var) { - MonoException *exc = nullptr; - MonoObject *ret = CACHED_METHOD_THUNK(Marshaling, variant_to_mono_object) - .invoke(&p_var, &exc); - - if (exc) { - GDMonoUtils::debug_print_unhandled_exception(exc); - return nullptr; - } - - return ret; -} - -static Variant mono_object_to_variant_impl(MonoObject *p_obj, bool p_fail_with_err) { - if (!p_obj) { - return Variant(); - } - - MonoBoolean fail_with_error = p_fail_with_err; - - Variant ret; - - MonoException *exc = nullptr; - CACHED_METHOD_THUNK(Marshaling, mono_object_to_variant_out) - .invoke(p_obj, fail_with_error, &ret, &exc); - - if (exc) { - GDMonoUtils::debug_print_unhandled_exception(exc); - return Variant(); - } - - return ret; -} - -Variant mono_object_to_variant(MonoObject *p_obj) { - return mono_object_to_variant_impl(p_obj, /* fail_with_err: */ true); -} - -Variant mono_object_to_variant_no_err(MonoObject *p_obj) { - return mono_object_to_variant_impl(p_obj, /* fail_with_err: */ false); -} - -MonoArray *PackedStringArray_to_mono_array(const PackedStringArray &p_array) { - const String *r = p_array.ptr(); - int length = p_array.size(); - - MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(String), length); - - for (int i = 0; i < length; i++) { - MonoString *boxed = mono_string_from_godot(r[i]); - mono_array_setref(ret, i, boxed); - } - - return ret; -} -} // namespace GDMonoMarshal diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h deleted file mode 100644 index fbe5795df5..0000000000 --- a/modules/mono/mono_gd/gd_mono_marshal.h +++ /dev/null @@ -1,97 +0,0 @@ -/*************************************************************************/ -/* gd_mono_marshal.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 GD_MONO_MARSHAL_H -#define GD_MONO_MARSHAL_H - -#include "core/variant/variant.h" - -#include "gd_mono.h" -#include "gd_mono_utils.h" - -namespace GDMonoMarshal { - -template <typename T> -T unbox(MonoObject *p_obj) { - return *(T *)mono_object_unbox(p_obj); -} - -Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_variant = nullptr); - -bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_elem_type); - -// String - -_FORCE_INLINE_ String mono_string_to_godot_not_null(MonoString *p_mono_string) { - char32_t *utf32 = (char32_t *)mono_string_to_utf32(p_mono_string); - String ret = String(utf32); - mono_free(utf32); - return ret; -} - -_FORCE_INLINE_ String mono_string_to_godot(MonoString *p_mono_string) { - if (p_mono_string == nullptr) { - return String(); - } - - return mono_string_to_godot_not_null(p_mono_string); -} - -_FORCE_INLINE_ MonoString *mono_string_from_godot(const String &p_string) { - return mono_string_from_utf32((mono_unichar4 *)(p_string.get_data())); -} - -// Variant - -MonoObject *variant_to_mono_object_of_type(const Variant &p_var, const ManagedType &p_type); - -MonoObject *variant_to_mono_object(const Variant &p_var); - -// These overloads were added to avoid passing a `const Variant *` to the `const Variant &` -// parameter. That would result in the `Variant(bool)` copy constructor being called as -// pointers are implicitly converted to bool. Implicit conversions are f-ing evil. - -_FORCE_INLINE_ MonoObject *variant_to_mono_object_of_type(const Variant *p_var, const ManagedType &p_type) { - return variant_to_mono_object_of_type(*p_var, p_type); -} -_FORCE_INLINE_ MonoObject *variant_to_mono_object(const Variant *p_var) { - return variant_to_mono_object(*p_var); -} - -Variant mono_object_to_variant(MonoObject *p_obj); -Variant mono_object_to_variant_no_err(MonoObject *p_obj); - -// PackedStringArray - -MonoArray *PackedStringArray_to_mono_array(const PackedStringArray &p_array); - -} // namespace GDMonoMarshal - -#endif // GD_MONO_MARSHAL_H diff --git a/modules/mono/mono_gd/gd_mono_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp deleted file mode 100644 index 97d3c82230..0000000000 --- a/modules/mono/mono_gd/gd_mono_method.cpp +++ /dev/null @@ -1,295 +0,0 @@ -/*************************************************************************/ -/* gd_mono_method.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 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 "gd_mono_method.h" - -#include <mono/metadata/attrdefs.h> -#include <mono/metadata/debug-helpers.h> - -#include "gd_mono_cache.h" -#include "gd_mono_class.h" -#include "gd_mono_marshal.h" -#include "gd_mono_utils.h" - -void GDMonoMethod::_update_signature() { - // Apparently MonoMethodSignature needs not to be freed. - // mono_method_signature caches the result, we don't need to cache it ourselves. - - MonoMethodSignature *method_sig = mono_method_signature(mono_method); - _update_signature(method_sig); -} - -void GDMonoMethod::_update_signature(MonoMethodSignature *p_method_sig) { - params_count = mono_signature_get_param_count(p_method_sig); - - MonoType *ret_type = mono_signature_get_return_type(p_method_sig); - if (ret_type) { - return_type.type_encoding = mono_type_get_type(ret_type); - - if (return_type.type_encoding != MONO_TYPE_VOID) { - MonoClass *ret_type_class = mono_class_from_mono_type(ret_type); - return_type.type_class = GDMono::get_singleton()->get_class(ret_type_class); - } - } - - void *iter = nullptr; - MonoType *param_raw_type; - while ((param_raw_type = mono_signature_get_params(p_method_sig, &iter)) != nullptr) { - ManagedType param_type; - - param_type.type_encoding = mono_type_get_type(param_raw_type); - - MonoClass *param_type_class = mono_class_from_mono_type(param_raw_type); - param_type.type_class = GDMono::get_singleton()->get_class(param_type_class); - - param_types.push_back(param_type); - } - - // clear the cache - method_info_fetched = false; - method_info = MethodInfo(); -} - -GDMonoClass *GDMonoMethod::get_enclosing_class() const { - return GDMono::get_singleton()->get_class(mono_method_get_class(mono_method)); -} - -bool GDMonoMethod::is_static() { - return mono_method_get_flags(mono_method, nullptr) & MONO_METHOD_ATTR_STATIC; -} - -IMonoClassMember::Visibility GDMonoMethod::get_visibility() { - switch (mono_method_get_flags(mono_method, nullptr) & MONO_METHOD_ATTR_ACCESS_MASK) { - case MONO_METHOD_ATTR_PRIVATE: - return IMonoClassMember::PRIVATE; - case MONO_METHOD_ATTR_FAM_AND_ASSEM: - return IMonoClassMember::PROTECTED_AND_INTERNAL; - case MONO_METHOD_ATTR_ASSEM: - return IMonoClassMember::INTERNAL; - case MONO_METHOD_ATTR_FAMILY: - return IMonoClassMember::PROTECTED; - case MONO_METHOD_ATTR_PUBLIC: - return IMonoClassMember::PUBLIC; - default: - ERR_FAIL_V(IMonoClassMember::PRIVATE); - } -} - -MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc) const { - MonoException *exc = nullptr; - MonoObject *ret; - - if (params_count > 0) { - MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), get_parameters_count()); - - for (int i = 0; i < params_count; i++) { - MonoObject *boxed_param = GDMonoMarshal::variant_to_mono_object_of_type(p_params[i], param_types[i]); - mono_array_setref(params, i, boxed_param); - } - - ret = GDMonoUtils::runtime_invoke_array(mono_method, p_object, params, &exc); - } else { - ret = GDMonoUtils::runtime_invoke(mono_method, p_object, nullptr, &exc); - } - - if (exc) { - ret = nullptr; - if (r_exc) { - *r_exc = exc; - } else { - GDMonoUtils::set_pending_exception(exc); - } - } - - return ret; -} - -MonoObject *GDMonoMethod::invoke(MonoObject *p_object, MonoException **r_exc) const { - ERR_FAIL_COND_V(get_parameters_count() > 0, nullptr); - return invoke_raw(p_object, nullptr, r_exc); -} - -MonoObject *GDMonoMethod::invoke_raw(MonoObject *p_object, void **p_params, MonoException **r_exc) const { - MonoException *exc = nullptr; - MonoObject *ret = GDMonoUtils::runtime_invoke(mono_method, p_object, p_params, &exc); - - if (exc) { - ret = nullptr; - if (r_exc) { - *r_exc = exc; - } else { - GDMonoUtils::set_pending_exception(exc); - } - } - - return ret; -} - -bool GDMonoMethod::has_attribute(GDMonoClass *p_attr_class) { - ERR_FAIL_NULL_V(p_attr_class, false); - - if (!attrs_fetched) { - fetch_attributes(); - } - - if (!attributes) { - return false; - } - - return mono_custom_attrs_has_attr(attributes, p_attr_class->get_mono_ptr()); -} - -MonoObject *GDMonoMethod::get_attribute(GDMonoClass *p_attr_class) { - ERR_FAIL_NULL_V(p_attr_class, nullptr); - - if (!attrs_fetched) { - fetch_attributes(); - } - - if (!attributes) { - return nullptr; - } - - return mono_custom_attrs_get_attr(attributes, p_attr_class->get_mono_ptr()); -} - -void GDMonoMethod::fetch_attributes() { - ERR_FAIL_COND(attributes != nullptr); - attributes = mono_custom_attrs_from_method(mono_method); - attrs_fetched = true; -} - -String GDMonoMethod::get_full_name(bool p_signature) const { - char *res = mono_method_full_name(mono_method, p_signature); - String full_name(res); - mono_free(res); - return full_name; -} - -String GDMonoMethod::get_full_name_no_class() const { - String res; - - MonoMethodSignature *method_sig = mono_method_signature(mono_method); - - char *ret_str = mono_type_full_name(mono_signature_get_return_type(method_sig)); - res += ret_str; - mono_free(ret_str); - - res += " "; - res += name; - res += "("; - - char *sig_desc = mono_signature_get_desc(method_sig, true); - res += sig_desc; - mono_free(sig_desc); - - res += ")"; - - return res; -} - -String GDMonoMethod::get_ret_type_full_name() const { - MonoMethodSignature *method_sig = mono_method_signature(mono_method); - char *ret_str = mono_type_full_name(mono_signature_get_return_type(method_sig)); - String res = ret_str; - mono_free(ret_str); - return res; -} - -String GDMonoMethod::get_signature_desc(bool p_namespaces) const { - MonoMethodSignature *method_sig = mono_method_signature(mono_method); - char *sig_desc = mono_signature_get_desc(method_sig, p_namespaces); - String res = sig_desc; - mono_free(sig_desc); - return res; -} - -void GDMonoMethod::get_parameter_names(Vector<StringName> &names) const { - if (params_count > 0) { - const char **_names = memnew_arr(const char *, params_count); - mono_method_get_param_names(mono_method, _names); - for (int i = 0; i < params_count; ++i) { - names.push_back(StringName(_names[i])); - } - memdelete_arr(_names); - } -} - -void GDMonoMethod::get_parameter_types(Vector<ManagedType> &types) const { - for (int i = 0; i < params_count; ++i) { - types.push_back(param_types[i]); - } -} - -const MethodInfo &GDMonoMethod::get_method_info() { - if (!method_info_fetched) { - method_info.name = name; - - bool nil_is_variant = false; - 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<StringName> names; - get_parameter_names(names); - - for (int i = 0; i < params_count; ++i) { - nil_is_variant = false; - PropertyInfo arg_info = PropertyInfo(GDMonoMarshal::managed_to_variant_type(param_types[i], &nil_is_variant), names[i]); - if (arg_info.type == Variant::NIL && nil_is_variant) { - arg_info.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; - } - - method_info.arguments.push_back(arg_info); - } - - // TODO: default arguments - - method_info_fetched = true; - } - - return method_info; -} - -GDMonoMethod::GDMonoMethod(StringName p_name, MonoMethod *p_method) : - name(p_name), mono_method(p_method) { - _update_signature(); -} - -GDMonoMethod::~GDMonoMethod() { - if (attributes) { - mono_custom_attrs_free(attributes); - } -} diff --git a/modules/mono/mono_gd/gd_mono_method.h b/modules/mono/mono_gd/gd_mono_method.h deleted file mode 100644 index 5398f34103..0000000000 --- a/modules/mono/mono_gd/gd_mono_method.h +++ /dev/null @@ -1,96 +0,0 @@ -/*************************************************************************/ -/* gd_mono_method.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 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 GD_MONO_METHOD_H -#define GD_MONO_METHOD_H - -#include "gd_mono.h" -#include "gd_mono_header.h" -#include "i_mono_class_member.h" - -class GDMonoMethod : public IMonoClassMember { - StringName name; - - uint16_t params_count; - ManagedType return_type; - Vector<ManagedType> param_types; - - bool method_info_fetched = false; - MethodInfo method_info; - - bool attrs_fetched = false; - MonoCustomAttrInfo *attributes = nullptr; - - void _update_signature(); - void _update_signature(MonoMethodSignature *p_method_sig); - - friend class GDMonoClass; - - MonoMethod *mono_method = nullptr; - -public: - virtual GDMonoClass *get_enclosing_class() const final; - - virtual MemberType get_member_type() const final { return MEMBER_TYPE_METHOD; } - - virtual StringName get_name() const final { return name; } - - virtual bool is_static() final; - - virtual Visibility get_visibility() final; - - virtual bool has_attribute(GDMonoClass *p_attr_class) final; - virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) final; - void fetch_attributes(); - - _FORCE_INLINE_ MonoMethod *get_mono_ptr() const { return mono_method; } - - _FORCE_INLINE_ uint16_t get_parameters_count() const { return params_count; } - _FORCE_INLINE_ ManagedType get_return_type() const { return return_type; } - - MonoObject *invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc = nullptr) const; - MonoObject *invoke(MonoObject *p_object, MonoException **r_exc = nullptr) const; - MonoObject *invoke_raw(MonoObject *p_object, void **p_params, MonoException **r_exc = nullptr) const; - - String get_full_name(bool p_signature = false) const; - String get_full_name_no_class() const; - String get_ret_type_full_name() const; - String get_signature_desc(bool p_namespaces = false) const; - - void get_parameter_names(Vector<StringName> &names) const; - void get_parameter_types(Vector<ManagedType> &types) const; - - const MethodInfo &get_method_info(); - - GDMonoMethod(StringName p_name, MonoMethod *p_method); - ~GDMonoMethod(); -}; - -#endif // GD_MONO_METHOD_H diff --git a/modules/mono/mono_gd/gd_mono_method_thunk.h b/modules/mono/mono_gd/gd_mono_method_thunk.h index 0180dee3ea..aa84a7f2d0 100644 --- a/modules/mono/mono_gd/gd_mono_method_thunk.h +++ b/modules/mono/mono_gd/gd_mono_method_thunk.h @@ -31,20 +31,19 @@ #ifndef GD_MONO_METHOD_THUNK_H #define GD_MONO_METHOD_THUNK_H +#include <mono/jit/jit.h> +#include <mono/metadata/attrdefs.h> #include <type_traits> -#include "gd_mono_class.h" -#include "gd_mono_header.h" -#include "gd_mono_marshal.h" -#include "gd_mono_method.h" +#include "core/error/error_macros.h" #include "gd_mono_utils.h" -#if !defined(JAVASCRIPT_ENABLED) && !defined(IOS_ENABLED) -#define HAVE_METHOD_THUNKS +#ifdef WIN32 +#define GD_MONO_STDCALL __stdcall +#else +#define GD_MONO_STDCALL #endif -#ifdef HAVE_METHOD_THUNKS - template <class... ParamTypes> struct GDMonoMethodThunk { typedef void(GD_MONO_STDCALL *M)(ParamTypes... p_args, MonoException **); @@ -58,33 +57,30 @@ public: GD_MONO_END_RUNTIME_INVOKE; } - _FORCE_INLINE_ bool is_null() { + bool is_null() { return mono_method_thunk == nullptr; } - _FORCE_INLINE_ void nullify() { - mono_method_thunk = nullptr; - } - - _FORCE_INLINE_ void set_from_method(GDMonoMethod *p_mono_method) { + void set_from_method(MonoMethod *p_mono_method) { #ifdef DEBUG_ENABLED CRASH_COND(p_mono_method == nullptr); - CRASH_COND(p_mono_method->get_return_type().type_encoding != MONO_TYPE_VOID); - if (p_mono_method->is_static()) { - CRASH_COND(p_mono_method->get_parameters_count() != sizeof...(ParamTypes)); - } else { - CRASH_COND(p_mono_method->get_parameters_count() != (sizeof...(ParamTypes) - 1)); - } + MonoMethodSignature *method_sig = mono_method_signature(p_mono_method); + MonoType *ret_type = mono_signature_get_return_type(method_sig); + int ret_type_encoding = ret_type ? mono_type_get_type(ret_type) : MONO_TYPE_VOID; + + CRASH_COND(ret_type_encoding != MONO_TYPE_VOID); + + bool is_static = mono_method_get_flags(p_mono_method, nullptr) & MONO_METHOD_ATTR_STATIC; + CRASH_COND(!is_static); + + uint32_t parameters_count = mono_signature_get_param_count(method_sig); + CRASH_COND(parameters_count != sizeof...(ParamTypes)); #endif - mono_method_thunk = (M)mono_method_get_unmanaged_thunk(p_mono_method->get_mono_ptr()); + mono_method_thunk = (M)mono_method_get_unmanaged_thunk(p_mono_method); } GDMonoMethodThunk() {} - - explicit GDMonoMethodThunk(GDMonoMethod *p_mono_method) { - set_from_method(p_mono_method); - } }; template <class R, class... ParamTypes> @@ -101,220 +97,30 @@ public: return r; } - _FORCE_INLINE_ bool is_null() { + bool is_null() { return mono_method_thunk == nullptr; } - _FORCE_INLINE_ void nullify() { - mono_method_thunk = nullptr; - } - - _FORCE_INLINE_ void set_from_method(GDMonoMethod *p_mono_method) { -#ifdef DEBUG_ENABLED - CRASH_COND(p_mono_method == nullptr); - CRASH_COND(p_mono_method->get_return_type().type_encoding == MONO_TYPE_VOID); - - if (p_mono_method->is_static()) { - CRASH_COND(p_mono_method->get_parameters_count() != sizeof...(ParamTypes)); - } else { - CRASH_COND(p_mono_method->get_parameters_count() != (sizeof...(ParamTypes) - 1)); - } -#endif - mono_method_thunk = (M)mono_method_get_unmanaged_thunk(p_mono_method->get_mono_ptr()); - } - - GDMonoMethodThunkR() {} - - explicit GDMonoMethodThunkR(GDMonoMethod *p_mono_method) { -#ifdef DEBUG_ENABLED - CRASH_COND(p_mono_method == nullptr); -#endif - mono_method_thunk = (M)mono_method_get_unmanaged_thunk(p_mono_method->get_mono_ptr()); - } -}; - -#else - -template <unsigned int ThunkParamCount, class P1, class... ParamTypes> -struct VariadicInvokeMonoMethodImpl { - static void invoke(GDMonoMethod *p_mono_method, P1 p_arg1, ParamTypes... p_args, MonoException **r_exc) { - if (p_mono_method->is_static()) { - void *args[ThunkParamCount] = { p_arg1, p_args... }; - p_mono_method->invoke_raw(nullptr, args, r_exc); - } else { - void *args[ThunkParamCount] = { p_args... }; - p_mono_method->invoke_raw((MonoObject *)p_arg1, args, r_exc); - } - } -}; - -template <unsigned int ThunkParamCount, class... ParamTypes> -struct VariadicInvokeMonoMethod { - static void invoke(GDMonoMethod *p_mono_method, ParamTypes... p_args, MonoException **r_exc) { - VariadicInvokeMonoMethodImpl<ThunkParamCount, ParamTypes...>::invoke(p_mono_method, p_args..., r_exc); - } -}; - -template <> -struct VariadicInvokeMonoMethod<0> { - static void invoke(GDMonoMethod *p_mono_method, MonoException **r_exc) { -#ifdef DEBUG_ENABLED - CRASH_COND(!p_mono_method->is_static()); -#endif - p_mono_method->invoke_raw(nullptr, nullptr, r_exc); - } -}; - -template <class P1> -struct VariadicInvokeMonoMethod<1, P1> { - static void invoke(GDMonoMethod *p_mono_method, P1 p_arg1, MonoException **r_exc) { - if (p_mono_method->is_static()) { - void *args[1] = { p_arg1 }; - p_mono_method->invoke_raw(nullptr, args, r_exc); - } else { - p_mono_method->invoke_raw((MonoObject *)p_arg1, nullptr, r_exc); - } - } -}; - -template <class R> -R unbox_if_needed(MonoObject *p_val, const ManagedType &, typename std::enable_if<!std::is_pointer<R>::value>::type * = 0) { - return GDMonoMarshal::unbox<R>(p_val); -} - -template <class R> -R unbox_if_needed(MonoObject *p_val, const ManagedType &p_type, typename std::enable_if<std::is_pointer<R>::value>::type * = 0) { - if (mono_class_is_valuetype(p_type.type_class->get_mono_ptr())) { - return GDMonoMarshal::unbox<R>(p_val); - } else { - // If it's not a value type, we assume 'R' is a pointer to 'MonoObject' or a compatible type, like 'MonoException'. - return (R)p_val; - } -} - -template <unsigned int ThunkParamCount, class R, class P1, class... ParamTypes> -struct VariadicInvokeMonoMethodRImpl { - static R invoke(GDMonoMethod *p_mono_method, P1 p_arg1, ParamTypes... p_args, MonoException **r_exc) { - if (p_mono_method->is_static()) { - void *args[ThunkParamCount] = { p_arg1, p_args... }; - MonoObject *r = p_mono_method->invoke_raw(nullptr, args, r_exc); - return unbox_if_needed<R>(r, p_mono_method->get_return_type()); - } else { - void *args[ThunkParamCount] = { p_args... }; - MonoObject *r = p_mono_method->invoke_raw((MonoObject *)p_arg1, args, r_exc); - return unbox_if_needed<R>(r, p_mono_method->get_return_type()); - } - } -}; - -template <unsigned int ThunkParamCount, class R, class... ParamTypes> -struct VariadicInvokeMonoMethodR { - static R invoke(GDMonoMethod *p_mono_method, ParamTypes... p_args, MonoException **r_exc) { - return VariadicInvokeMonoMethodRImpl<ThunkParamCount, R, ParamTypes...>::invoke(p_mono_method, p_args..., r_exc); - } -}; - -template <class R> -struct VariadicInvokeMonoMethodR<0, R> { - static R invoke(GDMonoMethod *p_mono_method, MonoException **r_exc) { -#ifdef DEBUG_ENABLED - CRASH_COND(!p_mono_method->is_static()); -#endif - MonoObject *r = p_mono_method->invoke_raw(nullptr, nullptr, r_exc); - return unbox_if_needed<R>(r, p_mono_method->get_return_type()); - } -}; - -template <class R, class P1> -struct VariadicInvokeMonoMethodR<1, R, P1> { - static R invoke(GDMonoMethod *p_mono_method, P1 p_arg1, MonoException **r_exc) { - if (p_mono_method->is_static()) { - void *args[1] = { p_arg1 }; - MonoObject *r = p_mono_method->invoke_raw(nullptr, args, r_exc); - return unbox_if_needed<R>(r, p_mono_method->get_return_type()); - } else { - MonoObject *r = p_mono_method->invoke_raw((MonoObject *)p_arg1, nullptr, r_exc); - return unbox_if_needed<R>(r, p_mono_method->get_return_type()); - } - } -}; - -template <class... ParamTypes> -struct GDMonoMethodThunk { - GDMonoMethod *mono_method = nullptr; - -public: - _FORCE_INLINE_ void invoke(ParamTypes... p_args, MonoException **r_exc) { - VariadicInvokeMonoMethod<sizeof...(ParamTypes), ParamTypes...>::invoke(mono_method, p_args..., r_exc); - } - - _FORCE_INLINE_ bool is_null() { - return mono_method == nullptr; - } - - _FORCE_INLINE_ void nullify() { - mono_method = nullptr; - } - - _FORCE_INLINE_ void set_from_method(GDMonoMethod *p_mono_method) { + void set_from_method(MonoMethod *p_mono_method) { #ifdef DEBUG_ENABLED CRASH_COND(p_mono_method == nullptr); - CRASH_COND(p_mono_method->get_return_type().type_encoding != MONO_TYPE_VOID); - if (p_mono_method->is_static()) { - CRASH_COND(p_mono_method->get_parameters_count() != sizeof...(ParamTypes)); - } else { - CRASH_COND(p_mono_method->get_parameters_count() != (sizeof...(ParamTypes) - 1)); - } -#endif - mono_method = p_mono_method; - } - - GDMonoMethodThunk() {} - - explicit GDMonoMethodThunk(GDMonoMethod *p_mono_method) { - set_from_method(p_mono_method); - } -}; + MonoMethodSignature *method_sig = mono_method_signature(p_mono_method); + MonoType *ret_type = mono_signature_get_return_type(method_sig); + int ret_type_encoding = ret_type ? mono_type_get_type(ret_type) : MONO_TYPE_VOID; -template <class R, class... ParamTypes> -struct GDMonoMethodThunkR { - GDMonoMethod *mono_method = nullptr; + CRASH_COND(ret_type_encoding == MONO_TYPE_VOID); -public: - _FORCE_INLINE_ R invoke(ParamTypes... p_args, MonoException **r_exc) { - return VariadicInvokeMonoMethodR<sizeof...(ParamTypes), R, ParamTypes...>::invoke(mono_method, p_args..., r_exc); - } + bool is_static = mono_method_get_flags(p_mono_method, nullptr) & MONO_METHOD_ATTR_STATIC; + CRASH_COND(!is_static); - _FORCE_INLINE_ bool is_null() { - return mono_method == nullptr; - } - - _FORCE_INLINE_ void nullify() { - mono_method = nullptr; - } - - _FORCE_INLINE_ void set_from_method(GDMonoMethod *p_mono_method) { -#ifdef DEBUG_ENABLED - CRASH_COND(p_mono_method == nullptr); - CRASH_COND(p_mono_method->get_return_type().type_encoding == MONO_TYPE_VOID); - - if (p_mono_method->is_static()) { - CRASH_COND(p_mono_method->get_parameters_count() != sizeof...(ParamTypes)); - } else { - CRASH_COND(p_mono_method->get_parameters_count() != (sizeof...(ParamTypes) - 1)); - } + uint32_t parameters_count = mono_signature_get_param_count(method_sig); + CRASH_COND(parameters_count != sizeof...(ParamTypes)); #endif - mono_method = p_mono_method; + mono_method_thunk = (M)mono_method_get_unmanaged_thunk(p_mono_method); } GDMonoMethodThunkR() {} - - explicit GDMonoMethodThunkR(GDMonoMethod *p_mono_method) { - set_from_method(p_mono_method); - } }; -#endif - #endif // GD_MONO_METHOD_THUNK_H diff --git a/modules/mono/mono_gd/gd_mono_property.cpp b/modules/mono/mono_gd/gd_mono_property.cpp deleted file mode 100644 index 7cbf5be151..0000000000 --- a/modules/mono/mono_gd/gd_mono_property.cpp +++ /dev/null @@ -1,197 +0,0 @@ -/*************************************************************************/ -/* gd_mono_property.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 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 "gd_mono_property.h" - -#include "gd_mono_cache.h" -#include "gd_mono_class.h" -#include "gd_mono_marshal.h" -#include "gd_mono_utils.h" - -#include <mono/metadata/attrdefs.h> - -GDMonoProperty::GDMonoProperty(MonoProperty *p_mono_property, GDMonoClass *p_owner) { - owner = p_owner; - mono_property = p_mono_property; - name = String::utf8(mono_property_get_name(mono_property)); - - MonoMethod *prop_method = mono_property_get_get_method(mono_property); - - if (prop_method) { - MonoMethodSignature *getter_sig = mono_method_signature(prop_method); - - MonoType *ret_type = mono_signature_get_return_type(getter_sig); - - type.type_encoding = mono_type_get_type(ret_type); - MonoClass *ret_type_class = mono_class_from_mono_type(ret_type); - type.type_class = GDMono::get_singleton()->get_class(ret_type_class); - } else { - prop_method = mono_property_get_set_method(mono_property); - - MonoMethodSignature *setter_sig = mono_method_signature(prop_method); - - void *iter = nullptr; - MonoType *param_raw_type = mono_signature_get_params(setter_sig, &iter); - - type.type_encoding = mono_type_get_type(param_raw_type); - MonoClass *param_type_class = mono_class_from_mono_type(param_raw_type); - type.type_class = GDMono::get_singleton()->get_class(param_type_class); - } - - attrs_fetched = false; - attributes = nullptr; -} - -GDMonoProperty::~GDMonoProperty() { - if (attributes) { - mono_custom_attrs_free(attributes); - } -} - -bool GDMonoProperty::is_static() { - MonoMethod *prop_method = mono_property_get_get_method(mono_property); - if (prop_method == nullptr) { - prop_method = mono_property_get_set_method(mono_property); - } - return mono_method_get_flags(prop_method, nullptr) & MONO_METHOD_ATTR_STATIC; -} - -IMonoClassMember::Visibility GDMonoProperty::get_visibility() { - MonoMethod *prop_method = mono_property_get_get_method(mono_property); - if (prop_method == nullptr) { - prop_method = mono_property_get_set_method(mono_property); - } - - switch (mono_method_get_flags(prop_method, nullptr) & MONO_METHOD_ATTR_ACCESS_MASK) { - case MONO_METHOD_ATTR_PRIVATE: - return IMonoClassMember::PRIVATE; - case MONO_METHOD_ATTR_FAM_AND_ASSEM: - return IMonoClassMember::PROTECTED_AND_INTERNAL; - case MONO_METHOD_ATTR_ASSEM: - return IMonoClassMember::INTERNAL; - case MONO_METHOD_ATTR_FAMILY: - return IMonoClassMember::PROTECTED; - case MONO_METHOD_ATTR_PUBLIC: - return IMonoClassMember::PUBLIC; - default: - ERR_FAIL_V(IMonoClassMember::PRIVATE); - } -} - -bool GDMonoProperty::has_attribute(GDMonoClass *p_attr_class) { - ERR_FAIL_NULL_V(p_attr_class, false); - - if (!attrs_fetched) { - fetch_attributes(); - } - - if (!attributes) { - return false; - } - - return mono_custom_attrs_has_attr(attributes, p_attr_class->get_mono_ptr()); -} - -MonoObject *GDMonoProperty::get_attribute(GDMonoClass *p_attr_class) { - ERR_FAIL_NULL_V(p_attr_class, nullptr); - - if (!attrs_fetched) { - fetch_attributes(); - } - - if (!attributes) { - return nullptr; - } - - return mono_custom_attrs_get_attr(attributes, p_attr_class->get_mono_ptr()); -} - -void GDMonoProperty::fetch_attributes() { - ERR_FAIL_COND(attributes != nullptr); - attributes = mono_custom_attrs_from_property(owner->get_mono_ptr(), mono_property); - attrs_fetched = true; -} - -bool GDMonoProperty::has_getter() { - return mono_property_get_get_method(mono_property) != nullptr; -} - -bool GDMonoProperty::has_setter() { - return mono_property_get_set_method(mono_property) != nullptr; -} - -void GDMonoProperty::set_value_from_variant(MonoObject *p_object, const Variant &p_value, MonoException **r_exc) { - MonoMethod *set_method = mono_property_get_set_method(mono_property); - ERR_FAIL_COND(set_method == nullptr); - - // Temporary solution, while moving code to C# - MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), 1); - MonoObject *boxed_param = GDMonoMarshal::variant_to_mono_object_of_type(p_value, type); - mono_array_setref(params, 0, boxed_param); - - MonoException *exc = nullptr; - GDMonoUtils::runtime_invoke_array(set_method, p_object, params, &exc); - if (exc) { - if (r_exc) { - *r_exc = exc; - } else { - GDMonoUtils::set_pending_exception(exc); - } - } -} - -MonoObject *GDMonoProperty::get_value(MonoObject *p_object, MonoException **r_exc) { - MonoException *exc = nullptr; - MonoObject *ret = GDMonoUtils::property_get_value(mono_property, p_object, nullptr, &exc); - - if (exc) { - ret = nullptr; - if (r_exc) { - *r_exc = exc; - } else { - GDMonoUtils::set_pending_exception(exc); - } - } - - return ret; -} - -bool GDMonoProperty::get_bool_value(MonoObject *p_object) { - return (bool)GDMonoMarshal::unbox<MonoBoolean>(get_value(p_object)); -} - -int GDMonoProperty::get_int_value(MonoObject *p_object) { - return GDMonoMarshal::unbox<int32_t>(get_value(p_object)); -} - -String GDMonoProperty::get_string_value(MonoObject *p_object) { - MonoObject *val = get_value(p_object); - return GDMonoMarshal::mono_string_to_godot((MonoString *)val); -} diff --git a/modules/mono/mono_gd/gd_mono_property.h b/modules/mono/mono_gd/gd_mono_property.h deleted file mode 100644 index 885ea8f011..0000000000 --- a/modules/mono/mono_gd/gd_mono_property.h +++ /dev/null @@ -1,78 +0,0 @@ -/*************************************************************************/ -/* gd_mono_property.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 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 GD_MONO_PROPERTY_H -#define GD_MONO_PROPERTY_H - -#include "gd_mono.h" -#include "gd_mono_header.h" -#include "i_mono_class_member.h" - -class GDMonoProperty : public IMonoClassMember { - GDMonoClass *owner = nullptr; - MonoProperty *mono_property = nullptr; - - StringName name; - ManagedType type; - - bool attrs_fetched; - MonoCustomAttrInfo *attributes = nullptr; - -public: - virtual GDMonoClass *get_enclosing_class() const final { return owner; } - - virtual MemberType get_member_type() const final { return MEMBER_TYPE_PROPERTY; } - - virtual StringName get_name() const final { return name; } - - virtual bool is_static() final; - virtual Visibility get_visibility() final; - - virtual bool has_attribute(GDMonoClass *p_attr_class) final; - virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) final; - void fetch_attributes(); - - bool has_getter(); - bool has_setter(); - - _FORCE_INLINE_ ManagedType get_type() const { return type; } - - void set_value_from_variant(MonoObject *p_object, const Variant &p_value, MonoException **r_exc = nullptr); - MonoObject *get_value(MonoObject *p_object, MonoException **r_exc = nullptr); - - bool get_bool_value(MonoObject *p_object); - int get_int_value(MonoObject *p_object); - String get_string_value(MonoObject *p_object); - - GDMonoProperty(MonoProperty *p_mono_property, GDMonoClass *p_owner); - ~GDMonoProperty(); -}; - -#endif // GD_MONO_PROPERTY_H diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp index e240381112..3e2f4b05d5 100644 --- a/modules/mono/mono_gd/gd_mono_utils.cpp +++ b/modules/mono/mono_gd/gd_mono_utils.cpp @@ -48,68 +48,9 @@ #include "../utils/macros.h" #include "gd_mono.h" #include "gd_mono_cache.h" -#include "gd_mono_class.h" -#include "gd_mono_marshal.h" namespace GDMonoUtils { -MonoObject *unmanaged_get_managed(Object *unmanaged) { - if (!unmanaged) { - return nullptr; - } - - if (unmanaged->get_script_instance()) { - CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(unmanaged->get_script_instance()); - - if (cs_instance) { - return cs_instance->get_mono_object(); - } - } - - // If the owner does not have a CSharpInstance... - - void *data = CSharpLanguage::get_instance_binding(unmanaged); - ERR_FAIL_NULL_V(data, nullptr); - CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->value(); - ERR_FAIL_COND_V(!script_binding.inited, nullptr); - - MonoGCHandleData &gchandle = script_binding.gchandle; - - MonoObject *target = gchandle.get_target(); - - if (target) { - return target; - } - - CSharpLanguage::get_singleton()->release_script_gchandle(gchandle); - - // Create a new one - -#ifdef DEBUG_ENABLED - CRASH_COND(script_binding.type_name == StringName()); - CRASH_COND(script_binding.wrapper_class == nullptr); -#endif - - MonoObject *mono_object = GDMonoUtils::create_managed_for_godot_object(script_binding.wrapper_class, script_binding.type_name, unmanaged); - ERR_FAIL_NULL_V(mono_object, nullptr); - - gchandle = MonoGCHandleData::new_strong_handle(mono_object); - - // Tie managed to unmanaged - RefCounted *rc = Object::cast_to<RefCounted>(unmanaged); - - if (rc) { - // 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) - rc->reference(); - CSharpLanguage::get_singleton()->post_unsafe_reference(rc); - } - - return mono_object; -} - void set_main_thread(MonoThread *p_thread) { mono_thread_set_main(p_thread); } @@ -148,90 +89,6 @@ bool is_thread_attached() { return mono_domain_get() != nullptr; } -uint32_t new_strong_gchandle(MonoObject *p_object) { - return mono_gchandle_new(p_object, /* pinned: */ false); -} - -uint32_t new_strong_gchandle_pinned(MonoObject *p_object) { - return mono_gchandle_new(p_object, /* pinned: */ true); -} - -uint32_t new_weak_gchandle(MonoObject *p_object) { - return mono_gchandle_new_weakref(p_object, /* track_resurrection: */ false); -} - -void free_gchandle(uint32_t p_gchandle) { - mono_gchandle_free(p_gchandle); -} - -void runtime_object_init(MonoObject *p_this_obj, GDMonoClass *p_class, MonoException **r_exc) { - GDMonoMethod *ctor = p_class->get_method(".ctor", 0); - ERR_FAIL_NULL(ctor); - ctor->invoke_raw(p_this_obj, nullptr, r_exc); -} - -GDMonoClass *get_object_class(MonoObject *p_object) { - return GDMono::get_singleton()->get_class(mono_object_get_class(p_object)); -} - -GDMonoClass *type_get_proxy_class(const StringName &p_type) { - String class_name = p_type; - - if (class_name[0] == '_') { - class_name = class_name.substr(1, class_name.length()); - } - - GDMonoClass *klass = GDMono::get_singleton()->get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, class_name); - - if (klass && klass->is_static()) { - // A static class means this is a Godot singleton class. If an instance is needed we use Godot.Object. - return GDMonoCache::cached_data.class_GodotObject; - } - -#ifdef TOOLS_ENABLED - if (!klass) { - return GDMono::get_singleton()->get_editor_api_assembly()->get_class(BINDINGS_NAMESPACE, class_name); - } -#endif - - return klass; -} - -GDMonoClass *get_class_native_base(GDMonoClass *p_class) { - GDMonoClass *klass = p_class; - - do { - const GDMonoAssembly *assembly = klass->get_assembly(); - - if (assembly == GDMono::get_singleton()->get_core_api_assembly()) { - return klass; - } -#ifdef TOOLS_ENABLED - if (assembly == GDMono::get_singleton()->get_editor_api_assembly()) { - return klass; - } -#endif - } while ((klass = klass->get_parent_class()) != nullptr); - - return nullptr; -} - -MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringName &p_native, Object *p_object) { - bool parent_is_object_class = ClassDB::is_parent_class(p_object->get_class_name(), p_native); - ERR_FAIL_COND_V_MSG(!parent_is_object_class, nullptr, - "Type inherits from native type '" + p_native + "', so it can't be instantiated in object of type: '" + p_object->get_class() + "'."); - - MonoObject *mono_object = mono_object_new(mono_domain_get(), p_class->get_mono_ptr()); - ERR_FAIL_NULL_V(mono_object, nullptr); - - CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, p_object); - - // Construct - GDMonoUtils::runtime_object_init(mono_object, p_class); - - return mono_object; -} - MonoDomain *create_domain(const String &p_friendly_name) { print_verbose("Mono: Creating domain '" + p_friendly_name + "'..."); @@ -247,14 +104,12 @@ MonoDomain *create_domain(const String &p_friendly_name) { return domain; } -String get_type_desc(MonoType *p_type) { - return mono_type_full_name(p_type); -} - -String get_type_desc(MonoReflectionType *p_reftype) { - return get_type_desc(mono_reflection_type_get_type(p_reftype)); -} +// TODO: +// Implement all of the disabled exception logging below. Once we move to .NET 6. +// It will have to be done from C# as UnmanagedCallersOnly doesn't allow throwing. +#warning TODO +#if 0 String get_exception_name_and_message(MonoException *p_exc) { String res; @@ -273,6 +128,7 @@ String get_exception_name_and_message(MonoException *p_exc) { return res; } +#endif void debug_print_unhandled_exception(MonoException *p_exc) { print_unhandled_exception(p_exc); @@ -284,7 +140,10 @@ void debug_send_unhandled_exception_error(MonoException *p_exc) { if (!EngineDebugger::is_active()) { #ifdef TOOLS_ENABLED if (Engine::get_singleton()->is_editor_hint()) { +#warning TODO +#if 0 ERR_PRINT(GDMonoUtils::get_exception_name_and_message(p_exc)); +#endif } #endif return; @@ -305,6 +164,8 @@ void debug_send_unhandled_exception_error(MonoException *p_exc) { Vector<ScriptLanguage::StackInfo> si; String exc_msg; +#warning TODO +#if 0 while (p_exc != nullptr) { GDMonoClass *st_klass = CACHED_CLASS(System_Diagnostics_StackTrace); MonoObject *stack_trace = mono_object_new(mono_domain_get(), st_klass->get_mono_ptr()); @@ -341,6 +202,7 @@ void debug_send_unhandled_exception_error(MonoException *p_exc) { p_exc = (MonoException *)inner_exc; } +#endif String file = si.size() ? si[0].file : __FILE__; String func = si.size() ? si[0].func : FUNCTION_STR; @@ -377,101 +239,6 @@ void set_pending_exception(MonoException *p_exc) { thread_local int current_invoke_count = 0; -MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, MonoException **r_exc) { - GD_MONO_BEGIN_RUNTIME_INVOKE; - MonoObject *ret = mono_runtime_invoke(p_method, p_obj, p_params, (MonoObject **)r_exc); - GD_MONO_END_RUNTIME_INVOKE; - return ret; -} - -MonoObject *runtime_invoke_array(MonoMethod *p_method, void *p_obj, MonoArray *p_params, MonoException **r_exc) { - GD_MONO_BEGIN_RUNTIME_INVOKE; - MonoObject *ret = mono_runtime_invoke_array(p_method, p_obj, p_params, (MonoObject **)r_exc); - GD_MONO_END_RUNTIME_INVOKE; - return ret; -} - -MonoString *object_to_string(MonoObject *p_obj, MonoException **r_exc) { - GD_MONO_BEGIN_RUNTIME_INVOKE; - MonoString *ret = mono_object_to_string(p_obj, (MonoObject **)r_exc); - GD_MONO_END_RUNTIME_INVOKE; - return ret; -} - -void property_set_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **r_exc) { - GD_MONO_BEGIN_RUNTIME_INVOKE; - mono_property_set_value(p_prop, p_obj, p_params, (MonoObject **)r_exc); - GD_MONO_END_RUNTIME_INVOKE; -} - -MonoObject *property_get_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **r_exc) { - GD_MONO_BEGIN_RUNTIME_INVOKE; - MonoObject *ret = mono_property_get_value(p_prop, p_obj, p_params, (MonoObject **)r_exc); - GD_MONO_END_RUNTIME_INVOKE; - return ret; -} - -uint64_t unbox_enum_value(MonoObject *p_boxed, MonoType *p_enum_basetype, bool &r_error) { - r_error = false; - switch (mono_type_get_type(p_enum_basetype)) { - case MONO_TYPE_BOOLEAN: - return (bool)GDMonoMarshal::unbox<MonoBoolean>(p_boxed) ? 1 : 0; - case MONO_TYPE_CHAR: - return GDMonoMarshal::unbox<uint16_t>(p_boxed); - case MONO_TYPE_U1: - return GDMonoMarshal::unbox<uint8_t>(p_boxed); - case MONO_TYPE_U2: - return GDMonoMarshal::unbox<uint16_t>(p_boxed); - case MONO_TYPE_U4: - return GDMonoMarshal::unbox<uint32_t>(p_boxed); - case MONO_TYPE_U8: - return GDMonoMarshal::unbox<uint64_t>(p_boxed); - case MONO_TYPE_I1: - return GDMonoMarshal::unbox<int8_t>(p_boxed); - case MONO_TYPE_I2: - return GDMonoMarshal::unbox<int16_t>(p_boxed); - case MONO_TYPE_I4: - return GDMonoMarshal::unbox<int32_t>(p_boxed); - case MONO_TYPE_I8: - return GDMonoMarshal::unbox<int64_t>(p_boxed); - default: - r_error = true; - return 0; - } -} - -void dispose(MonoObject *p_mono_object, MonoException **r_exc) { - CACHED_METHOD_THUNK(GodotObject, Dispose).invoke(p_mono_object, r_exc); -} - -namespace Marshal { - -#ifdef MONO_GLUE_ENABLED -#ifdef TOOLS_ENABLED -#define NO_GLUE_RET(m_ret) \ - { \ - if (!GDMonoCache::cached_data.godot_api_cache_updated) \ - return m_ret; \ - } -#else -#define NO_GLUE_RET(m_ret) \ - {} -#endif -#else -#define NO_GLUE_RET(m_ret) \ - { return m_ret; } -#endif - -bool type_has_flags_attribute(MonoReflectionType *p_reftype) { - NO_GLUE_RET(false); - MonoException *exc = nullptr; - MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeHasFlagsAttribute).invoke(p_reftype, &exc); - UNHANDLED_EXCEPTION(exc); - return (bool)res; -} - -} // namespace Marshal - ScopeThreadAttach::ScopeThreadAttach() { if (likely(GDMono::get_singleton()->is_runtime_initialized()) && unlikely(!mono_domain_get())) { mono_thread = GDMonoUtils::attach_current_thread(); @@ -483,9 +250,4 @@ ScopeThreadAttach::~ScopeThreadAttach() { GDMonoUtils::detach_current_thread(mono_thread); } } - -StringName get_native_godot_class_name(GDMonoClass *p_class) { - MonoObject *native_name_obj = p_class->get_field(BINDINGS_NATIVE_NAME_FIELD)->get_value(nullptr); - return (StringName)GDMonoMarshal::mono_object_to_variant(native_name_obj); -} } // namespace GDMonoUtils diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h index ee1be979e7..16fc3cc757 100644 --- a/modules/mono/mono_gd/gd_mono_utils.h +++ b/modules/mono/mono_gd/gd_mono_utils.h @@ -35,7 +35,6 @@ #include "../mono_gc_handle.h" #include "../utils/macros.h" -#include "gd_mono_header.h" #ifdef JAVASCRIPT_ENABLED #include "gd_mono_wasm_m2n.h" #endif @@ -60,13 +59,6 @@ _FORCE_INLINE_ void hash_combine(uint32_t &p_hash, const uint32_t &p_with_hash) p_hash ^= p_with_hash + 0x9e3779b9 + (p_hash << 6) + (p_hash >> 2); } -/** - * If the object has a csharp script, returns the target of the gchandle stored in the script instance - * Otherwise returns a newly constructed MonoObject* which is attached to the object - * Returns nullptr on error - */ -MonoObject *unmanaged_get_managed(Object *unmanaged); - void set_main_thread(MonoThread *p_thread); MonoThread *attach_current_thread(); void detach_current_thread(); @@ -74,24 +66,8 @@ void detach_current_thread(MonoThread *p_mono_thread); MonoThread *get_current_thread(); bool is_thread_attached(); -uint32_t new_strong_gchandle(MonoObject *p_object); -uint32_t new_strong_gchandle_pinned(MonoObject *p_object); -uint32_t new_weak_gchandle(MonoObject *p_object); -void free_gchandle(uint32_t p_gchandle); - -void runtime_object_init(MonoObject *p_this_obj, GDMonoClass *p_class, MonoException **r_exc = nullptr); - -GDMonoClass *get_object_class(MonoObject *p_object); -GDMonoClass *type_get_proxy_class(const StringName &p_type); -GDMonoClass *get_class_native_base(GDMonoClass *p_class); - -MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringName &p_native, Object *p_object); - MonoDomain *create_domain(const String &p_friendly_name); -String get_type_desc(MonoType *p_type); -String get_type_desc(MonoReflectionType *p_reftype); - String get_exception_name_and_message(MonoException *p_exc); void debug_print_unhandled_exception(MonoException *p_exc); @@ -116,18 +92,8 @@ _FORCE_INLINE_ int &get_runtime_invoke_count_ref() { return current_invoke_count; } -MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, MonoException **r_exc); -MonoObject *runtime_invoke_array(MonoMethod *p_method, void *p_obj, MonoArray *p_params, MonoException **r_exc); - -MonoString *object_to_string(MonoObject *p_obj, MonoException **r_exc); - -void property_set_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **r_exc); -MonoObject *property_get_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **r_exc); - uint64_t unbox_enum_value(MonoObject *p_boxed, MonoType *p_enum_basetype, bool &r_error); -void dispose(MonoObject *p_mono_object, MonoException **r_exc); - struct ScopeThreadAttach { ScopeThreadAttach(); ~ScopeThreadAttach(); @@ -136,8 +102,6 @@ private: MonoThread *mono_thread = nullptr; }; -StringName get_native_godot_class_name(GDMonoClass *p_class); - template <typename... P> void add_internal_call(const char *p_name, void (*p_func)(P...)) { #ifdef JAVASCRIPT_ENABLED @@ -155,8 +119,6 @@ void add_internal_call(const char *p_name, R (*p_func)(P...)) { } } // namespace GDMonoUtils -#define NATIVE_GDMONOCLASS_NAME(m_class) (GDMonoUtils::get_native_godot_class_name(m_class)) - #define GD_MONO_BEGIN_RUNTIME_INVOKE \ int &_runtime_invoke_count_ref = GDMonoUtils::get_runtime_invoke_count_ref(); \ _runtime_invoke_count_ref += 1; \ diff --git a/modules/mono/mono_gd/i_mono_class_member.h b/modules/mono/mono_gd/i_mono_class_member.h deleted file mode 100644 index 14e8ca82b9..0000000000 --- a/modules/mono/mono_gd/i_mono_class_member.h +++ /dev/null @@ -1,70 +0,0 @@ -/*************************************************************************/ -/* i_mono_class_member.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 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 I_MONO_CLASS_MEMBER_H -#define I_MONO_CLASS_MEMBER_H - -#include "gd_mono_header.h" - -#include <mono/metadata/object.h> - -class IMonoClassMember { -public: - enum Visibility { - PRIVATE, - PROTECTED_AND_INTERNAL, // FAM_AND_ASSEM - INTERNAL, // ASSEMBLY - PROTECTED, // FAMILY - PUBLIC - }; - - enum MemberType { - MEMBER_TYPE_FIELD, - MEMBER_TYPE_PROPERTY, - MEMBER_TYPE_METHOD - }; - - virtual ~IMonoClassMember() {} - - virtual GDMonoClass *get_enclosing_class() const = 0; - - virtual MemberType get_member_type() const = 0; - - virtual StringName get_name() const = 0; - - virtual bool is_static() = 0; - - virtual Visibility get_visibility() = 0; - - virtual bool has_attribute(GDMonoClass *p_attr_class) = 0; - virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) = 0; -}; - -#endif // I_MONO_CLASS_MEMBER_H diff --git a/modules/mono/mono_gd/managed_type.cpp b/modules/mono/mono_gd/managed_type.cpp deleted file mode 100644 index 5860d7db1e..0000000000 --- a/modules/mono/mono_gd/managed_type.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/*************************************************************************/ -/* managed_type.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 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 "managed_type.h" - -#include "gd_mono.h" -#include "gd_mono_class.h" - -ManagedType ManagedType::from_class(GDMonoClass *p_class) { - return ManagedType(mono_type_get_type(p_class->get_mono_type()), p_class); -} - -ManagedType ManagedType::from_class(MonoClass *p_mono_class) { - GDMonoClass *tclass = GDMono::get_singleton()->get_class(p_mono_class); - ERR_FAIL_COND_V(!tclass, ManagedType()); - - return ManagedType(mono_type_get_type(tclass->get_mono_type()), tclass); -} - -ManagedType ManagedType::from_type(MonoType *p_mono_type) { - MonoClass *mono_class = mono_class_from_mono_type(p_mono_type); - GDMonoClass *tclass = GDMono::get_singleton()->get_class(mono_class); - ERR_FAIL_COND_V(!tclass, ManagedType()); - - return ManagedType(mono_type_get_type(p_mono_type), tclass); -} - -ManagedType ManagedType::from_reftype(MonoReflectionType *p_mono_reftype) { - MonoType *mono_type = mono_reflection_type_get_type(p_mono_reftype); - return from_type(mono_type); -} diff --git a/modules/mono/mono_gd/managed_type.h b/modules/mono/mono_gd/managed_type.h deleted file mode 100644 index 603ff3aca1..0000000000 --- a/modules/mono/mono_gd/managed_type.h +++ /dev/null @@ -1,55 +0,0 @@ -/*************************************************************************/ -/* managed_type.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 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 MANAGED_TYPE_H -#define MANAGED_TYPE_H - -#include <mono/metadata/object.h> - -#include "gd_mono_header.h" - -struct ManagedType { - int type_encoding = 0; - GDMonoClass *type_class = nullptr; - - static ManagedType from_class(GDMonoClass *p_class); - static ManagedType from_class(MonoClass *p_mono_class); - static ManagedType from_type(MonoType *p_mono_type); - static ManagedType from_reftype(MonoReflectionType *p_mono_reftype); - - ManagedType() {} - - ManagedType(int p_type_encoding, GDMonoClass *p_type_class) : - type_encoding(p_type_encoding), - type_class(p_type_class) { - } -}; - -#endif // MANAGED_TYPE_H diff --git a/modules/mono/mono_gd/support/android_support.cpp b/modules/mono/mono_gd/support/android_support.cpp index 4797d5dae1..7fb983cd37 100644 --- a/modules/mono/mono_gd/support/android_support.cpp +++ b/modules/mono/mono_gd/support/android_support.cpp @@ -359,7 +359,7 @@ MonoArray *_gd_mono_android_cert_store_lookup(MonoString *p_alias) { ScopedLocalRef<jbyteArray> encoded(env, (jbyteArray)env->CallObjectMethod(certificate, getEncoded)); jsize encodedLength = env->GetArrayLength(encoded); - MonoArray *encoded_ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(uint8_t), encodedLength); + MonoArray *encoded_ret = mono_array_new(mono_domain_get(), mono_get_byte_class(), encodedLength); uint8_t *dest = (uint8_t *)mono_array_addr(encoded_ret, uint8_t, 0); env->GetByteArrayRegion(encoded, 0, encodedLength, reinterpret_cast<jbyte *>(dest)); diff --git a/modules/mono/signal_awaiter_utils.cpp b/modules/mono/signal_awaiter_utils.cpp index f26459554c..b71e5f392b 100644 --- a/modules/mono/signal_awaiter_utils.cpp +++ b/modules/mono/signal_awaiter_utils.cpp @@ -32,16 +32,15 @@ #include "csharp_script.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" -Error gd_mono_connect_signal_awaiter(Object *p_source, const StringName &p_signal, Object *p_target, MonoObject *p_awaiter) { +Error gd_mono_connect_signal_awaiter(Object *p_source, const StringName &p_signal, Object *p_target, GCHandleIntPtr p_awaiter_handle_ptr) { ERR_FAIL_NULL_V(p_source, ERR_INVALID_DATA); ERR_FAIL_NULL_V(p_target, ERR_INVALID_DATA); // TODO: Use pooling for ManagedCallable instances. - SignalAwaiterCallable *awaiter_callable = memnew(SignalAwaiterCallable(p_target, p_awaiter, p_signal)); + MonoGCHandleData awaiter_handle(p_awaiter_handle_ptr, gdmono::GCHandleType::STRONG_HANDLE); + SignalAwaiterCallable *awaiter_callable = memnew(SignalAwaiterCallable(p_target, awaiter_handle, p_signal)); Callable callable = Callable(awaiter_callable); return p_source->connect(p_signal, callable, Object::CONNECT_ONESHOT); @@ -51,7 +50,7 @@ bool SignalAwaiterCallable::compare_equal(const CallableCustom *p_a, const Calla // Only called if both instances are of type SignalAwaiterCallable. Static cast is safe. const SignalAwaiterCallable *a = static_cast<const SignalAwaiterCallable *>(p_a); const SignalAwaiterCallable *b = static_cast<const SignalAwaiterCallable *>(p_b); - return a->awaiter_handle.handle == b->awaiter_handle.handle; + return a->awaiter_handle.handle.value == b->awaiter_handle.handle.value; } bool SignalAwaiterCallable::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) { @@ -105,38 +104,26 @@ void SignalAwaiterCallable::call(const Variant **p_arguments, int p_argcount, Va "Resumed after await, but class instance is gone."); #endif - MonoArray *signal_args = nullptr; - - if (p_argcount > 0) { - signal_args = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), p_argcount); + MonoException *exc = nullptr; + bool awaiter_is_null = false; + GDMonoCache::cached_data.methodthunk_SignalAwaiter_SignalCallback.invoke(awaiter_handle.get_intptr(), p_arguments, p_argcount, &awaiter_is_null, &exc); - for (int i = 0; i < p_argcount; i++) { - MonoObject *boxed = GDMonoMarshal::variant_to_mono_object(*p_arguments[i]); - mono_array_setref(signal_args, i, boxed); - } + if (exc) { + GDMonoUtils::set_pending_exception(exc); + ERR_FAIL(); } - MonoObject *awaiter = awaiter_handle.get_target(); - - if (!awaiter) { + if (awaiter_is_null) { r_call_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL; return; } - MonoException *exc = nullptr; - CACHED_METHOD_THUNK(SignalAwaiter, SignalCallback).invoke(awaiter, signal_args, &exc); - - if (exc) { - GDMonoUtils::set_pending_exception(exc); - ERR_FAIL(); - } else { - r_call_error.error = Callable::CallError::CALL_OK; - } + r_call_error.error = Callable::CallError::CALL_OK; } -SignalAwaiterCallable::SignalAwaiterCallable(Object *p_target, MonoObject *p_awaiter, const StringName &p_signal) : +SignalAwaiterCallable::SignalAwaiterCallable(Object *p_target, MonoGCHandleData p_awaiter_handle, const StringName &p_signal) : target_id(p_target->get_instance_id()), - awaiter_handle(MonoGCHandleData::new_strong_handle(p_awaiter)), + awaiter_handle(p_awaiter_handle), signal(p_signal) { } @@ -152,7 +139,7 @@ bool EventSignalCallable::compare_equal(const CallableCustom *p_a, const Callabl return false; } - if (a->event_signal != b->event_signal) { + if (a->event_signal_name != b->event_signal_name) { return false; } @@ -167,7 +154,7 @@ bool EventSignalCallable::compare_less(const CallableCustom *p_a, const Callable } uint32_t EventSignalCallable::hash() const { - uint32_t hash = event_signal->field->get_name().hash(); + uint32_t hash = event_signal_name.hash(); return hash_murmur3_one_64(owner->get_instance_id(), hash); } @@ -177,8 +164,7 @@ String EventSignalCallable::get_as_text() const { if (script.is_valid() && script->get_path().is_resource_file()) { class_name += "(" + script->get_path().get_file() + ")"; } - StringName signal = event_signal->field->get_name(); - return class_name + "::EventSignalMiddleman::" + String(signal); + return class_name + "::EventSignalMiddleman::" + String(event_signal_name); } CallableCustom::CompareEqualFunc EventSignalCallable::get_compare_equal_func() const { @@ -194,39 +180,38 @@ ObjectID EventSignalCallable::get_object() const { } StringName EventSignalCallable::get_signal() const { - return event_signal->field->get_name(); + return event_signal_name; } void EventSignalCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const { r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; // Can't find anything better r_return_value = Variant(); - ERR_FAIL_COND(p_argcount < event_signal->invoke_method->get_parameters_count()); - CSharpInstance *csharp_instance = CAST_CSHARP_INSTANCE(owner->get_script_instance()); ERR_FAIL_NULL(csharp_instance); - MonoObject *owner_managed = csharp_instance->get_mono_object(); - ERR_FAIL_NULL(owner_managed); - - MonoObject *delegate_field_value = event_signal->field->get_value(owner_managed); - if (!delegate_field_value) { - r_call_error.error = Callable::CallError::CALL_OK; - return; - } + GCHandleIntPtr owner_gchandle_intptr = csharp_instance->get_gchandle_intptr(); MonoException *exc = nullptr; - event_signal->invoke_method->invoke(delegate_field_value, p_arguments, &exc); + bool awaiter_is_null = false; + GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_RaiseEventSignal.invoke( + owner_gchandle_intptr, &event_signal_name, + p_arguments, p_argcount, &awaiter_is_null, &exc); if (exc) { GDMonoUtils::set_pending_exception(exc); ERR_FAIL(); - } else { - r_call_error.error = Callable::CallError::CALL_OK; } + + if (awaiter_is_null) { + r_call_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL; + return; + } + + r_call_error.error = Callable::CallError::CALL_OK; } -EventSignalCallable::EventSignalCallable(Object *p_owner, const CSharpScript::EventSignal *p_event_signal) : +EventSignalCallable::EventSignalCallable(Object *p_owner, const StringName &p_event_signal_name) : owner(p_owner), - event_signal(p_event_signal) { + event_signal_name(p_event_signal_name) { } diff --git a/modules/mono/signal_awaiter_utils.h b/modules/mono/signal_awaiter_utils.h index 4f4986baf3..a53ae56bf5 100644 --- a/modules/mono/signal_awaiter_utils.h +++ b/modules/mono/signal_awaiter_utils.h @@ -36,7 +36,7 @@ #include "csharp_script.h" #include "mono_gc_handle.h" -Error gd_mono_connect_signal_awaiter(Object *p_source, const StringName &p_signal, Object *p_target, MonoObject *p_awaiter); +Error gd_mono_connect_signal_awaiter(Object *p_source, const StringName &p_signal, Object *p_target, GCHandleIntPtr p_awaiter_handle_ptr); class BaseSignalCallable : public CallableCustom { public: @@ -68,13 +68,13 @@ public: void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override; - SignalAwaiterCallable(Object *p_target, MonoObject *p_awaiter, const StringName &p_signal); + SignalAwaiterCallable(Object *p_target, MonoGCHandleData p_awaiter_handle, const StringName &p_signal); ~SignalAwaiterCallable(); }; class EventSignalCallable : public BaseSignalCallable { Object *owner = nullptr; - const CSharpScript::EventSignal *event_signal; + StringName event_signal_name; public: static bool compare_equal(const CallableCustom *p_a, const CallableCustom *p_b); @@ -96,7 +96,7 @@ public: void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override; - EventSignalCallable(Object *p_owner, const CSharpScript::EventSignal *p_event_signal); + EventSignalCallable(Object *p_owner, const StringName &p_event_signal_name); }; #endif // SIGNAL_AWAITER_UTILS_H |