diff options
Diffstat (limited to 'modules/mono')
54 files changed, 2300 insertions, 1179 deletions
diff --git a/modules/mono/SCsub b/modules/mono/SCsub index a9afa7ccf6..457edfaeed 100644 --- a/modules/mono/SCsub +++ b/modules/mono/SCsub @@ -42,14 +42,14 @@ mono_configure.configure(env, env_mono) if env_mono['tools'] and env_mono['mono_glue']: import build_scripts.api_solution_build as api_solution_build - api_solution_build.build(env_mono) + api_sln_cmd = api_solution_build.build(env_mono) # Build GodotTools if env_mono['tools']: import build_scripts.godot_tools_build as godot_tools_build if env_mono['mono_glue']: - godot_tools_build.build(env_mono) + godot_tools_build.build(env_mono, api_sln_cmd) else: # Building without the glue sources so the Godot API solution may be missing. # GodotTools depends on the Godot API solution. As such, we will only build diff --git a/modules/mono/build_scripts/api_solution_build.py b/modules/mono/build_scripts/api_solution_build.py index 1fe00a3028..be54d0a679 100644 --- a/modules/mono/build_scripts/api_solution_build.py +++ b/modules/mono/build_scripts/api_solution_build.py @@ -55,12 +55,22 @@ def build(env_mono): 'GodotSharpEditor.dll', 'GodotSharpEditor.pdb', 'GodotSharpEditor.xml' ] + depend_cmd = [] + for build_config in ['Debug', 'Release']: output_dir = Dir('#bin').abspath editor_api_dir = os.path.join(output_dir, 'GodotSharp', 'Api', build_config) targets = [os.path.join(editor_api_dir, filename) for filename in target_filenames] - cmd = env_mono.CommandNoCache(targets, [], build_api_solution, + cmd = env_mono.CommandNoCache(targets, depend_cmd, build_api_solution, module_dir=os.getcwd(), solution_build_config=build_config) env_mono.AlwaysBuild(cmd) + + # Make the Release build of the API solution depend on the Debug build. + # We do this in order to prevent SCons from building them in parallel, + # which can freak out MSBuild. In many cases, one of the builds would + # hang indefinitely requiring a key to be pressed for it to continue. + depend_cmd = cmd + + return depend_cmd diff --git a/modules/mono/build_scripts/godot_tools_build.py b/modules/mono/build_scripts/godot_tools_build.py index 35daa6d307..03aaa925f0 100644 --- a/modules/mono/build_scripts/godot_tools_build.py +++ b/modules/mono/build_scripts/godot_tools_build.py @@ -74,15 +74,11 @@ def build_godot_tools_project_editor(source, target, env): copy_target(str(scons_target)) -def build(env_mono): +def build(env_mono, api_sln_cmd): assert env_mono['tools'] output_dir = Dir('#bin').abspath editor_tools_dir = os.path.join(output_dir, 'GodotSharp', 'Tools') - editor_api_dir = os.path.join(output_dir, 'GodotSharp', 'Api', 'Debug') - - source_filenames = ['GodotSharp.dll', 'GodotSharpEditor.dll'] - sources = [os.path.join(editor_api_dir, filename) for filename in source_filenames] target_filenames = [ 'GodotTools.dll', 'GodotTools.IdeConnection.dll', 'GodotTools.BuildLogger.dll', @@ -97,7 +93,7 @@ def build(env_mono): targets = [os.path.join(editor_tools_dir, filename) for filename in target_filenames] - cmd = env_mono.CommandNoCache(targets, sources, build_godot_tools, module_dir=os.getcwd()) + cmd = env_mono.CommandNoCache(targets, api_sln_cmd, build_godot_tools, module_dir=os.getcwd()) env_mono.AlwaysBuild(cmd) diff --git a/modules/mono/build_scripts/mono_configure.py b/modules/mono/build_scripts/mono_configure.py index c5c8b9adbd..c09690ba6d 100644 --- a/modules/mono/build_scripts/mono_configure.py +++ b/modules/mono/build_scripts/mono_configure.py @@ -44,9 +44,33 @@ def copy_file(src_dir, dst_dir, name): copy(src_path, dst_dir) +def is_desktop(platform): + return platform in ['windows', 'osx', 'x11', 'server', 'uwp', 'haiku'] + + +def is_unix_like(platform): + return platform in ['osx', 'x11', 'server', 'android', 'haiku'] + + +def module_supports_tools_on(platform): + return platform not in ['android', 'javascript'] + + +def find_wasm_src_dir(mono_root): + hint_dirs = [ + os.path.join(mono_root, 'src'), + os.path.join(mono_root, '../src'), + ] + for hint_dir in hint_dirs: + if os.path.isfile(os.path.join(hint_dir, 'driver.c')): + return hint_dir + return '' + + def configure(env, env_mono): bits = env['bits'] is_android = env['platform'] == 'android' + is_javascript = env['platform'] == 'javascript' tools_enabled = env['tools'] mono_static = env['mono_static'] @@ -63,17 +87,21 @@ def configure(env, env_mono): env_mono.Append(CPPDEFINES=['NO_PENDING_EXCEPTIONS']) if is_android and not env['android_arch'] in android_arch_dirs: - raise RuntimeError('This module does not support for the specified \'android_arch\': ' + env['android_arch']) + raise RuntimeError('This module does not support the specified \'android_arch\': ' + env['android_arch']) - if is_android and tools_enabled: - # TODO: Implement this. We have to add the data directory to the apk, concretely the Api and Tools folders. - raise RuntimeError('This module does not currently support building for android with tools enabled') + if tools_enabled and not module_supports_tools_on(env['platform']): + # TODO: + # Android: We have to add the data directory to the apk, concretely the Api and Tools folders. + raise RuntimeError('This module does not currently support building for this platform with tools enabled') if is_android and mono_static: - # When static linking and doing something that requires libmono-native, we get a dlopen error as libmono-native seems to depend on libmonosgen-2.0 - raise RuntimeError('Linking Mono statically is not currently supported on Android') + # Android: When static linking and doing something that requires libmono-native, we get a dlopen error as libmono-native seems to depend on libmonosgen-2.0 + raise RuntimeError('Statically linking Mono is not currently supported on this platform') - if (os.getenv('MONO32_PREFIX') or os.getenv('MONO64_PREFIX')) and not mono_prefix: + if is_javascript: + mono_static = True + + if not mono_prefix and (os.getenv('MONO32_PREFIX') or os.getenv('MONO64_PREFIX')): print("WARNING: The environment variables 'MONO32_PREFIX' and 'MONO64_PREFIX' are deprecated; use the 'mono_prefix' SCons parameter instead") if env['platform'] == 'windows': @@ -92,9 +120,9 @@ def configure(env, env_mono): env.Append(LIBPATH=mono_lib_path) env_mono.Prepend(CPPPATH=os.path.join(mono_root, 'include', 'mono-2.0')) - if mono_static: - lib_suffix = Environment()['LIBSUFFIX'] + lib_suffix = Environment()['LIBSUFFIX'] + if mono_static: if env.msvc: mono_static_lib_name = 'libmono-static-sgen' else: @@ -116,13 +144,13 @@ def configure(env, env_mono): env.Append(LIBS=['psapi']) env.Append(LIBS=['version']) else: - mono_lib_name = find_file_in_dir(mono_lib_path, mono_lib_names, extension='.lib') + mono_lib_name = find_file_in_dir(mono_lib_path, mono_lib_names, extension=lib_suffix) if not mono_lib_name: raise RuntimeError('Could not find mono library in: ' + mono_lib_path) if env.msvc: - env.Append(LINKFLAGS=mono_lib_name + Environment()['LIBSUFFIX']) + env.Append(LINKFLAGS=mono_lib_name + lib_suffix) else: env.Append(LIBS=[mono_lib_name]) @@ -143,7 +171,7 @@ def configure(env, env_mono): mono_lib_path = '' mono_so_name = '' - if not mono_root and is_android: + if not mono_root and (is_android or is_javascript): raise RuntimeError("Mono installation directory not found; specify one manually with the 'mono_prefix' SCons parameter") if not mono_root and is_apple: @@ -167,7 +195,7 @@ def configure(env, env_mono): mono_lib_path = os.path.join(mono_root, 'lib') - env.Append(LIBPATH=mono_lib_path) + env.Append(LIBPATH=[mono_lib_path]) env_mono.Prepend(CPPPATH=os.path.join(mono_root, 'include', 'mono-2.0')) mono_lib = find_file_in_dir(mono_lib_path, mono_lib_names, prefix='lib', extension='.a') @@ -183,7 +211,30 @@ def configure(env, env_mono): if is_apple: env.Append(LINKFLAGS=['-Wl,-force_load,' + mono_lib_file]) else: + assert is_desktop(env['platform']) or is_android or is_javascript env.Append(LINKFLAGS=['-Wl,-whole-archive', mono_lib_file, '-Wl,-no-whole-archive']) + + if is_javascript: + env.Append(LIBS=['mono-icall-table', 'mono-native', 'mono-ilgen', 'mono-ee-interp']) + + wasm_src_dir = os.path.join(mono_root, 'src') + if not os.path.isdir(wasm_src_dir): + raise RuntimeError('Could not find mono wasm src directory') + + # Ideally this should be defined only for 'driver.c', but I can't fight scons for another 2 hours + env_mono.Append(CPPDEFINES=['CORE_BINDINGS']) + + env_mono.add_source_files(env.modules_sources, [ + os.path.join(wasm_src_dir, 'driver.c'), + os.path.join(wasm_src_dir, 'zlib-helper.c'), + os.path.join(wasm_src_dir, 'corebindings.c') + ]) + + env.Append(LINKFLAGS=[ + '--js-library', os.path.join(wasm_src_dir, 'library_mono.js'), + '--js-library', os.path.join(wasm_src_dir, 'binding_support.js'), + '--js-library', os.path.join(wasm_src_dir, 'dotnet_support.js') + ]) else: env.Append(LIBS=[mono_lib]) @@ -191,6 +242,8 @@ def configure(env, env_mono): env.Append(LIBS=['iconv', 'pthread']) elif is_android: pass # Nothing + elif is_javascript: + env.Append(LIBS=['m', 'rt', 'dl', 'pthread']) else: env.Append(LIBS=['m', 'rt', 'dl', 'pthread']) @@ -230,19 +283,22 @@ def configure(env, env_mono): env.Append(LINKFLAGS='-rdynamic') - if not tools_enabled and not is_android: - if not mono_root: - mono_root = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip() + if not tools_enabled: + if is_desktop(env['platform']): + if not mono_root: + mono_root = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip() - make_template_dir(env, mono_root) - elif not tools_enabled and is_android: - # Compress Android Mono Config - from . import make_android_mono_config - config_file_path = os.path.join(mono_root, 'etc', 'mono', 'config') - make_android_mono_config.generate_compressed_config(config_file_path, 'mono_gd/') + make_template_dir(env, mono_root) + elif is_android: + # Compress Android Mono Config + from . import make_android_mono_config + config_file_path = os.path.join(mono_root, 'etc', 'mono', 'config') + make_android_mono_config.generate_compressed_config(config_file_path, 'mono_gd/') - # Copy the required shared libraries - copy_mono_shared_libs(env, mono_root, None) + # Copy the required shared libraries + copy_mono_shared_libs(env, mono_root, None) + elif is_javascript: + pass # No data directory for this platform if copy_mono_root: if not mono_root: @@ -251,7 +307,7 @@ def configure(env, env_mono): if tools_enabled: copy_mono_root_files(env, mono_root) else: - print("Ignoring option: 'copy_mono_root'. Only available for builds with 'tools' enabled.") + print("Ignoring option: 'copy_mono_root'; only available for builds with 'tools' enabled.") def make_template_dir(env, mono_root): @@ -262,10 +318,9 @@ def make_template_dir(env, mono_root): template_dir_name = '' - if platform in ['windows', 'osx', 'x11', 'android', 'server']: - template_dir_name = 'data.mono.%s.%s.%s' % (platform, env['bits'], target) - else: - assert False + assert is_desktop(platform) + + template_dir_name = 'data.mono.%s.%s.%s' % (platform, env['bits'], target) output_dir = Dir('#bin').abspath template_dir = os.path.join(output_dir, template_dir_name) @@ -278,7 +333,7 @@ def make_template_dir(env, mono_root): # Copy etc/mono/ template_mono_config_dir = os.path.join(template_mono_root_dir, 'etc', 'mono') - copy_mono_etc_dir(mono_root, template_mono_config_dir, env['platform']) + copy_mono_etc_dir(mono_root, template_mono_config_dir, platform) # Copy the required shared libraries @@ -371,12 +426,19 @@ def copy_mono_shared_libs(env, mono_root, target_mono_root_dir): platform = env['platform'] if platform == 'windows': + src_mono_bin_dir = os.path.join(mono_root, 'bin') target_mono_bin_dir = os.path.join(target_mono_root_dir, 'bin') if not os.path.isdir(target_mono_bin_dir): os.makedirs(target_mono_bin_dir) - copy(os.path.join(mono_root, 'bin', 'MonoPosixHelper.dll'), target_mono_bin_dir) + mono_posix_helper_name = find_file_in_dir(src_mono_bin_dir, ['MonoPosixHelper', 'libMonoPosixHelper'], extension='.dll') + copy(os.path.join(src_mono_bin_dir, mono_posix_helper_name + '.dll'), os.path.join(target_mono_bin_dir, 'MonoPosixHelper.dll')) + + # For newer versions + btls_dll_path = os.path.join(src_mono_bin_dir, 'libmono-btls-shared.dll') + if os.path.isfile(btls_dll_path): + copy(btls_dll_path, target_mono_bin_dir) else: target_mono_lib_dir = get_android_out_dir(env) if platform == 'android' else os.path.join(target_mono_root_dir, 'lib') @@ -386,7 +448,7 @@ def copy_mono_shared_libs(env, mono_root, target_mono_root_dir): if platform == 'osx': # TODO: Make sure nothing is missing copy(os.path.join(mono_root, 'lib', 'libMonoPosixHelper.dylib'), target_mono_lib_dir) - elif platform == 'x11' or platform == 'android' or platform == 'server': + elif is_unix_like(platform): lib_file_names = [lib_name + '.so' for lib_name in [ 'libmono-btls-shared', 'libmono-ee-interp', 'libmono-native', 'libMonoPosixHelper', 'libmono-profiler-aot', 'libmono-profiler-coverage', 'libmono-profiler-log', 'libMonoSupportW' diff --git a/modules/mono/build_scripts/patches/fix-mono-android-tkill.diff b/modules/mono/build_scripts/patches/fix-mono-android-tkill.diff deleted file mode 100644 index 05f8dcadcc..0000000000 --- a/modules/mono/build_scripts/patches/fix-mono-android-tkill.diff +++ /dev/null @@ -1,70 +0,0 @@ -diff --git a/libgc/include/private/gcconfig.h b/libgc/include/private/gcconfig.h -index e2bdf13ac3e..f962200ba4e 100644 ---- a/libgc/include/private/gcconfig.h -+++ b/libgc/include/private/gcconfig.h -@@ -2255,6 +2255,14 @@ - # define GETPAGESIZE() getpagesize() - # endif - -+#if defined(HOST_ANDROID) && !(__ANDROID_API__ >= 23) \ -+ && ((defined(MIPS) && (CPP_WORDSZ == 32)) \ -+ || defined(ARM32) || defined(I386) /* but not x32 */) -+ /* tkill() exists only on arm32/mips(32)/x86. */ -+ /* NDK r11+ deprecates tkill() but keeps it for Mono clients. */ -+# define USE_TKILL_ON_ANDROID -+#endif -+ - # if defined(SUNOS5) || defined(DRSNX) || defined(UTS4) - /* OS has SVR4 generic features. Probably others also qualify. */ - # define SVR4 -diff --git a/libgc/pthread_stop_world.c b/libgc/pthread_stop_world.c -index f93ce26b562..4a49a6d578c 100644 ---- a/libgc/pthread_stop_world.c -+++ b/libgc/pthread_stop_world.c -@@ -336,7 +336,7 @@ void GC_push_all_stacks() - pthread_t GC_stopping_thread; - int GC_stopping_pid; - --#ifdef HOST_ANDROID -+#ifdef USE_TKILL_ON_ANDROID - static - int android_thread_kill(pid_t tid, int sig) - { -diff --git a/mono/metadata/threads.c b/mono/metadata/threads.c -index ad9b8823f8f..3542b32b540 100644 ---- a/mono/metadata/threads.c -+++ b/mono/metadata/threads.c -@@ -77,8 +77,12 @@ mono_native_thread_join_handle (HANDLE thread_handle, gboolean close_handle); - #include <zircon/syscalls.h> - #endif - --#if defined(HOST_ANDROID) && !defined(TARGET_ARM64) && !defined(TARGET_AMD64) --#define USE_TKILL_ON_ANDROID 1 -+#if defined(HOST_ANDROID) && !(__ANDROID_API__ >= 23) \ -+ && ((defined(MIPS) && (CPP_WORDSZ == 32)) \ -+ || defined(ARM32) || defined(I386) /* but not x32 */) -+ /* tkill() exists only on arm32/mips(32)/x86. */ -+ /* NDK r11+ deprecates tkill() but keeps it for Mono clients. */ -+# define USE_TKILL_ON_ANDROID - #endif - - #ifdef HOST_ANDROID -diff --git a/mono/utils/mono-threads-posix.c b/mono/utils/mono-threads-posix.c -index 3e4bf93de5f..79c9f731fe7 100644 ---- a/mono/utils/mono-threads-posix.c -+++ b/mono/utils/mono-threads-posix.c -@@ -31,8 +31,12 @@ - - #include <errno.h> - --#if defined(HOST_ANDROID) && !defined(TARGET_ARM64) && !defined(TARGET_AMD64) --#define USE_TKILL_ON_ANDROID 1 -+#if defined(HOST_ANDROID) && !(__ANDROID_API__ >= 23) \ -+ && ((defined(MIPS) && (CPP_WORDSZ == 32)) \ -+ || defined(ARM32) || defined(I386) /* but not x32 */) -+ /* tkill() exists only on arm32/mips(32)/x86. */ -+ /* NDK r11+ deprecates tkill() but keeps it for Mono clients. */ -+# define USE_TKILL_ON_ANDROID - #endif - - #ifdef USE_TKILL_ON_ANDROID diff --git a/modules/mono/config.py b/modules/mono/config.py index 9adf4ee6e5..70cb464c7a 100644 --- a/modules/mono/config.py +++ b/modules/mono/config.py @@ -1,10 +1,11 @@ def can_build(env, platform): - if platform in ['javascript']: - return False # Not yet supported return True def configure(env): + if env['platform'] not in ['windows', 'osx', 'x11', 'server', 'android', 'haiku', 'javascript']: + raise RuntimeError('This module does not currently support building for this platform') + env.use_ptrcall = True env.add_module_version_string('mono') @@ -18,6 +19,13 @@ def configure(env): envvars.Add(BoolVariable('xbuild_fallback', 'If MSBuild is not found, fallback to xbuild', False)) envvars.Update(env) + if env['platform'] == 'javascript': + # Mono wasm already has zlib builtin, so we need this workaround to avoid symbol collisions + print('Compiling with Mono wasm disables \'builtin_zlib\'') + env['builtin_zlib'] = False + thirdparty_zlib_dir = "#thirdparty/zlib/" + env.Prepend(CPPPATH=[thirdparty_zlib_dir]) + def get_doc_classes(): return [ diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 83be10dee3..4536614379 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -50,8 +50,10 @@ #include "editor/editor_internal_calls.h" #include "godotsharp_dirs.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" #include "utils/mutex_utils.h" @@ -126,12 +128,11 @@ void CSharpLanguage::init() { print_line("Run this binary with '--generate-mono-glue path/to/modules/mono/glue'"); #endif - gdmono->initialize_load_assemblies(); + if (gdmono->is_runtime_initialized()) + gdmono->initialize_load_assemblies(); #ifdef TOOLS_ENABLED EditorNode::add_init_callback(&_editor_init_callback); - - GLOBAL_DEF("mono/export/include_scripts_content", false); #endif } @@ -545,7 +546,7 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info() #ifdef DEBUG_ENABLED _TLS_RECURSION_GUARD_V_(Vector<StackInfo>()); - if (!gdmono->is_runtime_initialized() || !GDMono::get_singleton()->get_core_api_assembly() || !GDMonoUtils::mono_cache.corlib_cache_updated) + if (!gdmono->is_runtime_initialized() || !GDMono::get_singleton()->get_core_api_assembly() || !GDMonoCache::cached_data.corlib_cache_updated) return Vector<StackInfo>(); MonoObject *stack_trace = mono_object_new(mono_domain_get(), CACHED_CLASS(System_Diagnostics_StackTrace)->get_mono_ptr()); @@ -571,7 +572,7 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObjec MonoException *exc = NULL; - MonoArray *frames = invoke_method_thunk(CACHED_METHOD_THUNK(System_Diagnostics_StackTrace, GetFrames), p_stack_trace, &exc); + MonoArray *frames = CACHED_METHOD_THUNK(System_Diagnostics_StackTrace, GetFrames).invoke(p_stack_trace, &exc); if (exc) { GDMonoUtils::debug_print_unhandled_exception(exc); @@ -583,8 +584,6 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObjec if (frame_count <= 0) return Vector<StackInfo>(); - GDMonoUtils::DebugUtils_StackFrameInfo get_sf_info = CACHED_METHOD_THUNK(DebuggingUtils, GetStackFrameInfo); - Vector<StackInfo> si; si.resize(frame_count); @@ -595,7 +594,7 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObjec MonoString *file_name; int file_line_num; MonoString *method_decl; - invoke_method_thunk(get_sf_info, frame, &file_name, &file_line_num, &method_decl, &exc); + CACHED_METHOD_THUNK(DebuggingUtils, GetStackFrameInfo).invoke(frame, &file_name, &file_line_num, &method_decl, &exc); if (exc) { GDMonoUtils::debug_print_unhandled_exception(exc); @@ -618,14 +617,14 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObjec void CSharpLanguage::frame() { if (gdmono && gdmono->is_runtime_initialized() && gdmono->get_core_api_assembly() != NULL) { - const Ref<MonoGCHandle> &task_scheduler_handle = GDMonoUtils::mono_cache.task_scheduler_handle; + const Ref<MonoGCHandle> &task_scheduler_handle = GDMonoCache::cached_data.task_scheduler_handle; if (task_scheduler_handle.is_valid()) { MonoObject *task_scheduler = task_scheduler_handle->get_target(); if (task_scheduler) { MonoException *exc = NULL; - invoke_method_thunk(CACHED_METHOD_THUNK(GodotTaskScheduler, Activate), task_scheduler, &exc); + CACHED_METHOD_THUNK(GodotTaskScheduler, Activate).invoke(task_scheduler, &exc); if (exc) { GDMonoUtils::debug_unhandled_exception(exc); @@ -1079,7 +1078,7 @@ bool CSharpLanguage::overrides_external_editor() { void CSharpLanguage::thread_enter() { #if 0 - if (mono->is_runtime_initialized()) { + if (gdmono->is_runtime_initialized()) { GDMonoUtils::attach_current_thread(); } #endif @@ -1088,7 +1087,7 @@ void CSharpLanguage::thread_enter() { void CSharpLanguage::thread_exit() { #if 0 - if (mono->is_runtime_initialized()) { + if (gdmono->is_runtime_initialized()) { GDMonoUtils::detach_current_thread(); } #endif diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs index 9a2b2e3a26..da90c960e5 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs @@ -44,7 +44,7 @@ namespace GodotTools.Build { get { - if (OS.IsWindows()) + if (OS.IsWindows) { return (BuildManager.BuildTool) EditorSettings.GetSetting("mono/builds/build_tool") == BuildManager.BuildTool.MsBuildMono; diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs index eb2c2dd77c..ad8a6516ab 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs @@ -21,7 +21,7 @@ namespace GodotTools.Build var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings(); var buildTool = (BuildManager.BuildTool) editorSettings.GetSetting("mono/builds/build_tool"); - if (OS.IsWindows()) + if (OS.IsWindows) { switch (buildTool) { @@ -59,7 +59,7 @@ namespace GodotTools.Build } } - if (OS.IsUnix()) + if (OS.IsUnixLike()) { if (buildTool == BuildManager.BuildTool.MsBuildMono) { @@ -91,7 +91,7 @@ namespace GodotTools.Build { var result = new List<string>(); - if (OS.IsOSX()) + if (OS.IsOSX) { result.Add("/Library/Frameworks/Mono.framework/Versions/Current/bin/"); result.Add("/usr/local/var/homebrew/linked/mono/bin/"); @@ -128,7 +128,7 @@ namespace GodotTools.Build private static string FindMsBuildToolsPathOnWindows() { - if (!OS.IsWindows()) + if (!OS.IsWindows) throw new PlatformNotSupportedException(); // Try to find 15.0 with vswhere diff --git a/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs b/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs index ab37d89955..217bf5c144 100644 --- a/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs +++ b/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs @@ -246,7 +246,7 @@ namespace GodotTools { // Build tool settings - EditorDef("mono/builds/build_tool", OS.IsWindows() ? BuildTool.MsBuildVs : BuildTool.MsBuildMono); + EditorDef("mono/builds/build_tool", OS.IsWindows ? BuildTool.MsBuildVs : BuildTool.MsBuildMono); var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings(); @@ -255,7 +255,7 @@ namespace GodotTools ["type"] = Godot.Variant.Type.Int, ["name"] = "mono/builds/build_tool", ["hint"] = Godot.PropertyHint.Enum, - ["hint_string"] = OS.IsWindows() ? + ["hint_string"] = OS.IsWindows ? $"{PropNameMsbuildMono},{PropNameMsbuildVs}" : $"{PropNameMsbuildMono}" }); diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs new file mode 100644 index 0000000000..aed25f5ac5 --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs @@ -0,0 +1,633 @@ +using Godot; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using GodotTools.Core; +using GodotTools.Internals; +using static GodotTools.Internals.Globals; +using Directory = GodotTools.Utils.Directory; +using File = GodotTools.Utils.File; +using OS = GodotTools.Utils.OS; +using Path = System.IO.Path; + +namespace GodotTools.Export +{ + public class ExportPlugin : EditorExportPlugin + { + public void RegisterExportSettings() + { + // TODO: These would be better as export preset options, but that doesn't seem to be supported yet + + GlobalDef("mono/export/include_scripts_content", false); + GlobalDef("mono/export/export_assemblies_inside_pck", true); + + GlobalDef("mono/export/aot/enabled", false); + GlobalDef("mono/export/aot/full_aot", false); + + // --aot or --aot=opt1,opt2 (use 'mono --aot=help AuxAssembly.dll' to list AOT options) + GlobalDef("mono/export/aot/extra_aot_options", new string[] { }); + // --optimize/-O=opt1,opt2 (use 'mono --list-opt'' to list optimize options) + GlobalDef("mono/export/aot/extra_optimizer_options", new string[] { }); + + GlobalDef("mono/export/aot/android_toolchain_path", ""); + } + + private string maybeLastExportError; + + private void AddFile(string srcPath, string dstPath, bool remap = false) + { + AddFile(dstPath, File.ReadAllBytes(srcPath), remap); + } + + public override void _ExportFile(string path, string type, string[] features) + { + base._ExportFile(path, type, features); + + if (type != Internal.CSharpLanguageType) + return; + + if (Path.GetExtension(path) != $".{Internal.CSharpLanguageExtension}") + 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 + + bool includeScriptsContent = (bool) ProjectSettings.GetSetting("mono/export/include_scripts_content"); + + if (!includeScriptsContent) + { + // We don't want to include the source code on exported games. + + // Sadly, Godot prints errors when adding an empty file (nothing goes wrong, it's just noise). + // Because of this, we add a file which contains a line break. + AddFile(path, System.Text.Encoding.UTF8.GetBytes("\n"), remap: false); + Skip(); + } + } + + public override void _ExportBegin(string[] features, bool isDebug, string path, int flags) + { + base._ExportBegin(features, isDebug, path, flags); + + try + { + _ExportBeginImpl(features, isDebug, path, flags); + } + catch (Exception e) + { + maybeLastExportError = e.Message; + GD.PushError($"Failed to export project: {e.Message}"); + Console.Error.WriteLine(e); + // TODO: Do something on error once _ExportBegin supports failing. + } + } + + private void _ExportBeginImpl(string[] features, bool isDebug, string path, int flags) + { + if (!File.Exists(GodotSharpDirs.ProjectSlnPath)) + return; + + string platform = DeterminePlatformFromFeatures(features); + + if (platform == null) + throw new NotSupportedException("Target platform not supported"); + + string outputDir = new FileInfo(path).Directory?.FullName ?? + throw new FileNotFoundException("Base directory not found"); + + string buildConfig = isDebug ? "Debug" : "Release"; + + string scriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, $"scripts_metadata.{(isDebug ? "debug" : "release")}"); + CsProjOperations.GenerateScriptsMetadata(GodotSharpDirs.ProjectCsProjPath, scriptsMetadataPath); + + AddFile(scriptsMetadataPath, scriptsMetadataPath); + + // Turn export features into defines + var godotDefines = features; + + if (!BuildManager.BuildProjectBlocking(buildConfig, godotDefines)) + throw new Exception("Failed to build project"); + + // Add dependency assemblies + + var dependencies = new Godot.Collections.Dictionary<string, string>(); + + var projectDllName = (string) ProjectSettings.GetSetting("application/config/name"); + if (projectDllName.Empty()) + { + projectDllName = "UnnamedProject"; + } + + string projectDllSrcDir = Path.Combine(GodotSharpDirs.ResTempAssembliesBaseDir, buildConfig); + string projectDllSrcPath = Path.Combine(projectDllSrcDir, $"{projectDllName}.dll"); + + dependencies[projectDllName] = projectDllSrcPath; + + { + string platformBclDir = DeterminePlatformBclDir(platform); + + internal_GetExportedAssemblyDependencies(projectDllName, projectDllSrcPath, buildConfig, platformBclDir, dependencies); + } + + string outputDataDir = null; + + if (PlatformHasTemplateDir(platform)) + outputDataDir = ExportDataDirectory(features, platform, isDebug, outputDir); + + string apiConfig = isDebug ? "Debug" : "Release"; + string resAssembliesDir = Path.Combine(GodotSharpDirs.ResAssembliesBaseDir, apiConfig); + + bool assembliesInsidePck = (bool) ProjectSettings.GetSetting("mono/export/export_assemblies_inside_pck") || outputDataDir == null; + + if (!assembliesInsidePck) + { + string outputDataGameAssembliesDir = Path.Combine(outputDataDir, "Assemblies"); + if (!Directory.Exists(outputDataGameAssembliesDir)) + Directory.CreateDirectory(outputDataGameAssembliesDir); + } + + foreach (var dependency in dependencies) + { + string dependSrcPath = dependency.Value; + + if (assembliesInsidePck) + { + string dependDstPath = Path.Combine(resAssembliesDir, dependSrcPath.GetFile()); + AddFile(dependSrcPath, dependDstPath); + } + else + { + string dependDstPath = Path.Combine(outputDataDir, "Assemblies", dependSrcPath.GetFile()); + File.Copy(dependSrcPath, dependDstPath); + } + } + + // AOT + + if ((bool) ProjectSettings.GetSetting("mono/export/aot/enabled")) + { + AotCompileDependencies(features, platform, isDebug, outputDir, outputDataDir, dependencies); + } + } + + public override void _ExportEnd() + { + base._ExportEnd(); + + string aotTempDir = Path.Combine(Path.GetTempPath(), $"godot-aot-{Process.GetCurrentProcess().Id}"); + + 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 + { + string lastExportError = maybeLastExportError; + maybeLastExportError = null; + + GodotSharpEditor.Instance.ShowErrorDialog(lastExportError, "Failed to export C# project"); + } + } + + private static string ExportDataDirectory(string[] features, string platform, bool isDebug, string outputDir) + { + string target = isDebug ? "release_debug" : "release"; + + // NOTE: Bits is ok for now as all platforms with a data directory have it, but that may change in the future. + string bits = features.Contains("64") ? "64" : "32"; + + string TemplateDirName() => $"data.mono.{platform}.{bits}.{target}"; + + string templateDirPath = Path.Combine(Internal.FullTemplatesDir, TemplateDirName()); + + if (!Directory.Exists(templateDirPath)) + { + templateDirPath = null; + + if (isDebug) + { + target = "debug"; // Support both 'release_debug' and 'debug' for the template data directory name + templateDirPath = Path.Combine(Internal.FullTemplatesDir, TemplateDirName()); + + if (!Directory.Exists(templateDirPath)) + templateDirPath = null; + } + } + + if (templateDirPath == null) + throw new FileNotFoundException("Data template directory not found"); + + string outputDataDir = Path.Combine(outputDir, DataDirName); + + if (Directory.Exists(outputDataDir)) + Directory.Delete(outputDataDir, recursive: true); // Clean first + + Directory.CreateDirectory(outputDataDir); + + foreach (string dir in Directory.GetDirectories(templateDirPath, "*", SearchOption.AllDirectories)) + { + Directory.CreateDirectory(Path.Combine(outputDataDir, dir.Substring(templateDirPath.Length + 1))); + } + + foreach (string file in Directory.GetFiles(templateDirPath, "*", SearchOption.AllDirectories)) + { + File.Copy(file, Path.Combine(outputDataDir, file.Substring(templateDirPath.Length + 1))); + } + + return outputDataDir; + } + + private void AotCompileDependencies(string[] features, string platform, bool isDebug, string outputDir, string outputDataDir, IDictionary<string, string> dependencies) + { + // TODO: WASM + + string bclDir = DeterminePlatformBclDir(platform) ?? typeof(object).Assembly.Location.GetBaseDir(); + + string aotTempDir = Path.Combine(Path.GetTempPath(), $"godot-aot-{Process.GetCurrentProcess().Id}"); + + if (!Directory.Exists(aotTempDir)) + Directory.CreateDirectory(aotTempDir); + + var assemblies = new Dictionary<string, string>(); + + foreach (var dependency in dependencies) + { + string assemblyName = dependency.Key; + string assemblyPath = dependency.Value; + + string assemblyPathInBcl = Path.Combine(bclDir, assemblyName + ".dll"); + + if (File.Exists(assemblyPathInBcl)) + { + // Don't create teporaries for assemblies from the BCL + assemblies.Add(assemblyName, assemblyPathInBcl); + } + else + { + string tempAssemblyPath = Path.Combine(aotTempDir, assemblyName + ".dll"); + File.Copy(assemblyPath, tempAssemblyPath); + assemblies.Add(assemblyName, tempAssemblyPath); + } + } + + foreach (var assembly in assemblies) + { + string assemblyName = assembly.Key; + string assemblyPath = assembly.Value; + + string sharedLibExtension = platform == OS.Platforms.Windows ? ".dll" : + platform == OS.Platforms.OSX ? ".dylib" : + platform == OS.Platforms.HTML5 ? ".wasm" : + ".so"; + + string outputFileName = assemblyName + ".dll" + sharedLibExtension; + + if (platform == OS.Platforms.Android) + { + // Not sure if the 'lib' prefix is an Android thing or just Godot being picky, + // but we use '-aot-' as well just in case to avoid conflicts with other libs. + outputFileName = "lib-aot-" + outputFileName; + } + + string outputFilePath = null; + string tempOutputFilePath; + + switch (platform) + { + case OS.Platforms.OSX: + tempOutputFilePath = Path.Combine(aotTempDir, outputFileName); + break; + case OS.Platforms.Android: + tempOutputFilePath = Path.Combine(aotTempDir, "%%ANDROID_ABI%%", outputFileName); + break; + case OS.Platforms.HTML5: + tempOutputFilePath = Path.Combine(aotTempDir, outputFileName); + outputFilePath = Path.Combine(outputDir, outputFileName); + break; + default: + tempOutputFilePath = Path.Combine(aotTempDir, outputFileName); + outputFilePath = Path.Combine(outputDataDir, "Mono", platform == OS.Platforms.Windows ? "bin" : "lib", outputFileName); + break; + } + + var data = new Dictionary<string, string>(); + var enabledAndroidAbis = platform == OS.Platforms.Android ? GetEnabledAndroidAbis(features).ToArray() : null; + + if (platform == OS.Platforms.Android) + { + Debug.Assert(enabledAndroidAbis != null); + + foreach (var abi in enabledAndroidAbis) + { + data["abi"] = abi; + var outputFilePathForThisAbi = tempOutputFilePath.Replace("%%ANDROID_ABI%%", abi); + + AotCompileAssembly(platform, isDebug, data, assemblyPath, outputFilePathForThisAbi); + + AddSharedObject(outputFilePathForThisAbi, tags: new[] {abi}); + } + } + else + { + string bits = features.Contains("64") ? "64" : features.Contains("64") ? "32" : null; + + if (bits != null) + data["bits"] = bits; + + AotCompileAssembly(platform, isDebug, data, assemblyPath, tempOutputFilePath); + + if (platform == OS.Platforms.OSX) + { + AddSharedObject(tempOutputFilePath, tags: null); + } + else + { + Debug.Assert(outputFilePath != null); + File.Copy(tempOutputFilePath, outputFilePath); + } + } + } + } + + private static void AotCompileAssembly(string platform, bool isDebug, Dictionary<string, string> data, string assemblyPath, string outputFilePath) + { + // Make sure the output directory exists + Directory.CreateDirectory(outputFilePath.GetBaseDir()); + + string exeExt = OS.IsWindows ? ".exe" : string.Empty; + + string monoCrossDirName = DetermineMonoCrossDirName(platform, data); + string monoCrossRoot = Path.Combine(GodotSharpDirs.DataEditorToolsDir, "aot-compilers", monoCrossDirName); + string monoCrossBin = Path.Combine(monoCrossRoot, "bin"); + + string toolPrefix = DetermineToolPrefix(monoCrossBin); + string monoExeName = System.IO.File.Exists(Path.Combine(monoCrossBin, $"{toolPrefix}mono{exeExt}")) ? "mono" : "mono-sgen"; + + string compilerCommand = Path.Combine(monoCrossBin, $"{toolPrefix}{monoExeName}{exeExt}"); + + bool fullAot = (bool) ProjectSettings.GetSetting("mono/export/aot/full_aot"); + + string EscapeOption(string option) => option.Contains(',') ? $"\"{option}\"" : option; + string OptionsToString(IEnumerable<string> options) => string.Join(",", options.Select(EscapeOption)); + + var aotOptions = new List<string>(); + var optimizerOptions = new List<string>(); + + if (fullAot) + aotOptions.Add("full"); + + aotOptions.Add(isDebug ? "soft-debug" : "nodebug"); + + if (platform == OS.Platforms.Android) + { + string abi = data["abi"]; + + string androidToolchain = (string) ProjectSettings.GetSetting("mono/export/aot/android_toolchain_path"); + + if (string.IsNullOrEmpty(androidToolchain)) + { + androidToolchain = Path.Combine(GodotSharpDirs.DataEditorToolsDir, "android-toolchains", $"{abi}"); // TODO: $"{abi}-{apiLevel}{(clang?"clang":"")}" + + if (!Directory.Exists(androidToolchain)) + throw new FileNotFoundException("Missing android toolchain. Specify one in the AOT export settings."); + } + else if (!Directory.Exists(androidToolchain)) + { + throw new FileNotFoundException("Android toolchain not found: " + androidToolchain); + } + + var androidToolPrefixes = new Dictionary<string, string> + { + ["armeabi-v7a"] = "arm-linux-androideabi-", + ["arm64-v8a"] = "aarch64-linux-android-", + ["x86"] = "i686-linux-android-", + ["x86_64"] = "x86_64-linux-android-" + }; + + aotOptions.Add("tool-prefix=" + Path.Combine(androidToolchain, "bin", androidToolPrefixes[abi])); + + string triple = GetAndroidTriple(abi); + aotOptions.Add ($"mtriple={triple}"); + } + + aotOptions.Add($"outfile={outputFilePath}"); + + var extraAotOptions = (string[]) ProjectSettings.GetSetting("mono/export/aot/extra_aot_options"); + var extraOptimizerOptions = (string[]) ProjectSettings.GetSetting("mono/export/aot/extra_optimizer_options"); + + if (extraAotOptions.Length > 0) + aotOptions.AddRange(extraAotOptions); + + if (extraOptimizerOptions.Length > 0) + optimizerOptions.AddRange(extraOptimizerOptions); + + var compilerArgs = new List<string>(); + + if (isDebug) + compilerArgs.Add("--debug"); // Required for --aot=soft-debug + + compilerArgs.Add(aotOptions.Count > 0 ? $"--aot={OptionsToString(aotOptions)}" : "--aot"); + + if (optimizerOptions.Count > 0) + compilerArgs.Add($"-O={OptionsToString(optimizerOptions)}"); + + compilerArgs.Add(ProjectSettings.GlobalizePath(assemblyPath)); + + // TODO: Once we move to .NET Standard 2.1 we can use ProcessStartInfo.ArgumentList instead + string CmdLineArgsToString(IEnumerable<string> args) + { + // Not perfect, but as long as we are careful... + return string.Join(" ", args.Select(arg => arg.Contains(" ") ? $@"""{arg}""" : arg)); + } + + using (var process = new Process()) + { + process.StartInfo = new ProcessStartInfo(compilerCommand, CmdLineArgsToString(compilerArgs)) + { + UseShellExecute = false + }; + + string platformBclDir = DeterminePlatformBclDir(platform); + process.StartInfo.EnvironmentVariables.Add("MONO_PATH", string.IsNullOrEmpty(platformBclDir) ? + typeof(object).Assembly.Location.GetBaseDir() : + platformBclDir); + + Console.WriteLine($"Running: \"{process.StartInfo.FileName}\" {process.StartInfo.Arguments}"); + + if (!process.Start()) + throw new Exception("Failed to start process for Mono AOT compiler"); + + process.WaitForExit(); + + if (process.ExitCode != 0) + throw new Exception($"Mono AOT compiler exited with error code: {process.ExitCode}"); + + if (!System.IO.File.Exists(outputFilePath)) + throw new Exception("Mono AOT compiler finished successfully but the output file is missing"); + } + } + + private static string DetermineMonoCrossDirName(string platform, IReadOnlyDictionary<string, string> data) + { + switch (platform) + { + case OS.Platforms.Windows: + case OS.Platforms.UWP: + { + string arch = data["bits"] == "64" ? "x86_64" : "i686"; + return $"windows-{arch}"; + } + case OS.Platforms.OSX: + { + string arch = "x86_64"; + return $"{platform}-{arch}"; + } + case OS.Platforms.X11: + case OS.Platforms.Server: + { + string arch = data["bits"] == "64" ? "x86_64" : "i686"; + return $"linux-{arch}"; + } + case OS.Platforms.Haiku: + { + string arch = data["bits"] == "64" ? "x86_64" : "i686"; + return $"{platform}-{arch}"; + } + case OS.Platforms.Android: + { + string abi = data["abi"]; + return $"{platform}-{abi}"; + } + case OS.Platforms.HTML5: + return "wasm-wasm32"; + default: + throw new NotSupportedException(); + } + } + + private static string DetermineToolPrefix(string monoCrossBin) + { + string exeExt = OS.IsWindows ? ".exe" : string.Empty; + + if (System.IO.File.Exists(Path.Combine(monoCrossBin, $"mono{exeExt}"))) + return string.Empty; + + if (System.IO.File.Exists(Path.Combine(monoCrossBin, $"mono-sgen{exeExt}" + exeExt))) + return string.Empty; + + var files = new DirectoryInfo(monoCrossBin).GetFiles($"*mono{exeExt}" + exeExt, SearchOption.TopDirectoryOnly); + if (files.Length > 0) + { + string fileName = files[0].Name; + return fileName.Substring(0, fileName.Length - $"mono{exeExt}".Length); + } + + files = new DirectoryInfo(monoCrossBin).GetFiles($"*mono-sgen{exeExt}" + exeExt, SearchOption.TopDirectoryOnly); + if (files.Length > 0) + { + string fileName = files[0].Name; + return fileName.Substring(0, fileName.Length - $"mono-sgen{exeExt}".Length); + } + + throw new FileNotFoundException($"Cannot find the mono runtime executable in {monoCrossBin}"); + } + + private static IEnumerable<string> GetEnabledAndroidAbis(string[] features) + { + var androidAbis = new[] + { + "armeabi-v7a", + "arm64-v8a", + "x86", + "x86_64" + }; + + return androidAbis.Where(features.Contains); + } + + private static string GetAndroidTriple(string abi) + { + var abiArchs = new Dictionary<string, string> + { + ["armeabi-v7a"] = "armv7", + ["arm64-v8a"] = "aarch64-v8a", + ["x86"] = "i686", + ["x86_64"] = "x86_64" + }; + + string arch = abiArchs[abi]; + + return $"{arch}-linux-android"; + } + + private static bool PlatformHasTemplateDir(string platform) + { + // OSX 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.OSX, OS.Platforms.Android, OS.Platforms.HTML5}.Contains(platform); + } + + private static string DeterminePlatformFromFeatures(IEnumerable<string> features) + { + foreach (var feature in features) + { + if (OS.PlatformNameMap.TryGetValue(feature, out string platform)) + return platform; + } + + return null; + } + + private static string DeterminePlatformBclDir(string platform) + { + string templatesDir = Internal.FullTemplatesDir; + string platformBclDir = Path.Combine(templatesDir, "bcl", platform); + + if (!File.Exists(Path.Combine(platformBclDir, "mscorlib.dll"))) + { + string profile = DeterminePlatformBclProfile(platform); + platformBclDir = Path.Combine(templatesDir, "bcl", profile); + + if (!File.Exists(Path.Combine(platformBclDir, "mscorlib.dll"))) + platformBclDir = null; // Use the one we're running on + } + + return platformBclDir; + } + + private static string DeterminePlatformBclProfile(string platform) + { + switch (platform) + { + case OS.Platforms.Windows: + case OS.Platforms.UWP: + case OS.Platforms.OSX: + case OS.Platforms.X11: + case OS.Platforms.Server: + case OS.Platforms.Haiku: + return "net_4_x"; + case OS.Platforms.Android: + return "monodroid"; + case OS.Platforms.HTML5: + return "wasm"; + default: + throw new NotSupportedException(); + } + } + + private static string DataDirName + { + get + { + var appName = (string) ProjectSettings.GetSetting("application/config/name"); + string appNameSafe = appName.ToSafeDirName(allowDirSeparator: false); + return $"data_{appNameSafe}"; + } + } + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern void internal_GetExportedAssemblyDependencies(string projectDllName, string projectDllSrcPath, + string buildConfig, string customBclDir, Godot.Collections.Dictionary<string, string> dependencies); + } +} diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs index 12edd651df..2a5d3de126 100644 --- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs +++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs @@ -1,4 +1,5 @@ using Godot; +using GodotTools.Export; using GodotTools.Utils; using System; using System.Collections.Generic; @@ -225,7 +226,7 @@ namespace GodotTools bool osxAppBundleInstalled = false; - if (OS.IsOSX()) + if (OS.IsOSX) { // The package path is '/Applications/Visual Studio Code.app' const string vscodeBundleId = "com.microsoft.VSCode"; @@ -265,7 +266,7 @@ namespace GodotTools string command; - if (OS.IsOSX()) + if (OS.IsOSX) { if (!osxAppBundleInstalled && _vsCodePath.Empty()) { @@ -415,18 +416,18 @@ namespace GodotTools string settingsHintStr = "Disabled"; - if (OS.IsWindows()) + if (OS.IsWindows) { settingsHintStr += $",MonoDevelop:{(int) ExternalEditorId.MonoDevelop}" + $",Visual Studio Code:{(int) ExternalEditorId.VsCode}"; } - else if (OS.IsOSX()) + else if (OS.IsOSX) { settingsHintStr += $",Visual Studio:{(int) ExternalEditorId.VisualStudioForMac}" + $",MonoDevelop:{(int) ExternalEditorId.MonoDevelop}" + $",Visual Studio Code:{(int) ExternalEditorId.VsCode}"; } - else if (OS.IsUnix()) + else if (OS.IsUnixLike()) { settingsHintStr += $",MonoDevelop:{(int) ExternalEditorId.MonoDevelop}" + $",Visual Studio Code:{(int) ExternalEditorId.VsCode}"; @@ -441,8 +442,9 @@ namespace GodotTools }); // Export plugin - var exportPlugin = new GodotSharpExport(); + var exportPlugin = new ExportPlugin(); AddExportPlugin(exportPlugin); + exportPlugin.RegisterExportSettings(); exportPluginWeak = WeakRef(exportPlugin); BuildManager.Initialize(); @@ -461,7 +463,7 @@ namespace GodotTools // Otherwise, if the GC disposes it at a later time, EditorExportPlatformAndroid // will be freed after EditorSettings already was, and its device polling thread // will try to access the EditorSettings singleton, resulting in null dereferencing. - (exportPluginWeak.GetRef() as GodotSharpExport)?.Dispose(); + (exportPluginWeak.GetRef() as ExportPlugin)?.Dispose(); exportPluginWeak.Dispose(); } diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpExport.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpExport.cs deleted file mode 100644 index 4f93ef8530..0000000000 --- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpExport.cs +++ /dev/null @@ -1,197 +0,0 @@ -using Godot; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.CompilerServices; -using GodotTools.Core; -using GodotTools.Internals; -using Directory = GodotTools.Utils.Directory; -using File = GodotTools.Utils.File; -using Path = System.IO.Path; - -namespace GodotTools -{ - public class GodotSharpExport : EditorExportPlugin - { - private void AddFile(string srcPath, string dstPath, bool remap = false) - { - AddFile(dstPath.Replace("\\", "/"), File.ReadAllBytes(srcPath), remap); - } - - public override void _ExportFile(string path, string type, string[] features) - { - base._ExportFile(path, type, features); - - if (type != Internal.CSharpLanguageType) - return; - - if (Path.GetExtension(path) != $".{Internal.CSharpLanguageExtension}") - 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 - - bool includeScriptsContent = (bool) ProjectSettings.GetSetting("mono/export/include_scripts_content"); - - if (!includeScriptsContent) - { - // We don't want to include the source code on exported games - AddFile(path, new byte[] { }, remap: false); - Skip(); - } - } - - public override void _ExportBegin(string[] features, bool isDebug, string path, int flags) - { - base._ExportBegin(features, isDebug, path, flags); - - try - { - _ExportBeginImpl(features, isDebug, path, flags); - } - catch (Exception e) - { - GD.PushError($"Failed to export project. Exception message: {e.Message}"); - Console.Error.WriteLine(e); - } - } - - public void _ExportBeginImpl(string[] features, bool isDebug, string path, int flags) - { - // TODO Right now there is no way to stop the export process with an error - - if (File.Exists(GodotSharpDirs.ProjectSlnPath)) - { - string buildConfig = isDebug ? "Debug" : "Release"; - - string scriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, $"scripts_metadata.{(isDebug ? "debug" : "release")}"); - CsProjOperations.GenerateScriptsMetadata(GodotSharpDirs.ProjectCsProjPath, scriptsMetadataPath); - - AddFile(scriptsMetadataPath, scriptsMetadataPath); - - // Turn export features into defines - var godotDefines = features; - - if (!BuildManager.BuildProjectBlocking(buildConfig, godotDefines)) - { - GD.PushError("Failed to build project"); - return; - } - - // Add dependency assemblies - - var dependencies = new Godot.Collections.Dictionary<string, string>(); - - var projectDllName = (string) ProjectSettings.GetSetting("application/config/name"); - if (projectDllName.Empty()) - { - projectDllName = "UnnamedProject"; - } - - string projectDllSrcDir = Path.Combine(GodotSharpDirs.ResTempAssembliesBaseDir, buildConfig); - string projectDllSrcPath = Path.Combine(projectDllSrcDir, $"{projectDllName}.dll"); - - dependencies[projectDllName] = projectDllSrcPath; - - { - string templatesDir = Internal.FullTemplatesDir; - string androidBclDir = Path.Combine(templatesDir, "android-bcl"); - - string customLibDir = features.Contains("Android") && Directory.Exists(androidBclDir) ? androidBclDir : string.Empty; - - GetExportedAssemblyDependencies(projectDllName, projectDllSrcPath, buildConfig, customLibDir, dependencies); - } - - string apiConfig = isDebug ? "Debug" : "Release"; - string resAssembliesDir = Path.Combine(GodotSharpDirs.ResAssembliesBaseDir, apiConfig); - - foreach (var dependency in dependencies) - { - string dependSrcPath = dependency.Value; - string dependDstPath = Path.Combine(resAssembliesDir, dependSrcPath.GetFile()); - AddFile(dependSrcPath, dependDstPath); - } - } - - // Mono specific export template extras (data dir) - ExportDataDirectory(features, isDebug, path); - } - - private static void ExportDataDirectory(IEnumerable<string> features, bool debug, string path) - { - var featureSet = new HashSet<string>(features); - - if (!PlatformHasTemplateDir(featureSet)) - return; - - string templateDirName = "data.mono"; - - if (featureSet.Contains("Windows")) - { - templateDirName += ".windows"; - templateDirName += featureSet.Contains("64") ? ".64" : ".32"; - } - else if (featureSet.Contains("X11")) - { - templateDirName += ".x11"; - templateDirName += featureSet.Contains("64") ? ".64" : ".32"; - } - else - { - throw new NotSupportedException("Target platform not supported"); - } - - templateDirName += debug ? ".release_debug" : ".release"; - - string templateDirPath = Path.Combine(Internal.FullTemplatesDir, templateDirName); - - if (!Directory.Exists(templateDirPath)) - throw new FileNotFoundException("Data template directory not found"); - - string outputDir = new FileInfo(path).Directory?.FullName ?? - throw new FileNotFoundException("Base directory not found"); - - string outputDataDir = Path.Combine(outputDir, DataDirName); - - if (Directory.Exists(outputDataDir)) - Directory.Delete(outputDataDir, recursive: true); // Clean first - - Directory.CreateDirectory(outputDataDir); - - foreach (string dir in Directory.GetDirectories(templateDirPath, "*", SearchOption.AllDirectories)) - { - Directory.CreateDirectory(Path.Combine(outputDataDir, dir.Substring(templateDirPath.Length + 1))); - } - - foreach (string file in Directory.GetFiles(templateDirPath, "*", SearchOption.AllDirectories)) - { - File.Copy(file, Path.Combine(outputDataDir, file.Substring(templateDirPath.Length + 1))); - } - } - - private static bool PlatformHasTemplateDir(IEnumerable<string> featureSet) - { - // OSX export templates are contained in a zip, so we place - // our custom template inside it and let Godot do the rest. - return !featureSet.Any(f => new[] {"OSX", "Android"}.Contains(f)); - } - - private static string DataDirName - { - get - { - var appName = (string) ProjectSettings.GetSetting("application/config/name"); - string appNameSafe = appName.ToSafeDirName(allowDirSeparator: false); - return $"data_{appNameSafe}"; - } - } - - private static void GetExportedAssemblyDependencies(string projectDllName, string projectDllSrcPath, - string buildConfig, string customLibDir, Godot.Collections.Dictionary<string, string> dependencies) => - internal_GetExportedAssemblyDependencies(projectDllName, projectDllSrcPath, buildConfig, customLibDir, dependencies); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void internal_GetExportedAssemblyDependencies(string projectDllName, string projectDllSrcPath, - string buildConfig, string customLibDir, Godot.Collections.Dictionary<string, string> dependencies); - } -} diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj index 3c57900873..fb2cbabc8e 100644 --- a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj +++ b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj @@ -30,6 +30,7 @@ <ConsolePause>false</ConsolePause> </PropertyGroup> <ItemGroup> + <Reference Include="Mono.Posix" /> <Reference Include="System" /> <Reference Include="GodotSharp"> <HintPath>$(GodotSourceRootPath)/bin/GodotSharp/Api/$(GodotApiConfiguration)/GodotSharp.dll</HintPath> @@ -40,6 +41,7 @@ </ItemGroup> <ItemGroup> <Compile Include="Build\MsBuildFinder.cs" /> + <Compile Include="Export\ExportPlugin.cs" /> <Compile Include="ExternalEditorId.cs" /> <Compile Include="Ides\GodotIdeManager.cs" /> <Compile Include="Ides\GodotIdeServer.cs" /> @@ -63,7 +65,6 @@ <Compile Include="BuildInfo.cs" /> <Compile Include="BuildTab.cs" /> <Compile Include="BottomPanel.cs" /> - <Compile Include="GodotSharpExport.cs" /> <Compile Include="CsProjOperations.cs" /> <Compile Include="Utils\CollectionExtensions.cs" /> </ItemGroup> diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs index 01aa0d0ab1..f94d6f998c 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs @@ -79,7 +79,7 @@ namespace GodotTools.Ides { MonoDevelop.Instance GetMonoDevelopInstance(string solutionPath) { - if (Utils.OS.IsOSX() && editor == ExternalEditorId.VisualStudioForMac) + if (Utils.OS.IsOSX && editor == ExternalEditorId.VisualStudioForMac) { vsForMacInstance = vsForMacInstance ?? new MonoDevelop.Instance(solutionPath, MonoDevelop.EditorId.VisualStudioForMac); diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs index 1fdccf5bbd..6026c109ad 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs @@ -24,7 +24,7 @@ namespace GodotTools.Ides.MonoDevelop string command; - if (OS.IsOSX()) + if (OS.IsOSX) { string bundleId = BundleIds[editorId]; @@ -81,7 +81,7 @@ namespace GodotTools.Ides.MonoDevelop public Instance(string solutionFile, EditorId editorId) { - if (editorId == EditorId.VisualStudioForMac && !OS.IsOSX()) + if (editorId == EditorId.VisualStudioForMac && !OS.IsOSX) throw new InvalidOperationException($"{nameof(EditorId.VisualStudioForMac)} not supported on this platform"); this.solutionFile = solutionFile; @@ -93,7 +93,7 @@ namespace GodotTools.Ides.MonoDevelop static Instance() { - if (OS.IsOSX()) + if (OS.IsOSX) { ExecutableNames = new Dictionary<EditorId, string> { @@ -107,7 +107,7 @@ namespace GodotTools.Ides.MonoDevelop {EditorId.VisualStudioForMac, "com.microsoft.visual-studio"} }; } - else if (OS.IsWindows()) + else if (OS.IsWindows) { ExecutableNames = new Dictionary<EditorId, string> { @@ -118,7 +118,7 @@ namespace GodotTools.Ides.MonoDevelop {EditorId.MonoDevelop, "MonoDevelop.exe"} }; } - else if (OS.IsUnix()) + else if (OS.IsUnixLike()) { ExecutableNames = new Dictionary<EditorId, string> { diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs index 836c9c11e4..de361ba844 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs @@ -52,7 +52,7 @@ namespace GodotTools.Internals public static void ScriptEditorDebugger_ReloadScripts() => internal_ScriptEditorDebugger_ReloadScripts(); - // Internal Calls + #region Internal [MethodImpl(MethodImplOptions.InternalCall)] private static extern string internal_UpdateApiAssembliesFromPrebuilt(string config); @@ -110,5 +110,7 @@ namespace GodotTools.Internals [MethodImpl(MethodImplOptions.InternalCall)] private static extern void internal_ScriptEditorDebugger_ReloadScripts(); + + #endregion } } diff --git a/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs b/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs index e48b1115db..1a8c26acd7 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs @@ -1,72 +1,102 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Runtime.CompilerServices; +using Mono.Unix.Native; namespace GodotTools.Utils { + [SuppressMessage("ReSharper", "InconsistentNaming")] public static class OS { [MethodImpl(MethodImplOptions.InternalCall)] - extern static string GetPlatformName(); + static extern string GetPlatformName(); - const string HaikuName = "Haiku"; - const string OSXName = "OSX"; - const string ServerName = "Server"; - const string UWPName = "UWP"; - const string WindowsName = "Windows"; - const string X11Name = "X11"; - - public static bool IsHaiku() + public static class Names { - return HaikuName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase); + public const string Windows = "Windows"; + public const string OSX = "OSX"; + public const string X11 = "X11"; + public const string Server = "Server"; + public const string UWP = "UWP"; + public const string Haiku = "Haiku"; + public const string Android = "Android"; + public const string HTML5 = "HTML5"; } - public static bool IsOSX() + public static class Platforms { - return OSXName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase); + public const string Windows = "windows"; + public const string OSX = "osx"; + public const string X11 = "x11"; + public const string Server = "server"; + public const string UWP = "uwp"; + public const string Haiku = "haiku"; + public const string Android = "android"; + public const string HTML5 = "javascript"; } - public static bool IsServer() + public static readonly Dictionary<string, string> PlatformNameMap = new Dictionary<string, string> { - return ServerName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase); - } - - public static bool IsUWP() + [Names.Windows] = Platforms.Windows, + [Names.OSX] = Platforms.OSX, + [Names.X11] = Platforms.X11, + [Names.Server] = Platforms.Server, + [Names.UWP] = Platforms.UWP, + [Names.Haiku] = Platforms.Haiku, + [Names.Android] = Platforms.Android, + [Names.HTML5] = Platforms.HTML5 + }; + + private static bool IsOS(string name) { - return UWPName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase); + return name.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase); } - public static bool IsWindows() - { - return WindowsName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase); - } - - public static bool IsX11() - { - return X11Name.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase); - } + private static readonly Lazy<bool> _isWindows = new Lazy<bool>(() => IsOS(Names.Windows)); + private static readonly Lazy<bool> _isOSX = new Lazy<bool>(() => IsOS(Names.OSX)); + private static readonly Lazy<bool> _isX11 = new Lazy<bool>(() => IsOS(Names.X11)); + private static readonly Lazy<bool> _isServer = new Lazy<bool>(() => IsOS(Names.Server)); + private static readonly Lazy<bool> _isUWP = new Lazy<bool>(() => IsOS(Names.UWP)); + private static readonly Lazy<bool> _isHaiku = new Lazy<bool>(() => IsOS(Names.Haiku)); + private static readonly Lazy<bool> _isAndroid = new Lazy<bool>(() => IsOS(Names.Android)); + private static readonly Lazy<bool> _isHTML5 = new Lazy<bool>(() => IsOS(Names.HTML5)); + + public static bool IsWindows => _isWindows.Value; + public static bool IsOSX => _isOSX.Value; + public static bool IsX11 => _isX11.Value; + public static bool IsServer => _isServer.Value; + public static bool IsUWP => _isUWP.Value; + public static bool IsHaiku => _isHaiku.Value; + public static bool IsAndroid => _isAndroid.Value; + public static bool IsHTML5 => _isHTML5.Value; private static bool? _isUnixCache; - private static readonly string[] UnixPlatforms = {HaikuName, OSXName, ServerName, X11Name}; + private static readonly string[] UnixLikePlatforms = {Names.OSX, Names.X11, Names.Server, Names.Haiku, Names.Android}; - public static bool IsUnix() + public static bool IsUnixLike() { if (_isUnixCache.HasValue) return _isUnixCache.Value; string osName = GetPlatformName(); - _isUnixCache = UnixPlatforms.Any(p => p.Equals(osName, StringComparison.OrdinalIgnoreCase)); + _isUnixCache = UnixLikePlatforms.Any(p => p.Equals(osName, StringComparison.OrdinalIgnoreCase)); return _isUnixCache.Value; } - public static char PathSep => IsWindows() ? ';' : ':'; + public static char PathSep => IsWindows ? ';' : ':'; public static string PathWhich(string name) { - string[] windowsExts = IsWindows() ? Environment.GetEnvironmentVariable("PATHEXT")?.Split(PathSep) : null; + return IsWindows ? PathWhichWindows(name) : PathWhichUnix(name); + } + + private static string PathWhichWindows(string name) + { + string[] windowsExts = Environment.GetEnvironmentVariable("PATHEXT")?.Split(PathSep) ?? new string[] { }; string[] pathDirs = Environment.GetEnvironmentVariable("PATH")?.Split(PathSep); var searchDirs = new List<string>(); @@ -74,40 +104,46 @@ namespace GodotTools.Utils if (pathDirs != null) searchDirs.AddRange(pathDirs); + string nameExt = Path.GetExtension(name); + bool hasPathExt = string.IsNullOrEmpty(nameExt) || windowsExts.Contains(nameExt, StringComparer.OrdinalIgnoreCase); + searchDirs.Add(System.IO.Directory.GetCurrentDirectory()); // last in the list - foreach (var dir in searchDirs) - { - string path = Path.Combine(dir, name); - - if (IsWindows() && windowsExts != null) - { - foreach (var extension in windowsExts) - { - string pathWithExtension = path + extension; - - if (File.Exists(pathWithExtension)) - return pathWithExtension; - } - } - else - { - if (File.Exists(path)) - return path; - } - } + if (hasPathExt) + return searchDirs.Select(dir => Path.Combine(dir, name)).FirstOrDefault(File.Exists); + + return (from dir in searchDirs + select Path.Combine(dir, name) + into path + from ext in windowsExts + select path + ext).FirstOrDefault(File.Exists); + } + + private static string PathWhichUnix(string name) + { + string[] pathDirs = Environment.GetEnvironmentVariable("PATH")?.Split(PathSep); + + var searchDirs = new List<string>(); + + if (pathDirs != null) + searchDirs.AddRange(pathDirs); + + searchDirs.Add(System.IO.Directory.GetCurrentDirectory()); // last in the list - return null; + return searchDirs.Select(dir => Path.Combine(dir, name)) + .FirstOrDefault(path => File.Exists(path) && Syscall.access(path, AccessModes.X_OK) == 0); } public static void RunProcess(string command, IEnumerable<string> arguments) { + // TODO: Once we move to .NET Standard 2.1 we can use ProcessStartInfo.ArgumentList instead string CmdLineArgsToString(IEnumerable<string> args) { + // Not perfect, but as long as we are careful... return string.Join(" ", args.Select(arg => arg.Contains(" ") ? $@"""{arg}""" : arg)); } - ProcessStartInfo startInfo = new ProcessStartInfo(command, CmdLineArgsToString(arguments)) + var startInfo = new ProcessStartInfo(command, CmdLineArgsToString(arguments)) { RedirectStandardOutput = true, RedirectStandardError = true, diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index 28cab2ab61..2252f7676d 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -97,7 +97,7 @@ #define C_METHOD_MONOARRAY_TO(m_type) C_NS_MONOMARSHAL "::mono_array_to_" #m_type #define C_METHOD_MONOARRAY_FROM(m_type) C_NS_MONOMARSHAL "::" #m_type "_to_mono_array" -#define BINDINGS_GENERATOR_VERSION UINT32_C(9) +#define BINDINGS_GENERATOR_VERSION UINT32_C(11) const char *BindingsGenerator::TypeInterface::DEFAULT_VARARG_C_IN("\t%0 %1_in = %1;\n"); @@ -731,13 +731,26 @@ void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) { i++; } + String im_type_out = return_type->im_type_out; + + if (return_type->ret_as_byref_arg) { + // Doesn't affect the unique signature + im_type_out = "void"; + + im_sig += ", "; + im_sig += return_type->im_type_out; + im_sig += " argRet"; + + i++; + } + // godot_icall_{argc}_{icallcount} String icall_method = ICALL_PREFIX; icall_method += itos(imethod.arguments.size()); icall_method += "_"; icall_method += itos(method_icalls.size()); - InternalCall im_icall = InternalCall(p_itype.api_type, icall_method, return_type->im_type_out, im_sig, im_unique_sig); + InternalCall im_icall = InternalCall(p_itype.api_type, icall_method, im_type_out, im_sig, im_unique_sig); List<InternalCall>::Element *match = method_icalls.find(im_icall); @@ -1685,17 +1698,18 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf const InternalCall *im_icall = match->value(); String im_call = im_icall->editor_only ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS; - im_call += "." + im_icall->name + "(" + icall_params + ")"; + im_call += "."; + im_call += im_icall->name; if (p_imethod.arguments.size()) p_output.append(cs_in_statements); if (return_type->cname == name_cache.type_void) { - p_output.append(im_call + ";\n"); + p_output.append(im_call + "(" + icall_params + ");\n"); } else if (return_type->cs_out.empty()) { - p_output.append("return " + im_call + ";\n"); + p_output.append("return " + im_call + "(" + icall_params + ");\n"); } else { - p_output.append(sformat(return_type->cs_out, im_call, return_type->cs_type, return_type->im_type_out)); + p_output.append(sformat(return_type->cs_out, im_call, icall_params, return_type->cs_type, return_type->im_type_out)); p_output.append("\n"); } @@ -1937,6 +1951,15 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte i++; } + if (return_type->ret_as_byref_arg) { + c_func_sig += ", "; + c_func_sig += return_type->c_type_in; + c_func_sig += " "; + c_func_sig += "arg_ret"; + + i++; + } + const Map<const MethodInterface *, const InternalCall *>::Element *match = method_icalls_map.find(&p_imethod); ERR_FAIL_NULL_V(match, ERR_BUG); @@ -1951,14 +1974,12 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte // Generate icall function - p_output.append(ret_void ? "void " : return_type->c_type_out + " "); + p_output.append((ret_void || return_type->ret_as_byref_arg) ? "void " : return_type->c_type_out + " "); p_output.append(icall_method); p_output.append("("); p_output.append(c_func_sig); p_output.append(") " OPEN_BLOCK); - String fail_ret = ret_void ? "" : ", " + (return_type->c_type_out.ends_with("*") ? "NULL" : return_type->c_type_out + "()"); - if (!ret_void) { String ptrcall_return_type; String initialization; @@ -1982,9 +2003,18 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte p_output.append("\t" + ptrcall_return_type); p_output.append(" " C_LOCAL_RET); p_output.append(initialization + ";\n"); - p_output.append("\tERR_FAIL_NULL_V(" CS_PARAM_INSTANCE); - p_output.append(fail_ret); - p_output.append(");\n"); + + String fail_ret = return_type->c_type_out.ends_with("*") && !return_type->ret_as_byref_arg ? "NULL" : return_type->c_type_out + "()"; + + if (return_type->ret_as_byref_arg) { + p_output.append("\tif (" CS_PARAM_INSTANCE " == NULL) { *arg_ret = "); + p_output.append(fail_ret); + p_output.append("; ERR_FAIL_MSG(\"Parameter ' arg_ret ' is null.\"); }\n"); + } else { + p_output.append("\tERR_FAIL_NULL_V(" CS_PARAM_INSTANCE ", "); + p_output.append(fail_ret); + p_output.append(");\n"); + } } else { p_output.append("\tERR_FAIL_NULL(" CS_PARAM_INSTANCE ");\n"); } @@ -2045,10 +2075,13 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte } if (!ret_void) { - if (return_type->c_out.empty()) + if (return_type->c_out.empty()) { p_output.append("\treturn " C_LOCAL_RET ";\n"); - else + } else if (return_type->ret_as_byref_arg) { + p_output.append(sformat(return_type->c_out, return_type->c_type_out, C_LOCAL_RET, return_type->name, "arg_ret")); + } else { p_output.append(sformat(return_type->c_out, return_type->c_type_out, C_LOCAL_RET, return_type->name)); + } } p_output.append(CLOSE_BLOCK "\n"); @@ -2620,30 +2653,32 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { TypeInterface itype; -#define INSERT_STRUCT_TYPE(m_type, m_type_in) \ +#define INSERT_STRUCT_TYPE(m_type) \ { \ itype = TypeInterface::create_value_type(String(#m_type)); \ itype.c_in = "\t%0 %1_in = MARSHALLED_IN(" #m_type ", %1);\n"; \ - itype.c_out = "\treturn MARSHALLED_OUT(" #m_type ", %1);\n"; \ + itype.c_out = "\t*%3 = MARSHALLED_OUT(" #m_type ", %1);\n"; \ itype.c_arg_in = "&%s_in"; \ itype.c_type_in = "GDMonoMarshal::M_" #m_type "*"; \ itype.c_type_out = "GDMonoMarshal::M_" #m_type; \ itype.cs_in = "ref %s"; \ - itype.cs_out = "return (%1)%0;"; \ - itype.im_type_out = itype.cs_type; \ + /* in cs_out, im_type_out (%3) includes the 'out ' part */ \ + itype.cs_out = "%0(%1, %3 argRet); return (%2)argRet;"; \ + itype.im_type_out = "out " + itype.cs_type; \ + itype.ret_as_byref_arg = true; \ builtin_types.insert(itype.cname, itype); \ } - INSERT_STRUCT_TYPE(Vector2, "real_t*") - INSERT_STRUCT_TYPE(Rect2, "real_t*") - INSERT_STRUCT_TYPE(Transform2D, "real_t*") - INSERT_STRUCT_TYPE(Vector3, "real_t*") - INSERT_STRUCT_TYPE(Basis, "real_t*") - INSERT_STRUCT_TYPE(Quat, "real_t*") - INSERT_STRUCT_TYPE(Transform, "real_t*") - INSERT_STRUCT_TYPE(AABB, "real_t*") - INSERT_STRUCT_TYPE(Color, "real_t*") - INSERT_STRUCT_TYPE(Plane, "real_t*") + INSERT_STRUCT_TYPE(Vector2) + INSERT_STRUCT_TYPE(Rect2) + INSERT_STRUCT_TYPE(Transform2D) + INSERT_STRUCT_TYPE(Vector3) + INSERT_STRUCT_TYPE(Basis) + INSERT_STRUCT_TYPE(Quat) + INSERT_STRUCT_TYPE(Transform) + INSERT_STRUCT_TYPE(AABB) + INSERT_STRUCT_TYPE(Color) + INSERT_STRUCT_TYPE(Plane) #undef INSERT_STRUCT_TYPE @@ -2687,11 +2722,44 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { INSERT_INT_TYPE("sbyte", int8_t, int64_t); INSERT_INT_TYPE("short", int16_t, int64_t); INSERT_INT_TYPE("int", int32_t, int64_t); - INSERT_INT_TYPE("long", int64_t, int64_t); INSERT_INT_TYPE("byte", uint8_t, int64_t); INSERT_INT_TYPE("ushort", uint16_t, int64_t); INSERT_INT_TYPE("uint", uint32_t, int64_t); - INSERT_INT_TYPE("ulong", uint64_t, int64_t); + + itype = TypeInterface::create_value_type(String("long")); + { + itype.c_out = "\treturn (%0)%1;\n"; + itype.c_in = "\t%0 %1_in = (%0)*%1;\n"; + itype.c_out = "\t*%3 = (%0)%1;\n"; + itype.c_type = "int64_t"; + itype.c_arg_in = "&%s_in"; + } + itype.c_type_in = "int64_t*"; + itype.c_type_out = "int64_t"; + itype.im_type_in = "ref " + itype.name; + itype.im_type_out = "out " + itype.name; + itype.cs_in = "ref %0"; + /* in cs_out, im_type_out (%3) includes the 'out ' part */ + itype.cs_out = "%0(%1, %3 argRet); return (%2)argRet;"; + itype.ret_as_byref_arg = true; + builtin_types.insert(itype.cname, itype); + + itype = TypeInterface::create_value_type(String("ulong")); + { + itype.c_in = "\t%0 %1_in = (%0)*%1;\n"; + itype.c_out = "\t*%3 = (%0)%1;\n"; + itype.c_type = "int64_t"; + itype.c_arg_in = "&%s_in"; + } + itype.c_type_in = "uint64_t*"; + itype.c_type_out = "uint64_t"; + itype.im_type_in = "ref " + itype.name; + itype.im_type_out = "out " + itype.name; + itype.cs_in = "ref %0"; + /* in cs_out, im_type_out (%3) includes the 'out ' part */ + itype.cs_out = "%0(%1, %3 argRet); return (%2)argRet;"; + itype.ret_as_byref_arg = true; + builtin_types.insert(itype.cname, itype); } // Floating point types @@ -2703,16 +2771,20 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.proxy_name = "float"; { // The expected type for 'float' in ptrcall is 'double' - itype.c_in = "\t%0 %1_in = (%0)%1;\n"; - itype.c_out = "\treturn (%0)%1;\n"; + itype.c_in = "\t%0 %1_in = (%0)*%1;\n"; + itype.c_out = "\t*%3 = (%0)%1;\n"; itype.c_type = "double"; - itype.c_type_in = "float"; + itype.c_type_in = "float*"; itype.c_type_out = "float"; itype.c_arg_in = "&%s_in"; } itype.cs_type = itype.proxy_name; - itype.im_type_in = itype.proxy_name; - itype.im_type_out = itype.proxy_name; + itype.im_type_in = "ref " + itype.proxy_name; + itype.im_type_out = "out " + itype.proxy_name; + itype.cs_in = "ref %0"; + /* in cs_out, im_type_out (%3) includes the 'out ' part */ + itype.cs_out = "%0(%1, %3 argRet); return (%2)argRet;"; + itype.ret_as_byref_arg = true; builtin_types.insert(itype.cname, itype); // double @@ -2720,13 +2792,21 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.name = "double"; itype.cname = itype.name; itype.proxy_name = "double"; - itype.c_type = "double"; - itype.c_type_in = "double"; - itype.c_type_out = "double"; - itype.c_arg_in = "&%s"; + { + itype.c_in = "\t%0 %1_in = (%0)*%1;\n"; + itype.c_out = "\t*%3 = (%0)%1;\n"; + itype.c_type = "double"; + itype.c_type_in = "double*"; + itype.c_type_out = "double"; + itype.c_arg_in = "&%s_in"; + } itype.cs_type = itype.proxy_name; - itype.im_type_in = itype.proxy_name; - itype.im_type_out = itype.proxy_name; + itype.im_type_in = "ref " + itype.proxy_name; + itype.im_type_out = "out " + itype.proxy_name; + itype.cs_in = "ref %0"; + /* in cs_out, im_type_out (%3) includes the 'out ' part */ + itype.cs_out = "%0(%1, %3 argRet); return (%2)argRet;"; + itype.ret_as_byref_arg = true; builtin_types.insert(itype.cname, itype); } @@ -2757,7 +2837,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.c_type_out = itype.c_type + "*"; itype.cs_type = itype.proxy_name; itype.cs_in = "NodePath." CS_SMETHOD_GETINSTANCE "(%0)"; - itype.cs_out = "return new %1(%0);"; + itype.cs_out = "return new %2(%0(%1));"; itype.im_type_in = "IntPtr"; itype.im_type_out = "IntPtr"; builtin_types.insert(itype.cname, itype); @@ -2773,7 +2853,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.c_type_out = itype.c_type + "*"; itype.cs_type = itype.proxy_name; itype.cs_in = "RID." CS_SMETHOD_GETINSTANCE "(%0)"; - itype.cs_out = "return new %1(%0);"; + itype.cs_out = "return new %2(%0(%1));"; itype.im_type_in = "IntPtr"; itype.im_type_out = "IntPtr"; builtin_types.insert(itype.cname, itype); @@ -2855,7 +2935,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.c_type_out = itype.c_type + "*"; itype.cs_type = BINDINGS_NAMESPACE_COLLECTIONS "." + itype.proxy_name; itype.cs_in = "%0." CS_SMETHOD_GETINSTANCE "()"; - itype.cs_out = "return new " + itype.cs_type + "(%0);"; + itype.cs_out = "return new " + itype.cs_type + "(%0(%1));"; itype.im_type_in = "IntPtr"; itype.im_type_out = "IntPtr"; builtin_types.insert(itype.cname, itype); @@ -2871,7 +2951,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.c_type_out = itype.c_type + "*"; itype.cs_type = BINDINGS_NAMESPACE_COLLECTIONS "." + itype.proxy_name; itype.cs_in = "%0." CS_SMETHOD_GETINSTANCE "()"; - itype.cs_out = "return new " + itype.cs_type + "(%0);"; + itype.cs_out = "return new " + itype.cs_type + "(%0(%1));"; itype.im_type_in = "IntPtr"; itype.im_type_out = "IntPtr"; builtin_types.insert(itype.cname, itype); diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h index 8f3676940b..07918a2d03 100644 --- a/modules/mono/editor/bindings_generator.h +++ b/modules/mono/editor/bindings_generator.h @@ -214,6 +214,14 @@ class BindingsGenerator { */ bool memory_own; + /** + * This must be set to true for any struct bigger than 32-bits. Those cannot be passed/returned by value + * with internal calls, so we must use pointers instead. Returns must be replace with out parameters. + * In this case, [c_out] and [cs_out] must have a different format, explained below. + * The Mono IL interpreter icall trampolines don't support passing structs bigger than 32-bits by value (at least not on WASM). + */ + bool ret_as_byref_arg; + // !! The comments of the following fields make reference to other fields via square brackets, e.g.: [field_name] // !! When renaming those fields, make sure to rename their references in the comments @@ -248,6 +256,14 @@ class BindingsGenerator { * %0: [c_type_out] of the return type * %1: name of the variable to be returned * %2: [name] of the return type + * --------------------------------------- + * If [ret_as_byref_arg] is true, the format is different. Instead of using a return statement, + * the value must be assigned to a parameter. This type of this parameter is a pointer to [c_type_out]. + * Formatting elements: + * %0: [c_type_out] of the return type + * %1: name of the variable to be returned + * %2: [name] of the return type + * %3: name of the parameter that must be assigned the return value */ String c_out; @@ -291,9 +307,10 @@ class BindingsGenerator { * One or more statements that determine how a variable of this type is returned from a method. * It must contain the return statement(s). * Formatting elements: - * %0: internal method call statement - * %1: [cs_type] of the return type - * %2: [im_type_out] of the return type + * %0: internal method name + * %1: internal method call arguments without surrounding parenthesis + * %2: [cs_type] of the return type + * %3: [im_type_out] of the return type */ String cs_out; @@ -417,7 +434,7 @@ class BindingsGenerator { r_enum_itype.cs_type = r_enum_itype.proxy_name; r_enum_itype.cs_in = "(int)%s"; - r_enum_itype.cs_out = "return (%1)%0;"; + r_enum_itype.cs_out = "return (%2)%0(%1);"; r_enum_itype.im_type_in = "int"; r_enum_itype.im_type_out = "int"; r_enum_itype.class_doc = &EditorHelp::get_doc_data()->class_list[r_enum_itype.proxy_name]; @@ -435,6 +452,8 @@ class BindingsGenerator { memory_own = false; + ret_as_byref_arg = false; + c_arg_in = "%s"; class_doc = NULL; diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp index 1564d73c2a..4055ec005a 100644 --- a/modules/mono/editor/editor_internal_calls.cpp +++ b/modules/mono/editor/editor_internal_calls.cpp @@ -219,15 +219,15 @@ int32_t godot_icall_ScriptClassParser_ParseFile(MonoString *p_filepath, MonoObje return err; } -uint32_t godot_icall_GodotSharpExport_GetExportedAssemblyDependencies(MonoString *p_project_dll_name, MonoString *p_project_dll_src_path, - MonoString *p_build_config, MonoString *p_custom_lib_dir, MonoObject *r_dependencies) { +uint32_t godot_icall_ExportPlugin_GetExportedAssemblyDependencies(MonoString *p_project_dll_name, MonoString *p_project_dll_src_path, + MonoString *p_build_config, MonoString *p_custom_bcl_dir, MonoObject *r_dependencies) { String project_dll_name = GDMonoMarshal::mono_string_to_godot(p_project_dll_name); String project_dll_src_path = GDMonoMarshal::mono_string_to_godot(p_project_dll_src_path); String build_config = GDMonoMarshal::mono_string_to_godot(p_build_config); - String custom_lib_dir = GDMonoMarshal::mono_string_to_godot(p_custom_lib_dir); + String custom_bcl_dir = GDMonoMarshal::mono_string_to_godot(p_custom_bcl_dir); Dictionary dependencies = GDMonoMarshal::mono_object_to_variant(r_dependencies); - return GodotSharpExport::get_exported_assembly_dependencies(project_dll_name, project_dll_src_path, build_config, custom_lib_dir, dependencies); + return GodotSharpExport::get_exported_assembly_dependencies(project_dll_name, project_dll_src_path, build_config, custom_bcl_dir, dependencies); } MonoString *godot_icall_Internal_UpdateApiAssembliesFromPrebuilt(MonoString *p_config) { @@ -411,8 +411,8 @@ void register_editor_internal_calls() { // ScriptClassParser mono_add_internal_call("GodotTools.Internals.ScriptClassParser::internal_ParseFile", (void *)godot_icall_ScriptClassParser_ParseFile); - // GodotSharpExport - mono_add_internal_call("GodotTools.GodotSharpExport::internal_GetExportedAssemblyDependencies", (void *)godot_icall_GodotSharpExport_GetExportedAssemblyDependencies); + // ExportPlugin + mono_add_internal_call("GodotTools.Export.ExportPlugin::internal_GetExportedAssemblyDependencies", (void *)godot_icall_ExportPlugin_GetExportedAssemblyDependencies); // Internals mono_add_internal_call("GodotTools.Internals.Internal::internal_UpdateApiAssembliesFromPrebuilt", (void *)godot_icall_Internal_UpdateApiAssembliesFromPrebuilt); diff --git a/modules/mono/editor/godotsharp_export.cpp b/modules/mono/editor/godotsharp_export.cpp index e83152d668..9ae9399e1d 100644 --- a/modules/mono/editor/godotsharp_export.cpp +++ b/modules/mono/editor/godotsharp_export.cpp @@ -36,6 +36,7 @@ #include "../mono_gd/gd_mono.h" #include "../mono_gd/gd_mono_assembly.h" +#include "../mono_gd/gd_mono_cache.h" namespace GodotSharpExport { diff --git a/modules/mono/glue/Managed/Files/NodePath.cs b/modules/mono/glue/Managed/Files/NodePath.cs index 4de4e1e6a9..8c5872ba5a 100644 --- a/modules/mono/glue/Managed/Files/NodePath.cs +++ b/modules/mono/glue/Managed/Files/NodePath.cs @@ -12,7 +12,7 @@ namespace Godot internal static IntPtr GetPtr(NodePath instance) { if (instance == null) - return IntPtr.Zero; + throw new NullReferenceException($"The instance of type {nameof(NodePath)} is null."); if (instance.disposed) throw new ObjectDisposedException(instance.GetType().FullName); diff --git a/modules/mono/glue/Managed/Files/RID.cs b/modules/mono/glue/Managed/Files/RID.cs index 12064beca2..94761531b1 100644 --- a/modules/mono/glue/Managed/Files/RID.cs +++ b/modules/mono/glue/Managed/Files/RID.cs @@ -12,7 +12,7 @@ namespace Godot internal static IntPtr GetPtr(RID instance) { if (instance == null) - return IntPtr.Zero; + throw new NullReferenceException($"The instance of type {nameof(RID)} is null."); if (instance.disposed) throw new ObjectDisposedException(instance.GetType().FullName); diff --git a/modules/mono/glue/base_object_glue.cpp b/modules/mono/glue/base_object_glue.cpp index 6d85f55b97..2488f78350 100644 --- a/modules/mono/glue/base_object_glue.cpp +++ b/modules/mono/glue/base_object_glue.cpp @@ -36,6 +36,7 @@ #include "core/string_name.h" #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_utils.h" diff --git a/modules/mono/glue/collections_glue.cpp b/modules/mono/glue/collections_glue.cpp index e67c8b9ad9..bfb7b0f775 100644 --- a/modules/mono/glue/collections_glue.cpp +++ b/modules/mono/glue/collections_glue.cpp @@ -34,6 +34,7 @@ #include <mono/metadata/exception.h> +#include "../mono_gd/gd_mono_cache.h" #include "../mono_gd/gd_mono_class.h" #include "../mono_gd/gd_mono_utils.h" diff --git a/modules/mono/glue/gd_glue.cpp b/modules/mono/glue/gd_glue.cpp index 8b9a1380d8..1381d79e2e 100644 --- a/modules/mono/glue/gd_glue.cpp +++ b/modules/mono/glue/gd_glue.cpp @@ -39,6 +39,7 @@ #include "core/variant.h" #include "core/variant_parser.h" +#include "../mono_gd/gd_mono_cache.h" #include "../mono_gd/gd_mono_utils.h" MonoObject *godot_icall_GD_bytes2var(MonoArray *p_bytes, MonoBoolean p_allow_objects) { @@ -211,7 +212,7 @@ MonoString *godot_icall_GD_var2str(MonoObject *p_var) { } MonoObject *godot_icall_DefaultGodotTaskScheduler() { - return GDMonoUtils::mono_cache.task_scheduler_handle->get_target(); + return GDMonoCache::cached_data.task_scheduler_handle->get_target(); } void godot_register_gd_icalls() { diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp index 5fa8aed5a9..ef30a52b72 100644 --- a/modules/mono/godotsharp_dirs.cpp +++ b/modules/mono/godotsharp_dirs.cpp @@ -39,8 +39,8 @@ #include "editor/editor_settings.h" #endif -#ifdef __ANDROID__ -#include "utils/android_utils.h" +#ifdef ANDROID_ENABLED +#include "mono_gd/gd_mono_android.h" #endif #include "mono_gd/gd_mono.h" @@ -108,6 +108,10 @@ public: 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. + String data_game_assemblies_dir; #endif String data_mono_etc_dir; @@ -130,7 +134,11 @@ 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()); +#ifdef JAVASCRIPT_ENABLED + mono_user_dir = "user://"; +#else mono_user_dir = _get_mono_user_dir(); +#endif mono_logs_dir = mono_user_dir.plus_file("mono_logs"); #ifdef TOOLS_ENABLED @@ -160,8 +168,8 @@ private: String data_mono_root_dir = data_dir_root.plus_file("Mono"); data_mono_etc_dir = data_mono_root_dir.plus_file("etc"); -#if __ANDROID__ - data_mono_lib_dir = GDMonoUtils::Android::get_app_native_lib_dir(); +#ifdef ANDROID_ENABLED + data_mono_lib_dir = GDMonoAndroid::get_app_native_lib_dir(); #else data_mono_lib_dir = data_mono_root_dir.plus_file("lib"); #endif @@ -197,10 +205,11 @@ private: String data_mono_root_dir = data_dir_root.plus_file("Mono"); data_mono_etc_dir = data_mono_root_dir.plus_file("etc"); -#if __ANDROID__ - data_mono_lib_dir = GDMonoUtils::Android::get_app_native_lib_dir(); +#ifdef ANDROID_ENABLED + data_mono_lib_dir = GDMonoAndroid::get_app_native_lib_dir(); #else data_mono_lib_dir = data_mono_root_dir.plus_file("lib"); + data_game_assemblies_dir = data_dir_root.plus_file("Assemblies"); #endif #ifdef WINDOWS_ENABLED @@ -212,6 +221,10 @@ private: data_mono_etc_dir = exe_dir.plus_file("../Resources/GodotSharp/Mono/etc"); data_mono_lib_dir = exe_dir.plus_file("../Frameworks/GodotSharp/Mono/lib"); } + + if (!DirAccess::exists(data_game_assemblies_dir)) { + data_game_assemblies_dir = exe_dir.plus_file("../Frameworks/GodotSharp/Assemblies"); + } #endif #endif @@ -291,6 +304,10 @@ String get_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; +} #endif String get_data_mono_etc_dir() { diff --git a/modules/mono/godotsharp_dirs.h b/modules/mono/godotsharp_dirs.h index ff51888d1c..43da44b0f5 100644 --- a/modules/mono/godotsharp_dirs.h +++ b/modules/mono/godotsharp_dirs.h @@ -56,6 +56,8 @@ 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 String get_data_mono_etc_dir(); diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index 504b8d41d0..a43311d281 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -46,6 +46,7 @@ #include "../csharp_script.h" #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" @@ -56,13 +57,26 @@ #ifdef ANDROID_ENABLED #include "android_mono_config.h" +#include "gd_mono_android.h" #endif +// TODO: +// This has turn into a gigantic mess. There's too much going on here. Too much #ifdef as well. +// It's just painful to read... It needs to be re-structured. Please, clean this up, future me. + GDMono *GDMono::singleton = NULL; namespace { -void setup_runtime_main_args() { +#if defined(JAVASCRIPT_ENABLED) +extern "C" { +void mono_wasm_load_runtime(const char *managed_path, int enable_debugging); +} +#endif + +#if !defined(JAVASCRIPT_ENABLED) + +void gd_mono_setup_runtime_main_args() { CharString execpath = OS::get_singleton()->get_executable_path().utf8(); List<String> cmdline_args = OS::get_singleton()->get_cmdline_args(); @@ -83,7 +97,7 @@ void setup_runtime_main_args() { mono_runtime_set_main_args(main_args.size(), main_args.ptrw()); } -void gdmono_profiler_init() { +void gd_mono_profiler_init() { String profiler_args = GLOBAL_DEF("mono/profiler/args", "log:calls,alloc,sample,output=output.mlpd"); bool profiler_enabled = GLOBAL_DEF("mono/profiler/enabled", false); if (profiler_enabled) { @@ -91,9 +105,9 @@ void gdmono_profiler_init() { } } -#ifdef DEBUG_ENABLED +#if defined(DEBUG_ENABLED) -bool _wait_for_debugger_msecs(uint32_t p_msecs) { +bool gd_mono_wait_for_debugger_msecs(uint32_t p_msecs) { do { if (mono_is_debugger_attached()) @@ -115,7 +129,7 @@ bool _wait_for_debugger_msecs(uint32_t p_msecs) { return mono_is_debugger_attached(); } -void gdmono_debug_init() { +void gd_mono_debug_init() { mono_debug_init(MONO_DEBUG_FORMAT_MONO); @@ -151,11 +165,37 @@ void gdmono_debug_init() { mono_jit_parse_options(2, (char **)options); } +#endif // defined(DEBUG_ENABLED) +#endif // !defined(JAVASCRIPT_ENABLED) + +#if defined(JAVASCRIPT_ENABLED) +MonoDomain *gd_initialize_mono_runtime() { + const char *vfs_prefix = "managed"; + int enable_debugging = 0; + +#ifdef DEBUG_ENABLED + enable_debugging = 1; +#endif + + mono_wasm_load_runtime(vfs_prefix, enable_debugging); + + return mono_get_root_domain(); +} +#else +MonoDomain *gd_initialize_mono_runtime() { +#ifdef DEBUG_ENABLED + gd_mono_debug_init(); +#endif + + return mono_jit_init_version("GodotEngine.RootDomain", "v4.0.30319"); +} #endif } // namespace void GDMono::add_mono_shared_libs_dir_to_path() { + // TODO: Replace this with a mono_dl_fallback + // By default Mono seems to search shared libraries in the following directories: // Current working directory, @executable_path@ and PATH // The parent directory of the image file (assembly where the dllimport method is declared) @@ -199,35 +239,22 @@ void GDMono::add_mono_shared_libs_dir_to_path() { #endif // WINDOWS_ENABLED || UNIX_ENABLED } -void GDMono::initialize() { +void GDMono::determine_mono_dirs(String &r_assembly_rootdir, String &r_config_dir) { - ERR_FAIL_NULL(Engine::get_singleton()); - - print_verbose("Mono: Initializing module..."); - - char *runtime_build_info = mono_get_runtime_build_info(); - print_verbose("Mono JIT compiler version " + String(runtime_build_info)); - mono_free(runtime_build_info); - -#ifdef DEBUG_METHODS_ENABLED - _initialize_and_check_api_hashes(); -#endif - - GDMonoLog::get_singleton()->initialize(); - - String assembly_rootdir; - String config_dir; + String bundled_assembly_rootdir = GodotSharpDirs::get_data_mono_lib_dir(); + String bundled_config_dir = GodotSharpDirs::get_data_mono_etc_dir(); #ifdef TOOLS_ENABLED + #if defined(WINDOWS_ENABLED) mono_reg_info = MonoRegUtils::find_mono(); if (mono_reg_info.assembly_dir.length() && DirAccess::exists(mono_reg_info.assembly_dir)) { - assembly_rootdir = mono_reg_info.assembly_dir; + r_assembly_rootdir = mono_reg_info.assembly_dir; } if (mono_reg_info.config_dir.length() && DirAccess::exists(mono_reg_info.config_dir)) { - config_dir = mono_reg_info.config_dir; + r_config_dir = mono_reg_info.config_dir; } #elif defined(OSX_ENABLED) const char *c_assembly_rootdir = mono_assembly_getrootdir(); @@ -244,29 +271,24 @@ void GDMono::initialize() { String hint_config_dir = path::join(locations[i], "etc"); if (FileAccess::exists(hint_mscorlib_path) && DirAccess::exists(hint_config_dir)) { - assembly_rootdir = hint_assembly_rootdir; - config_dir = hint_config_dir; + r_assembly_rootdir = hint_assembly_rootdir; + r_config_dir = hint_config_dir; break; } } } #endif -#endif // TOOLS_ENABLED - String bundled_assembly_rootdir = GodotSharpDirs::get_data_mono_lib_dir(); - String bundled_config_dir = GodotSharpDirs::get_data_mono_etc_dir(); - -#ifdef TOOLS_ENABLED if (DirAccess::exists(bundled_assembly_rootdir)) { - assembly_rootdir = bundled_assembly_rootdir; + r_assembly_rootdir = bundled_assembly_rootdir; } if (DirAccess::exists(bundled_config_dir)) { - config_dir = bundled_config_dir; + r_config_dir = bundled_config_dir; } #ifdef WINDOWS_ENABLED - if (assembly_rootdir.empty() || config_dir.empty()) { + if (r_assembly_rootdir.empty() || r_config_dir.empty()) { ERR_PRINT("Cannot find Mono in the registry."); // Assertion: if they are not set, then they weren't found in the registry CRASH_COND(mono_reg_info.assembly_dir.length() > 0 || mono_reg_info.config_dir.length() > 0); @@ -274,35 +296,47 @@ void GDMono::initialize() { #endif // WINDOWS_ENABLED #else - // These are always the directories in export templates - assembly_rootdir = bundled_assembly_rootdir; - config_dir = bundled_config_dir; -#endif // TOOLS_ENABLED + // Export templates always use the bundled directories + r_assembly_rootdir = bundled_assembly_rootdir; + r_config_dir = bundled_config_dir; +#endif +} + +void GDMono::initialize() { + + ERR_FAIL_NULL(Engine::get_singleton()); + + print_verbose("Mono: Initializing module..."); + + char *runtime_build_info = mono_get_runtime_build_info(); + print_verbose("Mono JIT compiler version " + String(runtime_build_info)); + mono_free(runtime_build_info); + + _init_godot_api_hashes(); + _init_exception_policy(); + + GDMonoLog::get_singleton()->initialize(); + +#if !defined(JAVASCRIPT_ENABLED) + String assembly_rootdir; + String config_dir; + determine_mono_dirs(assembly_rootdir, config_dir); // Leak if we call mono_set_dirs more than once mono_set_dirs(assembly_rootdir.length() ? assembly_rootdir.utf8().get_data() : NULL, config_dir.length() ? config_dir.utf8().get_data() : NULL); add_mono_shared_libs_dir_to_path(); +#endif - { - PropertyInfo exc_policy_prop = PropertyInfo(Variant::INT, "mono/unhandled_exception_policy", PROPERTY_HINT_ENUM, - vformat("Terminate Application:%s,Log Error:%s", (int)POLICY_TERMINATE_APP, (int)POLICY_LOG_ERROR)); - unhandled_exception_policy = (UnhandledExceptionPolicy)(int)GLOBAL_DEF(exc_policy_prop.name, (int)POLICY_TERMINATE_APP); - ProjectSettings::get_singleton()->set_custom_property_info(exc_policy_prop.name, exc_policy_prop); - - if (Engine::get_singleton()->is_editor_hint()) { - // Unhandled exceptions should not terminate the editor - unhandled_exception_policy = POLICY_LOG_ERROR; - } - } +#if defined(ANDROID_ENABLED) + GDMonoAndroid::register_android_dl_fallback(); +#endif GDMonoAssembly::initialize(); - gdmono_profiler_init(); - -#ifdef DEBUG_ENABLED - gdmono_debug_init(); +#if !defined(JAVASCRIPT_ENABLED) + gd_mono_profiler_init(); #endif #ifdef ANDROID_ENABLED @@ -314,9 +348,12 @@ void GDMono::initialize() { mono_install_unhandled_exception_hook(&unhandled_exception_hook, NULL); #ifndef TOOLS_ENABLED - // Export templates only load the Mono runtime if the project uses it - if (!DirAccess::exists("res://.mono")) + // Exported games that don't use C# must still work. They likely don't ship with mscorlib. + // We only initialize the Mono runtime if we can find mscorlib. Otherwise it would crash. + if (GDMonoAssembly::find_assembly("mscorlib.dll").empty()) { + print_verbose("Mono: Skipping runtime initialization because 'mscorlib.dll' could not be found"); return; + } #endif #if !defined(WINDOWS_ENABLED) && !defined(NO_MONO_THREADS_SUSPEND_WORKAROUND) @@ -326,12 +363,14 @@ void GDMono::initialize() { } #endif - root_domain = mono_jit_init_version("GodotEngine.RootDomain", "v4.0.30319"); + root_domain = gd_initialize_mono_runtime(); ERR_FAIL_NULL_MSG(root_domain, "Mono: Failed to initialize runtime."); GDMonoUtils::set_main_thread(GDMonoUtils::get_current_thread()); - setup_runtime_main_args(); // Required for System.Environment.GetCommandLineArgs +#if !defined(JAVASCRIPT_ENABLED) + gd_mono_setup_runtime_main_args(); // Required for System.Environment.GetCommandLineArgs +#endif runtime_initialized = true; @@ -344,8 +383,8 @@ void GDMono::initialize() { Error domain_load_err = _load_scripts_domain(); ERR_FAIL_COND_MSG(domain_load_err != OK, "Mono: Failed to load scripts domain."); -#ifdef DEBUG_ENABLED - bool debugger_attached = _wait_for_debugger_msecs(500); +#if defined(DEBUG_ENABLED) && !defined(JAVASCRIPT_ENABLED) + bool debugger_attached = gd_mono_wait_for_debugger_msecs(500); if (!debugger_attached && OS::get_singleton()->is_stdout_verbose()) print_error("Mono: Debugger wait timeout"); #endif @@ -381,7 +420,7 @@ 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 || !GDMonoUtils::mono_cache.godot_api_cache_updated); + 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; @@ -429,9 +468,8 @@ void GDMono::_register_internal_calls() { GodotSharpBindings::register_generated_icalls(); } -void GDMono::_initialize_and_check_api_hashes() { -#ifdef MONO_GLUE_ENABLED -#ifdef DEBUG_METHODS_ENABLED +void GDMono::_init_godot_api_hashes() { +#if defined(MONO_GLUE_ENABLED) && defined(DEBUG_METHODS_ENABLED) if (get_api_core_hash() != GodotSharpBindings::get_core_api_hash()) { ERR_PRINT("Mono: Core API hash mismatch."); } @@ -441,8 +479,19 @@ void GDMono::_initialize_and_check_api_hashes() { ERR_PRINT("Mono: Editor API hash mismatch."); } #endif // TOOLS_ENABLED -#endif // DEBUG_METHODS_ENABLED -#endif // MONO_GLUE_ENABLED +#endif // MONO_GLUE_ENABLED && DEBUG_METHODS_ENABLED +} + +void GDMono::_init_exception_policy() { + PropertyInfo exc_policy_prop = PropertyInfo(Variant::INT, "mono/unhandled_exception_policy", PROPERTY_HINT_ENUM, + vformat("Terminate Application:%s,Log Error:%s", (int)POLICY_TERMINATE_APP, (int)POLICY_LOG_ERROR)); + unhandled_exception_policy = (UnhandledExceptionPolicy)(int)GLOBAL_DEF(exc_policy_prop.name, (int)POLICY_TERMINATE_APP); + ProjectSettings::get_singleton()->set_custom_property_info(exc_policy_prop.name, exc_policy_prop); + + if (Engine::get_singleton()->is_editor_hint()) { + // Unhandled exceptions should not terminate the editor + unhandled_exception_policy = POLICY_LOG_ERROR; + } } void GDMono::add_assembly(uint32_t p_domain_id, GDMonoAssembly *p_assembly) { @@ -561,7 +610,7 @@ bool GDMono::_load_corlib_assembly() { bool success = load_assembly("mscorlib", &corlib_assembly); if (success) - GDMonoUtils::update_corlib_cache(); + GDMonoCache::update_corlib_cache(); return success; } @@ -834,9 +883,9 @@ bool GDMono::_try_load_api_assemblies(LoadedApiAssembly &r_core_api_assembly, Lo } bool GDMono::_on_core_api_assembly_loaded() { - GDMonoUtils::update_godot_api_cache(); + GDMonoCache::update_godot_api_cache(); - if (!GDMonoUtils::mono_cache.godot_api_cache_updated) + if (!GDMonoCache::cached_data.godot_api_cache_updated) return false; get_singleton()->_install_trace_listener(); @@ -884,7 +933,7 @@ void GDMono::_load_api_assemblies() { 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 (!GDMonoUtils::mono_cache.godot_api_cache_updated) { + } 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."); } @@ -984,7 +1033,7 @@ Error GDMono::_unload_scripts_domain() { mono_gc_collect(mono_gc_max_generation()); - GDMonoUtils::clear_godot_api_cache(); + GDMonoCache::clear_godot_api_cache(); _domain_assemblies_cleanup(mono_domain_get_id(scripts_domain)); diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h index e14a0d8409..7fb03b82ad 100644 --- a/modules/mono/mono_gd/gd_mono.h +++ b/modules/mono/mono_gd/gd_mono.h @@ -153,7 +153,8 @@ private: #ifdef TOOLS_ENABLED uint64_t api_editor_hash; #endif - void _initialize_and_check_api_hashes(); + void _init_godot_api_hashes(); + void _init_exception_policy(); GDMonoLog *gdmono_log; @@ -162,6 +163,7 @@ private: #endif void add_mono_shared_libs_dir_to_path(); + void determine_mono_dirs(String &r_assembly_rootdir, String &r_config_dir); protected: static GDMono *singleton; diff --git a/modules/mono/utils/android_utils.cpp b/modules/mono/mono_gd/gd_mono_android.cpp index 7dd67e3b8e..1ee035589d 100644 --- a/modules/mono/utils/android_utils.cpp +++ b/modules/mono/mono_gd/gd_mono_android.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* android_utils.cpp */ +/* gd_mono_android.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,16 +28,25 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "android_utils.h" +#include "gd_mono_android.h" -#ifdef __ANDROID__ +#if defined(ANDROID_ENABLED) +#include <dlfcn.h> // dlopen, dlsym +#include <mono/utils/mono-dl-fallback.h> + +#include "core/os/os.h" +#include "core/ustring.h" #include "platform/android/thread_jandroid.h" -namespace GDMonoUtils { -namespace Android { +#include "../utils/path_utils.h" +#include "../utils/string_utils.h" -String get_app_native_lib_dir() { +namespace GDMonoAndroid { + +String app_native_lib_dir_cache; + +String determine_app_native_lib_dir() { JNIEnv *env = ThreadAndroid::get_env(); jclass activityThreadClass = env->FindClass("android/app/ActivityThread"); @@ -62,7 +71,76 @@ String get_app_native_lib_dir() { return result; } -} // namespace Android -} // namespace GDMonoUtils +String get_app_native_lib_dir() { + if (app_native_lib_dir_cache.empty()) + app_native_lib_dir_cache = determine_app_native_lib_dir(); + return app_native_lib_dir_cache; +} + +int gd_mono_convert_dl_flags(int flags) { + // from mono's runtime-bootstrap.c + + int lflags = flags & MONO_DL_LOCAL ? 0 : RTLD_GLOBAL; + + if (flags & MONO_DL_LAZY) + lflags |= RTLD_LAZY; + else + lflags |= RTLD_NOW; + + return lflags; +} + +void *gd_mono_android_dlopen(const char *p_name, int p_flags, char **r_err, void *p_user_data) { + String name = String::utf8(p_name); + + if (name.ends_with(".dll.so") || name.ends_with(".exe.so")) { + String app_native_lib_dir = get_app_native_lib_dir(); + + String orig_so_name = name.get_file(); + String so_name = "lib-aot-" + orig_so_name; + String so_path = path::join(app_native_lib_dir, so_name); + + if (!FileAccess::exists(so_path)) { + if (OS::get_singleton()->is_stdout_verbose()) + OS::get_singleton()->print("Cannot find shared library: '%s'\n", so_path.utf8().get_data()); + return NULL; + } + + int lflags = gd_mono_convert_dl_flags(p_flags); + + void *handle = dlopen(so_path.utf8().get_data(), lflags); + + if (!handle) { + if (OS::get_singleton()->is_stdout_verbose()) + OS::get_singleton()->print("Failed to open shared library: '%s'. Error: '%s'\n", so_path.utf8().get_data(), dlerror()); + return NULL; + } + + if (OS::get_singleton()->is_stdout_verbose()) + OS::get_singleton()->print("Successfully loaded AOT shared library: '%s'\n", so_path.utf8().get_data()); + + return handle; + } + + return NULL; +} + +void *gd_mono_android_dlsym(void *p_handle, const char *p_name, char **r_err, void *p_user_data) { + void *sym_addr = dlsym(p_handle, p_name); + + if (sym_addr) + return sym_addr; + + if (r_err) + *r_err = str_format_new("%s\n", dlerror()); + + return NULL; +} + +void register_android_dl_fallback() { + mono_dl_fallback_register(gd_mono_android_dlopen, gd_mono_android_dlsym, NULL, NULL); +} + +} // namespace GDMonoAndroid -#endif // __ANDROID__ +#endif diff --git a/modules/mono/utils/android_utils.h b/modules/mono/mono_gd/gd_mono_android.h index f911c3fdfe..72bc799bfd 100644 --- a/modules/mono/utils/android_utils.h +++ b/modules/mono/mono_gd/gd_mono_android.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* android_utils.h */ +/* gd_mono_android.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,21 +28,21 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef ANDROID_UTILS_H -#define ANDROID_UTILS_H +#ifndef GD_MONO_ANDROID_H +#define GD_MONO_ANDROID_H -#ifdef __ANDROID__ +#if defined(ANDROID_ENABLED) #include "core/ustring.h" -namespace GDMonoUtils { -namespace Android { +namespace GDMonoAndroid { String get_app_native_lib_dir(); -} // namespace Android -} // namespace GDMonoUtils +void register_android_dl_fallback(); -#endif // __ANDROID__ +} // namespace GDMonoAndroid -#endif // ANDROID_UTILS_H +#endif // ANDROID_ENABLED + +#endif // GD_MONO_ANDROID_H diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp index a82bb42731..105560fe9a 100644 --- a/modules/mono/mono_gd/gd_mono_assembly.cpp +++ b/modules/mono/mono_gd/gd_mono_assembly.cpp @@ -39,6 +39,7 @@ #include "core/project_settings.h" #include "../godotsharp_dirs.h" +#include "gd_mono_cache.h" #include "gd_mono_class.h" bool GDMonoAssembly::no_search = false; @@ -61,6 +62,13 @@ void GDMonoAssembly::fill_search_dirs(Vector<String> &r_search_dirs, const Strin r_search_dirs.push_back(framework_dir.plus_file("Facades")); } +#if !defined(TOOLS_ENABLED) + String data_game_assemblies_dir = GodotSharpDirs::get_data_game_assemblies_dir(); + if (!data_game_assemblies_dir.empty()) { + r_search_dirs.push_back(data_game_assemblies_dir); + } +#endif + if (p_custom_config.length()) { r_search_dirs.push_back(GodotSharpDirs::get_res_temp_assemblies_base_dir().plus_file(p_custom_config)); } else { @@ -146,10 +154,6 @@ MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **, vo (void)user_data; // UNUSED - if (search_dirs.empty()) { - fill_search_dirs(search_dirs); - } - { // If we find the assembly here, we load it with 'mono_assembly_load_from_full', // which in turn invokes load hooks before returning the MonoAssembly to us. @@ -227,6 +231,33 @@ GDMonoAssembly *GDMonoAssembly::_load_assembly_search(const String &p_name, cons return NULL; } +String GDMonoAssembly::find_assembly(const String &p_name) { + + String path; + + bool has_extension = p_name.ends_with(".dll") || p_name.ends_with(".exe"); + + for (int i = 0; i < search_dirs.size(); i++) { + const String &search_dir = search_dirs[i]; + + if (has_extension) { + path = search_dir.plus_file(p_name); + if (FileAccess::exists(path)) + return path; + } else { + path = search_dir.plus_file(p_name + ".dll"); + if (FileAccess::exists(path)) + return path; + + path = search_dir.plus_file(p_name + ".exe"); + if (FileAccess::exists(path)) + return path; + } + } + + return String(); +} + GDMonoAssembly *GDMonoAssembly::_load_assembly_from(const String &p_name, const String &p_path, bool p_refonly) { GDMonoAssembly *assembly = memnew(GDMonoAssembly(p_name, p_path)); @@ -263,6 +294,8 @@ void GDMonoAssembly::_wrap_mono_assembly(MonoAssembly *assembly) { void GDMonoAssembly::initialize() { + fill_search_dirs(search_dirs); + mono_install_assembly_search_hook(&assembly_search_hook, NULL); mono_install_assembly_refonly_search_hook(&assembly_refonly_search_hook, NULL); mono_install_assembly_preload_hook(&assembly_preload_hook, NULL); diff --git a/modules/mono/mono_gd/gd_mono_assembly.h b/modules/mono/mono_gd/gd_mono_assembly.h index 39749dfc1d..04a219f742 100644 --- a/modules/mono/mono_gd/gd_mono_assembly.h +++ b/modules/mono/mono_gd/gd_mono_assembly.h @@ -122,6 +122,8 @@ public: GDMonoClass *get_object_derived_class(const StringName &p_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()); static GDMonoAssembly *load_from(const String &p_name, const String &p_path, bool p_refonly); diff --git a/modules/mono/mono_gd/gd_mono_cache.cpp b/modules/mono/mono_gd/gd_mono_cache.cpp new file mode 100644 index 0000000000..caa1ca9203 --- /dev/null +++ b/modules/mono/mono_gd/gd_mono_cache.cpp @@ -0,0 +1,312 @@ +/*************************************************************************/ +/* gd_mono_cache.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 != NULL); \ + m_var = m_val; \ + ERR_FAIL_COND_MSG(m_var == NULL, "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_NS_CLASS_AND_CHECK(m_ns, m_class, m_val) CACHE_AND_CHECK(cached_data.class_##m_ns##_##m_class, m_val) +#define CACHE_RAW_MONO_CLASS_AND_CHECK(m_class, m_val) CACHE_AND_CHECK(cached_data.rawclass_##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 == NULL, "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 = NULL; + class_bool = NULL; + class_int8_t = NULL; + class_int16_t = NULL; + class_int32_t = NULL; + class_int64_t = NULL; + class_uint8_t = NULL; + class_uint16_t = NULL; + class_uint32_t = NULL; + class_uint64_t = NULL; + class_float = NULL; + class_double = NULL; + class_String = NULL; + class_IntPtr = NULL; + + class_System_Collections_IEnumerable = NULL; + class_System_Collections_IDictionary = NULL; + +#ifdef DEBUG_ENABLED + class_System_Diagnostics_StackTrace = NULL; + methodthunk_System_Diagnostics_StackTrace_GetFrames.nullify(); + method_System_Diagnostics_StackTrace_ctor_bool = NULL; + method_System_Diagnostics_StackTrace_ctor_Exception_bool = NULL; +#endif + + class_KeyNotFoundException = NULL; +} + +void CachedData::clear_godot_api_cache() { + + godot_api_cache_updated = false; + + rawclass_Dictionary = NULL; + + class_Vector2 = NULL; + class_Rect2 = NULL; + class_Transform2D = NULL; + class_Vector3 = NULL; + class_Basis = NULL; + class_Quat = NULL; + class_Transform = NULL; + class_AABB = NULL; + class_Color = NULL; + class_Plane = NULL; + class_NodePath = NULL; + class_RID = NULL; + class_GodotObject = NULL; + class_GodotResource = NULL; + class_Node = NULL; + class_Control = NULL; + class_Spatial = NULL; + class_WeakRef = NULL; + class_Array = NULL; + class_Dictionary = NULL; + class_MarshalUtils = NULL; + class_ISerializationListener = NULL; + +#ifdef DEBUG_ENABLED + class_DebuggingUtils = NULL; + methodthunk_DebuggingUtils_GetStackFrameInfo.nullify(); +#endif + + class_ExportAttribute = NULL; + field_ExportAttribute_hint = NULL; + field_ExportAttribute_hintString = NULL; + class_SignalAttribute = NULL; + class_ToolAttribute = NULL; + class_RemoteAttribute = NULL; + class_SyncAttribute = NULL; + class_MasterAttribute = NULL; + class_PuppetAttribute = NULL; + class_SlaveAttribute = NULL; + class_RemoteSyncAttribute = NULL; + class_MasterSyncAttribute = NULL; + class_PuppetSyncAttribute = NULL; + class_GodotMethodAttribute = NULL; + field_GodotMethodAttribute_methodName = NULL; + + field_GodotObject_ptr = NULL; + field_NodePath_ptr = NULL; + field_Image_ptr = NULL; + field_RID_ptr = NULL; + + methodthunk_GodotObject_Dispose.nullify(); + methodthunk_Array_GetPtr.nullify(); + methodthunk_Dictionary_GetPtr.nullify(); + methodthunk_SignalAwaiter_SignalCallback.nullify(); + methodthunk_SignalAwaiter_FailureCallback.nullify(); + methodthunk_GodotTaskScheduler_Activate.nullify(); + + // Start of MarshalUtils methods + + methodthunk_MarshalUtils_TypeIsGenericArray.nullify(); + methodthunk_MarshalUtils_TypeIsGenericDictionary.nullify(); + + methodthunk_MarshalUtils_ArrayGetElementType.nullify(); + methodthunk_MarshalUtils_DictionaryGetKeyValueTypes.nullify(); + + methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType.nullify(); + methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType.nullify(); + methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType_with_info.nullify(); + methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType_with_info.nullify(); + + methodthunk_MarshalUtils_MakeGenericArrayType.nullify(); + methodthunk_MarshalUtils_MakeGenericDictionaryType.nullify(); + + methodthunk_MarshalUtils_EnumerableToArray.nullify(); + methodthunk_MarshalUtils_IDictionaryToDictionary.nullify(); + methodthunk_MarshalUtils_GenericIDictionaryToDictionary.nullify(); + + // End of MarshalUtils methods + + task_scheduler_handle = Ref<MonoGCHandle>(); +} + +#define GODOT_API_CLASS(m_class) (GDMono::get_singleton()->get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, #m_class)) +#define GODOT_API_NS_CLASS(m_ns, m_class) (GDMono::get_singleton()->get_core_api_assembly()->get_class(m_ns, #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(bool, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_boolean_class())); + CACHE_CLASS_AND_CHECK(int8_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_sbyte_class())); + CACHE_CLASS_AND_CHECK(int16_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_int16_class())); + CACHE_CLASS_AND_CHECK(int32_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_int32_class())); + CACHE_CLASS_AND_CHECK(int64_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_int64_class())); + CACHE_CLASS_AND_CHECK(uint8_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_byte_class())); + CACHE_CLASS_AND_CHECK(uint16_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_uint16_class())); + CACHE_CLASS_AND_CHECK(uint32_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_uint32_class())); + CACHE_CLASS_AND_CHECK(uint64_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_uint64_class())); + CACHE_CLASS_AND_CHECK(float, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_single_class())); + CACHE_CLASS_AND_CHECK(double, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_double_class())); + CACHE_CLASS_AND_CHECK(String, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_string_class())); + CACHE_CLASS_AND_CHECK(IntPtr, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_intptr_class())); + + CACHE_CLASS_AND_CHECK(System_Collections_IEnumerable, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections", "IEnumerable")); + CACHE_CLASS_AND_CHECK(System_Collections_IDictionary, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections", "IDictionary")); + +#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_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(Vector2, GODOT_API_CLASS(Vector2)); + CACHE_CLASS_AND_CHECK(Rect2, GODOT_API_CLASS(Rect2)); + CACHE_CLASS_AND_CHECK(Transform2D, GODOT_API_CLASS(Transform2D)); + CACHE_CLASS_AND_CHECK(Vector3, GODOT_API_CLASS(Vector3)); + CACHE_CLASS_AND_CHECK(Basis, GODOT_API_CLASS(Basis)); + CACHE_CLASS_AND_CHECK(Quat, GODOT_API_CLASS(Quat)); + CACHE_CLASS_AND_CHECK(Transform, GODOT_API_CLASS(Transform)); + CACHE_CLASS_AND_CHECK(AABB, GODOT_API_CLASS(AABB)); + CACHE_CLASS_AND_CHECK(Color, GODOT_API_CLASS(Color)); + CACHE_CLASS_AND_CHECK(Plane, GODOT_API_CLASS(Plane)); + CACHE_CLASS_AND_CHECK(NodePath, GODOT_API_CLASS(NodePath)); + CACHE_CLASS_AND_CHECK(RID, GODOT_API_CLASS(RID)); + 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(Spatial, GODOT_API_CLASS(Spatial)); + CACHE_CLASS_AND_CHECK(WeakRef, GODOT_API_CLASS(WeakRef)); + CACHE_CLASS_AND_CHECK(Array, GODOT_API_NS_CLASS(BINDINGS_NAMESPACE_COLLECTIONS, Array)); + CACHE_CLASS_AND_CHECK(Dictionary, GODOT_API_NS_CLASS(BINDINGS_NAMESPACE_COLLECTIONS, Dictionary)); + CACHE_CLASS_AND_CHECK(MarshalUtils, GODOT_API_CLASS(MarshalUtils)); + 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(SyncAttribute, GODOT_API_CLASS(SyncAttribute)); + CACHE_CLASS_AND_CHECK(MasterAttribute, GODOT_API_CLASS(MasterAttribute)); + CACHE_CLASS_AND_CHECK(PuppetAttribute, GODOT_API_CLASS(PuppetAttribute)); + CACHE_CLASS_AND_CHECK(SlaveAttribute, GODOT_API_CLASS(SlaveAttribute)); + CACHE_CLASS_AND_CHECK(RemoteSyncAttribute, GODOT_API_CLASS(RemoteSyncAttribute)); + CACHE_CLASS_AND_CHECK(MasterSyncAttribute, GODOT_API_CLASS(MasterSyncAttribute)); + CACHE_CLASS_AND_CHECK(PuppetSyncAttribute, GODOT_API_CLASS(PuppetSyncAttribute)); + CACHE_CLASS_AND_CHECK(GodotMethodAttribute, GODOT_API_CLASS(GodotMethodAttribute)); + CACHE_FIELD_AND_CHECK(GodotMethodAttribute, methodName, CACHED_CLASS(GodotMethodAttribute)->get_field("methodName")); + + CACHE_FIELD_AND_CHECK(GodotObject, ptr, CACHED_CLASS(GodotObject)->get_field(BINDINGS_PTR_FIELD)); + CACHE_FIELD_AND_CHECK(NodePath, ptr, CACHED_CLASS(NodePath)->get_field(BINDINGS_PTR_FIELD)); + CACHE_FIELD_AND_CHECK(RID, ptr, CACHED_CLASS(RID)->get_field(BINDINGS_PTR_FIELD)); + + CACHE_METHOD_THUNK_AND_CHECK(GodotObject, Dispose, CACHED_CLASS(GodotObject)->get_method("Dispose", 0)); + CACHE_METHOD_THUNK_AND_CHECK(Array, GetPtr, GODOT_API_NS_CLASS(BINDINGS_NAMESPACE_COLLECTIONS, Array)->get_method("GetPtr", 0)); + CACHE_METHOD_THUNK_AND_CHECK(Dictionary, GetPtr, GODOT_API_NS_CLASS(BINDINGS_NAMESPACE_COLLECTIONS, Dictionary)->get_method("GetPtr", 0)); + CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, SignalCallback, GODOT_API_CLASS(SignalAwaiter)->get_method("SignalCallback", 1)); + CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, FailureCallback, GODOT_API_CLASS(SignalAwaiter)->get_method("FailureCallback", 0)); + CACHE_METHOD_THUNK_AND_CHECK(GodotTaskScheduler, Activate, GODOT_API_CLASS(GodotTaskScheduler)->get_method("Activate", 0)); + + // Start of MarshalUtils methods + + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericArray, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericArray", 1)); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericDictionary, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericDictionary", 1)); + + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, ArrayGetElementType, GODOT_API_CLASS(MarshalUtils)->get_method("ArrayGetElementType", 2)); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, DictionaryGetKeyValueTypes, GODOT_API_CLASS(MarshalUtils)->get_method("DictionaryGetKeyValueTypes", 3)); + + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIEnumerableIsAssignableFromType, GODOT_API_CLASS(MarshalUtils)->get_method("GenericIEnumerableIsAssignableFromType", 1)); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIDictionaryIsAssignableFromType, GODOT_API_CLASS(MarshalUtils)->get_method("GenericIDictionaryIsAssignableFromType", 1)); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIEnumerableIsAssignableFromType_with_info, GODOT_API_CLASS(MarshalUtils)->get_method("GenericIEnumerableIsAssignableFromType", 2)); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIDictionaryIsAssignableFromType_with_info, GODOT_API_CLASS(MarshalUtils)->get_method("GenericIDictionaryIsAssignableFromType", 3)); + + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, MakeGenericArrayType, GODOT_API_CLASS(MarshalUtils)->get_method("MakeGenericArrayType", 1)); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, MakeGenericDictionaryType, GODOT_API_CLASS(MarshalUtils)->get_method("MakeGenericDictionaryType", 2)); + + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, EnumerableToArray, GODOT_API_CLASS(MarshalUtils)->get_method("EnumerableToArray", 2)); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, IDictionaryToDictionary, GODOT_API_CLASS(MarshalUtils)->get_method("IDictionaryToDictionary", 2)); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIDictionaryToDictionary, GODOT_API_CLASS(MarshalUtils)->get_method("GenericIDictionaryToDictionary", 2)); + + // End of MarshalUtils methods + +#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 = MonoGCHandle::create_strong(task_scheduler); + + cached_data.godot_api_cache_updated = true; +} + +} // namespace GDMonoCache diff --git a/modules/mono/mono_gd/gd_mono_cache.h b/modules/mono/mono_gd/gd_mono_cache.h new file mode 100644 index 0000000000..b21f92cdd8 --- /dev/null +++ b/modules/mono/mono_gd/gd_mono_cache.h @@ -0,0 +1,204 @@ +/*************************************************************************/ +/* gd_mono_cache.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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_CACHE_H +#define GD_MONO_CACHE_H + +#include "gd_mono_header.h" +#include "gd_mono_method_thunk.h" + +namespace GDMonoCache { + +struct CachedData { + + // ----------------------------------------------- + // corlib classes + + // Let's use the no-namespace format for these too + GDMonoClass *class_MonoObject; + GDMonoClass *class_bool; + GDMonoClass *class_int8_t; + GDMonoClass *class_int16_t; + GDMonoClass *class_int32_t; + GDMonoClass *class_int64_t; + GDMonoClass *class_uint8_t; + GDMonoClass *class_uint16_t; + GDMonoClass *class_uint32_t; + GDMonoClass *class_uint64_t; + GDMonoClass *class_float; + GDMonoClass *class_double; + GDMonoClass *class_String; + GDMonoClass *class_IntPtr; + + GDMonoClass *class_System_Collections_IEnumerable; + GDMonoClass *class_System_Collections_IDictionary; + +#ifdef DEBUG_ENABLED + GDMonoClass *class_System_Diagnostics_StackTrace; + GDMonoMethodThunkR<MonoArray *, MonoObject *> methodthunk_System_Diagnostics_StackTrace_GetFrames; + GDMonoMethod *method_System_Diagnostics_StackTrace_ctor_bool; + GDMonoMethod *method_System_Diagnostics_StackTrace_ctor_Exception_bool; +#endif + + GDMonoClass *class_KeyNotFoundException; + + MonoClass *rawclass_Dictionary; + // ----------------------------------------------- + + GDMonoClass *class_Vector2; + GDMonoClass *class_Rect2; + GDMonoClass *class_Transform2D; + GDMonoClass *class_Vector3; + GDMonoClass *class_Basis; + GDMonoClass *class_Quat; + GDMonoClass *class_Transform; + GDMonoClass *class_AABB; + GDMonoClass *class_Color; + GDMonoClass *class_Plane; + GDMonoClass *class_NodePath; + GDMonoClass *class_RID; + GDMonoClass *class_GodotObject; + GDMonoClass *class_GodotResource; + GDMonoClass *class_Node; + GDMonoClass *class_Control; + GDMonoClass *class_Spatial; + GDMonoClass *class_WeakRef; + GDMonoClass *class_Array; + GDMonoClass *class_Dictionary; + GDMonoClass *class_MarshalUtils; + GDMonoClass *class_ISerializationListener; + +#ifdef DEBUG_ENABLED + GDMonoClass *class_DebuggingUtils; + GDMonoMethodThunk<MonoObject *, MonoString **, int *, MonoString **> methodthunk_DebuggingUtils_GetStackFrameInfo; +#endif + + GDMonoClass *class_ExportAttribute; + GDMonoField *field_ExportAttribute_hint; + GDMonoField *field_ExportAttribute_hintString; + GDMonoClass *class_SignalAttribute; + GDMonoClass *class_ToolAttribute; + GDMonoClass *class_RemoteAttribute; + GDMonoClass *class_SyncAttribute; + GDMonoClass *class_RemoteSyncAttribute; + GDMonoClass *class_MasterSyncAttribute; + GDMonoClass *class_PuppetSyncAttribute; + GDMonoClass *class_MasterAttribute; + GDMonoClass *class_PuppetAttribute; + GDMonoClass *class_SlaveAttribute; + GDMonoClass *class_GodotMethodAttribute; + GDMonoField *field_GodotMethodAttribute_methodName; + + GDMonoField *field_GodotObject_ptr; + GDMonoField *field_NodePath_ptr; + GDMonoField *field_Image_ptr; + GDMonoField *field_RID_ptr; + + GDMonoMethodThunk<MonoObject *> methodthunk_GodotObject_Dispose; + GDMonoMethodThunkR<Array *, MonoObject *> methodthunk_Array_GetPtr; + GDMonoMethodThunkR<Dictionary *, MonoObject *> methodthunk_Dictionary_GetPtr; + GDMonoMethodThunk<MonoObject *, MonoArray *> methodthunk_SignalAwaiter_SignalCallback; + GDMonoMethodThunk<MonoObject *> methodthunk_SignalAwaiter_FailureCallback; + GDMonoMethodThunk<MonoObject *> methodthunk_GodotTaskScheduler_Activate; + + // Start of MarshalUtils methods + + GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericArray; + GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericDictionary; + + GDMonoMethodThunk<MonoReflectionType *, MonoReflectionType **> methodthunk_MarshalUtils_ArrayGetElementType; + GDMonoMethodThunk<MonoReflectionType *, MonoReflectionType **, MonoReflectionType **> methodthunk_MarshalUtils_DictionaryGetKeyValueTypes; + + GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType; + GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType; + GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *, MonoReflectionType **> methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType_with_info; + GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *, MonoReflectionType **, MonoReflectionType **> methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType_with_info; + + GDMonoMethodThunkR<MonoReflectionType *, MonoReflectionType *> methodthunk_MarshalUtils_MakeGenericArrayType; + GDMonoMethodThunkR<MonoReflectionType *, MonoReflectionType *, MonoReflectionType *> methodthunk_MarshalUtils_MakeGenericDictionaryType; + + GDMonoMethodThunk<MonoObject *, Array *> methodthunk_MarshalUtils_EnumerableToArray; + GDMonoMethodThunk<MonoObject *, Dictionary *> methodthunk_MarshalUtils_IDictionaryToDictionary; + GDMonoMethodThunk<MonoObject *, Dictionary *> methodthunk_MarshalUtils_GenericIDictionaryToDictionary; + + // End of MarshalUtils methods + + Ref<MonoGCHandle> task_scheduler_handle; + + 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(); + } +}; + +extern CachedData cached_data; + +void update_corlib_cache(); +void update_godot_api_cache(); + +inline void clear_corlib_cache() { + cached_data.clear_corlib_cache(); +} + +inline void clear_godot_api_cache() { + cached_data.clear_godot_api_cache(); +} + +_FORCE_INLINE_ bool tools_godot_api_check() { +#ifdef TOOLS_ENABLED + return cached_data.godot_api_cache_updated; +#else + return true; // Assume it's updated if this was called, otherwise it's a bug +#endif +} + +} // 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_RAW_MONO_CLASS(m_class) (GDMonoCache::cached_data.rawclass_##m_class) +#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) + +#ifdef REAL_T_IS_DOUBLE +#define REAL_T_MONOCLASS CACHED_CLASS_RAW(double) +#else +#define REAL_T_MONOCLASS CACHED_CLASS_RAW(float) +#endif + +#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 index 89a88fcfb2..fb9b6be3d4 100644 --- a/modules/mono/mono_gd/gd_mono_class.cpp +++ b/modules/mono/mono_gd/gd_mono_class.cpp @@ -33,6 +33,7 @@ #include <mono/metadata/attrdefs.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) { @@ -332,12 +333,6 @@ GDMonoMethod *GDMonoClass::get_method_with_desc(const String &p_description, boo return get_method(method); } -void *GDMonoClass::get_method_thunk(const StringName &p_name, int p_params_count) { - - GDMonoMethod *method = get_method(p_name, p_params_count); - return method ? method->get_thunk() : NULL; -} - GDMonoField *GDMonoClass::get_field(const StringName &p_name) { Map<StringName, GDMonoField *>::Element *result = fields.find(p_name); diff --git a/modules/mono/mono_gd/gd_mono_class.h b/modules/mono/mono_gd/gd_mono_class.h index 40e1574927..562c337822 100644 --- a/modules/mono/mono_gd/gd_mono_class.h +++ b/modules/mono/mono_gd/gd_mono_class.h @@ -144,8 +144,6 @@ public: GDMonoMethod *get_method(MonoMethod *p_raw_method, const StringName &p_name, int p_params_count); GDMonoMethod *get_method_with_desc(const String &p_description, bool p_include_namespace); - void *get_method_thunk(const StringName &p_name, int p_params_count = 0); - GDMonoField *get_field(const StringName &p_name); const Vector<GDMonoField *> &get_all_fields(); diff --git a/modules/mono/mono_gd/gd_mono_field.cpp b/modules/mono/mono_gd/gd_mono_field.cpp index 7b8e6f89e9..d84359b1ab 100644 --- a/modules/mono/mono_gd/gd_mono_field.cpp +++ b/modules/mono/mono_gd/gd_mono_field.cpp @@ -32,8 +32,10 @@ #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_raw(MonoObject *p_object, void *p_ptr) { mono_field_set_value(p_object, mono_field, &p_ptr); @@ -337,7 +339,7 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ } if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { - if (GDMonoUtils::tools_godot_api_check()) { + if (GDMonoCache::tools_godot_api_check()) { MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array)); mono_field_set_value(p_object, mono_field, managed); break; @@ -491,7 +493,7 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ } if (type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { - if (GDMonoUtils::tools_godot_api_check()) { + if (GDMonoCache::tools_godot_api_check()) { MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array)); mono_field_set_value(p_object, mono_field, managed); break; diff --git a/modules/mono/mono_gd/gd_mono_header.h b/modules/mono/mono_gd/gd_mono_header.h index d7962eac8b..173e1613bf 100644 --- a/modules/mono/mono_gd/gd_mono_header.h +++ b/modules/mono/mono_gd/gd_mono_header.h @@ -33,6 +33,12 @@ #include "core/int_types.h" +#ifdef WIN32 +#define GD_MONO_STDCALL __stdcall +#else +#define GD_MONO_STDCALL +#endif + class GDMonoAssembly; class GDMonoClass; class GDMonoField; diff --git a/modules/mono/mono_gd/gd_mono_log.cpp b/modules/mono/mono_gd/gd_mono_log.cpp index 7b3421fdb3..261b800619 100644 --- a/modules/mono/mono_gd/gd_mono_log.cpp +++ b/modules/mono/mono_gd/gd_mono_log.cpp @@ -30,7 +30,6 @@ #include "gd_mono_log.h" -#include <mono/utils/mono-logger.h> #include <stdlib.h> // abort #include "core/os/dir_access.h" @@ -39,7 +38,19 @@ #include "../godotsharp_dirs.h" #include "../utils/string_utils.h" -static int log_level_get_id(const char *p_log_level) { +static CharString get_default_log_level() { +#ifdef DEBUG_ENABLED + return String("info").utf8(); +#else + return String("warning").utf8(); +#endif +} + +GDMonoLog *GDMonoLog::singleton = NULL; + +#if !defined(JAVASCRIPT_ENABLED) + +static int get_log_level_id(const char *p_log_level) { const char *valid_log_levels[] = { "error", "critical", "warning", "message", "info", "debug", NULL }; @@ -53,11 +64,11 @@ static int log_level_get_id(const char *p_log_level) { return -1; } -static void mono_log_callback(const char *log_domain, const char *log_level, const char *message, mono_bool fatal, void *user_data) { +void GDMonoLog::mono_log_callback(const char *log_domain, const char *log_level, const char *message, mono_bool fatal, void *) { - FileAccess *f = GDMonoLog::get_singleton()->get_log_file(); + FileAccess *f = GDMonoLog::get_singleton()->log_file; - if (GDMonoLog::get_singleton()->get_log_level_id() >= log_level_get_id(log_level)) { + if (GDMonoLog::get_singleton()->log_level_id >= get_log_level_id(log_level)) { String text(message); text += " (in domain "; text += log_domain; @@ -72,7 +83,7 @@ static void mono_log_callback(const char *log_domain, const char *log_level, con } if (fatal) { - ERR_PRINTS("Mono: FATAL ERROR, ABORTING! Logfile: '" + GDMonoLog::get_singleton()->get_log_file_path() + "'."); + ERR_PRINTS("Mono: FATAL ERROR, ABORTING! Logfile: '" + GDMonoLog::get_singleton()->log_file_path + "'."); // Make sure to flush before aborting f->flush(); f->close(); @@ -82,8 +93,6 @@ static void mono_log_callback(const char *log_domain, const char *log_level, con } } -GDMonoLog *GDMonoLog::singleton = NULL; - bool GDMonoLog::_try_create_logs_dir(const String &p_logs_dir) { if (!DirAccess::exists(p_logs_dir)) { @@ -129,17 +138,13 @@ void GDMonoLog::initialize() { CharString log_level = OS::get_singleton()->get_environment("GODOT_MONO_LOG_LEVEL").utf8(); - if (log_level.length() != 0 && log_level_get_id(log_level.get_data()) == -1) { + if (log_level.length() != 0 && get_log_level_id(log_level.get_data()) == -1) { ERR_PRINTS(String() + "Mono: Ignoring invalid log level (GODOT_MONO_LOG_LEVEL): '" + log_level.get_data() + "'."); log_level = CharString(); } if (log_level.length() == 0) { -#ifdef DEBUG_ENABLED - log_level = String("info").utf8(); -#else - log_level = String("warning").utf8(); -#endif + log_level = get_default_log_level(); } String logs_dir = GodotSharpDirs::get_mono_logs_dir(); @@ -149,11 +154,14 @@ void GDMonoLog::initialize() { OS::Date date_now = OS::get_singleton()->get_date(); OS::Time time_now = OS::get_singleton()->get_time(); - int pid = OS::get_singleton()->get_process_id(); - String log_file_name = str_format("%d_%02d_%02d %02d.%02d.%02d (%d).txt", + String log_file_name = str_format("%d_%02d_%02d %02d.%02d.%02d", date_now.year, date_now.month, date_now.day, - time_now.hour, time_now.min, time_now.sec, pid); + time_now.hour, time_now.min, time_now.sec); + + log_file_name += str_format(" (%d)", OS::get_singleton()->get_process_id()); + + log_file_name += ".txt"; log_file_path = logs_dir.plus_file(log_file_name); @@ -164,7 +172,7 @@ void GDMonoLog::initialize() { } mono_trace_set_level_string(log_level.get_data()); - log_level_id = log_level_get_id(log_level.get_data()); + log_level_id = get_log_level_id(log_level.get_data()); if (log_file) { OS::get_singleton()->print("Mono: Logfile is: %s\n", log_file_path.utf8().get_data()); @@ -190,3 +198,22 @@ GDMonoLog::~GDMonoLog() { memdelete(log_file); } } + +#else + +void GDMonoLog::initialize() { + CharString log_level = get_default_log_level(); + mono_trace_set_level_string(log_level.get_data()); +} + +GDMonoLog::GDMonoLog() { + + singleton = this; +} + +GDMonoLog::~GDMonoLog() { + + singleton = NULL; +} + +#endif // !defined(JAVASCRIPT_ENABLED) diff --git a/modules/mono/mono_gd/gd_mono_log.h b/modules/mono/mono_gd/gd_mono_log.h index 91d0557aa3..4cd5a662fb 100644 --- a/modules/mono/mono_gd/gd_mono_log.h +++ b/modules/mono/mono_gd/gd_mono_log.h @@ -31,10 +31,17 @@ #ifndef GD_MONO_LOG_H #define GD_MONO_LOG_H +#include <mono/utils/mono-logger.h> + +#include "core/typedefs.h" + +#if !defined(JAVASCRIPT_ENABLED) #include "core/os/file_access.h" +#endif class GDMonoLog { +#if !defined(JAVASCRIPT_ENABLED) int log_level_id; FileAccess *log_file; @@ -43,6 +50,9 @@ class GDMonoLog { bool _try_create_logs_dir(const String &p_logs_dir); void _delete_old_log_files(const String &p_logs_dir); + static void mono_log_callback(const char *log_domain, const char *log_level, const char *message, mono_bool fatal, void *user_data); +#endif + static GDMonoLog *singleton; public: @@ -50,10 +60,6 @@ public: void initialize(); - _FORCE_INLINE_ FileAccess *get_log_file() { return log_file; } - _FORCE_INLINE_ String get_log_file_path() { return log_file_path; } - _FORCE_INLINE_ int get_log_level_id() { return log_level_id; } - GDMonoLog(); ~GDMonoLog(); }; diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp index 7aac691102..3827960e6b 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.cpp +++ b/modules/mono/mono_gd/gd_mono_marshal.cpp @@ -31,6 +31,7 @@ #include "gd_mono_marshal.h" #include "gd_mono.h" +#include "gd_mono_cache.h" #include "gd_mono_class.h" namespace GDMonoMarshal { @@ -556,7 +557,7 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty } if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { - if (GDMonoUtils::tools_godot_api_check()) { + if (GDMonoCache::tools_godot_api_check()) { return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array)); } else { return (MonoObject *)GDMonoMarshal::Array_to_mono_array(p_var->operator Array()); @@ -683,7 +684,7 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty } if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { - if (GDMonoUtils::tools_godot_api_check()) { + if (GDMonoCache::tools_godot_api_check()) { return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array)); } else { return (MonoObject *)GDMonoMarshal::Array_to_mono_array(p_var->operator Array()); @@ -834,14 +835,14 @@ Variant mono_object_to_variant(MonoObject *p_obj) { if (CACHED_CLASS(Array) == type_class) { MonoException *exc = NULL; - Array *ptr = invoke_method_thunk(CACHED_METHOD_THUNK(Array, GetPtr), p_obj, &exc); + Array *ptr = CACHED_METHOD_THUNK(Array, GetPtr).invoke(p_obj, &exc); UNHANDLED_EXCEPTION(exc); return ptr ? Variant(*ptr) : Variant(); } if (CACHED_CLASS(Dictionary) == type_class) { MonoException *exc = NULL; - Dictionary *ptr = invoke_method_thunk(CACHED_METHOD_THUNK(Dictionary, GetPtr), p_obj, &exc); + Dictionary *ptr = CACHED_METHOD_THUNK(Dictionary, GetPtr).invoke(p_obj, &exc); UNHANDLED_EXCEPTION(exc); return ptr ? Variant(*ptr) : Variant(); } diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h index 3fa958ac32..53eae45320 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.h +++ b/modules/mono/mono_gd/gd_mono_marshal.h @@ -43,6 +43,11 @@ T unbox(MonoObject *p_obj) { return *(T *)mono_object_unbox(p_obj); } +template <typename T> +T *unbox_addr(MonoObject *p_obj) { + return (T *)mono_object_unbox(p_obj); +} + #define BOX_DOUBLE(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(double), &x) #define BOX_FLOAT(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(float), &x) #define BOX_INT64(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(int64_t), &x) diff --git a/modules/mono/mono_gd/gd_mono_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp index 968b316a3e..080b3a676a 100644 --- a/modules/mono/mono_gd/gd_mono_method.cpp +++ b/modules/mono/mono_gd/gd_mono_method.cpp @@ -30,8 +30,10 @@ #include "gd_mono_method.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> @@ -99,10 +101,6 @@ IMonoClassMember::Visibility GDMonoMethod::get_visibility() { } } -void *GDMonoMethod::get_thunk() { - return mono_method_get_unmanaged_thunk(mono_method); -} - MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc) { if (get_return_type().type_encoding != MONO_TYPE_VOID || get_parameters_count() > 0) { MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), get_parameters_count()); diff --git a/modules/mono/mono_gd/gd_mono_method.h b/modules/mono/mono_gd/gd_mono_method.h index 2fc8628f27..abbae92e29 100644 --- a/modules/mono/mono_gd/gd_mono_method.h +++ b/modules/mono/mono_gd/gd_mono_method.h @@ -71,11 +71,11 @@ public: virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) GD_FINAL; void fetch_attributes(); + _FORCE_INLINE_ MonoMethod *get_mono_ptr() { return mono_method; } + _FORCE_INLINE_ int get_parameters_count() { return params_count; } _FORCE_INLINE_ ManagedType get_return_type() { return return_type; } - void *get_thunk(); - MonoObject *invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc = NULL); MonoObject *invoke(MonoObject *p_object, MonoException **r_exc = NULL); MonoObject *invoke_raw(MonoObject *p_object, void **p_params, MonoException **r_exc = NULL); diff --git a/modules/mono/mono_gd/gd_mono_method_thunk.h b/modules/mono/mono_gd/gd_mono_method_thunk.h new file mode 100644 index 0000000000..f8cc736ec3 --- /dev/null +++ b/modules/mono/mono_gd/gd_mono_method_thunk.h @@ -0,0 +1,332 @@ +/*************************************************************************/ +/* gd_mono_method_thunk.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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_THUNK_H +#define GD_MONO_METHOD_THUNK_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 "gd_mono_utils.h" + +#if !defined(JAVASCRIPT_ENABLED) +#define HAVE_METHOD_THUNKS +#endif + +#ifdef HAVE_METHOD_THUNKS + +template <class... ParamTypes> +struct GDMonoMethodThunk { + + typedef void(GD_MONO_STDCALL *M)(ParamTypes... p_args, MonoException **); + + M mono_method_thunk; + +public: + _FORCE_INLINE_ void invoke(ParamTypes... p_args, MonoException **r_exc) { + GD_MONO_BEGIN_RUNTIME_INVOKE; + mono_method_thunk(p_args..., r_exc); + GD_MONO_END_RUNTIME_INVOKE; + } + + _FORCE_INLINE_ bool is_null() { + return mono_method_thunk == NULL; + } + + _FORCE_INLINE_ void nullify() { + mono_method_thunk = NULL; + } + + _FORCE_INLINE_ void set_from_method(GDMonoMethod *p_mono_method) { +#ifdef DEBUG_ENABLED + CRASH_COND(p_mono_method == NULL); + 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()); + } + + GDMonoMethodThunk() : + mono_method_thunk(NULL) { + } + + explicit GDMonoMethodThunk(GDMonoMethod *p_mono_method) { + set_from_method(p_mono_method); + } +}; + +template <class R, class... ParamTypes> +struct GDMonoMethodThunkR { + + typedef R(GD_MONO_STDCALL *M)(ParamTypes... p_args, MonoException **); + + M mono_method_thunk; + +public: + _FORCE_INLINE_ R invoke(ParamTypes... p_args, MonoException **r_exc) { + GD_MONO_BEGIN_RUNTIME_INVOKE; + R r = mono_method_thunk(p_args..., r_exc); + GD_MONO_END_RUNTIME_INVOKE; + return r; + } + + _FORCE_INLINE_ bool is_null() { + return mono_method_thunk == NULL; + } + + _FORCE_INLINE_ void nullify() { + mono_method_thunk = NULL; + } + + _FORCE_INLINE_ void set_from_method(GDMonoMethod *p_mono_method) { +#ifdef DEBUG_ENABLED + CRASH_COND(p_mono_method == NULL); + 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() : + mono_method_thunk(NULL) { + } + + explicit GDMonoMethodThunkR(GDMonoMethod *p_mono_method) { +#ifdef DEBUG_ENABLED + CRASH_COND(p_mono_method == NULL); +#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(NULL, 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(NULL, NULL, 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(NULL, args, r_exc); + } else { + p_mono_method->invoke_raw((MonoObject *)p_arg1, NULL, 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(NULL, 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(NULL, NULL, 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(NULL, 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, NULL, r_exc); + return unbox_if_needed<R>(r, p_mono_method->get_return_type()); + } + } +}; + +template <class... ParamTypes> +struct GDMonoMethodThunk { + + GDMonoMethod *mono_method; + +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 == NULL; + } + + _FORCE_INLINE_ void nullify() { + mono_method = NULL; + } + + _FORCE_INLINE_ void set_from_method(GDMonoMethod *p_mono_method) { +#ifdef DEBUG_ENABLED + CRASH_COND(p_mono_method == NULL); + 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() : + mono_method(NULL) { + } + + explicit GDMonoMethodThunk(GDMonoMethod *p_mono_method) { + set_from_method(p_mono_method); + } +}; + +template <class R, class... ParamTypes> +struct GDMonoMethodThunkR { + + GDMonoMethod *mono_method; + +public: + _FORCE_INLINE_ R invoke(ParamTypes... p_args, MonoException **r_exc) { + return VariadicInvokeMonoMethodR<sizeof...(ParamTypes), R, ParamTypes...>::invoke(mono_method, p_args..., r_exc); + } + + _FORCE_INLINE_ bool is_null() { + return mono_method == NULL; + } + + _FORCE_INLINE_ void nullify() { + mono_method = NULL; + } + + _FORCE_INLINE_ void set_from_method(GDMonoMethod *p_mono_method) { +#ifdef DEBUG_ENABLED + CRASH_COND(p_mono_method == NULL); + 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; + } + + GDMonoMethodThunkR() : + mono_method(NULL) { + } + + 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 index f1da00638f..277fe10087 100644 --- a/modules/mono/mono_gd/gd_mono_property.cpp +++ b/modules/mono/mono_gd/gd_mono_property.cpp @@ -30,8 +30,10 @@ #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> diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp index 6504fbe423..8d7aaa97f2 100644 --- a/modules/mono/mono_gd/gd_mono_utils.cpp +++ b/modules/mono/mono_gd/gd_mono_utils.cpp @@ -45,273 +45,13 @@ #include "../utils/macros.h" #include "../utils/mutex_utils.h" #include "gd_mono.h" +#include "gd_mono_cache.h" #include "gd_mono_class.h" #include "gd_mono_marshal.h" +#include "gd_mono_method_thunk.h" namespace GDMonoUtils { -MonoCache mono_cache; - -#define CACHE_AND_CHECK(m_var, m_val) \ - { \ - CRASH_COND(m_var != NULL); \ - m_var = m_val; \ - ERR_FAIL_COND_MSG(!m_var, "Mono Cache: Member " #m_var " is null."); \ - } - -#define CACHE_CLASS_AND_CHECK(m_class, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.class_##m_class, m_val) -#define CACHE_NS_CLASS_AND_CHECK(m_ns, m_class, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.class_##m_ns##_##m_class, m_val) -#define CACHE_RAW_MONO_CLASS_AND_CHECK(m_class, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.rawclass_##m_class, m_val) -#define CACHE_FIELD_AND_CHECK(m_class, m_field, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.field_##m_class##_##m_field, m_val) -#define CACHE_METHOD_AND_CHECK(m_class, m_method, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.method_##m_class##_##m_method, m_val) -#define CACHE_METHOD_THUNK_AND_CHECK(m_class, m_method, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.methodthunk_##m_class##_##m_method, m_val) -#define CACHE_PROPERTY_AND_CHECK(m_class, m_property, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.property_##m_class##_##m_property, m_val) - -void MonoCache::clear_corlib_cache() { - - corlib_cache_updated = false; - - class_MonoObject = NULL; - class_bool = NULL; - class_int8_t = NULL; - class_int16_t = NULL; - class_int32_t = NULL; - class_int64_t = NULL; - class_uint8_t = NULL; - class_uint16_t = NULL; - class_uint32_t = NULL; - class_uint64_t = NULL; - class_float = NULL; - class_double = NULL; - class_String = NULL; - class_IntPtr = NULL; - - class_System_Collections_IEnumerable = NULL; - class_System_Collections_IDictionary = NULL; - -#ifdef DEBUG_ENABLED - class_System_Diagnostics_StackTrace = NULL; - methodthunk_System_Diagnostics_StackTrace_GetFrames = NULL; - method_System_Diagnostics_StackTrace_ctor_bool = NULL; - method_System_Diagnostics_StackTrace_ctor_Exception_bool = NULL; -#endif - - class_KeyNotFoundException = NULL; -} - -void MonoCache::clear_godot_api_cache() { - - godot_api_cache_updated = false; - - rawclass_Dictionary = NULL; - - class_Vector2 = NULL; - class_Rect2 = NULL; - class_Transform2D = NULL; - class_Vector3 = NULL; - class_Basis = NULL; - class_Quat = NULL; - class_Transform = NULL; - class_AABB = NULL; - class_Color = NULL; - class_Plane = NULL; - class_NodePath = NULL; - class_RID = NULL; - class_GodotObject = NULL; - class_GodotResource = NULL; - class_Node = NULL; - class_Control = NULL; - class_Spatial = NULL; - class_WeakRef = NULL; - class_Array = NULL; - class_Dictionary = NULL; - class_MarshalUtils = NULL; - class_ISerializationListener = NULL; - -#ifdef DEBUG_ENABLED - class_DebuggingUtils = NULL; - methodthunk_DebuggingUtils_GetStackFrameInfo = NULL; -#endif - - class_ExportAttribute = NULL; - field_ExportAttribute_hint = NULL; - field_ExportAttribute_hintString = NULL; - class_SignalAttribute = NULL; - class_ToolAttribute = NULL; - class_RemoteAttribute = NULL; - class_SyncAttribute = NULL; - class_MasterAttribute = NULL; - class_PuppetAttribute = NULL; - class_SlaveAttribute = NULL; - class_RemoteSyncAttribute = NULL; - class_MasterSyncAttribute = NULL; - class_PuppetSyncAttribute = NULL; - class_GodotMethodAttribute = NULL; - field_GodotMethodAttribute_methodName = NULL; - - field_GodotObject_ptr = NULL; - field_NodePath_ptr = NULL; - field_Image_ptr = NULL; - field_RID_ptr = NULL; - - methodthunk_GodotObject_Dispose = NULL; - methodthunk_Array_GetPtr = NULL; - methodthunk_Dictionary_GetPtr = NULL; - methodthunk_SignalAwaiter_SignalCallback = NULL; - methodthunk_SignalAwaiter_FailureCallback = NULL; - methodthunk_GodotTaskScheduler_Activate = NULL; - - // Start of MarshalUtils methods - - methodthunk_MarshalUtils_TypeIsGenericArray = NULL; - methodthunk_MarshalUtils_TypeIsGenericDictionary = NULL; - - methodthunk_MarshalUtils_ArrayGetElementType = NULL; - methodthunk_MarshalUtils_DictionaryGetKeyValueTypes = NULL; - - methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType = NULL; - methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType = NULL; - methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType_with_info = NULL; - methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType_with_info = NULL; - - methodthunk_MarshalUtils_MakeGenericArrayType = NULL; - methodthunk_MarshalUtils_MakeGenericDictionaryType = NULL; - - methodthunk_MarshalUtils_EnumerableToArray = NULL; - methodthunk_MarshalUtils_IDictionaryToDictionary = NULL; - methodthunk_MarshalUtils_GenericIDictionaryToDictionary = NULL; - - // End of MarshalUtils methods - - task_scheduler_handle = Ref<MonoGCHandle>(); -} - -#define GODOT_API_CLASS(m_class) (GDMono::get_singleton()->get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, #m_class)) -#define GODOT_API_NS_CLAS(m_ns, m_class) (GDMono::get_singleton()->get_core_api_assembly()->get_class(m_ns, #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(bool, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_boolean_class())); - CACHE_CLASS_AND_CHECK(int8_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_sbyte_class())); - CACHE_CLASS_AND_CHECK(int16_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_int16_class())); - CACHE_CLASS_AND_CHECK(int32_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_int32_class())); - CACHE_CLASS_AND_CHECK(int64_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_int64_class())); - CACHE_CLASS_AND_CHECK(uint8_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_byte_class())); - CACHE_CLASS_AND_CHECK(uint16_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_uint16_class())); - CACHE_CLASS_AND_CHECK(uint32_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_uint32_class())); - CACHE_CLASS_AND_CHECK(uint64_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_uint64_class())); - CACHE_CLASS_AND_CHECK(float, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_single_class())); - CACHE_CLASS_AND_CHECK(double, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_double_class())); - CACHE_CLASS_AND_CHECK(String, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_string_class())); - CACHE_CLASS_AND_CHECK(IntPtr, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_intptr_class())); - - CACHE_CLASS_AND_CHECK(System_Collections_IEnumerable, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections", "IEnumerable")); - CACHE_CLASS_AND_CHECK(System_Collections_IDictionary, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections", "IDictionary")); - -#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, (StackTrace_GetFrames)CACHED_CLASS(System_Diagnostics_StackTrace)->get_method_thunk("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_CLASS_AND_CHECK(KeyNotFoundException, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections.Generic", "KeyNotFoundException")); - - mono_cache.corlib_cache_updated = true; -} - -void update_godot_api_cache() { - - CACHE_CLASS_AND_CHECK(Vector2, GODOT_API_CLASS(Vector2)); - CACHE_CLASS_AND_CHECK(Rect2, GODOT_API_CLASS(Rect2)); - CACHE_CLASS_AND_CHECK(Transform2D, GODOT_API_CLASS(Transform2D)); - CACHE_CLASS_AND_CHECK(Vector3, GODOT_API_CLASS(Vector3)); - CACHE_CLASS_AND_CHECK(Basis, GODOT_API_CLASS(Basis)); - CACHE_CLASS_AND_CHECK(Quat, GODOT_API_CLASS(Quat)); - CACHE_CLASS_AND_CHECK(Transform, GODOT_API_CLASS(Transform)); - CACHE_CLASS_AND_CHECK(AABB, GODOT_API_CLASS(AABB)); - CACHE_CLASS_AND_CHECK(Color, GODOT_API_CLASS(Color)); - CACHE_CLASS_AND_CHECK(Plane, GODOT_API_CLASS(Plane)); - CACHE_CLASS_AND_CHECK(NodePath, GODOT_API_CLASS(NodePath)); - CACHE_CLASS_AND_CHECK(RID, GODOT_API_CLASS(RID)); - 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(Spatial, GODOT_API_CLASS(Spatial)); - CACHE_CLASS_AND_CHECK(WeakRef, GODOT_API_CLASS(WeakRef)); - CACHE_CLASS_AND_CHECK(Array, GODOT_API_NS_CLAS(BINDINGS_NAMESPACE_COLLECTIONS, Array)); - CACHE_CLASS_AND_CHECK(Dictionary, GODOT_API_NS_CLAS(BINDINGS_NAMESPACE_COLLECTIONS, Dictionary)); - CACHE_CLASS_AND_CHECK(MarshalUtils, GODOT_API_CLASS(MarshalUtils)); - 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(SyncAttribute, GODOT_API_CLASS(SyncAttribute)); - CACHE_CLASS_AND_CHECK(MasterAttribute, GODOT_API_CLASS(MasterAttribute)); - CACHE_CLASS_AND_CHECK(PuppetAttribute, GODOT_API_CLASS(PuppetAttribute)); - CACHE_CLASS_AND_CHECK(SlaveAttribute, GODOT_API_CLASS(SlaveAttribute)); - CACHE_CLASS_AND_CHECK(RemoteSyncAttribute, GODOT_API_CLASS(RemoteSyncAttribute)); - CACHE_CLASS_AND_CHECK(MasterSyncAttribute, GODOT_API_CLASS(MasterSyncAttribute)); - CACHE_CLASS_AND_CHECK(PuppetSyncAttribute, GODOT_API_CLASS(PuppetSyncAttribute)); - CACHE_CLASS_AND_CHECK(GodotMethodAttribute, GODOT_API_CLASS(GodotMethodAttribute)); - CACHE_FIELD_AND_CHECK(GodotMethodAttribute, methodName, CACHED_CLASS(GodotMethodAttribute)->get_field("methodName")); - - CACHE_FIELD_AND_CHECK(GodotObject, ptr, CACHED_CLASS(GodotObject)->get_field(BINDINGS_PTR_FIELD)); - CACHE_FIELD_AND_CHECK(NodePath, ptr, CACHED_CLASS(NodePath)->get_field(BINDINGS_PTR_FIELD)); - CACHE_FIELD_AND_CHECK(RID, ptr, CACHED_CLASS(RID)->get_field(BINDINGS_PTR_FIELD)); - - CACHE_METHOD_THUNK_AND_CHECK(GodotObject, Dispose, (GodotObject_Dispose)CACHED_CLASS(GodotObject)->get_method_thunk("Dispose", 0)); - CACHE_METHOD_THUNK_AND_CHECK(Array, GetPtr, (Array_GetPtr)GODOT_API_NS_CLAS(BINDINGS_NAMESPACE_COLLECTIONS, Array)->get_method_thunk("GetPtr", 0)); - CACHE_METHOD_THUNK_AND_CHECK(Dictionary, GetPtr, (Dictionary_GetPtr)GODOT_API_NS_CLAS(BINDINGS_NAMESPACE_COLLECTIONS, Dictionary)->get_method_thunk("GetPtr", 0)); - CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, SignalCallback, (SignalAwaiter_SignalCallback)GODOT_API_CLASS(SignalAwaiter)->get_method_thunk("SignalCallback", 1)); - CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, FailureCallback, (SignalAwaiter_FailureCallback)GODOT_API_CLASS(SignalAwaiter)->get_method_thunk("FailureCallback", 0)); - CACHE_METHOD_THUNK_AND_CHECK(GodotTaskScheduler, Activate, (GodotTaskScheduler_Activate)GODOT_API_CLASS(GodotTaskScheduler)->get_method_thunk("Activate", 0)); - - // Start of MarshalUtils methods - - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericArray, (TypeIsGenericArray)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("TypeIsGenericArray", 1)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericDictionary, (TypeIsGenericDictionary)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("TypeIsGenericDictionary", 1)); - - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, ArrayGetElementType, (ArrayGetElementType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("ArrayGetElementType", 2)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, DictionaryGetKeyValueTypes, (DictionaryGetKeyValueTypes)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("DictionaryGetKeyValueTypes", 3)); - - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIEnumerableIsAssignableFromType, (GenericIEnumerableIsAssignableFromType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIEnumerableIsAssignableFromType", 1)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIDictionaryIsAssignableFromType, (GenericIDictionaryIsAssignableFromType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIDictionaryIsAssignableFromType", 1)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIEnumerableIsAssignableFromType_with_info, (GenericIEnumerableIsAssignableFromType_with_info)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIEnumerableIsAssignableFromType", 2)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIDictionaryIsAssignableFromType_with_info, (GenericIDictionaryIsAssignableFromType_with_info)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIDictionaryIsAssignableFromType", 3)); - - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, MakeGenericArrayType, (MakeGenericArrayType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("MakeGenericArrayType", 1)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, MakeGenericDictionaryType, (MakeGenericDictionaryType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("MakeGenericDictionaryType", 2)); - - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, EnumerableToArray, (EnumerableToArray)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("EnumerableToArray", 2)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, IDictionaryToDictionary, (IDictionaryToDictionary)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("IDictionaryToDictionary", 2)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIDictionaryToDictionary, (GenericIDictionaryToDictionary)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIDictionaryToDictionary", 2)); - - // End of MarshalUtils methods - -#ifdef DEBUG_ENABLED - CACHE_METHOD_THUNK_AND_CHECK(DebuggingUtils, GetStackFrameInfo, (DebugUtils_StackFrameInfo)GODOT_API_CLASS(DebuggingUtils)->get_method_thunk("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)); - mono_cache.task_scheduler_handle = MonoGCHandle::create_strong(task_scheduler); - - mono_cache.godot_api_cache_updated = true; -} - MonoObject *unmanaged_get_managed(Object *unmanaged) { if (!unmanaged) @@ -386,7 +126,7 @@ void set_main_thread(MonoThread *p_thread) { void attach_current_thread() { ERR_FAIL_COND(!GDMono::get_singleton()->is_runtime_initialized()); - MonoThread *mono_thread = mono_thread_attach(mono_domain_get()); + MonoThread *mono_thread = mono_thread_attach(mono_get_root_domain()); ERR_FAIL_NULL(mono_thread); } @@ -421,7 +161,7 @@ GDMonoClass *type_get_proxy_class(const StringName &p_type) { 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 mono_cache.class_GodotObject; + return GDMonoCache::cached_data.class_GodotObject; } #ifdef TOOLS_ENABLED @@ -751,16 +491,16 @@ uint64_t unbox_enum_value(MonoObject *p_boxed, MonoType *p_enum_basetype, bool & } void dispose(MonoObject *p_mono_object, MonoException **r_exc) { - invoke_method_thunk(CACHED_METHOD_THUNK(GodotObject, Dispose), p_mono_object, 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 (!mono_cache.godot_api_cache_updated) return m_ret; \ +#define NO_GLUE_RET(m_ret) \ + { \ + if (!GDMonoCache::cached_data.godot_api_cache_updated) return m_ret; \ } #else #define NO_GLUE_RET(m_ret) \ @@ -773,68 +513,60 @@ namespace Marshal { bool type_is_generic_array(MonoReflectionType *p_reftype) { NO_GLUE_RET(false); - TypeIsGenericArray thunk = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray); MonoException *exc = NULL; - MonoBoolean res = invoke_method_thunk(thunk, p_reftype, &exc); + MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray).invoke(p_reftype, &exc); UNHANDLED_EXCEPTION(exc); return (bool)res; } bool type_is_generic_dictionary(MonoReflectionType *p_reftype) { NO_GLUE_RET(false); - TypeIsGenericDictionary thunk = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary); MonoException *exc = NULL; - MonoBoolean res = invoke_method_thunk(thunk, p_reftype, &exc); + MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary).invoke(p_reftype, &exc); UNHANDLED_EXCEPTION(exc); return (bool)res; } void array_get_element_type(MonoReflectionType *p_array_reftype, MonoReflectionType **r_elem_reftype) { - ArrayGetElementType thunk = CACHED_METHOD_THUNK(MarshalUtils, ArrayGetElementType); MonoException *exc = NULL; - invoke_method_thunk(thunk, p_array_reftype, r_elem_reftype, &exc); + CACHED_METHOD_THUNK(MarshalUtils, ArrayGetElementType).invoke(p_array_reftype, r_elem_reftype, &exc); UNHANDLED_EXCEPTION(exc); } void dictionary_get_key_value_types(MonoReflectionType *p_dict_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype) { - DictionaryGetKeyValueTypes thunk = CACHED_METHOD_THUNK(MarshalUtils, DictionaryGetKeyValueTypes); MonoException *exc = NULL; - invoke_method_thunk(thunk, p_dict_reftype, r_key_reftype, r_value_reftype, &exc); + CACHED_METHOD_THUNK(MarshalUtils, DictionaryGetKeyValueTypes).invoke(p_dict_reftype, r_key_reftype, r_value_reftype, &exc); UNHANDLED_EXCEPTION(exc); } bool generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype) { NO_GLUE_RET(false); - GenericIEnumerableIsAssignableFromType thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIEnumerableIsAssignableFromType); MonoException *exc = NULL; - MonoBoolean res = invoke_method_thunk(thunk, p_reftype, &exc); + MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, GenericIEnumerableIsAssignableFromType).invoke(p_reftype, &exc); UNHANDLED_EXCEPTION(exc); return (bool)res; } bool generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype) { NO_GLUE_RET(false); - GenericIDictionaryIsAssignableFromType thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryIsAssignableFromType); MonoException *exc = NULL; - MonoBoolean res = invoke_method_thunk(thunk, p_reftype, &exc); + MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryIsAssignableFromType).invoke(p_reftype, &exc); UNHANDLED_EXCEPTION(exc); return (bool)res; } bool generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_elem_reftype) { NO_GLUE_RET(false); - GenericIEnumerableIsAssignableFromType_with_info thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIEnumerableIsAssignableFromType_with_info); MonoException *exc = NULL; - MonoBoolean res = invoke_method_thunk(thunk, p_reftype, r_elem_reftype, &exc); + MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, GenericIEnumerableIsAssignableFromType_with_info).invoke(p_reftype, r_elem_reftype, &exc); UNHANDLED_EXCEPTION(exc); return (bool)res; } bool generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype) { NO_GLUE_RET(false); - GenericIDictionaryIsAssignableFromType_with_info thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryIsAssignableFromType_with_info); MonoException *exc = NULL; - MonoBoolean res = invoke_method_thunk(thunk, p_reftype, r_key_reftype, r_value_reftype, &exc); + MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryIsAssignableFromType_with_info).invoke(p_reftype, r_key_reftype, r_value_reftype, &exc); UNHANDLED_EXCEPTION(exc); return (bool)res; } @@ -842,9 +574,8 @@ bool generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype, MonoR Array enumerable_to_array(MonoObject *p_enumerable) { NO_GLUE_RET(Array()); Array result; - EnumerableToArray thunk = CACHED_METHOD_THUNK(MarshalUtils, EnumerableToArray); MonoException *exc = NULL; - invoke_method_thunk(thunk, p_enumerable, &result, &exc); + CACHED_METHOD_THUNK(MarshalUtils, EnumerableToArray).invoke(p_enumerable, &result, &exc); UNHANDLED_EXCEPTION(exc); return result; } @@ -852,9 +583,8 @@ Array enumerable_to_array(MonoObject *p_enumerable) { Dictionary idictionary_to_dictionary(MonoObject *p_idictionary) { NO_GLUE_RET(Dictionary()); Dictionary result; - IDictionaryToDictionary thunk = CACHED_METHOD_THUNK(MarshalUtils, IDictionaryToDictionary); MonoException *exc = NULL; - invoke_method_thunk(thunk, p_idictionary, &result, &exc); + CACHED_METHOD_THUNK(MarshalUtils, IDictionaryToDictionary).invoke(p_idictionary, &result, &exc); UNHANDLED_EXCEPTION(exc); return result; } @@ -862,27 +592,24 @@ Dictionary idictionary_to_dictionary(MonoObject *p_idictionary) { Dictionary generic_idictionary_to_dictionary(MonoObject *p_generic_idictionary) { NO_GLUE_RET(Dictionary()); Dictionary result; - GenericIDictionaryToDictionary thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryToDictionary); MonoException *exc = NULL; - invoke_method_thunk(thunk, p_generic_idictionary, &result, &exc); + CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryToDictionary).invoke(p_generic_idictionary, &result, &exc); UNHANDLED_EXCEPTION(exc); return result; } GDMonoClass *make_generic_array_type(MonoReflectionType *p_elem_reftype) { NO_GLUE_RET(NULL); - MakeGenericArrayType thunk = CACHED_METHOD_THUNK(MarshalUtils, MakeGenericArrayType); MonoException *exc = NULL; - MonoReflectionType *reftype = invoke_method_thunk(thunk, p_elem_reftype, &exc); + MonoReflectionType *reftype = CACHED_METHOD_THUNK(MarshalUtils, MakeGenericArrayType).invoke(p_elem_reftype, &exc); UNHANDLED_EXCEPTION(exc); return GDMono::get_singleton()->get_class(mono_class_from_mono_type(mono_reflection_type_get_type(reftype))); } GDMonoClass *make_generic_dictionary_type(MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype) { NO_GLUE_RET(NULL); - MakeGenericDictionaryType thunk = CACHED_METHOD_THUNK(MarshalUtils, MakeGenericDictionaryType); MonoException *exc = NULL; - MonoReflectionType *reftype = invoke_method_thunk(thunk, p_key_reftype, p_value_reftype, &exc); + MonoReflectionType *reftype = CACHED_METHOD_THUNK(MarshalUtils, MakeGenericDictionaryType).invoke(p_key_reftype, p_value_reftype, &exc); UNHANDLED_EXCEPTION(exc); return GDMono::get_singleton()->get_class(mono_class_from_mono_type(mono_reflection_type_get_type(reftype))); } diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h index d73743bf0b..848df843fe 100644 --- a/modules/mono/mono_gd/gd_mono_utils.h +++ b/modules/mono/mono_gd/gd_mono_utils.h @@ -49,33 +49,6 @@ namespace GDMonoUtils { -typedef void (*GodotObject_Dispose)(MonoObject *, MonoException **); -typedef Array *(*Array_GetPtr)(MonoObject *, MonoException **); -typedef Dictionary *(*Dictionary_GetPtr)(MonoObject *, MonoException **); -typedef MonoObject *(*SignalAwaiter_SignalCallback)(MonoObject *, MonoArray *, MonoException **); -typedef MonoObject *(*SignalAwaiter_FailureCallback)(MonoObject *, MonoException **); -typedef MonoObject *(*GodotTaskScheduler_Activate)(MonoObject *, MonoException **); -typedef MonoArray *(*StackTrace_GetFrames)(MonoObject *, MonoException **); -typedef void (*DebugUtils_StackFrameInfo)(MonoObject *, MonoString **, int *, MonoString **, MonoException **); - -typedef MonoBoolean (*TypeIsGenericArray)(MonoReflectionType *, MonoException **); -typedef MonoBoolean (*TypeIsGenericDictionary)(MonoReflectionType *, MonoException **); - -typedef void (*ArrayGetElementType)(MonoReflectionType *, MonoReflectionType **, MonoException **); -typedef void (*DictionaryGetKeyValueTypes)(MonoReflectionType *, MonoReflectionType **, MonoReflectionType **, MonoException **); - -typedef MonoBoolean (*GenericIEnumerableIsAssignableFromType)(MonoReflectionType *, MonoException **); -typedef MonoBoolean (*GenericIDictionaryIsAssignableFromType)(MonoReflectionType *, MonoException **); -typedef MonoBoolean (*GenericIEnumerableIsAssignableFromType_with_info)(MonoReflectionType *, MonoReflectionType **, MonoException **); -typedef MonoBoolean (*GenericIDictionaryIsAssignableFromType_with_info)(MonoReflectionType *, MonoReflectionType **, MonoReflectionType **, MonoException **); - -typedef MonoReflectionType *(*MakeGenericArrayType)(MonoReflectionType *, MonoException **); -typedef MonoReflectionType *(*MakeGenericDictionaryType)(MonoReflectionType *, MonoReflectionType *, MonoException **); - -typedef void (*EnumerableToArray)(MonoObject *, Array *, MonoException **); -typedef void (*IDictionaryToDictionary)(MonoObject *, Dictionary *, MonoException **); -typedef void (*GenericIDictionaryToDictionary)(MonoObject *, Dictionary *, MonoException **); - namespace Marshal { bool type_is_generic_array(MonoReflectionType *p_reftype); @@ -98,157 +71,6 @@ Dictionary generic_idictionary_to_dictionary(MonoObject *p_generic_idictionary); } // namespace Marshal -// End of MarshalUtils methods - -struct MonoCache { - - // ----------------------------------------------- - // corlib classes - - // Let's use the no-namespace format for these too - GDMonoClass *class_MonoObject; - GDMonoClass *class_bool; - GDMonoClass *class_int8_t; - GDMonoClass *class_int16_t; - GDMonoClass *class_int32_t; - GDMonoClass *class_int64_t; - GDMonoClass *class_uint8_t; - GDMonoClass *class_uint16_t; - GDMonoClass *class_uint32_t; - GDMonoClass *class_uint64_t; - GDMonoClass *class_float; - GDMonoClass *class_double; - GDMonoClass *class_String; - GDMonoClass *class_IntPtr; - - GDMonoClass *class_System_Collections_IEnumerable; - GDMonoClass *class_System_Collections_IDictionary; - -#ifdef DEBUG_ENABLED - GDMonoClass *class_System_Diagnostics_StackTrace; - StackTrace_GetFrames methodthunk_System_Diagnostics_StackTrace_GetFrames; - GDMonoMethod *method_System_Diagnostics_StackTrace_ctor_bool; - GDMonoMethod *method_System_Diagnostics_StackTrace_ctor_Exception_bool; -#endif - - GDMonoClass *class_KeyNotFoundException; - - MonoClass *rawclass_Dictionary; - // ----------------------------------------------- - - GDMonoClass *class_Vector2; - GDMonoClass *class_Rect2; - GDMonoClass *class_Transform2D; - GDMonoClass *class_Vector3; - GDMonoClass *class_Basis; - GDMonoClass *class_Quat; - GDMonoClass *class_Transform; - GDMonoClass *class_AABB; - GDMonoClass *class_Color; - GDMonoClass *class_Plane; - GDMonoClass *class_NodePath; - GDMonoClass *class_RID; - GDMonoClass *class_GodotObject; - GDMonoClass *class_GodotResource; - GDMonoClass *class_Node; - GDMonoClass *class_Control; - GDMonoClass *class_Spatial; - GDMonoClass *class_WeakRef; - GDMonoClass *class_Array; - GDMonoClass *class_Dictionary; - GDMonoClass *class_MarshalUtils; - GDMonoClass *class_ISerializationListener; - -#ifdef DEBUG_ENABLED - GDMonoClass *class_DebuggingUtils; - DebugUtils_StackFrameInfo methodthunk_DebuggingUtils_GetStackFrameInfo; -#endif - - GDMonoClass *class_ExportAttribute; - GDMonoField *field_ExportAttribute_hint; - GDMonoField *field_ExportAttribute_hintString; - GDMonoClass *class_SignalAttribute; - GDMonoClass *class_ToolAttribute; - GDMonoClass *class_RemoteAttribute; - GDMonoClass *class_SyncAttribute; - GDMonoClass *class_RemoteSyncAttribute; - GDMonoClass *class_MasterSyncAttribute; - GDMonoClass *class_PuppetSyncAttribute; - GDMonoClass *class_MasterAttribute; - GDMonoClass *class_PuppetAttribute; - GDMonoClass *class_SlaveAttribute; - GDMonoClass *class_GodotMethodAttribute; - GDMonoField *field_GodotMethodAttribute_methodName; - - GDMonoField *field_GodotObject_ptr; - GDMonoField *field_NodePath_ptr; - GDMonoField *field_Image_ptr; - GDMonoField *field_RID_ptr; - - GodotObject_Dispose methodthunk_GodotObject_Dispose; - Array_GetPtr methodthunk_Array_GetPtr; - Dictionary_GetPtr methodthunk_Dictionary_GetPtr; - SignalAwaiter_SignalCallback methodthunk_SignalAwaiter_SignalCallback; - SignalAwaiter_FailureCallback methodthunk_SignalAwaiter_FailureCallback; - GodotTaskScheduler_Activate methodthunk_GodotTaskScheduler_Activate; - - // Start of MarshalUtils methods - - TypeIsGenericArray methodthunk_MarshalUtils_TypeIsGenericArray; - TypeIsGenericDictionary methodthunk_MarshalUtils_TypeIsGenericDictionary; - - ArrayGetElementType methodthunk_MarshalUtils_ArrayGetElementType; - DictionaryGetKeyValueTypes methodthunk_MarshalUtils_DictionaryGetKeyValueTypes; - - GenericIEnumerableIsAssignableFromType methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType; - GenericIDictionaryIsAssignableFromType methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType; - GenericIEnumerableIsAssignableFromType_with_info methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType_with_info; - GenericIDictionaryIsAssignableFromType_with_info methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType_with_info; - - MakeGenericArrayType methodthunk_MarshalUtils_MakeGenericArrayType; - MakeGenericDictionaryType methodthunk_MarshalUtils_MakeGenericDictionaryType; - - EnumerableToArray methodthunk_MarshalUtils_EnumerableToArray; - IDictionaryToDictionary methodthunk_MarshalUtils_IDictionaryToDictionary; - GenericIDictionaryToDictionary methodthunk_MarshalUtils_GenericIDictionaryToDictionary; - - // End of MarshalUtils methods - - Ref<MonoGCHandle> task_scheduler_handle; - - bool corlib_cache_updated; - bool godot_api_cache_updated; - - void clear_corlib_cache(); - void clear_godot_api_cache(); - - MonoCache() { - clear_corlib_cache(); - clear_godot_api_cache(); - } -}; - -extern MonoCache mono_cache; - -void update_corlib_cache(); -void update_godot_api_cache(); - -inline void clear_corlib_cache() { - mono_cache.clear_corlib_cache(); -} - -inline void clear_godot_api_cache() { - mono_cache.clear_godot_api_cache(); -} - -_FORCE_INLINE_ bool tools_godot_api_check() { -#ifdef TOOLS_ENABLED - return mono_cache.godot_api_cache_updated; -#else - return true; // Assume it's updated if this was called, otherwise it's a bug -#endif -} - _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); } @@ -324,20 +146,6 @@ void dispose(MonoObject *p_mono_object, MonoException **r_exc); #define NATIVE_GDMONOCLASS_NAME(m_class) (GDMonoMarshal::mono_string_to_godot((MonoString *)m_class->get_field(BINDINGS_NATIVE_NAME_FIELD)->get_value(NULL))) -#define CACHED_CLASS(m_class) (GDMonoUtils::mono_cache.class_##m_class) -#define CACHED_CLASS_RAW(m_class) (GDMonoUtils::mono_cache.class_##m_class->get_mono_ptr()) -#define CACHED_RAW_MONO_CLASS(m_class) (GDMonoUtils::mono_cache.rawclass_##m_class) -#define CACHED_FIELD(m_class, m_field) (GDMonoUtils::mono_cache.field_##m_class##_##m_field) -#define CACHED_METHOD(m_class, m_method) (GDMonoUtils::mono_cache.method_##m_class##_##m_method) -#define CACHED_METHOD_THUNK(m_class, m_method) (GDMonoUtils::mono_cache.methodthunk_##m_class##_##m_method) -#define CACHED_PROPERTY(m_class, m_property) (GDMonoUtils::mono_cache.property_##m_class##_##m_property) - -#ifdef REAL_T_IS_DOUBLE -#define REAL_T_MONOCLASS CACHED_CLASS_RAW(double) -#else -#define REAL_T_MONOCLASS CACHED_CLASS_RAW(float) -#endif - #define GD_MONO_BEGIN_RUNTIME_INVOKE \ int &_runtime_invoke_count_ref = GDMonoUtils::get_runtime_invoke_count_ref(); \ _runtime_invoke_count_ref += 1; @@ -345,93 +153,4 @@ void dispose(MonoObject *p_mono_object, MonoException **r_exc); #define GD_MONO_END_RUNTIME_INVOKE \ _runtime_invoke_count_ref -= 1; -inline void invoke_method_thunk(void (*p_method_thunk)()) { - GD_MONO_BEGIN_RUNTIME_INVOKE; - p_method_thunk(); - GD_MONO_END_RUNTIME_INVOKE; -} - -template <class R> -R invoke_method_thunk(R (*p_method_thunk)()) { - GD_MONO_BEGIN_RUNTIME_INVOKE; - R r = p_method_thunk(); - GD_MONO_END_RUNTIME_INVOKE; - return r; -} - -template <class P1> -void invoke_method_thunk(void (*p_method_thunk)(P1), P1 p_arg1) { - GD_MONO_BEGIN_RUNTIME_INVOKE; - p_method_thunk(p_arg1); - GD_MONO_END_RUNTIME_INVOKE; -} - -template <class R, class P1> -R invoke_method_thunk(R (*p_method_thunk)(P1), P1 p_arg1) { - GD_MONO_BEGIN_RUNTIME_INVOKE; - R r = p_method_thunk(p_arg1); - GD_MONO_END_RUNTIME_INVOKE; - return r; -} - -template <class P1, class P2> -void invoke_method_thunk(void (*p_method_thunk)(P1, P2), P1 p_arg1, P2 p_arg2) { - GD_MONO_BEGIN_RUNTIME_INVOKE; - p_method_thunk(p_arg1, p_arg2); - GD_MONO_END_RUNTIME_INVOKE; -} - -template <class R, class P1, class P2> -R invoke_method_thunk(R (*p_method_thunk)(P1, P2), P1 p_arg1, P2 p_arg2) { - GD_MONO_BEGIN_RUNTIME_INVOKE; - R r = p_method_thunk(p_arg1, p_arg2); - GD_MONO_END_RUNTIME_INVOKE; - return r; -} - -template <class P1, class P2, class P3> -void invoke_method_thunk(void (*p_method_thunk)(P1, P2, P3), P1 p_arg1, P2 p_arg2, P3 p_arg3) { - GD_MONO_BEGIN_RUNTIME_INVOKE; - p_method_thunk(p_arg1, p_arg2, p_arg3); - GD_MONO_END_RUNTIME_INVOKE; -} - -template <class R, class P1, class P2, class P3> -R invoke_method_thunk(R (*p_method_thunk)(P1, P2, P3), P1 p_arg1, P2 p_arg2, P3 p_arg3) { - GD_MONO_BEGIN_RUNTIME_INVOKE; - R r = p_method_thunk(p_arg1, p_arg2, p_arg3); - GD_MONO_END_RUNTIME_INVOKE; - return r; -} - -template <class P1, class P2, class P3, class P4> -void invoke_method_thunk(void (*p_method_thunk)(P1, P2, P3, P4), P1 p_arg1, P2 p_arg2, P3 p_arg3, P4 p_arg4) { - GD_MONO_BEGIN_RUNTIME_INVOKE; - p_method_thunk(p_arg1, p_arg2, p_arg3, p_arg4); - GD_MONO_END_RUNTIME_INVOKE; -} - -template <class R, class P1, class P2, class P3, class P4> -R invoke_method_thunk(R (*p_method_thunk)(P1, P2, P3, P4), P1 p_arg1, P2 p_arg2, P3 p_arg3, P4 p_arg4) { - GD_MONO_BEGIN_RUNTIME_INVOKE; - R r = p_method_thunk(p_arg1, p_arg2, p_arg3, p_arg4); - GD_MONO_END_RUNTIME_INVOKE; - return r; -} - -template <class P1, class P2, class P3, class P4, class P5> -void invoke_method_thunk(void (*p_method_thunk)(P1, P2, P3, P4, P5), P1 p_arg1, P2 p_arg2, P3 p_arg3, P4 p_arg4, P5 p_arg5) { - GD_MONO_BEGIN_RUNTIME_INVOKE; - p_method_thunk(p_arg1, p_arg2, p_arg3, p_arg4, p_arg5); - GD_MONO_END_RUNTIME_INVOKE; -} - -template <class R, class P1, class P2, class P3, class P4, class P5> -R invoke_method_thunk(R (*p_method_thunk)(P1, P2, P3, P4, P5), P1 p_arg1, P2 p_arg2, P3 p_arg3, P4 p_arg4, P5 p_arg5) { - GD_MONO_BEGIN_RUNTIME_INVOKE; - R r = p_method_thunk(p_arg1, p_arg2, p_arg3, p_arg4, p_arg5); - GD_MONO_END_RUNTIME_INVOKE; - return r; -} - #endif // GD_MONOUTILS_H diff --git a/modules/mono/signal_awaiter_utils.cpp b/modules/mono/signal_awaiter_utils.cpp index 189ceaab1b..ee16327856 100644 --- a/modules/mono/signal_awaiter_utils.cpp +++ b/modules/mono/signal_awaiter_utils.cpp @@ -31,6 +31,7 @@ #include "signal_awaiter_utils.h" #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" @@ -98,7 +99,7 @@ Variant SignalAwaiterHandle::_signal_callback(const Variant **p_args, int p_argc MonoException *exc = NULL; GD_MONO_BEGIN_RUNTIME_INVOKE; - invoke_method_thunk(CACHED_METHOD_THUNK(SignalAwaiter, SignalCallback), get_target(), signal_args, &exc); + CACHED_METHOD_THUNK(SignalAwaiter, SignalCallback).invoke(get_target(), signal_args, &exc); GD_MONO_END_RUNTIME_INVOKE; if (exc) { @@ -130,7 +131,7 @@ SignalAwaiterHandle::~SignalAwaiterHandle() { if (awaiter) { MonoException *exc = NULL; GD_MONO_BEGIN_RUNTIME_INVOKE; - invoke_method_thunk(CACHED_METHOD_THUNK(SignalAwaiter, FailureCallback), awaiter, &exc); + CACHED_METHOD_THUNK(SignalAwaiter, FailureCallback).invoke(awaiter, &exc); GD_MONO_END_RUNTIME_INVOKE; if (exc) { diff --git a/modules/mono/utils/string_utils.cpp b/modules/mono/utils/string_utils.cpp index e9efc7626d..88366a6a03 100644 --- a/modules/mono/utils/string_utils.cpp +++ b/modules/mono/utils/string_utils.cpp @@ -216,6 +216,25 @@ String str_format(const char *p_format, ...) { #endif String str_format(const char *p_format, va_list p_list) { + char *buffer = str_format_new(p_format, p_list); + + String res(buffer); + memdelete_arr(buffer); + + return res; +} + +char *str_format_new(const char *p_format, ...) { + va_list list; + + va_start(list, p_format); + char *res = str_format_new(p_format, list); + va_end(list); + + return res; +} + +char *str_format_new(const char *p_format, va_list p_list) { va_list list; va_copy(list, p_list); @@ -230,8 +249,5 @@ String str_format(const char *p_format, va_list p_list) { gd_vsnprintf(buffer, len, p_format, list); va_end(list); - String res(buffer); - memdelete_arr(buffer); - - return res; + return buffer; } diff --git a/modules/mono/utils/string_utils.h b/modules/mono/utils/string_utils.h index 565b9bb644..e7f02955bd 100644 --- a/modules/mono/utils/string_utils.h +++ b/modules/mono/utils/string_utils.h @@ -56,5 +56,7 @@ Error read_all_file_utf8(const String &p_path, String &r_content); String str_format(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_1_2; String str_format(const char *p_format, va_list p_list) _PRINTF_FORMAT_ATTRIBUTE_1_0; +char *str_format_new(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_1_2; +char *str_format_new(const char *p_format, va_list p_list) _PRINTF_FORMAT_ATTRIBUTE_1_0; #endif // STRING_FORMAT_H |