/*************************************************************************/ /* gd_mono_cache.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_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."); \ } #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_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."); \ } #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; class_MonoObject = nullptr; class_String = 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 class_KeyNotFoundException = nullptr; } void CachedData::clear_godot_api_cache() { godot_api_cache_updated = false; class_GodotObject = nullptr; class_GodotResource = 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_RemoteAttribute = nullptr; class_MasterAttribute = nullptr; class_PuppetAttribute = 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_GodotTaskScheduler_Activate.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(); task_scheduler_handle = Ref(); } #define GODOT_API_CLASS(m_class) (GDMono::get_singleton()->get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, #m_class)) 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())); #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(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(RemoteAttribute, GODOT_API_CLASS(RemoteAttribute)); CACHE_CLASS_AND_CHECK(MasterAttribute, GODOT_API_CLASS(MasterAttribute)); CACHE_CLASS_AND_CHECK(PuppetAttribute, GODOT_API_CLASS(PuppetAttribute)); 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(GodotTaskScheduler, Activate, GODOT_API_CLASS(GodotTaskScheduler)->get_method("Activate", 0)); CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, TrySerializeDelegateWithGCHandle, GODOT_API_CLASS(DelegateUtils)->get_method("TrySerializeDelegateWithGCHandle", 2)); CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, TryDeserializeDelegateWithGCHandle, GODOT_API_CLASS(DelegateUtils)->get_method("TryDeserializeDelegateWithGCHandle", 2)); 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)); #ifdef DEBUG_ENABLED CACHE_METHOD_THUNK_AND_CHECK(DebuggingUtils, GetStackFrameInfo, GODOT_API_CLASS(DebuggingUtils)->get_method("GetStackFrameInfo", 4)); #endif // TODO Move to CSharpLanguage::init() and do handle disposal MonoObject *task_scheduler = mono_object_new(mono_domain_get(), GODOT_API_CLASS(GodotTaskScheduler)->get_mono_ptr()); GDMonoUtils::runtime_object_init(task_scheduler, GODOT_API_CLASS(GodotTaskScheduler)); cached_data.task_scheduler_handle = MonoGCHandleRef::create_strong(task_scheduler); cached_data.godot_api_cache_updated = true; } } // namespace GDMonoCache