summaryrefslogtreecommitdiffstats
path: root/modules/mono/csharp_script.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'modules/mono/csharp_script.cpp')
-rw-r--r--modules/mono/csharp_script.cpp1311
1 files changed, 509 insertions, 802 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> &params) {
- 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> &params = E.value;
- for (int i = 0; i < params.size(); i++) {
- const SignalParameter &param = 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> &params = event_signal.parameters;
for (int i = 0; i < params.size(); i++) {
- const SignalParameter &param = 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");
}