diff options
96 files changed, 2379 insertions, 5519 deletions
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index f72aeff469..9e2c3440c7 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -1433,20 +1433,6 @@ <member name="memory/limits/multithreaded_server/rid_pool_prealloc" type="int" setter="" getter="" default="60"> This is used by servers when used in multi-threading mode (servers and visual). RIDs are preallocated to avoid stalling the server requesting them on threads. If servers get stalled too often when loading resources in a thread, increase this number. </member> - <member name="mono/debugger_agent/port" type="int" setter="" getter="" default="23685"> - </member> - <member name="mono/debugger_agent/wait_for_debugger" type="bool" setter="" getter="" default="false"> - </member> - <member name="mono/debugger_agent/wait_timeout" type="int" setter="" getter="" default="3000"> - </member> - <member name="mono/profiler/args" type="String" setter="" getter="" default=""log:calls,alloc,sample,output=output.mlpd""> - </member> - <member name="mono/profiler/enabled" type="bool" setter="" getter="" default="false"> - </member> - <member name="mono/runtime/unhandled_exception_policy" type="int" setter="" getter="" default="0"> - The policy to use for unhandled Mono (C#) exceptions. The default "Terminate Application" exits the project as soon as an unhandled exception is thrown. "Log Error" logs an error message to the console instead, and will not interrupt the project execution when an unhandled exception is thrown. - [b]Note:[/b] The unhandled exception policy is always set to "Log Error" in the editor, which also includes C# [code]tool[/code] scripts running within the editor as well as editor plugin code. - </member> <member name="navigation/2d/default_cell_size" type="int" setter="" getter="" default="1"> Default cell size for 2D navigation maps. See [method NavigationServer2D.map_set_cell_size]. </member> diff --git a/main/main.cpp b/main/main.cpp index 6559b69f2e..740e3cc69d 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -2255,18 +2255,6 @@ bool Main::start() { ERR_FAIL_COND_V_MSG(da.is_null(), false, "Argument supplied to --doctool must be a valid directory path."); } -#ifndef MODULE_MONO_ENABLED - // Hack to define Mono-specific project settings even on non-Mono builds, - // so that we don't lose their descriptions and default values in DocData. - // Default values should be synced with mono_gd/gd_mono.cpp. - GLOBAL_DEF("mono/debugger_agent/port", 23685); - GLOBAL_DEF("mono/debugger_agent/wait_for_debugger", false); - GLOBAL_DEF("mono/debugger_agent/wait_timeout", 3000); - GLOBAL_DEF("mono/profiler/args", "log:calls,alloc,sample,output=output.mlpd"); - GLOBAL_DEF("mono/profiler/enabled", false); - GLOBAL_DEF("mono/runtime/unhandled_exception_policy", 0); -#endif - Error err; DocTools doc; doc.generate(doc_base); diff --git a/methods.py b/methods.py index ba7474ea02..c02517d137 100644 --- a/methods.py +++ b/methods.py @@ -818,18 +818,21 @@ def generate_vs_project(env, num_jobs): module_configs = ModuleConfigs() if env.get("module_mono_enabled"): - import modules.mono.build_scripts.mono_reg_utils as mono_reg + import modules.mono.build_scripts.mono_configure as mono_configure - mono_root = env.get("mono_prefix") or mono_reg.find_mono_root_dir(env["bits"]) - if mono_root: + app_host_dir = mono_configure.find_dotnet_app_host_dir(env) + if app_host_dir and os.path.isdir(app_host_dir): + mono_defines = [("NETHOST_USE_AS_STATIC",)] + if env["tools"]: + mono_defines += [("GD_MONO_HOT_RELOAD",)] module_configs.add_mode( "mono", - includes=os.path.join(mono_root, "include", "mono-2.0"), - cli_args="module_mono_enabled=yes mono_glue=yes", - defines=[("MONO_GLUE_ENABLED",)], + includes=app_host_dir, + cli_args="module_mono_enabled=yes", + defines=mono_defines, ) else: - print("Mono installation directory not found. Generated project will not have build variants for Mono.") + print(".NET App Host directory not found. Generated project will not have build variants for .NET.") env["MSVSBUILDCOM"] = module_configs.build_commandline("scons") env["MSVSREBUILDCOM"] = module_configs.build_commandline("scons vsproj=yes") diff --git a/modules/mono/build_scripts/build_assemblies.py b/modules/mono/build_scripts/build_assemblies.py index dd96f40f6b..67c2f7f70e 100755 --- a/modules/mono/build_scripts/build_assemblies.py +++ b/modules/mono/build_scripts/build_assemblies.py @@ -203,6 +203,9 @@ def build_godot_api(msbuild_tool, module_dir, output_dir): "GodotSharpEditor.dll", "GodotSharpEditor.pdb", "GodotSharpEditor.xml", + "GodotPlugins.dll", + "GodotPlugins.pdb", + "GodotPlugins.runtimeconfig.json", ] for build_config in ["Debug", "Release"]: @@ -223,6 +226,7 @@ def build_godot_api(msbuild_tool, module_dir, output_dir): core_src_dir = os.path.abspath(os.path.join(sln, os.pardir, "GodotSharp", "bin", build_config)) editor_src_dir = os.path.abspath(os.path.join(sln, os.pardir, "GodotSharpEditor", "bin", build_config)) + plugins_src_dir = os.path.abspath(os.path.join(sln, os.pardir, "GodotPlugins", "bin", build_config, "net5.0")) if not os.path.isdir(editor_api_dir): assert not os.path.isfile(editor_api_dir) @@ -236,6 +240,8 @@ def build_godot_api(msbuild_tool, module_dir, output_dir): src_path = os.path.join(core_src_dir, filename) if not os.path.isfile(src_path): src_path = os.path.join(editor_src_dir, filename) + if not os.path.isfile(src_path): + src_path = os.path.join(plugins_src_dir, filename) print(f"Copying assembly to {target_path}...") copy(src_path, target_path) diff --git a/modules/mono/build_scripts/make_android_mono_config.py b/modules/mono/build_scripts/make_android_mono_config.py deleted file mode 100644 index 3459244bc2..0000000000 --- a/modules/mono/build_scripts/make_android_mono_config.py +++ /dev/null @@ -1,55 +0,0 @@ -def generate_compressed_config(config_src, output_dir): - import os.path - - # Source file - with open(os.path.join(output_dir, "android_mono_config.gen.cpp"), "w") as cpp: - with open(config_src, "rb") as f: - buf = f.read() - decompr_size = len(buf) - import zlib - - # Use maximum zlib compression level to further reduce file size - # (at the cost of initial build times). - buf = zlib.compress(buf, zlib.Z_BEST_COMPRESSION) - compr_size = len(buf) - - bytes_seq_str = "" - for i, buf_idx in enumerate(range(compr_size)): - if i > 0: - bytes_seq_str += ", " - bytes_seq_str += str(buf[buf_idx]) - - cpp.write( - """/* THIS FILE IS GENERATED DO NOT EDIT */ -#include "android_mono_config.h" - -#ifdef ANDROID_ENABLED - -#include "core/io/compression.h" - - -namespace { - -// config -static const int config_compressed_size = %d; -static const int config_uncompressed_size = %d; -static const unsigned char config_compressed_data[] = { %s }; -} // namespace - -String get_godot_android_mono_config() { - Vector<uint8_t> data; - data.resize(config_uncompressed_size); - uint8_t* w = data.ptrw(); - Compression::decompress(w, config_uncompressed_size, config_compressed_data, - config_compressed_size, Compression::MODE_DEFLATE); - String s; - if (s.parse_utf8((const char *)w, data.size()) != OK) { - ERR_FAIL_V(String()); - } - return s; -} - -#endif // ANDROID_ENABLED -""" - % (compr_size, decompr_size, bytes_seq_str) - ) diff --git a/modules/mono/build_scripts/mono_android_config.xml b/modules/mono/build_scripts/mono_android_config.xml deleted file mode 100644 index e79670afd2..0000000000 --- a/modules/mono/build_scripts/mono_android_config.xml +++ /dev/null @@ -1,28 +0,0 @@ -<configuration> - <dllmap wordsize="32" dll="i:cygwin1.dll" target="/system/lib/libc.so" /> - <dllmap wordsize="64" dll="i:cygwin1.dll" target="/system/lib64/libc.so" /> - <dllmap wordsize="32" dll="libc" target="/system/lib/libc.so" /> - <dllmap wordsize="64" dll="libc" target="/system/lib64/libc.so" /> - <dllmap wordsize="32" dll="intl" target="/system/lib/libc.so" /> - <dllmap wordsize="64" dll="intl" target="/system/lib64/libc.so" /> - <dllmap wordsize="32" dll="libintl" target="/system/lib/libc.so" /> - <dllmap wordsize="64" dll="libintl" target="/system/lib64/libc.so" /> - <dllmap dll="MonoPosixHelper" target="libMonoPosixHelper.so" /> - <dllmap dll="System.Native" target="libmono-native.so" /> - <dllmap wordsize="32" dll="i:msvcrt" target="/system/lib/libc.so" /> - <dllmap wordsize="64" dll="i:msvcrt" target="/system/lib64/libc.so" /> - <dllmap wordsize="32" dll="i:msvcrt.dll" target="/system/lib/libc.so" /> - <dllmap wordsize="64" dll="i:msvcrt.dll" target="/system/lib64/libc.so" /> - <dllmap wordsize="32" dll="sqlite" target="/system/lib/libsqlite.so" /> - <dllmap wordsize="64" dll="sqlite" target="/system/lib64/libsqlite.so" /> - <dllmap wordsize="32" dll="sqlite3" target="/system/lib/libsqlite.so" /> - <dllmap wordsize="64" dll="sqlite3" target="/system/lib64/libsqlite.so" /> - <dllmap wordsize="32" dll="liblog" target="/system/lib/liblog.so" /> - <dllmap wordsize="64" dll="liblog" target="/system/lib64/liblog.so" /> - <dllmap dll="i:kernel32.dll"> - <dllentry dll="__Internal" name="CopyMemory" target="mono_win32_compat_CopyMemory"/> - <dllentry dll="__Internal" name="FillMemory" target="mono_win32_compat_FillMemory"/> - <dllentry dll="__Internal" name="MoveMemory" target="mono_win32_compat_MoveMemory"/> - <dllentry dll="__Internal" name="ZeroMemory" target="mono_win32_compat_ZeroMemory"/> - </dllmap> -</configuration> diff --git a/modules/mono/build_scripts/mono_configure.py b/modules/mono/build_scripts/mono_configure.py index e69904c54b..071693d758 100644 --- a/modules/mono/build_scripts/mono_configure.py +++ b/modules/mono/build_scripts/mono_configure.py @@ -1,65 +1,5 @@ import os import os.path -import subprocess - -from SCons.Script import Dir, Environment - -if os.name == "nt": - from . import mono_reg_utils as monoreg - - -android_arch_dirs = { - "armv7": "armeabi-v7a", - "arm64v8": "arm64-v8a", - "x86": "x86", - "x86_64": "x86_64", -} - - -def get_android_out_dir(env): - return os.path.join( - Dir("#platform/android/java/lib/libs").abspath, - "release" if env["target"] == "release" else "debug", - android_arch_dirs[env["android_arch"]], - ) - - -def find_name_in_dir_files(directory, names, prefixes=[""], extensions=[""]): - for extension in extensions: - if extension and not extension.startswith("."): - extension = "." + extension - for prefix in prefixes: - for curname in names: - if os.path.isfile(os.path.join(directory, prefix + curname + extension)): - return curname - return "" - - -def find_file_in_dir(directory, names, prefixes=[""], extensions=[""]): - for extension in extensions: - if extension and not extension.startswith("."): - extension = "." + extension - for prefix in prefixes: - for curname in names: - filename = prefix + curname + extension - if os.path.isfile(os.path.join(directory, filename)): - return filename - return "" - - -def copy_file(src_dir, dst_dir, src_name, dst_name=""): - from shutil import copy - - src_path = os.path.join(Dir(src_dir).abspath, src_name) - dst_dir = Dir(dst_dir).abspath - - if not os.path.isdir(dst_dir): - os.makedirs(dst_dir) - - if dst_name: - copy(src_path, os.path.join(dst_dir, dst_name)) - else: - copy(src_path, dst_dir) def is_desktop(platform): @@ -71,504 +11,176 @@ def is_unix_like(platform): def module_supports_tools_on(platform): - return platform not in ["android", "javascript", "ios"] - - -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 "" + return is_desktop(platform) def configure(env, env_mono): - bits = env["bits"] - is_android = env["platform"] == "android" - is_javascript = env["platform"] == "javascript" - is_ios = env["platform"] == "ios" - is_ios_sim = is_ios and env["arch"] in ["x86", "x86_64"] + # is_android = env["platform"] == "android" + # is_javascript = env["platform"] == "javascript" + # is_ios = env["platform"] == "ios" + # is_ios_sim = is_ios and env["arch"] in ["x86", "x86_64"] tools_enabled = env["tools"] - mono_static = env["mono_static"] - copy_mono_root = env["copy_mono_root"] - - mono_prefix = env["mono_prefix"] - mono_bcl = env["mono_bcl"] - - mono_lib_names = ["mono-2.0-sgen", "monosgen-2.0"] - - if is_android and not env["android_arch"] in android_arch_dirs: - raise RuntimeError("This module does not support the specified 'android_arch': " + env["android_arch"]) 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: - # FIXME: 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'. Could be fixed by re-directing to '__Internal' with a dllmap or in the dlopen hook. - raise RuntimeError("Statically linking Mono is not currently supported for this platform") - - if not mono_static and (is_javascript or is_ios): - raise RuntimeError("Dynamically linking Mono is not currently supported for this platform") - - 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" - ) - - # Although we don't support building with tools for any platform where we currently use static AOT, - # if these are supported in the future, we won't be using static AOT for them as that would be - # too restrictive for the editor. These builds would probably be made to only use the interpreter. - mono_aot_static = (is_ios and not is_ios_sim) and not env["tools"] - - # Static AOT is only supported on the root domain - mono_single_appdomain = mono_aot_static - - if mono_single_appdomain: - env_mono.Append(CPPDEFINES=["GD_MONO_SINGLE_APPDOMAIN"]) - - if (env["tools"] or env["target"] != "release") and not mono_single_appdomain: + if env["tools"] or env["target"] != "release": env_mono.Append(CPPDEFINES=["GD_MONO_HOT_RELOAD"]) - if env["platform"] == "windows": - mono_root = mono_prefix - - if not mono_root and os.name == "nt": - mono_root = monoreg.find_mono_root_dir(bits) - - if not mono_root: - raise RuntimeError( - "Mono installation directory not found; specify one manually with the 'mono_prefix' SCons parameter" - ) - - print("Found Mono root directory: " + mono_root) - - mono_lib_path = os.path.join(mono_root, "lib") - - env.Append(LIBPATH=mono_lib_path) - env_mono.Prepend(CPPPATH=os.path.join(mono_root, "include", "mono-2.0")) + app_host_dir = find_dotnet_app_host_dir(env) - lib_suffixes = [".lib"] + def check_app_host_file_exists(file): + file_path = os.path.join(app_host_dir, file) + if not os.path.isfile(file_path): + raise RuntimeError("File not found: " + file_path) - if not env.msvc: - # MingW supports both '.a' and '.lib' - lib_suffixes.insert(0, ".a") + # TODO: + # All libnethost does for us is provide a function to find hostfxr. + # If we could handle that logic ourselves we could void linking it. - if mono_static: - if env.msvc: - mono_static_lib_name = "libmono-static-sgen" - else: - mono_static_lib_name = "libmonosgen-2.0" + # nethost file names: + # static: libnethost.a/lib + # shared: libnethost.a/dylib and nethost.dll + check_app_host_file_exists("libnethost.lib" if os.name == "nt" else "libnethost.a") + check_app_host_file_exists("nethost.h") + check_app_host_file_exists("hostfxr.h") + check_app_host_file_exists("coreclr_delegates.h") - mono_static_lib_file = find_file_in_dir(mono_lib_path, [mono_static_lib_name], extensions=lib_suffixes) + env.Append(LIBPATH=[app_host_dir]) + env_mono.Prepend(CPPPATH=app_host_dir) - if not mono_static_lib_file: - raise RuntimeError("Could not find static mono library in: " + mono_lib_path) + libnethost_path = os.path.join(app_host_dir, "libnethost.lib" if os.name == "nt" else "libnethost.a") - if env.msvc: - env.Append(LINKFLAGS=mono_static_lib_file) - - env.Append(LINKFLAGS="Mincore.lib") - env.Append(LINKFLAGS="msvcrt.lib") - env.Append(LINKFLAGS="LIBCMT.lib") - env.Append(LINKFLAGS="Psapi.lib") - else: - mono_static_lib_file_path = os.path.join(mono_lib_path, mono_static_lib_file) - env.Append(LINKFLAGS=["-Wl,-whole-archive", mono_static_lib_file_path, "-Wl,-no-whole-archive"]) + if env["platform"] == "windows": + env_mono.Append(CPPDEFINES=["NETHOST_USE_AS_STATIC"]) - env.Append(LIBS=["psapi"]) - env.Append(LIBS=["version"]) + if env.msvc: + env.Append(LINKFLAGS="libnethost.lib") else: - mono_lib_file = find_file_in_dir(mono_lib_path, mono_lib_names, extensions=lib_suffixes) - - if not mono_lib_file: - raise RuntimeError("Could not find mono library in: " + mono_lib_path) - - if env.msvc: - env.Append(LINKFLAGS=mono_lib_file) - else: - mono_lib_file_path = os.path.join(mono_lib_path, mono_lib_file) - env.Append(LINKFLAGS=mono_lib_file_path) - - mono_bin_path = os.path.join(mono_root, "bin") - - mono_dll_file = find_file_in_dir(mono_bin_path, mono_lib_names, prefixes=["", "lib"], extensions=[".dll"]) - - if not mono_dll_file: - raise RuntimeError("Could not find mono shared library in: " + mono_bin_path) - - copy_file(mono_bin_path, "#bin", mono_dll_file) + env.Append(LINKFLAGS=["-Wl,-whole-archive", libnethost_path, "-Wl,-no-whole-archive"]) else: is_apple = env["platform"] in ["macos", "ios"] - is_macos = is_apple and not is_ios - - sharedlib_ext = ".dylib" if is_apple else ".so" - - mono_root = mono_prefix - mono_lib_path = "" - mono_so_file = "" - - if not mono_root and (is_android or is_javascript or is_ios): - raise RuntimeError( - "Mono installation directory not found; specify one manually with the 'mono_prefix' SCons parameter" - ) - - if not mono_root and is_macos: - # Try with some known directories under macOS - hint_dirs = ["/Library/Frameworks/Mono.framework/Versions/Current", "/usr/local/var/homebrew/linked/mono"] - for hint_dir in hint_dirs: - if os.path.isdir(hint_dir): - mono_root = hint_dir - break - - # We can't use pkg-config to link mono statically, - # but we can still use it to find the mono root directory - if not mono_root and mono_static: - mono_root = pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext) - if not mono_root: - raise RuntimeError( - "Building with mono_static=yes, but failed to find the mono prefix with pkg-config; " - + "specify one manually with the 'mono_prefix' SCons parameter" - ) - - if is_ios and not is_ios_sim: - env_mono.Append(CPPDEFINES=["IOS_DEVICE"]) - - if mono_root: - print("Found Mono root directory: " + mono_root) - - mono_lib_path = os.path.join(mono_root, "lib") - - env.Append(LIBPATH=[mono_lib_path]) - env_mono.Prepend(CPPPATH=os.path.join(mono_root, "include", "mono-2.0")) - - mono_lib = find_name_in_dir_files(mono_lib_path, mono_lib_names, prefixes=["lib"], extensions=[".a"]) - - if not mono_lib: - raise RuntimeError("Could not find mono library in: " + mono_lib_path) - - env_mono.Append(CPPDEFINES=["_REENTRANT"]) - - if mono_static: - if not is_javascript: - env.Append(LINKFLAGS=["-rdynamic"]) - - mono_lib_file = os.path.join(mono_lib_path, "lib" + mono_lib + ".a") - - if is_apple: - if is_macos: - env.Append(LINKFLAGS=["-Wl,-force_load," + mono_lib_file]) - else: - arch = env["arch"] - - def copy_mono_lib(libname_wo_ext): - copy_file( - mono_lib_path, "#bin", libname_wo_ext + ".a", "%s.ios.%s.a" % (libname_wo_ext, arch) - ) - - # Copy Mono libraries to the output folder. These are meant to be bundled with - # the export templates and added to the Xcode project when exporting a game. - copy_mono_lib("lib" + mono_lib) - copy_mono_lib("libmono-native") - copy_mono_lib("libmono-profiler-log") - - if not is_ios_sim: - copy_mono_lib("libmono-ee-interp") - copy_mono_lib("libmono-icall-table") - copy_mono_lib("libmono-ilgen") - 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]) - - if is_macos: - env.Append(LIBS=["iconv", "pthread"]) - elif is_android: - pass # Nothing - elif is_ios: - pass # Nothing, linking is delegated to the exported Xcode project - elif is_javascript: - env.Append(LIBS=["m", "rt", "dl", "pthread"]) - else: - env.Append(LIBS=["m", "rt", "dl", "pthread"]) - - if not mono_static: - mono_so_file = find_file_in_dir( - mono_lib_path, mono_lib_names, prefixes=["lib"], extensions=[sharedlib_ext] - ) - - if not mono_so_file: - raise RuntimeError("Could not find mono shared library in: " + mono_lib_path) - else: - assert not mono_static + # is_macos = is_apple and not is_ios - # TODO: Add option to force using pkg-config - print("Mono root directory not found. Using pkg-config instead") + # if is_ios and not is_ios_sim: + # env_mono.Append(CPPDEFINES=["IOS_DEVICE"]) - env.ParseConfig("pkg-config monosgen-2 --libs") - env_mono.ParseConfig("pkg-config monosgen-2 --cflags") + if is_apple: + env.Append(LINKFLAGS=["-Wl,-force_load," + libnethost_path]) + else: + env.Append(LINKFLAGS=["-Wl,-whole-archive", libnethost_path, "-Wl,-no-whole-archive"]) - tmpenv = Environment() - tmpenv.AppendENVPath("PKG_CONFIG_PATH", os.getenv("PKG_CONFIG_PATH")) - tmpenv.ParseConfig("pkg-config monosgen-2 --libs-only-L") - for hint_dir in tmpenv["LIBPATH"]: - file_found = find_file_in_dir(hint_dir, mono_lib_names, prefixes=["lib"], extensions=[sharedlib_ext]) - if file_found: - mono_lib_path = hint_dir - mono_so_file = file_found - break +def find_dotnet_app_host_dir(env): + dotnet_root = env["dotnet_root"] - if not mono_so_file: - raise RuntimeError("Could not find mono shared library in: " + str(tmpenv["LIBPATH"])) + if not dotnet_root: + dotnet_exe = find_executable("dotnet") + if dotnet_exe: + dotnet_exe_realpath = os.path.realpath(dotnet_exe) # Eliminate symbolic links + dotnet_root = os.path.abspath(os.path.join(dotnet_exe_realpath, os.pardir)) + else: + raise RuntimeError("Cannot find .NET Core Sdk") - if not mono_static: - libs_output_dir = get_android_out_dir(env) if is_android else "#bin" - copy_file(mono_lib_path, libs_output_dir, mono_so_file) + print("Found .NET Core Sdk root directory: " + dotnet_root) - 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() - ) + dotnet_cmd = os.path.join(dotnet_root, "dotnet.exe" if os.name == "nt" else "dotnet") - make_template_dir(env, mono_root) - elif is_android: - # Compress Android Mono Config - from . import make_android_mono_config + runtime_identifier = determine_runtime_identifier(env) - module_dir = os.getcwd() - config_file_path = os.path.join(module_dir, "build_scripts", "mono_android_config.xml") - make_android_mono_config.generate_compressed_config(config_file_path, "mono_gd/") + # TODO: In the future, if it can't be found this way, we want to obtain it + # from the runtime.{runtime_identifier}.Microsoft.NETCore.DotNetAppHost NuGet package. + app_host_search_version = "5.0" + app_host_version = find_app_host_version(dotnet_cmd, app_host_search_version) + if not app_host_version: + raise RuntimeError("Cannot find .NET app host for version: " + app_host_search_version) - # Copy the required shared libraries - copy_mono_shared_libs(env, mono_root, None) - elif is_javascript: - pass # No data directory for this platform - elif is_ios: - pass # No data directory for this platform + app_host_dir = os.path.join( + dotnet_root, + "packs", + "Microsoft.NETCore.App.Host." + runtime_identifier, + app_host_version, + "runtimes", + runtime_identifier, + "native", + ) - if copy_mono_root: - if not mono_root: - mono_root = subprocess.check_output(["pkg-config", "mono-2", "--variable=prefix"]).decode("utf8").strip() + return app_host_dir - if tools_enabled: - # Only supported for editor builds. - copy_mono_root_files(env, mono_root, mono_bcl) +def determine_runtime_identifier(env): + names_map = { + "windows": "win", + "macos": "osx", + "linuxbsd": "linux", + "server": "linux", # FIXME: Is server linux only, or also macos? + } -def make_template_dir(env, mono_root): - from shutil import rmtree + # .NET RID architectures: x86, x64, arm, or arm64 platform = env["platform"] - target = env["target"] - - template_dir_name = "" - - 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) - - template_mono_root_dir = os.path.join(template_dir, "Mono") - - if os.path.isdir(template_mono_root_dir): - rmtree(template_mono_root_dir) # Clean first - # 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, platform) - - # Copy the required shared libraries - - copy_mono_shared_libs(env, mono_root, template_mono_root_dir) - - -def copy_mono_root_files(env, mono_root, mono_bcl): - from glob import glob - from shutil import copy - from shutil import rmtree - - if not mono_root: - raise RuntimeError("Mono installation directory not found") - - output_dir = Dir("#bin").abspath - editor_mono_root_dir = os.path.join(output_dir, "GodotSharp", "Mono") - - if os.path.isdir(editor_mono_root_dir): - rmtree(editor_mono_root_dir) # Clean first - - # Copy etc/mono/ - - editor_mono_config_dir = os.path.join(editor_mono_root_dir, "etc", "mono") - copy_mono_etc_dir(mono_root, editor_mono_config_dir, env["platform"]) - - # Copy the required shared libraries - - copy_mono_shared_libs(env, mono_root, editor_mono_root_dir) + if is_desktop(platform): + if env["arch"] in ["arm", "arm32"]: + rid = "arm" + elif env["arch"] == "arm64": + rid = "arm64" + else: + bits = env["bits"] + bit_arch_map = {"64": "x64", "32": "x86"} + rid = bit_arch_map[bits] + return "%s-%s" % (names_map[platform], rid) + else: + raise NotImplementedError() - # Copy framework assemblies - mono_framework_dir = mono_bcl or os.path.join(mono_root, "lib", "mono", "4.5") - mono_framework_facades_dir = os.path.join(mono_framework_dir, "Facades") +def find_app_host_version(dotnet_cmd, search_version): + import subprocess - editor_mono_framework_dir = os.path.join(editor_mono_root_dir, "lib", "mono", "4.5") - editor_mono_framework_facades_dir = os.path.join(editor_mono_framework_dir, "Facades") + try: + lines = subprocess.check_output([dotnet_cmd, "--list-runtimes"]).splitlines() - if not os.path.isdir(editor_mono_framework_dir): - os.makedirs(editor_mono_framework_dir) - if not os.path.isdir(editor_mono_framework_facades_dir): - os.makedirs(editor_mono_framework_facades_dir) + for line_bytes in lines: + line = line_bytes.decode("utf-8") + if not line.startswith("Microsoft.NETCore.App "): + continue - for assembly in glob(os.path.join(mono_framework_dir, "*.dll")): - copy(assembly, editor_mono_framework_dir) - for assembly in glob(os.path.join(mono_framework_facades_dir, "*.dll")): - copy(assembly, editor_mono_framework_facades_dir) + parts = line.split(" ") + if len(parts) < 2: + continue + version = parts[1] -def copy_mono_etc_dir(mono_root, target_mono_config_dir, platform): - from distutils.dir_util import copy_tree - from glob import glob - from shutil import copy + # Look for 6.0.0 or 6.0.0-* + if version.startswith(search_version + "."): + return version + except (subprocess.CalledProcessError, OSError): + pass + return "" - if not os.path.isdir(target_mono_config_dir): - os.makedirs(target_mono_config_dir) - mono_etc_dir = os.path.join(mono_root, "etc", "mono") - if not os.path.isdir(mono_etc_dir): - mono_etc_dir = "" - etc_hint_dirs = [] - if platform != "windows": - etc_hint_dirs += ["/etc/mono", "/usr/local/etc/mono"] - if "MONO_CFG_DIR" in os.environ: - etc_hint_dirs += [os.path.join(os.environ["MONO_CFG_DIR"], "mono")] - for etc_hint_dir in etc_hint_dirs: - if os.path.isdir(etc_hint_dir): - mono_etc_dir = etc_hint_dir - break - if not mono_etc_dir: - raise RuntimeError("Mono installation etc directory not found") +ENV_PATH_SEP = ";" if os.name == "nt" else ":" - copy_tree(os.path.join(mono_etc_dir, "2.0"), os.path.join(target_mono_config_dir, "2.0")) - copy_tree(os.path.join(mono_etc_dir, "4.0"), os.path.join(target_mono_config_dir, "4.0")) - copy_tree(os.path.join(mono_etc_dir, "4.5"), os.path.join(target_mono_config_dir, "4.5")) - if os.path.isdir(os.path.join(mono_etc_dir, "mconfig")): - copy_tree(os.path.join(mono_etc_dir, "mconfig"), os.path.join(target_mono_config_dir, "mconfig")) - for file in glob(os.path.join(mono_etc_dir, "*")): - if os.path.isfile(file): - copy(file, target_mono_config_dir) +def find_executable(name): + is_windows = os.name == "nt" + windows_exts = os.environ["PATHEXT"].split(ENV_PATH_SEP) if is_windows else None + path_dirs = os.environ["PATH"].split(ENV_PATH_SEP) + search_dirs = path_dirs + [os.getcwd()] # cwd is last in the list -def copy_mono_shared_libs(env, mono_root, target_mono_root_dir): - from shutil import copy + for dir in search_dirs: + path = os.path.join(dir, name) - def copy_if_exists(src, dst): - if os.path.isfile(src): - copy(src, dst) + if is_windows: + for extension in windows_exts: + path_with_ext = path + extension - platform = env["platform"] + if os.path.isfile(path_with_ext) and os.access(path_with_ext, os.X_OK): + return path_with_ext + else: + if os.path.isfile(path) and os.access(path, os.X_OK): + return path - 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) - - mono_posix_helper_file = find_file_in_dir( - src_mono_bin_dir, ["MonoPosixHelper"], prefixes=["", "lib"], extensions=[".dll"] - ) - copy( - os.path.join(src_mono_bin_dir, mono_posix_helper_file), - 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") - ) - - if not os.path.isdir(target_mono_lib_dir): - os.makedirs(target_mono_lib_dir) - - lib_file_names = [] - if platform == "macos": - lib_file_names = [ - lib_name + ".dylib" - for lib_name in ["libmono-btls-shared", "libmono-native-compat", "libMonoPosixHelper"] - ] - 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", - ] - ] - - for lib_file_name in lib_file_names: - copy_if_exists(os.path.join(mono_root, "lib", lib_file_name), target_mono_lib_dir) - - -def pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext): - tmpenv = Environment() - tmpenv.AppendENVPath("PKG_CONFIG_PATH", os.getenv("PKG_CONFIG_PATH")) - tmpenv.ParseConfig("pkg-config monosgen-2 --libs-only-L") - for hint_dir in tmpenv["LIBPATH"]: - name_found = find_name_in_dir_files(hint_dir, mono_lib_names, prefixes=["lib"], extensions=[sharedlib_ext]) - if name_found and os.path.isdir(os.path.join(hint_dir, "..", "include", "mono-2.0")): - return os.path.join(hint_dir, "..") return "" diff --git a/modules/mono/build_scripts/mono_reg_utils.py b/modules/mono/build_scripts/mono_reg_utils.py deleted file mode 100644 index 43c1ec8f8a..0000000000 --- a/modules/mono/build_scripts/mono_reg_utils.py +++ /dev/null @@ -1,112 +0,0 @@ -import os -import platform - -if os.name == "nt": - import winreg - - -def _reg_open_key(key, subkey): - try: - return winreg.OpenKey(key, subkey) - except OSError: - if platform.architecture()[0] == "32bit": - bitness_sam = winreg.KEY_WOW64_64KEY - else: - bitness_sam = winreg.KEY_WOW64_32KEY - return winreg.OpenKey(key, subkey, 0, winreg.KEY_READ | bitness_sam) - - -def _reg_open_key_bits(key, subkey, bits): - sam = winreg.KEY_READ - - if platform.architecture()[0] == "32bit": - if bits == "64": - # Force 32bit process to search in 64bit registry - sam |= winreg.KEY_WOW64_64KEY - else: - if bits == "32": - # Force 64bit process to search in 32bit registry - sam |= winreg.KEY_WOW64_32KEY - - return winreg.OpenKey(key, subkey, 0, sam) - - -def _find_mono_in_reg(subkey, bits): - try: - with _reg_open_key_bits(winreg.HKEY_LOCAL_MACHINE, subkey, bits) as hKey: - value = winreg.QueryValueEx(hKey, "SdkInstallRoot")[0] - return value - except OSError: - return None - - -def _find_mono_in_reg_old(subkey, bits): - try: - with _reg_open_key_bits(winreg.HKEY_LOCAL_MACHINE, subkey, bits) as hKey: - default_clr = winreg.QueryValueEx(hKey, "DefaultCLR")[0] - if default_clr: - return _find_mono_in_reg(subkey + "\\" + default_clr, bits) - return None - except OSError: - return None - - -def find_mono_root_dir(bits): - root_dir = _find_mono_in_reg(r"SOFTWARE\Mono", bits) - if root_dir is not None: - return str(root_dir) - root_dir = _find_mono_in_reg_old(r"SOFTWARE\Novell\Mono", bits) - if root_dir is not None: - return str(root_dir) - return "" - - -def find_msbuild_tools_path_reg(): - import subprocess - - vswhere = os.getenv("PROGRAMFILES(X86)") - if not vswhere: - vswhere = os.getenv("PROGRAMFILES") - vswhere += r"\Microsoft Visual Studio\Installer\vswhere.exe" - - vswhere_args = ["-latest", "-products", "*", "-requires", "Microsoft.Component.MSBuild"] - - try: - lines = subprocess.check_output([vswhere] + vswhere_args).splitlines() - - for line in lines: - parts = line.decode("utf-8").split(":", 1) - - if len(parts) < 2 or parts[0] != "installationPath": - continue - - val = parts[1].strip() - - if not val: - raise ValueError("Value of `installationPath` entry is empty") - - # Since VS2019, the directory is simply named "Current" - msbuild_dir = os.path.join(val, "MSBuild\\Current\\Bin") - if os.path.isdir(msbuild_dir): - return msbuild_dir - - # Directory name "15.0" is used in VS 2017 - return os.path.join(val, "MSBuild\\15.0\\Bin") - - raise ValueError("Cannot find `installationPath` entry") - except ValueError as e: - print("Error reading output from vswhere: " + e.message) - except subprocess.CalledProcessError as e: - print(e.output) - except OSError as e: - print(e) - - # Try to find 14.0 in the Registry - - try: - subkey = r"SOFTWARE\Microsoft\MSBuild\ToolsVersions\14.0" - with _reg_open_key(winreg.HKEY_LOCAL_MACHINE, subkey) as hKey: - value = winreg.QueryValueEx(hKey, "MSBuildToolsPath")[0] - return value - except OSError: - return "" diff --git a/modules/mono/config.py b/modules/mono/config.py index 010f1ef1e5..d156877929 100644 --- a/modules/mono/config.py +++ b/modules/mono/config.py @@ -1,4 +1,6 @@ -supported_platforms = ["windows", "macos", "linuxbsd", "server", "android", "haiku", "javascript", "ios"] +# Prior to .NET Core, we supported these: ["windows", "macos", "linuxbsd", "server", "android", "haiku", "javascript", "ios"] +# Eventually support for each them should be added back (except Haiku if not supported by .NET Core) +supported_platforms = ["windows", "macos", "linuxbsd", "server"] def can_build(env, platform): @@ -13,26 +15,11 @@ def get_opts(platform): return [ PathVariable( - "mono_prefix", - "Path to the Mono installation directory for the target platform and architecture", + "dotnet_root", + "Path to the .NET Sdk installation directory for the target platform and architecture", "", PathVariable.PathAccept, ), - PathVariable( - "mono_bcl", - "Path to a custom Mono BCL (Base Class Library) directory for the target platform", - "", - PathVariable.PathAccept, - ), - BoolVariable("mono_static", "Statically link Mono", default_mono_static), - BoolVariable("build_cil", "Build C# solutions", True), - BoolVariable( - "copy_mono_root", "Make a copy of the Mono installation directory to bundle with the editor", True - ), - # TODO: It would be great if this could be detected automatically instead - BoolVariable( - "mono_bundles_zlib", "Specify if the Mono runtime was built with bundled zlib", default_mono_bundles_zlib - ), ] @@ -44,13 +31,6 @@ def configure(env): env.add_module_version_string("mono") - if env["mono_bundles_zlib"]: - # Mono may come with zlib bundled for WASM or on newer version when built with MinGW. - print("This Mono runtime comes with zlib bundled. Disabling '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 b088271b18..772c705981 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -30,8 +30,6 @@ #include "csharp_script.h" -#include <mono/metadata/threads.h> -#include <mono/metadata/tokentype.h> #include <stdint.h> #include "core/config/project_settings.h" @@ -59,7 +57,6 @@ #include "godotsharp_dirs.h" #include "managed_callable.h" #include "mono_gd/gd_mono_cache.h" -#include "mono_gd/gd_mono_utils.h" #include "signal_awaiter_utils.h" #include "utils/macros.h" #include "utils/string_utils.h" @@ -107,8 +104,12 @@ Error CSharpLanguage::execute_file(const String &p_path) { return OK; } -extern void *godotsharp_pinvoke_funcs[164]; +extern void *godotsharp_pinvoke_funcs[176]; [[maybe_unused]] volatile void **do_not_strip_godotsharp_pinvoke_funcs; +#ifdef TOOLS_ENABLED +extern void *godotsharp_editor_pinvoke_funcs[32]; +[[maybe_unused]] volatile void **do_not_strip_godotsharp_editor_pinvoke_funcs; +#endif void CSharpLanguage::init() { #ifdef DEBUG_METHODS_ENABLED @@ -122,6 +123,9 @@ void CSharpLanguage::init() { // Hopefully this will be enough for all compilers. Otherwise we could use the printf on fake getenv trick. do_not_strip_godotsharp_pinvoke_funcs = (volatile void **)godotsharp_pinvoke_funcs; +#ifdef TOOLS_ENABLED + do_not_strip_godotsharp_editor_pinvoke_funcs = (volatile void **)godotsharp_editor_pinvoke_funcs; +#endif #if defined(TOOLS_ENABLED) && defined(DEBUG_METHODS_ENABLED) // Generate the bindings here, before loading assemblies. The Godot assemblies @@ -709,19 +713,14 @@ void CSharpLanguage::pre_unsafe_unreference(Object *p_obj) { } void CSharpLanguage::frame() { - if (gdmono && gdmono->is_runtime_initialized() && gdmono->get_core_api_assembly() != nullptr) { - MonoException *exc = nullptr; - GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_FrameCallback.invoke(&exc); - if (exc) { - GDMonoUtils::debug_unhandled_exception(exc); - } + if (gdmono && gdmono->is_runtime_initialized() && GDMonoCache::godot_api_cache_updated) { + GDMonoCache::managed_callbacks.ScriptManagerBridge_FrameCallback(); } } void CSharpLanguage::reload_all_scripts() { #ifdef GD_MONO_HOT_RELOAD if (is_assembly_reloading_needed()) { - GD_MONO_SCOPE_THREAD_ATTACH; reload_assemblies(false); } #endif @@ -738,7 +737,6 @@ void CSharpLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_soft #ifdef GD_MONO_HOT_RELOAD if (is_assembly_reloading_needed()) { - GD_MONO_SCOPE_THREAD_ATTACH; reload_assemblies(p_soft_reload); } #endif @@ -750,6 +748,8 @@ bool CSharpLanguage::is_assembly_reloading_needed() { return false; } +#warning TODO +#if 0 GDMonoAssembly *proj_assembly = gdmono->get_project_assembly(); String appname_safe = ProjectSettings::get_singleton()->get_safe_project_name(); @@ -777,6 +777,9 @@ bool CSharpLanguage::is_assembly_reloading_needed() { } return true; +#else + return false; +#endif } void CSharpLanguage::reload_assemblies(bool p_soft_reload) { @@ -812,7 +815,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { MonoObject *managed_serialized_data = GDMonoMarshal::variant_to_mono_object(serialized_data); MonoException *exc = nullptr; - bool success = (bool)GDMonoCache::cached_data.methodthunk_DelegateUtils_TrySerializeDelegateWithGCHandle + bool success = (bool)GDMonoCache::managed_callbacks.methodthunk_DelegateUtils_TrySerializeDelegateWithGCHandle .invoke(managed_callable->delegate_handle, managed_serialized_data, &exc); @@ -1098,7 +1101,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { MonoDelegate *delegate = nullptr; MonoException *exc = nullptr; - bool success = (bool)GDMonoCache::cached_data.methodthunk_DelegateUtils_TryDeserializeDelegate.invoke(managed_serialized_data, &delegate, &exc); + bool success = (bool)GDMonoCache::managed_callbacks.methodthunk_DelegateUtils_TryDeserializeDelegate.invoke(managed_serialized_data, &delegate, &exc); if (exc) { GDMonoUtils::debug_print_unhandled_exception(exc); @@ -1135,7 +1138,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { void *delegate = nullptr; MonoException *exc = nullptr; - bool success = (bool)GDMonoCache::cached_data.methodthunk_DelegateUtils_TryDeserializeDelegateWithGCHandle + bool success = (bool)GDMonoCache::managed_callbacks.methodthunk_DelegateUtils_TryDeserializeDelegateWithGCHandle .invoke(managed_serialized_data, &delegate, &exc); if (exc) { @@ -1179,22 +1182,6 @@ bool CSharpLanguage::overrides_external_editor() { } #endif -void CSharpLanguage::thread_enter() { -#if 0 - if (gdmono->is_runtime_initialized()) { - GDMonoUtils::attach_current_thread(); - } -#endif -} - -void CSharpLanguage::thread_exit() { -#if 0 - if (gdmono->is_runtime_initialized()) { - GDMonoUtils::detach_current_thread(); - } -#endif -} - bool CSharpLanguage::debug_break_parse(const String &p_file, int p_line, const String &p_error) { // Not a parser error in our case, but it's still used for other type of errors if (EngineDebugger::is_active() && Thread::get_caller_id() == Thread::get_main_id()) { @@ -1241,27 +1228,16 @@ void CSharpLanguage::_on_scripts_domain_about_to_unload() { #ifdef TOOLS_ENABLED void CSharpLanguage::_editor_init_callback() { - register_editor_internal_calls(); - - // Initialize GodotSharpEditor - - MonoClass *editor_klass = mono_class_from_name( - GDMono::get_singleton()->get_tools_assembly()->get_image(), - "GodotTools", "GodotSharpEditor"); - CRASH_COND(editor_klass == nullptr); + // Load GodotTools and initialize GodotSharpEditor - MonoMethod *create_instance = mono_class_get_method_from_name(editor_klass, "InternalCreateInstance", 0); - CRASH_COND(create_instance == nullptr); - - MonoException *exc = nullptr; - MonoObject *ret = mono_runtime_invoke(create_instance, nullptr, nullptr, (MonoObject **)&exc); - UNHANDLED_EXCEPTION(exc); - - EditorPlugin *godotsharp_editor = *(EditorPlugin **)mono_object_unbox(ret); + Object *editor_plugin_obj = GDMono::get_singleton()->plugin_callbacks.LoadToolsAssemblyCallback( + GodotSharpDirs::get_data_editor_tools_dir().plus_file("GodotTools.dll").utf16()); + CRASH_COND(editor_plugin_obj == nullptr); + EditorPlugin *godotsharp_editor = Object::cast_to<EditorPlugin>(editor_plugin_obj); CRASH_COND(godotsharp_editor == nullptr); - // Enable it as a plugin + // Add plugin to EditorNode and enable it EditorNode::add_editor_plugin(godotsharp_editor); ED_SHORTCUT("mono/build_solution", TTR("Build Solution"), KeyModifierMask::ALT | Key::B); godotsharp_editor->enable_plugin(); @@ -1328,15 +1304,8 @@ bool CSharpLanguage::setup_csharp_script_binding(CSharpScriptBinding &r_script_b ERR_FAIL_COND_V_MSG(!parent_is_object_class, false, "Type inherits from native type '" + type_name + "', so it can't be instantiated in object of type: '" + p_object->get_class() + "'."); - MonoException *exc = nullptr; GCHandleIntPtr strong_gchandle = - GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_CreateManagedForGodotObjectBinding - .invoke(&type_name, p_object, &exc); - - if (exc) { - GDMonoUtils::set_pending_exception(exc); - return false; - } + GDMonoCache::managed_callbacks.ScriptManagerBridge_CreateManagedForGodotObjectBinding(&type_name, p_object); ERR_FAIL_NULL_V(strong_gchandle.value, false); @@ -1395,8 +1364,6 @@ void CSharpLanguage::_instance_binding_free_callback(void *, void *, void *p_bin return; // inside CSharpLanguage::finish(), all the gchandle bindings are released there } - GD_MONO_ASSERT_THREAD_ATTACHED; - { MutexLock lock(csharp_lang->language_bind_mutex); @@ -1407,10 +1374,8 @@ void CSharpLanguage::_instance_binding_free_callback(void *, void *, void *p_bin if (script_binding.inited) { // Set the native instance field to IntPtr.Zero, if not yet garbage collected. // This is done to avoid trying to dispose the native instance from Dispose(bool). - MonoException *exc = nullptr; - GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_SetGodotObjectPtr - .invoke(script_binding.gchandle.get_intptr(), nullptr, &exc); - UNHANDLED_EXCEPTION(exc); + GDMonoCache::managed_callbacks.ScriptManagerBridge_SetGodotObjectPtr( + script_binding.gchandle.get_intptr(), nullptr); script_binding.gchandle.release(); script_binding.inited = false; @@ -1442,8 +1407,6 @@ GDNativeBool CSharpLanguage::_instance_binding_reference_callback(void *p_token, if (p_reference) { // Refcount incremented if (refcount > 1 && gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0 - GD_MONO_SCOPE_THREAD_ATTACH; - // The reference count was increased after the managed side was the only one referencing our owner. // This means the owner is being referenced again by the unmanaged side, // so the owner must hold the managed side alive again to avoid it from being GCed. @@ -1455,10 +1418,8 @@ GDNativeBool CSharpLanguage::_instance_binding_reference_callback(void *p_token, GCHandleIntPtr new_gchandle; bool create_weak = false; - MonoException *exc = nullptr; - bool target_alive = GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_SwapGCHandleForType - .invoke(old_gchandle, &new_gchandle, create_weak, &exc); - UNHANDLED_EXCEPTION(exc); + bool target_alive = GDMonoCache::managed_callbacks.ScriptManagerBridge_SwapGCHandleForType( + old_gchandle, &new_gchandle, create_weak); if (!target_alive) { return false; // Called after the managed side was collected, so nothing to do here @@ -1471,8 +1432,6 @@ GDNativeBool CSharpLanguage::_instance_binding_reference_callback(void *p_token, } else { // Refcount decremented if (refcount == 1 && !gchandle.is_released() && !gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0 - GD_MONO_SCOPE_THREAD_ATTACH; - // If owner owner is no longer referenced by the unmanaged side, // the managed instance takes responsibility of deleting the owner when GCed. @@ -1483,10 +1442,8 @@ GDNativeBool CSharpLanguage::_instance_binding_reference_callback(void *p_token, GCHandleIntPtr new_gchandle; bool create_weak = true; - MonoException *exc = nullptr; - bool target_alive = GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_SwapGCHandleForType - .invoke(old_gchandle, &new_gchandle, create_weak, &exc); - UNHANDLED_EXCEPTION(exc); + bool target_alive = GDMonoCache::managed_callbacks.ScriptManagerBridge_SwapGCHandleForType( + old_gchandle, &new_gchandle, create_weak); if (!target_alive) { return refcount == 0; // Called after the managed side was collected, so nothing to do here @@ -1668,35 +1625,19 @@ Object *CSharpInstance::get_owner() { bool CSharpInstance::set(const StringName &p_name, const Variant &p_value) { ERR_FAIL_COND_V(!script.is_valid(), false); - GD_MONO_SCOPE_THREAD_ATTACH; - - MonoException *exc = nullptr; - bool ret = GDMonoCache::cached_data.methodthunk_CSharpInstanceBridge_Set.invoke( - gchandle.get_intptr(), &p_name, &p_value, &exc); - - if (exc) { - GDMonoUtils::set_pending_exception(exc); - } else if (ret) { - return true; - } - - return false; + return GDMonoCache::managed_callbacks.CSharpInstanceBridge_Set( + gchandle.get_intptr(), &p_name, &p_value); } bool CSharpInstance::get(const StringName &p_name, Variant &r_ret) const { ERR_FAIL_COND_V(!script.is_valid(), false); - GD_MONO_SCOPE_THREAD_ATTACH; - Variant ret_value; - MonoException *exc = nullptr; - bool ret = GDMonoCache::cached_data.methodthunk_CSharpInstanceBridge_Get.invoke( - gchandle.get_intptr(), &p_name, &ret_value, &exc); + bool ret = GDMonoCache::managed_callbacks.CSharpInstanceBridge_Get( + gchandle.get_intptr(), &p_name, &ret_value); - if (exc) { - GDMonoUtils::set_pending_exception(exc); - } else if (ret) { + if (ret) { r_ret = ret_value; return true; } @@ -1756,7 +1697,7 @@ void CSharpInstance::get_event_signals_state_for_reloading(List<Pair<StringName, MonoObject *managed_serialized_data = GDMonoMarshal::variant_to_mono_object(serialized_data); MonoException *exc = nullptr; - bool success = (bool)GDMonoCache::cached_data.methodthunk_DelegateUtils_TrySerializeDelegate + bool success = (bool)GDMonoCache::managed_callbacks.methodthunk_DelegateUtils_TrySerializeDelegate .invoke(delegate_field_value, managed_serialized_data, &exc); if (exc) { @@ -1781,26 +1722,25 @@ void CSharpInstance::get_property_list(List<PropertyInfo> *p_properties) const { ERR_FAIL_COND(!script.is_valid()); - GD_MONO_SCOPE_THREAD_ATTACH; - StringName method = SNAME("_get_property_list"); Variant ret; Callable::CallError call_error; - MonoException *exc = nullptr; - GDMonoCache::cached_data.methodthunk_CSharpInstanceBridge_Call.invoke( - gchandle.get_intptr(), &method, nullptr, 0, &call_error, &ret, &exc); - - if (exc) { - GDMonoUtils::set_pending_exception(exc); - } - - ERR_FAIL_COND_MSG(call_error.error != Callable::CallError::CALL_OK, - "Error calling '_get_property_list': " + Variant::get_call_error_text(method, nullptr, 0, call_error)); - - Array array = ret; - for (int i = 0, size = array.size(); i < size; i++) { - p_properties->push_back(PropertyInfo::from_dict(array.get(i))); + bool ok = GDMonoCache::managed_callbacks.CSharpInstanceBridge_Call( + gchandle.get_intptr(), &method, nullptr, 0, &call_error, &ret); + + // CALL_ERROR_INVALID_METHOD would simply mean it was not overridden + if (call_error.error != Callable::CallError::CALL_ERROR_INVALID_METHOD) { + if (call_error.error != Callable::CallError::CALL_OK) { + ERR_PRINT("Error calling '_get_property_list': " + Variant::get_call_error_text(method, nullptr, 0, call_error)); + } else if (!ok) { + ERR_PRINT("Unexpected error calling '_get_property_list'"); + } else { + Array array = ret; + for (int i = 0, size = array.size(); i < size; i++) { + p_properties->push_back(PropertyInfo::from_dict(array.get(i))); + } + } } for (const PropertyInfo &prop : props) { @@ -1826,22 +1766,13 @@ Variant::Type CSharpInstance::get_property_type(const StringName &p_name, bool * bool CSharpInstance::property_can_revert(const StringName &p_name) const { ERR_FAIL_COND_V(!script.is_valid(), false); - GD_MONO_SCOPE_THREAD_ATTACH; - - Callable::CallError call_error; - Variant name_arg = p_name; const Variant *args[1] = { &name_arg }; Variant ret; - MonoException *exc = nullptr; - GDMonoCache::cached_data.methodthunk_CSharpInstanceBridge_Call.invoke( - gchandle.get_intptr(), &CACHED_STRING_NAME(_property_can_revert), args, 1, &call_error, &ret, &exc); - - if (exc) { - GDMonoUtils::set_pending_exception(exc); - return false; - } + Callable::CallError call_error; + GDMonoCache::managed_callbacks.CSharpInstanceBridge_Call( + gchandle.get_intptr(), &CACHED_STRING_NAME(_property_can_revert), args, 1, &call_error, &ret); if (call_error.error != Callable::CallError::CALL_OK) { return false; @@ -1853,22 +1784,13 @@ bool CSharpInstance::property_can_revert(const StringName &p_name) const { bool CSharpInstance::property_get_revert(const StringName &p_name, Variant &r_ret) const { ERR_FAIL_COND_V(!script.is_valid(), false); - GD_MONO_SCOPE_THREAD_ATTACH; - - Callable::CallError call_error; - Variant name_arg = p_name; const Variant *args[1] = { &name_arg }; Variant ret; - MonoException *exc = nullptr; - GDMonoCache::cached_data.methodthunk_CSharpInstanceBridge_Call.invoke( - gchandle.get_intptr(), &CACHED_STRING_NAME(_property_get_revert), args, 1, &call_error, &ret, &exc); - - if (exc) { - GDMonoUtils::set_pending_exception(exc); - return false; - } + Callable::CallError call_error; + GDMonoCache::managed_callbacks.CSharpInstanceBridge_Call( + gchandle.get_intptr(), &CACHED_STRING_NAME(_property_get_revert), args, 1, &call_error, &ret); if (call_error.error != Callable::CallError::CALL_OK) { return false; @@ -1909,36 +1831,23 @@ bool CSharpInstance::has_method(const StringName &p_method) const { return false; } - GD_MONO_SCOPE_THREAD_ATTACH; - - if (!GDMonoCache::cached_data.godot_api_cache_updated) { + if (!GDMonoCache::godot_api_cache_updated) { return false; } String method = p_method; bool deep = true; - MonoException *exc = nullptr; - bool found = GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_HasMethodUnknownParams - .invoke(script.ptr(), &method, deep, &exc); - UNHANDLED_EXCEPTION(exc); - - return found; + return GDMonoCache::managed_callbacks.ScriptManagerBridge_HasMethodUnknownParams( + script.ptr(), &method, deep); } Variant CSharpInstance::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { ERR_FAIL_COND_V(!script.is_valid(), Variant()); - GD_MONO_SCOPE_THREAD_ATTACH; - Variant ret; - MonoException *exc = nullptr; - GDMonoCache::cached_data.methodthunk_CSharpInstanceBridge_Call.invoke( - gchandle.get_intptr(), &p_method, p_args, p_argcount, &r_error, &ret, &exc); - - if (exc) { - GDMonoUtils::set_pending_exception(exc); - } + GDMonoCache::managed_callbacks.CSharpInstanceBridge_Call( + gchandle.get_intptr(), &p_method, p_args, p_argcount, &r_error, &ret); return ret; } @@ -1992,13 +1901,10 @@ bool CSharpInstance::_internal_new_managed() { ERR_FAIL_NULL_V(owner, false); ERR_FAIL_COND_V(script.is_null(), false); - MonoException *exc = nullptr; - GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance - .invoke(script.ptr(), owner, nullptr, 0, &exc); - - if (exc) { - GDMonoUtils::set_pending_exception(exc); + bool ok = GDMonoCache::managed_callbacks.ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance( + script.ptr(), owner, nullptr, 0); + if (!ok) { // Important to clear this before destroying the script instance here script = Ref<CSharpScript>(); owner = nullptr; @@ -2084,8 +1990,6 @@ void CSharpInstance::refcount_incremented() { RefCounted *rc_owner = Object::cast_to<RefCounted>(owner); if (rc_owner->reference_get_count() > 1 && gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0 - GD_MONO_SCOPE_THREAD_ATTACH; - // The reference count was increased after the managed side was the only one referencing our owner. // This means the owner is being referenced again by the unmanaged side, // so the owner must hold the managed side alive again to avoid it from being GCed. @@ -2097,10 +2001,8 @@ void CSharpInstance::refcount_incremented() { GCHandleIntPtr new_gchandle; bool create_weak = false; - MonoException *exc = nullptr; - bool target_alive = GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_SwapGCHandleForType - .invoke(old_gchandle, &new_gchandle, create_weak, &exc); - UNHANDLED_EXCEPTION(exc); + bool target_alive = GDMonoCache::managed_callbacks.ScriptManagerBridge_SwapGCHandleForType( + old_gchandle, &new_gchandle, create_weak); if (!target_alive) { return; // Called after the managed side was collected, so nothing to do here @@ -2121,8 +2023,6 @@ bool CSharpInstance::refcount_decremented() { int refcount = rc_owner->reference_get_count(); if (refcount == 1 && !gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0 - GD_MONO_SCOPE_THREAD_ATTACH; - // If owner owner is no longer referenced by the unmanaged side, // the managed instance takes responsibility of deleting the owner when GCed. @@ -2133,10 +2033,8 @@ bool CSharpInstance::refcount_decremented() { GCHandleIntPtr new_gchandle; bool create_weak = true; - MonoException *exc = nullptr; - bool target_alive = GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_SwapGCHandleForType - .invoke(old_gchandle, &new_gchandle, create_weak, &exc); - UNHANDLED_EXCEPTION(exc); + bool target_alive = GDMonoCache::managed_callbacks.ScriptManagerBridge_SwapGCHandleForType( + old_gchandle, &new_gchandle, create_weak); if (!target_alive) { return refcount == 0; // Called after the managed side was collected, so nothing to do here @@ -2157,8 +2055,6 @@ const Variant CSharpInstance::get_rpc_config() const { } void CSharpInstance::notification(int p_notification) { - GD_MONO_SCOPE_THREAD_ATTACH; - if (p_notification == Object::NOTIFICATION_PREDELETE) { // When NOTIFICATION_PREDELETE is sent, we also take the chance to call Dispose(). // It's safe to call Dispose() multiple times and NOTIFICATION_PREDELETE is guaranteed @@ -2178,13 +2074,8 @@ void CSharpInstance::notification(int p_notification) { _call_notification(p_notification); - MonoException *exc = nullptr; - GDMonoCache::cached_data.methodthunk_CSharpInstanceBridge_CallDispose - .invoke(gchandle.get_intptr(), /* okIfNull */ false, &exc); - - if (exc) { - GDMonoUtils::set_pending_exception(exc); - } + GDMonoCache::managed_callbacks.CSharpInstanceBridge_CallDispose( + gchandle.get_intptr(), /* okIfNull */ false); return; } @@ -2193,8 +2084,6 @@ void CSharpInstance::notification(int p_notification) { } void CSharpInstance::_call_notification(int p_notification) { - GD_MONO_ASSERT_THREAD_ATTACHED; - Variant arg = p_notification; const Variant *args[1] = { &arg }; StringName method_name = SNAME("_notification"); @@ -2202,32 +2091,16 @@ void CSharpInstance::_call_notification(int p_notification) { Callable::CallError call_error; Variant ret; - MonoException *exc = nullptr; - GDMonoCache::cached_data.methodthunk_CSharpInstanceBridge_Call.invoke( - gchandle.get_intptr(), &method_name, args, 1, &call_error, &ret, &exc); - - if (exc) { - GDMonoUtils::set_pending_exception(exc); - } + GDMonoCache::managed_callbacks.CSharpInstanceBridge_Call( + gchandle.get_intptr(), &method_name, args, 1, &call_error, &ret); } String CSharpInstance::to_string(bool *r_valid) { - GD_MONO_SCOPE_THREAD_ATTACH; - String res; bool valid; - MonoException *exc = nullptr; - GDMonoCache::cached_data.methodthunk_CSharpInstanceBridge_CallToString - .invoke(gchandle.get_intptr(), &res, &valid, &exc); - - if (exc) { - GDMonoUtils::set_pending_exception(exc); - if (r_valid) { - *r_valid = false; - } - return String(); - } + GDMonoCache::managed_callbacks.CSharpInstanceBridge_CallToString( + gchandle.get_intptr(), &res, &valid); if (r_valid) { *r_valid = valid; @@ -2249,8 +2122,6 @@ CSharpInstance::CSharpInstance(const Ref<CSharpScript> &p_script) : } CSharpInstance::~CSharpInstance() { - GD_MONO_SCOPE_THREAD_ATTACH; - destructing_script_instance = true; // Must make sure event signals are not left dangling @@ -2264,13 +2135,8 @@ CSharpInstance::~CSharpInstance() { // we must call Dispose here, because Dispose calls owner->set_script_instance(nullptr) // and that would mess up with the new script instance if called later. - MonoException *exc = nullptr; - GDMonoCache::cached_data.methodthunk_CSharpInstanceBridge_CallDispose - .invoke(gchandle.get_intptr(), /* okIfNull */ true, &exc); - - if (exc) { - GDMonoUtils::set_pending_exception(exc); - } + GDMonoCache::managed_callbacks.CSharpInstanceBridge_CallDispose( + gchandle.get_intptr(), /* okIfNull */ true); } gchandle.release(); // Make sure the gchandle is released @@ -2341,8 +2207,6 @@ void CSharpScript::_update_exports_values(HashMap<StringName, Variant> &values, void CSharpScript::_update_member_info_no_exports() { if (exports_invalidated) { - GD_MONO_ASSERT_THREAD_ATTACHED; - exports_invalidated = false; member_info.clear(); @@ -2888,10 +2752,8 @@ void CSharpScript::update_script_class_info(Ref<CSharpScript> p_script) { // only for this, so need to call the destructor manually before passing this to C#. rpc_functions_dict.~Dictionary(); - MonoException *exc = nullptr; - GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_UpdateScriptClassInfo - .invoke(p_script.ptr(), &tool, &rpc_functions_dict, &exc); - UNHANDLED_EXCEPTION(exc); + GDMonoCache::managed_callbacks.ScriptManagerBridge_UpdateScriptClassInfo( + p_script.ptr(), &tool, &rpc_functions_dict); p_script->tool = tool; @@ -2910,13 +2772,7 @@ bool CSharpScript::can_instantiate() const { // For tool scripts, this will never fire if the class is not found. That's because we // don't know if it's a tool script if we can't find the class to access the attributes. if (extra_cond && !valid) { - if (GDMono::get_singleton()->get_project_assembly() == nullptr) { - // The project assembly is not loaded - ERR_FAIL_V_MSG(false, "Cannot instance script because the project assembly is not loaded. Script: '" + get_path() + "'."); - } else { - // The project assembly is loaded, but the class could not found - ERR_FAIL_V_MSG(false, "Cannot instance script because the associated class could not be found. Script: '" + get_path() + "'."); - } + ERR_FAIL_V_MSG(false, "Cannot instance script because the associated class could not be found. Script: '" + get_path() + "'."); } return valid && extra_cond; @@ -2924,16 +2780,11 @@ bool CSharpScript::can_instantiate() const { StringName CSharpScript::get_instance_base_type() const { StringName native_name; - MonoException *exc = nullptr; - GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_GetScriptNativeName - .invoke(this, &native_name, &exc); - UNHANDLED_EXCEPTION(exc); + GDMonoCache::managed_callbacks.ScriptManagerBridge_GetScriptNativeName(this, &native_name); return native_name; } CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_is_ref_counted, Callable::CallError &r_error) { - GD_MONO_ASSERT_THREAD_ATTACHED; - /* STEP 1, CREATE */ Ref<RefCounted> ref; @@ -2949,13 +2800,8 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->get(); if (script_binding.inited && !script_binding.gchandle.is_released()) { - MonoException *exc = nullptr; - GDMonoCache::cached_data.methodthunk_CSharpInstanceBridge_CallDispose - .invoke(script_binding.gchandle.get_intptr(), /* okIfNull */ true, &exc); - - if (exc) { - GDMonoUtils::set_pending_exception(exc); - } + GDMonoCache::managed_callbacks.CSharpInstanceBridge_CallDispose( + script_binding.gchandle.get_intptr(), /* okIfNull */ true); script_binding.gchandle.release(); // Just in case script_binding.inited = false; @@ -2969,13 +2815,10 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg /* STEP 2, INITIALIZE AND CONSTRUCT */ - MonoException *exc = nullptr; - GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance - .invoke(this, p_owner, p_args, p_argcount, &exc); - - if (exc) { - GDMonoUtils::set_pending_exception(exc); + bool ok = GDMonoCache::managed_callbacks.ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance( + this, p_owner, p_args, p_argcount); + if (!ok) { // Important to clear this before destroying the script instance here instance->script = Ref<CSharpScript>(); instance->owner = nullptr; @@ -3001,15 +2844,10 @@ Variant CSharpScript::_new(const Variant **p_args, int p_argcount, Callable::Cal r_error.error = Callable::CallError::CALL_OK; StringName native_name; - MonoException *exc = nullptr; - GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_GetScriptNativeName - .invoke(this, &native_name, &exc); - UNHANDLED_EXCEPTION(exc); + GDMonoCache::managed_callbacks.ScriptManagerBridge_GetScriptNativeName(this, &native_name); ERR_FAIL_COND_V(native_name == StringName(), Variant()); - GD_MONO_SCOPE_THREAD_ATTACH; - Object *owner = ClassDB::instantiate(native_name); Ref<RefCounted> ref; @@ -3038,13 +2876,8 @@ ScriptInstance *CSharpScript::instance_create(Object *p_this) { CRASH_COND(!valid); #endif - GD_MONO_SCOPE_THREAD_ATTACH; - StringName native_name; - MonoException *exc = nullptr; - GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_GetScriptNativeName - .invoke(this, &native_name, &exc); - UNHANDLED_EXCEPTION(exc); + GDMonoCache::managed_callbacks.ScriptManagerBridge_GetScriptNativeName(this, &native_name); ERR_FAIL_COND_V(native_name == StringName(), nullptr); @@ -3100,8 +2933,6 @@ void CSharpScript::get_script_method_list(List<MethodInfo> *p_list) const { return; } - GD_MONO_SCOPE_THREAD_ATTACH; - #warning TODO #if 0 // TODO: We're filtering out constructors but there may be other methods unsuitable for explicit calls. @@ -3126,19 +2957,15 @@ bool CSharpScript::has_method(const StringName &p_method) const { return false; } - GD_MONO_SCOPE_THREAD_ATTACH; - - if (!GDMonoCache::cached_data.godot_api_cache_updated) { + if (!GDMonoCache::godot_api_cache_updated) { return false; } String method = p_method; bool deep = false; - MonoException *exc = nullptr; - bool found = GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_HasMethodUnknownParams - .invoke(this, &method, deep, &exc); - UNHANDLED_EXCEPTION(exc); + bool found = GDMonoCache::managed_callbacks.ScriptManagerBridge_HasMethodUnknownParams( + this, &method, deep); return found; } @@ -3148,8 +2975,6 @@ MethodInfo CSharpScript::get_method_info(const StringName &p_method) const { return MethodInfo(); } - GD_MONO_SCOPE_THREAD_ATTACH; - #warning TODO #if 0 GDMonoClass *top = script_class; @@ -3176,14 +3001,9 @@ Error CSharpScript::reload(bool p_keep_state) { // That's done separately via domain reloading. reload_invalidated = false; - GD_MONO_SCOPE_THREAD_ATTACH; - String script_path = get_path(); - MonoException *exc = nullptr; - valid = GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_AddScriptBridge - .invoke(this, &script_path, &exc); - UNHANDLED_EXCEPTION(exc); + valid = GDMonoCache::managed_callbacks.ScriptManagerBridge_AddScriptBridge(this, &script_path); if (valid) { #ifdef DEBUG_ENABLED @@ -3230,18 +3050,13 @@ bool CSharpScript::has_script_signal(const StringName &p_signal) const { return false; } - if (!GDMonoCache::cached_data.godot_api_cache_updated) { + if (!GDMonoCache::godot_api_cache_updated) { return false; } String signal = p_signal; - MonoException *exc = nullptr; - bool res = GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_HasScriptSignal - .invoke(this, &signal, &exc); - UNHANDLED_EXCEPTION(exc); - - return res; + return GDMonoCache::managed_callbacks.ScriptManagerBridge_HasScriptSignal(this, &signal); } void CSharpScript::get_script_signal_list(List<MethodInfo> *r_signals) const { @@ -3251,7 +3066,7 @@ void CSharpScript::get_script_signal_list(List<MethodInfo> *r_signals) const { // Performance is not critical here as this will be replaced with source generators. - if (!GDMonoCache::cached_data.godot_api_cache_updated) { + if (!GDMonoCache::godot_api_cache_updated) { return; } @@ -3260,10 +3075,7 @@ void CSharpScript::get_script_signal_list(List<MethodInfo> *r_signals) const { // only for this, so need to call the destructor manually before passing this to C#. signals_dict.~Dictionary(); - MonoException *exc = nullptr; - GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_GetScriptSignalList - .invoke(this, &signals_dict, &exc); - UNHANDLED_EXCEPTION(exc); + GDMonoCache::managed_callbacks.ScriptManagerBridge_GetScriptSignalList(this, &signals_dict); for (const Variant *s = signals_dict.next(nullptr); s != nullptr; s = signals_dict.next(s)) { MethodInfo mi; @@ -3296,16 +3108,11 @@ bool CSharpScript::inherits_script(const Ref<Script> &p_script) const { return false; } - if (!GDMonoCache::cached_data.godot_api_cache_updated) { + if (!GDMonoCache::godot_api_cache_updated) { return false; } - MonoException *exc = nullptr; - bool res = GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_ScriptIsOrInherits - .invoke(this, cs.ptr(), &exc); - UNHANDLED_EXCEPTION(exc); - - return res; + return GDMonoCache::managed_callbacks.ScriptManagerBridge_ScriptIsOrInherits(this, cs.ptr()); } Ref<Script> CSharpScript::get_base_script() const { @@ -3379,10 +3186,8 @@ CSharpScript::~CSharpScript() { CSharpLanguage::get_singleton()->script_list.remove(&this->script_list); #endif - if (GDMonoCache::cached_data.godot_api_cache_updated) { - MonoException *exc = nullptr; - GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_RemoveScriptBridge.invoke(this, &exc); - UNHANDLED_EXCEPTION(exc); + if (GDMonoCache::godot_api_cache_updated) { + GDMonoCache::managed_callbacks.ScriptManagerBridge_RemoveScriptBridge(this); } } diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h index 34b2e7f735..9be4c9c130 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -39,7 +39,6 @@ #include "mono_gc_handle.h" #include "mono_gd/gd_mono.h" -#include "mono_gd/gd_mono_internals.h" #ifdef TOOLS_ENABLED #include "editor/editor_plugin.h" @@ -465,10 +464,6 @@ public: bool overrides_external_editor() override; #endif - /* THREAD ATTACHING */ - void thread_enter() override; - void thread_exit() override; - RBMap<Object *, CSharpScriptBinding>::Element *insert_script_binding(Object *p_object, const CSharpScriptBinding &p_script_binding); bool setup_csharp_script_binding(CSharpScriptBinding &r_script_binding, Object *p_object); @@ -476,9 +471,12 @@ public: static void tie_user_managed_to_unmanaged(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged, CSharpScript *p_script, bool p_ref_counted); static void tie_managed_to_unmanaged_with_pre_setup(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged); +#warning TODO +#if 0 #ifdef DEBUG_ENABLED Vector<StackInfo> stack_trace_get_info(MonoObject *p_stack_trace); #endif +#endif void post_unsafe_reference(Object *p_obj); void pre_unsafe_unreference(Object *p_obj); diff --git a/modules/mono/doc_classes/GodotSharp.xml b/modules/mono/doc_classes/GodotSharp.xml index b981542801..faf3512da7 100644 --- a/modules/mono/doc_classes/GodotSharp.xml +++ b/modules/mono/doc_classes/GodotSharp.xml @@ -10,55 +10,10 @@ <tutorials> </tutorials> <methods> - <method name="attach_thread"> - <return type="void" /> - <description> - Attaches the current thread to the Mono runtime. - </description> - </method> - <method name="detach_thread"> - <return type="void" /> - <description> - Detaches the current thread from the Mono runtime. - </description> - </method> - <method name="get_domain_id"> - <return type="int" /> - <description> - Returns the current MonoDomain ID. - [b]Note:[/b] The Mono runtime must be initialized for this method to work (use [method is_runtime_initialized] to check). If the Mono runtime isn't initialized at the time this method is called, the engine will crash. - </description> - </method> - <method name="get_scripts_domain_id"> - <return type="int" /> - <description> - Returns the scripts MonoDomain's ID. This will be the same MonoDomain ID as [method get_domain_id], unless the scripts domain isn't loaded. - [b]Note:[/b] The Mono runtime must be initialized for this method to work (use [method is_runtime_initialized] to check). If the Mono runtime isn't initialized at the time this method is called, the engine will crash. - </description> - </method> - <method name="is_domain_finalizing_for_unload"> - <return type="bool" /> - <param index="0" name="domain_id" type="int" /> - <description> - Returns [code]true[/code] if the domain is being finalized, [code]false[/code] otherwise. - </description> - </method> <method name="is_runtime_initialized"> <return type="bool" /> <description> - Returns [code]true[/code] if the Mono runtime is initialized, [code]false[/code] otherwise. - </description> - </method> - <method name="is_runtime_shutting_down"> - <return type="bool" /> - <description> - Returns [code]true[/code] if the Mono runtime is shutting down, [code]false[/code] otherwise. - </description> - </method> - <method name="is_scripts_domain_loaded"> - <return type="bool" /> - <description> - Returns [code]true[/code] if the scripts domain is loaded, [code]false[/code] otherwise. + Returns [code]true[/code] if the .NET runtime is initialized, [code]false[/code] otherwise. </description> </method> </methods> diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk.sln b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk.sln index d1868f52ef..03a7dc453c 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk.sln +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Godot.NET.Sdk", "Godot.NET.Sdk\Godot.NET.Sdk.csproj", "{31B00BFA-DEA1-42FA-A472-9E54A92A8A5F}" EndProject diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Godot.SourceGenerators.Sample.csproj b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Godot.SourceGenerators.Sample.csproj index 24f7909861..c5a29a53f7 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Godot.SourceGenerators.Sample.csproj +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Godot.SourceGenerators.Sample.csproj @@ -1,7 +1,7 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <TargetFramework>netstandard2.1</TargetFramework> + <TargetFramework>net5.0</TargetFramework> </PropertyGroup> <PropertyGroup> diff --git a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs index 2bf1cb7a18..01aa65bfc3 100644 --- a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs +++ b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs @@ -7,8 +7,6 @@ namespace GodotTools.BuildLogger { public class GodotBuildLogger : ILogger { - public static readonly string AssemblyPath = Path.GetFullPath(typeof(GodotBuildLogger).Assembly.Location); - public string Parameters { get; set; } public LoggerVerbosity Verbosity { get; set; } diff --git a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotTools.BuildLogger.csproj b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotTools.BuildLogger.csproj index 0afec970c6..9e36497b06 100644 --- a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotTools.BuildLogger.csproj +++ b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotTools.BuildLogger.csproj @@ -5,6 +5,6 @@ <LangVersion>7.2</LangVersion> </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.Build.Framework" Version="16.5.0" /> + <PackageReference Include="Microsoft.Build.Framework" Version="15.1.548" ExcludeAssets="runtime" /> </ItemGroup> </Project> diff --git a/modules/mono/editor/GodotTools/GodotTools.Core/GodotTools.Core.csproj b/modules/mono/editor/GodotTools/GodotTools.Core/GodotTools.Core.csproj index d6d8962f90..caf0b9c7bb 100644 --- a/modules/mono/editor/GodotTools/GodotTools.Core/GodotTools.Core.csproj +++ b/modules/mono/editor/GodotTools/GodotTools.Core/GodotTools.Core.csproj @@ -1,7 +1,7 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <ProjectGuid>{639E48BD-44E5-4091-8EDD-22D36DC0768D}</ProjectGuid> - <TargetFramework>netstandard2.0</TargetFramework> + <TargetFramework>net5.0</TargetFramework> <LangVersion>7.2</LangVersion> </PropertyGroup> </Project> diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/GodotTools.IdeMessaging.CLI.csproj b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/GodotTools.IdeMessaging.CLI.csproj index 303ca3a293..d2132115f3 100644 --- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/GodotTools.IdeMessaging.CLI.csproj +++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/GodotTools.IdeMessaging.CLI.csproj @@ -1,4 +1,4 @@ -<Project Sdk="Microsoft.NET.Sdk"> +<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <ProjectGuid>{B06C2951-C8E3-4F28-80B2-717CF327EB19}</ProjectGuid> <OutputType>Exe</OutputType> diff --git a/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj index 5b3ed0b1b7..c05096bdcc 100644 --- a/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj +++ b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj @@ -1,4 +1,4 @@ -<Project Sdk="Microsoft.NET.Sdk"> +<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <ProjectGuid>{EAFFF236-FA96-4A4D-BD23-0E51EF988277}</ProjectGuid> <OutputType>Exe</OutputType> diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj index 37123ba2b2..dff40fb846 100644 --- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj +++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj @@ -1,32 +1,16 @@ -<Project Sdk="Microsoft.NET.Sdk"> +<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <ProjectGuid>{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}</ProjectGuid> - <TargetFramework>net472</TargetFramework> + <TargetFramework>net5.0</TargetFramework> <LangVersion>7.2</LangVersion> </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.Build" Version="16.5.0" /> + <PackageReference Include="Microsoft.Build" Version="15.1.548" ExcludeAssets="runtime" /> + <PackageReference Include="Microsoft.Build.Locator" Version="1.2.6" /> <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\GodotTools.Core\GodotTools.Core.csproj" /> <ProjectReference Include="..\GodotTools.Shared\GodotTools.Shared.csproj" /> </ItemGroup> - <!-- - The Microsoft.Build.Runtime package is too problematic so we create a MSBuild.exe stub. The workaround described - here doesn't work with Microsoft.NETFramework.ReferenceAssemblies: https://github.com/microsoft/msbuild/issues/3486 - We need a MSBuild.exe file as there's an issue in Microsoft.Build where it executes platform dependent code when - searching for MSBuild.exe before the fallback to not using it. A stub is fine as it should never be executed. - --> - <ItemGroup> - <None Include="MSBuild.exe" CopyToOutputDirectory="Always" /> - </ItemGroup> - <Target Name="CopyMSBuildStubWindows" AfterTargets="Build" Condition=" '$(GodotPlatform)' == 'windows' Or ( '$(GodotPlatform)' == '' And '$(OS)' == 'Windows_NT' ) "> - <PropertyGroup> - <GodotSourceRootPath>$(SolutionDir)/../../../../</GodotSourceRootPath> - <GodotOutputDataDir>$(GodotSourceRootPath)/bin/GodotSharp</GodotOutputDataDir> - </PropertyGroup> - <!-- Need to copy it here as well on Windows --> - <Copy SourceFiles="MSBuild.exe" DestinationFiles="$(GodotOutputDataDir)\Mono\lib\mono\v4.0\MSBuild.exe" /> - </Target> </Project> diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/MSBuild.exe b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/MSBuild.exe deleted file mode 100644 index e69de29bb2..0000000000 --- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/MSBuild.exe +++ /dev/null diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs index 7d49d251dd..c549cf5f12 100644 --- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs +++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs @@ -21,7 +21,8 @@ namespace GodotTools.ProjectEditor root.Sdk = GodotSdkAttrValue; var mainGroup = root.AddPropertyGroup(); - mainGroup.AddProperty("TargetFramework", "netstandard2.1"); + mainGroup.AddProperty("TargetFramework", "net5.0"); + mainGroup.AddProperty("EnableDynamicLoading", "true"); string sanitizedName = IdentifierUtils.SanitizeQualifiedIdentifier(name, allowEmptyIdentifiers: true); diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs index cdac9acb25..9b921c517c 100644 --- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs +++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs @@ -19,6 +19,9 @@ namespace GodotTools.ProjectEditor public static class ProjectUtils { + public static void MSBuildLocatorRegisterDefaults() + => Microsoft.Build.Locator.MSBuildLocator.RegisterDefaults(); + public static MSBuildProject Open(string path) { var root = ProjectRootElement.Open(path); @@ -42,7 +45,8 @@ namespace GodotTools.ProjectEditor var root = project.Root; string godotSdkAttrValue = ProjectGenerator.GodotSdkAttrValue; - if (!string.IsNullOrEmpty(root.Sdk) && root.Sdk.Trim().Equals(godotSdkAttrValue, StringComparison.OrdinalIgnoreCase)) + if (!string.IsNullOrEmpty(root.Sdk) && + root.Sdk.Trim().Equals(godotSdkAttrValue, StringComparison.OrdinalIgnoreCase)) return; root.Sdk = godotSdkAttrValue; diff --git a/modules/mono/editor/GodotTools/GodotTools.Shared/GodotTools.Shared.csproj b/modules/mono/editor/GodotTools/GodotTools.Shared/GodotTools.Shared.csproj index 3bc1698c15..4b058a5daa 100644 --- a/modules/mono/editor/GodotTools/GodotTools.Shared/GodotTools.Shared.csproj +++ b/modules/mono/editor/GodotTools/GodotTools.Shared/GodotTools.Shared.csproj @@ -1,6 +1,6 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <TargetFramework>netstandard2.0</TargetFramework> + <TargetFramework>net5.0</TargetFramework> </PropertyGroup> <Import Project="GenerateGodotNupkgsVersions.targets" /> </Project> diff --git a/modules/mono/editor/GodotTools/GodotTools.sln b/modules/mono/editor/GodotTools/GodotTools.sln index d3107a69db..415e49b426 100644 --- a/modules/mono/editor/GodotTools/GodotTools.sln +++ b/modules/mono/editor/GodotTools/GodotTools.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2012 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.ProjectEditor", "GodotTools.ProjectEditor\GodotTools.ProjectEditor.csproj", "{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}" diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs index 33967ffa71..58677625c6 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs @@ -1,9 +1,9 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Threading.Tasks; using GodotTools.Ides.Rider; using GodotTools.Internals; -using JetBrains.Annotations; using static GodotTools.Internals.Globals; using File = GodotTools.Utils.File; using OS = GodotTools.Utils.OS; @@ -159,7 +159,7 @@ namespace GodotTools.Build } } - public static bool BuildProjectBlocking(string config, [CanBeNull] string[] targets = null, [CanBeNull] string platform = null) + public static bool BuildProjectBlocking(string config, [MaybeNull] string[] targets = null, [MaybeNull] string platform = null) { var buildInfo = new BuildInfo(GodotSharpDirs.ProjectSlnPath, targets ?? new[] {"Build"}, config, restore: true); diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs index ebdaca0ce8..ed5ee10585 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs @@ -1,8 +1,8 @@ using Godot; using System; +using System.Diagnostics.CodeAnalysis; using Godot.Collections; using GodotTools.Internals; -using JetBrains.Annotations; using File = GodotTools.Utils.File; using Path = System.IO.Path; diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs index 02e9d98647..0e793a44ba 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs @@ -127,7 +127,7 @@ namespace GodotTools.Build arguments += $@" /t:{string.Join(",", buildInfo.Targets)} " + $@"""/p:{"Configuration=" + buildInfo.Configuration}"" /v:normal " + - $@"""/l:{typeof(GodotBuildLogger).FullName},{GodotBuildLogger.AssemblyPath};{buildInfo.LogsDirPath}"""; + $@"""{AddLoggerArgument(buildInfo)}"""; foreach (string customProperty in buildInfo.CustomProperties) { @@ -137,6 +137,14 @@ namespace GodotTools.Build return arguments; } + private static string AddLoggerArgument(BuildInfo buildInfo) + { + string buildLoggerPath = Path.Combine(GodotSharpDirs.DataEditorToolsDir, + "GodotTools.BuildLogger.dll"); + + return $"/l:{typeof(GodotBuildLogger).FullName},{buildLoggerPath};{buildInfo.LogsDirPath}"; + } + private static void RemovePlatformVariable(StringDictionary environmentVariables) { // EnvironmentVariables is case sensitive? Seriously? diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs index 3c020a2589..13b3ab7da2 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs @@ -1,7 +1,6 @@ using System; using Godot; using GodotTools.Internals; -using JetBrains.Annotations; using static GodotTools.Internals.Globals; using File = GodotTools.Utils.File; @@ -28,7 +27,6 @@ namespace GodotTools.Build BuildOutputView.UpdateIssuesList(); } - [UsedImplicitly] public void BuildSolution() { if (!File.Exists(GodotSharpDirs.ProjectSlnPath)) @@ -57,7 +55,6 @@ namespace GodotTools.Build Internal.ReloadAssemblies(softReload: false); } - [UsedImplicitly] private void RebuildSolution() { if (!File.Exists(GodotSharpDirs.ProjectSlnPath)) @@ -86,7 +83,6 @@ namespace GodotTools.Build Internal.ReloadAssemblies(softReload: false); } - [UsedImplicitly] private void CleanSolution() { if (!File.Exists(GodotSharpDirs.ProjectSlnPath)) diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs index ee3f5999cd..25c2e4ab59 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs @@ -3,13 +3,13 @@ using Godot.NativeInterop; using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Runtime.CompilerServices; using GodotTools.Build; using GodotTools.Core; using GodotTools.Internals; -using JetBrains.Annotations; using static GodotTools.Internals.Globals; using Directory = GodotTools.Utils.Directory; using File = GodotTools.Utils.File; @@ -238,8 +238,9 @@ namespace GodotTools.Export using godot_string buildConfigAux = Marshaling.mono_string_to_godot(buildConfig); using godot_string bclDirAux = Marshaling.mono_string_to_godot(bclDir); godot_dictionary assembliesAux = ((Godot.Collections.Dictionary)assemblies).NativeValue; - internal_GetExportedAssemblyDependencies(initialAssembliesAux, buildConfigAux, bclDirAux, - ref assembliesAux); + // TODO + throw new NotImplementedException(); + //internal_GetExportedAssemblyDependencies(initialAssembliesAux, buildConfigAux, bclDirAux, ref assembliesAux); AddI18NAssemblies(assemblies, bclDir); @@ -349,7 +350,7 @@ namespace GodotTools.Export } } - [NotNull] + [return: NotNull] private static string ExportDataDirectory(string[] features, string platform, bool isDebug, string outputDir) { string target = isDebug ? "release_debug" : "release"; @@ -498,10 +499,5 @@ namespace GodotTools.Export string appNameSafe = appName.ToSafeDirName(); return $"data_{appNameSafe}"; } - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void internal_GetExportedAssemblyDependencies( - in godot_dictionary initialAssemblies, in godot_string buildConfig, - in godot_string customBclDir, ref godot_dictionary dependencyAssemblies); } } diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs index 5167333716..2d85513766 100644 --- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs +++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs @@ -51,6 +51,7 @@ namespace GodotTools } } + [UsedImplicitly] private bool CreateProjectSolution() { using (var pr = new EditorProgress("create_csharp_solution", "Generating solution...".TTR(), 2)) @@ -75,7 +76,7 @@ namespace GodotTools { Guid = guid, PathRelativeToSolution = name + ".csproj", - Configs = new List<string> {"Debug", "ExportDebug", "ExportRelease"} + Configs = new List<string> { "Debug", "ExportDebug", "ExportRelease" } }; solution.AddNewProject(name, projectInfo); @@ -123,7 +124,8 @@ namespace GodotTools try { string fallbackFolder = NuGetUtils.GodotFallbackFolderPath; - NuGetUtils.AddFallbackFolderToUserNuGetConfigs(NuGetUtils.GodotFallbackFolderName, fallbackFolder); + NuGetUtils.AddFallbackFolderToUserNuGetConfigs(NuGetUtils.GodotFallbackFolderName, + fallbackFolder); NuGetUtils.AddBundledPackagesToFallbackFolder(fallbackFolder); } catch (Exception e) @@ -201,13 +203,15 @@ namespace GodotTools try { if (Godot.OS.IsStdoutVerbose()) - Console.WriteLine($"Running: \"{command}\" {string.Join(" ", args.Select(a => $"\"{a}\""))}"); + Console.WriteLine( + $"Running: \"{command}\" {string.Join(" ", args.Select(a => $"\"{a}\""))}"); OS.RunProcess(command, args); } catch (Exception e) { - GD.PushError($"Error when trying to run code editor: VisualStudio. Exception message: '{e.Message}'"); + GD.PushError( + $"Error when trying to run code editor: VisualStudio. Exception message: '{e.Message}'"); } break; @@ -378,6 +382,8 @@ namespace GodotTools { base._EnablePlugin(); + ProjectUtils.MSBuildLocatorRegisterDefaults(); + if (Instance != null) throw new InvalidOperationException(); Instance = this; @@ -393,7 +399,7 @@ namespace GodotTools MSBuildPanel = new MSBuildPanel(); _bottomPanelBtn = AddControlToBottomPanel(MSBuildPanel, "MSBuild".TTR()); - AddChild(new HotReloadAssemblyWatcher {Name = "HotReloadAssemblyWatcher"}); + AddChild(new HotReloadAssemblyWatcher { Name = "HotReloadAssemblyWatcher" }); _menuPopup = new PopupMenu(); _menuPopup.Hide(); @@ -469,7 +475,8 @@ namespace GodotTools try { // At startup we make sure NuGet.Config files have our Godot NuGet fallback folder included - NuGetUtils.AddFallbackFolderToUserNuGetConfigs(NuGetUtils.GodotFallbackFolderName, NuGetUtils.GodotFallbackFolderPath); + NuGetUtils.AddFallbackFolderToUserNuGetConfigs(NuGetUtils.GodotFallbackFolderName, + NuGetUtils.GodotFallbackFolderPath); } catch (Exception e) { diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj index d44cd75155..d0fae02d5d 100644 --- a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj +++ b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj @@ -1,7 +1,8 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <ProjectGuid>{27B00618-A6F2-4828-B922-05CAEB08C286}</ProjectGuid> - <TargetFramework>net472</TargetFramework> + <TargetFramework>net5.0</TargetFramework> + <EnableDynamicLoading>true</EnableDynamicLoading> <LangVersion>8</LangVersion> <!-- The Godot editor uses the Debug Godot API assemblies --> <GodotApiConfiguration>Debug</GodotApiConfiguration> @@ -21,6 +22,8 @@ <PackageReference Include="JetBrains.Annotations" Version="2019.1.3.0" ExcludeAssets="runtime" PrivateAssets="all" /> <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> + <!-- For RiderPathLocator --> + <PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" /> <Reference Include="GodotSharp"> <HintPath>$(GodotApiAssembliesDir)/GodotSharp.dll</HintPath> <Private>False</Private> diff --git a/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs b/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs index 43b1cf0f64..414729b18e 100644 --- a/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs +++ b/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs @@ -1,5 +1,6 @@ using Godot; using GodotTools.Internals; +using JetBrains.Annotations; using static GodotTools.Internals.Globals; namespace GodotTools @@ -25,6 +26,7 @@ namespace GodotTools Internal.ReloadAssemblies(softReload: false); } + [UsedImplicitly] public void RestartTimer() { _watchTimer.Stop(); diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs index 71055f0125..4caab035de 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs @@ -1,9 +1,10 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; +using System.Runtime.Versioning; using Godot; -using JetBrains.Annotations; using Microsoft.Win32; using Newtonsoft.Json; using Directory = System.IO.Directory; @@ -113,6 +114,7 @@ namespace GodotTools.Ides.Rider return installInfos.ToArray(); } + [SupportedOSPlatform("windows")] private static RiderInfo[] CollectRiderInfosWindows() { var installInfos = new List<RiderInfo>(); @@ -217,6 +219,7 @@ namespace GodotTools.Ides.Rider throw new Exception("Unknown OS."); } + [SupportedOSPlatform("windows")] private static void CollectPathsFromRegistry(string registryKey, List<string> installPaths) { using (var key = Registry.CurrentUser.OpenSubKey(registryKey)) @@ -229,6 +232,7 @@ namespace GodotTools.Ides.Rider } } + [SupportedOSPlatform("windows")] private static void CollectPathsFromRegistry(List<string> installPaths, RegistryKey key) { if (key == null) return; @@ -324,7 +328,7 @@ namespace GodotTools.Ides.Rider { public string install_location; - [CanBeNull] + [return: MaybeNull] public static string GetInstallLocationFromJson(string json) { try @@ -378,7 +382,7 @@ namespace GodotTools.Ides.Rider public string version; public string versionSuffix; - [CanBeNull] + [return: MaybeNull] internal static ProductInfo GetProductInfo(string json) { try @@ -402,7 +406,7 @@ namespace GodotTools.Ides.Rider // ReSharper disable once InconsistentNaming public ActiveApplication active_application; - [CanBeNull] + [return: MaybeNull] public static string GetLatestBuildFromJson(string json) { try diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/EditorProgress.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/EditorProgress.cs index b221ae7c5c..7d2eb2d869 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Internals/EditorProgress.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Internals/EditorProgress.cs @@ -9,23 +9,12 @@ namespace GodotTools.Internals { public string Task { get; } - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void internal_Create(in godot_string task, in godot_string label, int amount, - bool canCancel); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void internal_Dispose(in godot_string task); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern bool internal_Step(in godot_string task, in godot_string state, int step, - bool forceRefresh); - public EditorProgress(string task, string label, int amount, bool canCancel = false) { Task = task; using godot_string taskIn = Marshaling.mono_string_to_godot(task); using godot_string labelIn = Marshaling.mono_string_to_godot(label); - internal_Create(taskIn, labelIn, amount, canCancel); + Internal.godot_icall_EditorProgress_Create(taskIn, labelIn, amount, canCancel); } ~EditorProgress() @@ -39,7 +28,7 @@ namespace GodotTools.Internals public void Dispose() { using godot_string taskIn = Marshaling.mono_string_to_godot(Task); - internal_Dispose(taskIn); + Internal.godot_icall_EditorProgress_Dispose(taskIn); GC.SuppressFinalize(this); } @@ -47,14 +36,14 @@ namespace GodotTools.Internals { using godot_string taskIn = Marshaling.mono_string_to_godot(Task); using godot_string stateIn = Marshaling.mono_string_to_godot(state); - internal_Step(taskIn, stateIn, step, forceRefresh); + Internal.godot_icall_EditorProgress_Step(taskIn, stateIn, step, forceRefresh); } public bool TryStep(string state, int step = -1, bool forceRefresh = true) { using godot_string taskIn = Marshaling.mono_string_to_godot(Task); using godot_string stateIn = Marshaling.mono_string_to_godot(state); - return internal_Step(taskIn, stateIn, step, forceRefresh); + return Internal.godot_icall_EditorProgress_Step(taskIn, stateIn, step, forceRefresh); } } } diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs index d79821de3c..3b65263aa9 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs @@ -6,13 +6,13 @@ namespace GodotTools.Internals { public static class Globals { - public static float EditorScale => internal_EditorScale(); + public static float EditorScale => Internal.godot_icall_Globals_EditorScale(); public static unsafe object GlobalDef(string setting, object defaultValue, bool restartIfChanged = false) { using godot_string settingIn = Marshaling.mono_string_to_godot(setting); using godot_variant defaultValueIn = Marshaling.mono_object_to_variant(defaultValue); - internal_GlobalDef(settingIn, defaultValueIn, restartIfChanged, out godot_variant result); + Internal.godot_icall_Globals_GlobalDef(settingIn, defaultValueIn, restartIfChanged, out godot_variant result); using (result) return Marshaling.variant_to_mono_object(&result); } @@ -21,7 +21,7 @@ namespace GodotTools.Internals { using godot_string settingIn = Marshaling.mono_string_to_godot(setting); using godot_variant defaultValueIn = Marshaling.mono_object_to_variant(defaultValue); - internal_EditorDef(settingIn, defaultValueIn, restartIfChanged, out godot_variant result); + Internal.godot_icall_Globals_EditorDef(settingIn, defaultValueIn, restartIfChanged, out godot_variant result); using (result) return Marshaling.variant_to_mono_object(&result); } @@ -29,7 +29,7 @@ namespace GodotTools.Internals public static unsafe object EditorShortcut(string setting) { using godot_string settingIn = Marshaling.mono_string_to_godot(setting); - internal_EditorShortcut(settingIn, out godot_variant result); + Internal.godot_icall_Globals_EditorShortcut(settingIn, out godot_variant result); using (result) return Marshaling.variant_to_mono_object(&result); } @@ -38,28 +38,9 @@ namespace GodotTools.Internals public static string TTR(this string text) { using godot_string textIn = Marshaling.mono_string_to_godot(text); - internal_TTR(textIn, out godot_string dest); + Internal.godot_icall_Globals_TTR(textIn, out godot_string dest); using (dest) return Marshaling.mono_string_from_godot(dest); } - - // Internal Calls - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern float internal_EditorScale(); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void internal_GlobalDef(in godot_string setting, in godot_variant defaultValue, - bool restartIfChanged, out godot_variant result); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void internal_EditorDef(in godot_string setting, in godot_variant defaultValue, - bool restartIfChanged, out godot_variant result); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void internal_EditorShortcut(in godot_string setting, out godot_variant result); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void internal_TTR(in godot_string text, out godot_string dest); } } diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs index b15ebc1ae2..9011662248 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs @@ -9,7 +9,7 @@ namespace GodotTools.Internals { get { - internal_ResMetadataDir(out godot_string dest); + Internal.godot_icall_GodotSharpDirs_ResMetadataDir(out godot_string dest); using (dest) return Marshaling.mono_string_from_godot(dest); } @@ -19,7 +19,7 @@ namespace GodotTools.Internals { get { - internal_ResTempAssembliesBaseDir(out godot_string dest); + Internal.godot_icall_GodotSharpDirs_ResTempAssembliesBaseDir(out godot_string dest); using (dest) return Marshaling.mono_string_from_godot(dest); } @@ -29,7 +29,7 @@ namespace GodotTools.Internals { get { - internal_MonoUserDir(out godot_string dest); + Internal.godot_icall_GodotSharpDirs_MonoUserDir(out godot_string dest); using (dest) return Marshaling.mono_string_from_godot(dest); } @@ -39,7 +39,7 @@ namespace GodotTools.Internals { get { - internal_BuildLogsDirs(out godot_string dest); + Internal.godot_icall_GodotSharpDirs_BuildLogsDirs(out godot_string dest); using (dest) return Marshaling.mono_string_from_godot(dest); } @@ -49,7 +49,7 @@ namespace GodotTools.Internals { get { - internal_ProjectSlnPath(out godot_string dest); + Internal.godot_icall_GodotSharpDirs_ProjectSlnPath(out godot_string dest); using (dest) return Marshaling.mono_string_from_godot(dest); } @@ -59,7 +59,7 @@ namespace GodotTools.Internals { get { - internal_ProjectCsProjPath(out godot_string dest); + Internal.godot_icall_GodotSharpDirs_ProjectCsProjPath(out godot_string dest); using (dest) return Marshaling.mono_string_from_godot(dest); } @@ -69,35 +69,10 @@ namespace GodotTools.Internals { get { - internal_DataEditorToolsDir(out godot_string dest); + Internal.godot_icall_GodotSharpDirs_DataEditorToolsDir(out godot_string dest); using (dest) return Marshaling.mono_string_from_godot(dest); } } - - #region Internal - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void internal_ResMetadataDir(out godot_string r_dest); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void internal_ResTempAssembliesBaseDir(out godot_string r_dest); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void internal_MonoUserDir(out godot_string r_dest); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void internal_BuildLogsDirs(out godot_string r_dest); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void internal_ProjectSlnPath(out godot_string r_dest); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void internal_ProjectCsProjPath(out godot_string r_dest); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void internal_DataEditorToolsDir(out godot_string r_dest); - - #endregion } } diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs index 7ba26939fa..8e4eb031db 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs @@ -1,12 +1,13 @@ using System; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using Godot; using Godot.NativeInterop; using GodotTools.IdeMessaging.Requests; namespace GodotTools.Internals { - public static class Internal + internal static class Internal { public const string CSharpLanguageType = "CSharpScript"; public const string CSharpLanguageExtension = ".cs"; @@ -15,7 +16,7 @@ namespace GodotTools.Internals { get { - internal_FullExportTemplatesDir(out godot_string dest); + godot_icall_Internal_FullExportTemplatesDir(out godot_string dest); using (dest) return Marshaling.mono_string_from_godot(dest); } @@ -26,99 +27,161 @@ namespace GodotTools.Internals public static bool IsMacOSAppBundleInstalled(string bundleId) { using godot_string bundleIdIn = Marshaling.mono_string_to_godot(bundleId); - return internal_IsMacOSAppBundleInstalled(bundleIdIn); + return godot_icall_Internal_IsMacOSAppBundleInstalled(bundleIdIn); } - public static bool GodotIs32Bits() => internal_GodotIs32Bits(); + public static bool GodotIs32Bits() => godot_icall_Internal_GodotIs32Bits(); - public static bool GodotIsRealTDouble() => internal_GodotIsRealTDouble(); + public static bool GodotIsRealTDouble() => godot_icall_Internal_GodotIsRealTDouble(); - public static void GodotMainIteration() => internal_GodotMainIteration(); + public static void GodotMainIteration() => godot_icall_Internal_GodotMainIteration(); - public static bool IsAssembliesReloadingNeeded() => internal_IsAssembliesReloadingNeeded(); + public static bool IsAssembliesReloadingNeeded() => godot_icall_Internal_IsAssembliesReloadingNeeded(); - public static void ReloadAssemblies(bool softReload) => internal_ReloadAssemblies(softReload); + public static void ReloadAssemblies(bool softReload) => godot_icall_Internal_ReloadAssemblies(softReload); - public static void EditorDebuggerNodeReloadScripts() => internal_EditorDebuggerNodeReloadScripts(); + public static void EditorDebuggerNodeReloadScripts() => godot_icall_Internal_EditorDebuggerNodeReloadScripts(); public static bool ScriptEditorEdit(Resource resource, int line, int col, bool grabFocus = true) => - internal_ScriptEditorEdit(resource.NativeInstance, line, col, grabFocus); + godot_icall_Internal_ScriptEditorEdit(resource.NativeInstance, line, col, grabFocus); - public static void EditorNodeShowScriptScreen() => internal_EditorNodeShowScriptScreen(); + public static void EditorNodeShowScriptScreen() => godot_icall_Internal_EditorNodeShowScriptScreen(); public static string MonoWindowsInstallRoot { get { - internal_MonoWindowsInstallRoot(out godot_string dest); + godot_icall_Internal_MonoWindowsInstallRoot(out godot_string dest); using (dest) return Marshaling.mono_string_from_godot(dest); } } - public static void EditorRunPlay() => internal_EditorRunPlay(); + public static void EditorRunPlay() => godot_icall_Internal_EditorRunPlay(); - public static void EditorRunStop() => internal_EditorRunStop(); + public static void EditorRunStop() => godot_icall_Internal_EditorRunStop(); - public static void ScriptEditorDebugger_ReloadScripts() => internal_ScriptEditorDebugger_ReloadScripts(); + public static void ScriptEditorDebugger_ReloadScripts() => + godot_icall_Internal_ScriptEditorDebugger_ReloadScripts(); public static unsafe string[] CodeCompletionRequest(CodeCompletionRequest.CompletionKind kind, string scriptFile) { using godot_string scriptFileIn = Marshaling.mono_string_to_godot(scriptFile); - internal_CodeCompletionRequest((int)kind, scriptFileIn, out godot_packed_string_array res); + godot_icall_Internal_CodeCompletionRequest((int)kind, scriptFileIn, out godot_packed_string_array res); using (res) return Marshaling.PackedStringArray_to_mono_array(&res); } #region Internal - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void internal_FullExportTemplatesDir(out godot_string dest); + private const string GodotDllName = "__Internal"; - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern bool internal_IsMacOSAppBundleInstalled(in godot_string bundleId); + [DllImport(GodotDllName)] + public static extern void godot_icall_GodotSharpDirs_ResMetadataDir(out godot_string r_dest); - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern bool internal_GodotIs32Bits(); + [DllImport(GodotDllName)] + public static extern void godot_icall_GodotSharpDirs_ResTempAssembliesBaseDir(out godot_string r_dest); - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern bool internal_GodotIsRealTDouble(); + [DllImport(GodotDllName)] + public static extern void godot_icall_GodotSharpDirs_MonoUserDir(out godot_string r_dest); - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void internal_GodotMainIteration(); + [DllImport(GodotDllName)] + public static extern void godot_icall_GodotSharpDirs_BuildLogsDirs(out godot_string r_dest); - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern bool internal_IsAssembliesReloadingNeeded(); + [DllImport(GodotDllName)] + public static extern void godot_icall_GodotSharpDirs_ProjectSlnPath(out godot_string r_dest); - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void internal_ReloadAssemblies(bool softReload); + [DllImport(GodotDllName)] + public static extern void godot_icall_GodotSharpDirs_ProjectCsProjPath(out godot_string r_dest); - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void internal_EditorDebuggerNodeReloadScripts(); + [DllImport(GodotDllName)] + public static extern void godot_icall_GodotSharpDirs_DataEditorToolsDir(out godot_string r_dest); - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern bool internal_ScriptEditorEdit(IntPtr resource, int line, int col, bool grabFocus); + [DllImport(GodotDllName)] + public static extern void godot_icall_EditorProgress_Create(in godot_string task, in godot_string label, + int amount, bool canCancel); - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void internal_EditorNodeShowScriptScreen(); + [DllImport(GodotDllName)] + public static extern void godot_icall_EditorProgress_Dispose(in godot_string task); - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void internal_MonoWindowsInstallRoot(out godot_string dest); + [DllImport(GodotDllName)] + public static extern bool godot_icall_EditorProgress_Step(in godot_string task, in godot_string state, int step, + bool forceRefresh); - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void internal_EditorRunPlay(); + [DllImport(GodotDllName)] + private static extern void godot_icall_Internal_FullExportTemplatesDir(out godot_string dest); - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void internal_EditorRunStop(); + [DllImport(GodotDllName)] + private static extern void godot_icall_Internal_SimplifyGodotPath(in godot_string path, out godot_string dest); - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void internal_ScriptEditorDebugger_ReloadScripts(); + [DllImport(GodotDllName)] + private static extern bool godot_icall_Internal_IsMacOSAppBundleInstalled(in godot_string bundleId); - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void internal_CodeCompletionRequest(int kind, in godot_string scriptFile, + [DllImport(GodotDllName)] + private static extern bool godot_icall_Internal_GodotIs32Bits(); + + [DllImport(GodotDllName)] + private static extern bool godot_icall_Internal_GodotIsRealTDouble(); + + [DllImport(GodotDllName)] + private static extern void godot_icall_Internal_GodotMainIteration(); + + [DllImport(GodotDllName)] + private static extern bool godot_icall_Internal_IsAssembliesReloadingNeeded(); + + [DllImport(GodotDllName)] + private static extern void godot_icall_Internal_ReloadAssemblies(bool softReload); + + [DllImport(GodotDllName)] + private static extern void godot_icall_Internal_EditorDebuggerNodeReloadScripts(); + + [DllImport(GodotDllName)] + private static extern bool godot_icall_Internal_ScriptEditorEdit(IntPtr resource, int line, int col, + bool grabFocus); + + [DllImport(GodotDllName)] + private static extern void godot_icall_Internal_EditorNodeShowScriptScreen(); + + [DllImport(GodotDllName)] + private static extern void godot_icall_Internal_MonoWindowsInstallRoot(out godot_string dest); + + [DllImport(GodotDllName)] + private static extern void godot_icall_Internal_EditorRunPlay(); + + [DllImport(GodotDllName)] + private static extern void godot_icall_Internal_EditorRunStop(); + + [DllImport(GodotDllName)] + private static extern void godot_icall_Internal_ScriptEditorDebugger_ReloadScripts(); + + [DllImport(GodotDllName)] + private static extern void godot_icall_Internal_CodeCompletionRequest(int kind, in godot_string scriptFile, out godot_packed_string_array res); + [DllImport(GodotDllName)] + public static extern float godot_icall_Globals_EditorScale(); + + [DllImport(GodotDllName)] + public static extern void godot_icall_Globals_GlobalDef(in godot_string setting, in godot_variant defaultValue, + bool restartIfChanged, out godot_variant result); + + [DllImport(GodotDllName)] + public static extern void godot_icall_Globals_EditorDef(in godot_string setting, in godot_variant defaultValue, + bool restartIfChanged, out godot_variant result); + + [DllImport(GodotDllName)] + public static extern void godot_icall_Globals_EditorShortcut(in godot_string setting, out godot_variant result); + + [DllImport(GodotDllName)] + public static extern void godot_icall_Globals_TTR(in godot_string text, out godot_string dest); + + [DllImport(GodotDllName)] + public static extern void godot_icall_Utils_OS_GetPlatformName(out godot_string dest); + + [DllImport(GodotDllName)] + public static extern bool godot_icall_Utils_OS_UnixFileHasExecutableAccess(in godot_string filePath); + #endregion } } diff --git a/modules/mono/editor/GodotTools/GodotTools/Utils/FsPathUtils.cs b/modules/mono/editor/GodotTools/GodotTools/Utils/FsPathUtils.cs index 05499339b1..4f03d46570 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Utils/FsPathUtils.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Utils/FsPathUtils.cs @@ -1,8 +1,8 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.IO; using Godot; using GodotTools.Core; -using JetBrains.Annotations; namespace GodotTools.Utils { @@ -30,7 +30,7 @@ namespace GodotTools.Utils return childPathNorm.PathStartsWithAlreadyNorm(parentPathNorm); } - [CanBeNull] + [return: MaybeNull] public static string LocalizePathWithCaseChecked(string path) { string pathNorm = path.NormalizePath() + Path.DirectorySeparatorChar; diff --git a/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs b/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs index aa100433c9..d9b5942237 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs @@ -6,19 +6,13 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Runtime.CompilerServices; -using JetBrains.Annotations; +using GodotTools.Internals; namespace GodotTools.Utils { [SuppressMessage("ReSharper", "InconsistentNaming")] public static class OS { - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void GetPlatformName(out godot_string dest); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern bool UnixFileHasExecutableAccess(in godot_string filePath); - public static class Names { public const string Windows = "Windows"; @@ -66,7 +60,7 @@ namespace GodotTools.Utils private static unsafe bool IsOS(string name) { - GetPlatformName(out godot_string dest); + Internal.godot_icall_Utils_OS_GetPlatformName(out godot_string dest); using (dest) { string platformName = Marshaling.mono_string_from_godot(dest); @@ -76,7 +70,7 @@ namespace GodotTools.Utils private static unsafe bool IsAnyOS(IEnumerable<string> names) { - GetPlatformName(out godot_string dest); + Internal.godot_icall_Utils_OS_GetPlatformName(out godot_string dest); using (dest) { string platformName = Marshaling.mono_string_from_godot(dest); @@ -102,14 +96,23 @@ namespace GodotTools.Utils private static readonly Lazy<bool> _isHTML5 = new Lazy<bool>(() => IsOS(Names.HTML5)); private static readonly Lazy<bool> _isUnixLike = new Lazy<bool>(() => IsAnyOS(UnixLikePlatforms)); + // TODO SupportedOSPlatformGuard once we target .NET 6 + // [SupportedOSPlatformGuard("windows")] public static bool IsWindows => _isWindows.Value || IsUWP; + // [SupportedOSPlatformGuard("osx")] public static bool IsMacOS => _isMacOS.Value; + // [SupportedOSPlatformGuard("linux")] public static bool IsLinuxBSD => _isLinuxBSD.Value; + // [SupportedOSPlatformGuard("linux")] public static bool IsServer => _isServer.Value; + // [SupportedOSPlatformGuard("windows")] public static bool IsUWP => _isUWP.Value; public static bool IsHaiku => _isHaiku.Value; + // [SupportedOSPlatformGuard("android")] public static bool IsAndroid => _isAndroid.Value; + // [SupportedOSPlatformGuard("ios")] public static bool IsiOS => _isiOS.Value; + // [SupportedOSPlatformGuard("browser")] public static bool IsHTML5 => _isHTML5.Value; public static bool IsUnixLike => _isUnixLike.Value; @@ -183,7 +186,7 @@ namespace GodotTools.Utils .FirstOrDefault(path => { using godot_string pathIn = Marshaling.mono_string_to_godot(path); - return File.Exists(path) && UnixFileHasExecutableAccess(pathIn); + return File.Exists(path) && Internal.godot_icall_Utils_OS_UnixFileHasExecutableAccess(pathIn); }); } diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index f70155f4e8..1d4750a2a5 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -1143,9 +1143,7 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir) { cs_icalls_content.append(INDENT1 "[SuppressMessage(\"ReSharper\", \"InconsistentNaming\")]\n"); cs_icalls_content.append(INDENT1 "[SuppressMessage(\"ReSharper\", \"RedundantUnsafeContext\")]\n"); cs_icalls_content.append(INDENT1 "[SuppressMessage(\"ReSharper\", \"RedundantNameQualifier\")]\n"); - cs_icalls_content.append("#if NET\n"); cs_icalls_content.append(INDENT1 "[System.Runtime.CompilerServices.SkipLocalsInit]\n"); - cs_icalls_content.append("#endif\n"); cs_icalls_content.append(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS "\n" INDENT1 "{"); cs_icalls_content.append(MEMBER_BEGIN "internal static ulong godot_api_hash = "); @@ -1252,9 +1250,7 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_proj_dir) { cs_icalls_content.append(INDENT1 "[SuppressMessage(\"ReSharper\", \"InconsistentNaming\")]\n"); cs_icalls_content.append(INDENT1 "[SuppressMessage(\"ReSharper\", \"RedundantUnsafeContext\")]\n"); cs_icalls_content.append(INDENT1 "[SuppressMessage(\"ReSharper\", \"RedundantNameQualifier\")]\n"); - cs_icalls_content.append("#if NET\n"); cs_icalls_content.append(INDENT1 "[System.Runtime.CompilerServices.SkipLocalsInit]\n"); - cs_icalls_content.append("#endif\n"); cs_icalls_content.append(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS_EDITOR "\n" OPEN_BLOCK_L1); cs_icalls_content.append(INDENT2 "internal static ulong godot_api_hash = "); @@ -1550,20 +1546,9 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str // Add native constructor static field output << MEMBER_BEGIN << "[DebuggerBrowsable(DebuggerBrowsableState.Never)]\n" - - << "#if NET\n" - << INDENT2 "private static unsafe readonly delegate* unmanaged<IntPtr> " << CS_STATIC_FIELD_NATIVE_CTOR " = " ICALL_CLASSDB_GET_CONSTRUCTOR - << "(" BINDINGS_NATIVE_NAME_FIELD ");\n" - - << "#else\n" - - // Get rid of this one once we switch to .NET 5/6 - << INDENT2 "private static readonly IntPtr " CS_STATIC_FIELD_NATIVE_CTOR - << " = " ICALL_CLASSDB_GET_CONSTRUCTOR "(" BINDINGS_NATIVE_NAME_FIELD ");\n" - - << "#endif\n"; + << "(" BINDINGS_NATIVE_NAME_FIELD ");\n"; } if (is_derived_type) { @@ -1576,20 +1561,9 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str // The engine will initialize the pointer field of the managed side before calling the constructor // This is why we only allocate a new native object from the constructor if the pointer field is not set output << INDENT3 "if (" BINDINGS_PTR_FIELD " == IntPtr.Zero)\n" OPEN_BLOCK_L3 - - << "#if NET\n" - << INDENT4 "unsafe\n" INDENT4 OPEN_BLOCK << INDENT5 BINDINGS_PTR_FIELD " = " CS_STATIC_FIELD_NATIVE_CTOR "();\n" << CLOSE_BLOCK_L4 - - << "#else\n" - - // Get rid of this one once we switch to .NET 5/6 - << INDENT4 BINDINGS_PTR_FIELD " = _gd__invoke_class_constructor(" CS_STATIC_FIELD_NATIVE_CTOR ");\n" - - << "#endif\n" - << INDENT4 C_METHOD_TIE_MANAGED_TO_UNMANAGED "(this, " BINDINGS_PTR_FIELD ", " << BINDINGS_NATIVE_NAME_FIELD << ", refCounted: " << (itype.is_ref_counted ? "true" : "false") << ", ((object)this).GetType(), _cachedType);\n" CLOSE_BLOCK_L3 @@ -3337,6 +3311,8 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { // bool itype = TypeInterface::create_value_type(String("bool")); + itype.cs_in = "%s.ToGodotBool()"; + itype.cs_out = "%5return %0(%1).ToBool();"; itype.c_type = "godot_bool"; itype.c_type_in = itype.c_type; itype.c_type_out = itype.c_type; diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp index dd0d94121d..5b1bc8ccbf 100644 --- a/modules/mono/editor/editor_internal_calls.cpp +++ b/modules/mono/editor/editor_internal_calls.cpp @@ -49,23 +49,40 @@ #include "../godotsharp_dirs.h" #include "../utils/macos_utils.h" #include "code_completion.h" -#include "godotsharp_export.h" #include "../interop_types.h" -void godot_icall_GodotSharpDirs_ResMetadataDir(godot_string *r_dest) { +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +#define MAYBE_UNUSED [[maybe_unused]] +#else +#define MAYBE_UNUSED +#endif + +#ifdef __GNUC__ +#define GD_PINVOKE_EXPORT MAYBE_UNUSED __attribute__((visibility("default"))) +#elif defined(_WIN32) +#define GD_PINVOKE_EXPORT MAYBE_UNUSED __declspec(dllexport) +#else +#define GD_PINVOKE_EXPORT MAYBE_UNUSED +#endif + +GD_PINVOKE_EXPORT void godot_icall_GodotSharpDirs_ResMetadataDir(godot_string *r_dest) { memnew_placement(r_dest, String(GodotSharpDirs::get_res_metadata_dir())); } -void godot_icall_GodotSharpDirs_ResTempAssembliesBaseDir(godot_string *r_dest) { +GD_PINVOKE_EXPORT void godot_icall_GodotSharpDirs_ResTempAssembliesBaseDir(godot_string *r_dest) { memnew_placement(r_dest, String(GodotSharpDirs::get_res_temp_assemblies_base_dir())); } -void godot_icall_GodotSharpDirs_MonoUserDir(godot_string *r_dest) { +GD_PINVOKE_EXPORT void godot_icall_GodotSharpDirs_MonoUserDir(godot_string *r_dest) { memnew_placement(r_dest, String(GodotSharpDirs::get_mono_user_dir())); } -void godot_icall_GodotSharpDirs_BuildLogsDirs(godot_string *r_dest) { +GD_PINVOKE_EXPORT void godot_icall_GodotSharpDirs_BuildLogsDirs(godot_string *r_dest) { #ifdef TOOLS_ENABLED memnew_placement(r_dest, String(GodotSharpDirs::get_build_logs_dir())); #else @@ -73,7 +90,7 @@ void godot_icall_GodotSharpDirs_BuildLogsDirs(godot_string *r_dest) { #endif } -void godot_icall_GodotSharpDirs_ProjectSlnPath(godot_string *r_dest) { +GD_PINVOKE_EXPORT void godot_icall_GodotSharpDirs_ProjectSlnPath(godot_string *r_dest) { #ifdef TOOLS_ENABLED memnew_placement(r_dest, String(GodotSharpDirs::get_project_sln_path())); #else @@ -81,7 +98,7 @@ void godot_icall_GodotSharpDirs_ProjectSlnPath(godot_string *r_dest) { #endif } -void godot_icall_GodotSharpDirs_ProjectCsProjPath(godot_string *r_dest) { +GD_PINVOKE_EXPORT void godot_icall_GodotSharpDirs_ProjectCsProjPath(godot_string *r_dest) { #ifdef TOOLS_ENABLED memnew_placement(r_dest, String(GodotSharpDirs::get_project_csproj_path())); #else @@ -89,7 +106,7 @@ void godot_icall_GodotSharpDirs_ProjectCsProjPath(godot_string *r_dest) { #endif } -void godot_icall_GodotSharpDirs_DataEditorToolsDir(godot_string *r_dest) { +GD_PINVOKE_EXPORT void godot_icall_GodotSharpDirs_DataEditorToolsDir(godot_string *r_dest) { #ifdef TOOLS_ENABLED memnew_placement(r_dest, String(GodotSharpDirs::get_data_editor_tools_dir())); #else @@ -97,39 +114,29 @@ void godot_icall_GodotSharpDirs_DataEditorToolsDir(godot_string *r_dest) { #endif } -void godot_icall_EditorProgress_Create(const godot_string *p_task, const godot_string *p_label, int32_t p_amount, bool p_can_cancel) { +GD_PINVOKE_EXPORT void godot_icall_EditorProgress_Create(const godot_string *p_task, const godot_string *p_label, int32_t p_amount, bool p_can_cancel) { String task = *reinterpret_cast<const String *>(p_task); String label = *reinterpret_cast<const String *>(p_label); EditorNode::progress_add_task(task, label, p_amount, (bool)p_can_cancel); } -void godot_icall_EditorProgress_Dispose(const godot_string *p_task) { +GD_PINVOKE_EXPORT void godot_icall_EditorProgress_Dispose(const godot_string *p_task) { String task = *reinterpret_cast<const String *>(p_task); EditorNode::progress_end_task(task); } -bool godot_icall_EditorProgress_Step(const godot_string *p_task, const godot_string *p_state, int32_t p_step, bool p_force_refresh) { +GD_PINVOKE_EXPORT bool godot_icall_EditorProgress_Step(const godot_string *p_task, const godot_string *p_state, int32_t p_step, bool p_force_refresh) { String task = *reinterpret_cast<const String *>(p_task); String state = *reinterpret_cast<const String *>(p_state); return EditorNode::progress_task_step(task, state, p_step, (bool)p_force_refresh); } -uint32_t godot_icall_ExportPlugin_GetExportedAssemblyDependencies(const godot_dictionary *p_initial_assemblies, - const godot_string *p_build_config, const godot_string *p_custom_bcl_dir, godot_dictionary *r_assembly_dependencies) { - Dictionary initial_dependencies = *reinterpret_cast<const Dictionary *>(p_initial_assemblies); - String build_config = *reinterpret_cast<const String *>(p_build_config); - String custom_bcl_dir = *reinterpret_cast<const String *>(p_custom_bcl_dir); - Dictionary assembly_dependencies = *reinterpret_cast<Dictionary *>(r_assembly_dependencies); - - return GodotSharpExport::get_exported_assembly_dependencies(initial_dependencies, build_config, custom_bcl_dir, assembly_dependencies); -} - -void godot_icall_Internal_FullExportTemplatesDir(godot_string *r_dest) { +GD_PINVOKE_EXPORT void godot_icall_Internal_FullExportTemplatesDir(godot_string *r_dest) { String full_templates_dir = EditorPaths::get_singleton()->get_export_templates_dir().plus_file(VERSION_FULL_CONFIG); memnew_placement(r_dest, String(full_templates_dir)); } -bool godot_icall_Internal_IsMacOSAppBundleInstalled(const godot_string *p_bundle_id) { +GD_PINVOKE_EXPORT bool godot_icall_Internal_IsMacOSAppBundleInstalled(const godot_string *p_bundle_id) { #ifdef MACOS_ENABLED String bundle_id = *reinterpret_cast<const String *>(p_bundle_id); return (bool)macos_is_app_bundle_installed(bundle_id); @@ -139,11 +146,11 @@ bool godot_icall_Internal_IsMacOSAppBundleInstalled(const godot_string *p_bundle #endif } -bool godot_icall_Internal_GodotIs32Bits() { +GD_PINVOKE_EXPORT bool godot_icall_Internal_GodotIs32Bits() { return sizeof(void *) == 4; } -bool godot_icall_Internal_GodotIsRealTDouble() { +GD_PINVOKE_EXPORT bool godot_icall_Internal_GodotIsRealTDouble() { #ifdef REAL_T_IS_DOUBLE return (bool)true; #else @@ -151,11 +158,11 @@ bool godot_icall_Internal_GodotIsRealTDouble() { #endif } -void godot_icall_Internal_GodotMainIteration() { +GD_PINVOKE_EXPORT void godot_icall_Internal_GodotMainIteration() { Main::iteration(); } -bool godot_icall_Internal_IsAssembliesReloadingNeeded() { +GD_PINVOKE_EXPORT bool godot_icall_Internal_IsAssembliesReloadingNeeded() { #ifdef GD_MONO_HOT_RELOAD return (bool)CSharpLanguage::get_singleton()->is_assembly_reloading_needed(); #else @@ -163,26 +170,26 @@ bool godot_icall_Internal_IsAssembliesReloadingNeeded() { #endif } -void godot_icall_Internal_ReloadAssemblies(bool p_soft_reload) { +GD_PINVOKE_EXPORT void godot_icall_Internal_ReloadAssemblies(bool p_soft_reload) { #ifdef GD_MONO_HOT_RELOAD mono_bind::GodotSharp::get_singleton()->call_deferred(SNAME("_reload_assemblies"), (bool)p_soft_reload); #endif } -void godot_icall_Internal_EditorDebuggerNodeReloadScripts() { +GD_PINVOKE_EXPORT void godot_icall_Internal_EditorDebuggerNodeReloadScripts() { EditorDebuggerNode::get_singleton()->reload_scripts(); } -bool godot_icall_Internal_ScriptEditorEdit(Resource *p_resource, int32_t p_line, int32_t p_col, bool p_grab_focus) { +GD_PINVOKE_EXPORT bool godot_icall_Internal_ScriptEditorEdit(Resource *p_resource, int32_t p_line, int32_t p_col, bool p_grab_focus) { Ref<Resource> resource = p_resource; return (bool)ScriptEditor::get_singleton()->edit(resource, p_line, p_col, (bool)p_grab_focus); } -void godot_icall_Internal_EditorNodeShowScriptScreen() { +GD_PINVOKE_EXPORT void godot_icall_Internal_EditorNodeShowScriptScreen() { EditorNode::get_singleton()->call("_editor_select", EditorNode::EDITOR_SCRIPT); } -void godot_icall_Internal_MonoWindowsInstallRoot(godot_string *r_dest) { +GD_PINVOKE_EXPORT void godot_icall_Internal_MonoWindowsInstallRoot(godot_string *r_dest) { #ifdef WINDOWS_ENABLED String install_root_dir = GDMono::get_singleton()->get_mono_reg_info().install_root_dir; memnew_placement(r_dest, String(install_root_dir)); @@ -192,62 +199,62 @@ void godot_icall_Internal_MonoWindowsInstallRoot(godot_string *r_dest) { #endif } -void godot_icall_Internal_EditorRunPlay() { +GD_PINVOKE_EXPORT void godot_icall_Internal_EditorRunPlay() { EditorNode::get_singleton()->run_play(); } -void godot_icall_Internal_EditorRunStop() { +GD_PINVOKE_EXPORT void godot_icall_Internal_EditorRunStop() { EditorNode::get_singleton()->run_stop(); } -void godot_icall_Internal_ScriptEditorDebugger_ReloadScripts() { +GD_PINVOKE_EXPORT void godot_icall_Internal_ScriptEditorDebugger_ReloadScripts() { EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton(); if (ed) { ed->reload_scripts(); } } -void godot_icall_Internal_CodeCompletionRequest(int32_t p_kind, const godot_string *p_script_file, godot_packed_array *r_ret) { +GD_PINVOKE_EXPORT void godot_icall_Internal_CodeCompletionRequest(int32_t p_kind, const godot_string *p_script_file, godot_packed_array *r_ret) { String script_file = *reinterpret_cast<const String *>(p_script_file); PackedStringArray suggestions = gdmono::get_code_completion((gdmono::CompletionKind)p_kind, script_file); memnew_placement(r_ret, PackedStringArray(suggestions)); } -float godot_icall_Globals_EditorScale() { +GD_PINVOKE_EXPORT float godot_icall_Globals_EditorScale() { return EDSCALE; } -void godot_icall_Globals_GlobalDef(const godot_string *p_setting, const godot_variant *p_default_value, bool p_restart_if_changed, godot_variant *r_result) { +GD_PINVOKE_EXPORT void godot_icall_Globals_GlobalDef(const godot_string *p_setting, const godot_variant *p_default_value, bool p_restart_if_changed, godot_variant *r_result) { String setting = *reinterpret_cast<const String *>(p_setting); Variant default_value = *reinterpret_cast<const Variant *>(p_default_value); Variant result = _GLOBAL_DEF(setting, default_value, (bool)p_restart_if_changed); memnew_placement(r_result, Variant(result)); } -void godot_icall_Globals_EditorDef(const godot_string *p_setting, const godot_variant *p_default_value, bool p_restart_if_changed, godot_variant *r_result) { +GD_PINVOKE_EXPORT void godot_icall_Globals_EditorDef(const godot_string *p_setting, const godot_variant *p_default_value, bool p_restart_if_changed, godot_variant *r_result) { String setting = *reinterpret_cast<const String *>(p_setting); Variant default_value = *reinterpret_cast<const Variant *>(p_default_value); Variant result = _EDITOR_DEF(setting, default_value, (bool)p_restart_if_changed); memnew_placement(r_result, Variant(result)); } -void godot_icall_Globals_EditorShortcut(const godot_string *p_setting, godot_variant *r_result) { +GD_PINVOKE_EXPORT void godot_icall_Globals_EditorShortcut(const godot_string *p_setting, godot_variant *r_result) { String setting = *reinterpret_cast<const String *>(p_setting); Ref<Shortcut> result = ED_GET_SHORTCUT(setting); memnew_placement(r_result, Variant(result)); } -void godot_icall_Globals_TTR(const godot_string *p_text, godot_string *r_dest) { +GD_PINVOKE_EXPORT void godot_icall_Globals_TTR(const godot_string *p_text, godot_string *r_dest) { String text = *reinterpret_cast<const String *>(p_text); memnew_placement(r_dest, String(TTR(text))); } -void godot_icall_Utils_OS_GetPlatformName(godot_string *r_dest) { +GD_PINVOKE_EXPORT void godot_icall_Utils_OS_GetPlatformName(godot_string *r_dest) { String os_name = OS::get_singleton()->get_name(); memnew_placement(r_dest, String(os_name)); } -bool godot_icall_Utils_OS_UnixFileHasExecutableAccess(const godot_string *p_file_path) { +GD_PINVOKE_EXPORT bool godot_icall_Utils_OS_UnixFileHasExecutableAccess(const godot_string *p_file_path) { #ifdef UNIX_ENABLED String file_path = *reinterpret_cast<const String *>(p_file_path); return access(file_path.utf8().get_data(), X_OK) == 0; @@ -256,49 +263,41 @@ bool godot_icall_Utils_OS_UnixFileHasExecutableAccess(const godot_string *p_file #endif } -void register_editor_internal_calls() { - // GodotSharpDirs - GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResMetadataDir", godot_icall_GodotSharpDirs_ResMetadataDir); - GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempAssembliesBaseDir", godot_icall_GodotSharpDirs_ResTempAssembliesBaseDir); - GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoUserDir", godot_icall_GodotSharpDirs_MonoUserDir); - GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_BuildLogsDirs", godot_icall_GodotSharpDirs_BuildLogsDirs); - GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ProjectSlnPath", godot_icall_GodotSharpDirs_ProjectSlnPath); - GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ProjectCsProjPath", godot_icall_GodotSharpDirs_ProjectCsProjPath); - GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataEditorToolsDir", godot_icall_GodotSharpDirs_DataEditorToolsDir); - - // EditorProgress - GDMonoUtils::add_internal_call("GodotTools.Internals.EditorProgress::internal_Create", godot_icall_EditorProgress_Create); - GDMonoUtils::add_internal_call("GodotTools.Internals.EditorProgress::internal_Dispose", godot_icall_EditorProgress_Dispose); - GDMonoUtils::add_internal_call("GodotTools.Internals.EditorProgress::internal_Step", godot_icall_EditorProgress_Step); - - // ExportPlugin - GDMonoUtils::add_internal_call("GodotTools.Export.ExportPlugin::internal_GetExportedAssemblyDependencies", godot_icall_ExportPlugin_GetExportedAssemblyDependencies); - - // Internals - GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_FullExportTemplatesDir", godot_icall_Internal_FullExportTemplatesDir); - GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_IsMacOSAppBundleInstalled", godot_icall_Internal_IsMacOSAppBundleInstalled); - GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GodotIs32Bits", godot_icall_Internal_GodotIs32Bits); - GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GodotIsRealTDouble", godot_icall_Internal_GodotIsRealTDouble); - GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GodotMainIteration", godot_icall_Internal_GodotMainIteration); - GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_IsAssembliesReloadingNeeded", godot_icall_Internal_IsAssembliesReloadingNeeded); - GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_ReloadAssemblies", godot_icall_Internal_ReloadAssemblies); - GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_EditorDebuggerNodeReloadScripts", godot_icall_Internal_EditorDebuggerNodeReloadScripts); - GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_ScriptEditorEdit", godot_icall_Internal_ScriptEditorEdit); - GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_EditorNodeShowScriptScreen", godot_icall_Internal_EditorNodeShowScriptScreen); - GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_MonoWindowsInstallRoot", godot_icall_Internal_MonoWindowsInstallRoot); - GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_EditorRunPlay", godot_icall_Internal_EditorRunPlay); - GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_EditorRunStop", godot_icall_Internal_EditorRunStop); - GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_ScriptEditorDebugger_ReloadScripts", godot_icall_Internal_ScriptEditorDebugger_ReloadScripts); - GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_CodeCompletionRequest", godot_icall_Internal_CodeCompletionRequest); - - // Globals - GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_EditorScale", godot_icall_Globals_EditorScale); - GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_GlobalDef", godot_icall_Globals_GlobalDef); - GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_EditorDef", godot_icall_Globals_EditorDef); - GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_EditorShortcut", godot_icall_Globals_EditorShortcut); - GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_TTR", godot_icall_Globals_TTR); - - // Utils.OS - GDMonoUtils::add_internal_call("GodotTools.Utils.OS::GetPlatformName", godot_icall_Utils_OS_GetPlatformName); - GDMonoUtils::add_internal_call("GodotTools.Utils.OS::UnixFileHasExecutableAccess", godot_icall_Utils_OS_UnixFileHasExecutableAccess); +#ifdef __cplusplus } +#endif + +void *godotsharp_editor_pinvoke_funcs[32] = { + (void *)godot_icall_GodotSharpDirs_ResMetadataDir, + (void *)godot_icall_GodotSharpDirs_ResTempAssembliesBaseDir, + (void *)godot_icall_GodotSharpDirs_MonoUserDir, + (void *)godot_icall_GodotSharpDirs_BuildLogsDirs, + (void *)godot_icall_GodotSharpDirs_ProjectSlnPath, + (void *)godot_icall_GodotSharpDirs_ProjectCsProjPath, + (void *)godot_icall_GodotSharpDirs_DataEditorToolsDir, + (void *)godot_icall_EditorProgress_Create, + (void *)godot_icall_EditorProgress_Dispose, + (void *)godot_icall_EditorProgress_Step, + (void *)godot_icall_Internal_FullExportTemplatesDir, + (void *)godot_icall_Internal_IsMacOSAppBundleInstalled, + (void *)godot_icall_Internal_GodotIs32Bits, + (void *)godot_icall_Internal_GodotIsRealTDouble, + (void *)godot_icall_Internal_GodotMainIteration, + (void *)godot_icall_Internal_IsAssembliesReloadingNeeded, + (void *)godot_icall_Internal_ReloadAssemblies, + (void *)godot_icall_Internal_EditorDebuggerNodeReloadScripts, + (void *)godot_icall_Internal_ScriptEditorEdit, + (void *)godot_icall_Internal_EditorNodeShowScriptScreen, + (void *)godot_icall_Internal_MonoWindowsInstallRoot, + (void *)godot_icall_Internal_EditorRunPlay, + (void *)godot_icall_Internal_EditorRunStop, + (void *)godot_icall_Internal_ScriptEditorDebugger_ReloadScripts, + (void *)godot_icall_Internal_CodeCompletionRequest, + (void *)godot_icall_Globals_EditorScale, + (void *)godot_icall_Globals_GlobalDef, + (void *)godot_icall_Globals_EditorDef, + (void *)godot_icall_Globals_EditorShortcut, + (void *)godot_icall_Globals_TTR, + (void *)godot_icall_Utils_OS_GetPlatformName, + (void *)godot_icall_Utils_OS_UnixFileHasExecutableAccess, +}; diff --git a/modules/mono/editor/godotsharp_export.cpp b/modules/mono/editor/godotsharp_export.cpp deleted file mode 100644 index d065aa30e5..0000000000 --- a/modules/mono/editor/godotsharp_export.cpp +++ /dev/null @@ -1,144 +0,0 @@ -/*************************************************************************/ -/* godotsharp_export.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "godotsharp_export.h" - -#include <mono/metadata/image.h> - -#include "core/config/project_settings.h" -#include "core/io/file_access_pack.h" -#include "core/os/os.h" - -#include "../mono_gd/gd_mono.h" -#include "../mono_gd/gd_mono_assembly.h" -#include "../mono_gd/gd_mono_cache.h" -#include "../utils/macros.h" - -namespace GodotSharpExport { - -MonoAssemblyName *new_mono_assembly_name() { - // Mono has no public API to create an empty MonoAssemblyName and the struct is private. - // As such the only way to create it is with a stub name and then clear it. - - MonoAssemblyName *aname = mono_assembly_name_new("stub"); - CRASH_COND(aname == nullptr); - mono_assembly_name_free(aname); // Frees the string fields, not the struct - return aname; -} - -struct AssemblyRefInfo { - String name; - uint16_t major = 0; - uint16_t minor = 0; - uint16_t build = 0; - uint16_t revision = 0; -}; - -AssemblyRefInfo get_assemblyref_name(MonoImage *p_image, int index) { - const MonoTableInfo *table_info = mono_image_get_table_info(p_image, MONO_TABLE_ASSEMBLYREF); - - uint32_t cols[MONO_ASSEMBLYREF_SIZE]; - - mono_metadata_decode_row(table_info, index, cols, MONO_ASSEMBLYREF_SIZE); - - return { - String::utf8(mono_metadata_string_heap(p_image, cols[MONO_ASSEMBLYREF_NAME])), - (uint16_t)cols[MONO_ASSEMBLYREF_MAJOR_VERSION], - (uint16_t)cols[MONO_ASSEMBLYREF_MINOR_VERSION], - (uint16_t)cols[MONO_ASSEMBLYREF_BUILD_NUMBER], - (uint16_t)cols[MONO_ASSEMBLYREF_REV_NUMBER] - }; -} - -Error get_assembly_dependencies(GDMonoAssembly *p_assembly, MonoAssemblyName *reusable_aname, const Vector<String> &p_search_dirs, Dictionary &r_assembly_dependencies) { - MonoImage *image = p_assembly->get_image(); - - for (int i = 0; i < mono_image_get_table_rows(image, MONO_TABLE_ASSEMBLYREF); i++) { - AssemblyRefInfo ref_info = get_assemblyref_name(image, i); - - const String &ref_name = ref_info.name; - - if (r_assembly_dependencies.has(ref_name)) { - continue; - } - - mono_assembly_get_assemblyref(image, i, reusable_aname); - - GDMonoAssembly *ref_assembly = nullptr; - if (!GDMono::get_singleton()->load_assembly(ref_name, reusable_aname, &ref_assembly, p_search_dirs)) { - ERR_FAIL_V_MSG(ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + ref_name + "'."); - } - - r_assembly_dependencies[ref_name] = ref_assembly->get_path(); - - Error err = get_assembly_dependencies(ref_assembly, reusable_aname, p_search_dirs, r_assembly_dependencies); - ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot load one of the dependencies for the assembly: '" + ref_name + "'."); - } - - return OK; -} - -Error get_exported_assembly_dependencies(const Dictionary &p_initial_assemblies, - const String &p_build_config, const String &p_custom_bcl_dir, Dictionary &r_assembly_dependencies) { - MonoDomain *export_domain = GDMonoUtils::create_domain("GodotEngine.Domain.ProjectExport"); - ERR_FAIL_NULL_V(export_domain, FAILED); - _GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(export_domain); - - _GDMONO_SCOPE_DOMAIN_(export_domain); - - Vector<String> search_dirs; - GDMonoAssembly::fill_search_dirs(search_dirs, p_build_config, p_custom_bcl_dir); - - if (p_custom_bcl_dir.length()) { - // Only one mscorlib can be loaded. We need this workaround to make sure we get it from the right BCL directory. - r_assembly_dependencies["mscorlib"] = p_custom_bcl_dir.plus_file("mscorlib.dll").simplify_path(); - } - - for (const Variant *key = p_initial_assemblies.next(); key; key = p_initial_assemblies.next(key)) { - String assembly_name = *key; - String assembly_path = p_initial_assemblies[*key]; - - GDMonoAssembly *assembly = nullptr; - bool load_success = GDMono::get_singleton()->load_assembly_from(assembly_name, assembly_path, &assembly); - - ERR_FAIL_COND_V_MSG(!load_success, ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + assembly_name + "'."); - - MonoAssemblyName *reusable_aname = new_mono_assembly_name(); - SCOPE_EXIT { mono_free(reusable_aname); }; - - Error err = get_assembly_dependencies(assembly, reusable_aname, search_dirs, r_assembly_dependencies); - if (err != OK) { - return err; - } - } - - return OK; -} -} // namespace GodotSharpExport diff --git a/modules/mono/editor/godotsharp_export.h b/modules/mono/editor/godotsharp_export.h deleted file mode 100644 index 20d6f6fe73..0000000000 --- a/modules/mono/editor/godotsharp_export.h +++ /dev/null @@ -1,44 +0,0 @@ -/*************************************************************************/ -/* godotsharp_export.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef GODOTSHARP_EXPORT_H -#define GODOTSHARP_EXPORT_H - -#include "core/error/error_list.h" -#include "core/string/ustring.h" -#include "core/variant/dictionary.h" - -namespace GodotSharpExport { - -Error get_exported_assembly_dependencies(const Dictionary &p_initial_assemblies, - const String &p_build_config, const String &p_custom_lib_dir, Dictionary &r_assembly_dependencies); -} // namespace GodotSharpExport - -#endif // GODOTSHARP_EXPORT_H diff --git a/modules/mono/glue/GodotSharp/GodotPlugins/GodotPlugins.csproj b/modules/mono/glue/GodotSharp/GodotPlugins/GodotPlugins.csproj new file mode 100644 index 0000000000..38cd2ece4e --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotPlugins/GodotPlugins.csproj @@ -0,0 +1,17 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>net5.0</TargetFramework> + <LangVersion>9</LangVersion> + <Nullable>enable</Nullable> + <AllowUnsafeBlocks>true</AllowUnsafeBlocks> + + <!-- To generate the .runtimeconfig.json file--> + <EnableDynamicLoading>true</EnableDynamicLoading> + </PropertyGroup> + + <ItemGroup> + <ProjectReference Include="..\GodotSharp\GodotSharp.csproj" /> + </ItemGroup> + +</Project> diff --git a/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs b/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs new file mode 100644 index 0000000000..9f938373c4 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs @@ -0,0 +1,197 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Runtime.Loader; +using Godot.NativeInterop; + +namespace GodotPlugins +{ + public static class Main + { + private static readonly List<AssemblyName> SharedAssemblies = new(); + private static readonly Assembly CoreApiAssembly = typeof(Godot.Object).Assembly; + private static Assembly? _editorApiAssembly; + + private static readonly AssemblyLoadContext MainLoadContext = + AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()) ?? + AssemblyLoadContext.Default; + + // Right now we do it this way for simplicity as hot-reload is disabled. It will need to be changed later. + [UnmanagedCallersOnly] + internal static unsafe godot_bool Initialize(godot_bool editorHint, + PluginsCallbacks* pluginsCallbacks, Godot.Bridge.ManagedCallbacks* managedCallbacks) + { + try + { + SharedAssemblies.Add(CoreApiAssembly.GetName()); + + if (editorHint.ToBool()) + { + _editorApiAssembly = Assembly.Load("GodotSharpEditor"); + SharedAssemblies.Add(_editorApiAssembly.GetName()); + } + + NativeLibrary.SetDllImportResolver(CoreApiAssembly, OnResolveDllImport); + + *pluginsCallbacks = new() + { + LoadProjectAssemblyCallback = &LoadProjectAssembly, + LoadToolsAssemblyCallback = &LoadToolsAssembly, + }; + + *managedCallbacks = Godot.Bridge.ManagedCallbacks.Create(); + + return godot_bool.True; + } + catch (Exception e) + { + Console.Error.WriteLine(e); + *pluginsCallbacks = default; + *managedCallbacks = default; + return false.ToGodotBool(); + } + } + + [StructLayout(LayoutKind.Sequential)] + internal struct PluginsCallbacks + { + public unsafe delegate* unmanaged<char*, godot_bool> LoadProjectAssemblyCallback; + public unsafe delegate* unmanaged<char*, IntPtr> LoadToolsAssemblyCallback; + } + + [UnmanagedCallersOnly] + internal static unsafe godot_bool LoadProjectAssembly(char* nAssemblyPath) + { + try + { + string assemblyPath = new(nAssemblyPath); + + var assembly = LoadPlugin(assemblyPath); + + var method = CoreApiAssembly.GetType("Godot.Bridge.ScriptManagerBridge")? + .GetMethod("LookupScriptsInAssembly", + BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); + + if (method == null) + { + throw new MissingMethodException("Godot.Bridge.ScriptManagerBridge", + "LookupScriptsInAssembly"); + } + + method.Invoke(null, new object[] { assembly }); + + return godot_bool.True; + } + catch (Exception e) + { + Console.Error.WriteLine(e); + return false.ToGodotBool(); + } + } + + [UnmanagedCallersOnly] + internal static unsafe IntPtr LoadToolsAssembly(char* nAssemblyPath) + { + try + { + string assemblyPath = new(nAssemblyPath); + + if (_editorApiAssembly == null) + throw new InvalidOperationException("The Godot editor API assembly is not loaded"); + + var assembly = LoadPlugin(assemblyPath); + + NativeLibrary.SetDllImportResolver(assembly, OnResolveDllImport); + + var method = assembly.GetType("GodotTools.GodotSharpEditor")? + .GetMethod("InternalCreateInstance", + BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); + + if (method == null) + { + throw new MissingMethodException("GodotTools.GodotSharpEditor", + "InternalCreateInstance"); + } + + return (IntPtr?)method.Invoke(null, null) ?? IntPtr.Zero; + } + catch (Exception e) + { + Console.Error.WriteLine(e); + return IntPtr.Zero; + } + } + + private static Assembly LoadPlugin(string assemblyPath) + { + string assemblyName = Path.GetFileNameWithoutExtension(assemblyPath); + + var sharedAssemblies = new List<string>(); + + foreach (var sharedAssembly in SharedAssemblies) + { + string? sharedAssemblyName = sharedAssembly.Name; + if (sharedAssemblyName != null) + sharedAssemblies.Add(sharedAssemblyName); + } + + var loadContext = new PluginLoadContext(assemblyPath, sharedAssemblies, MainLoadContext); + return loadContext.LoadFromAssemblyName(new AssemblyName(assemblyName)); + } + + public static IntPtr OnResolveDllImport(string libraryName, Assembly assembly, DllImportSearchPath? searchPath) + { + if (libraryName == "__Internal") + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return Win32.GetModuleHandle(IntPtr.Zero); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + return Linux.dlopen(IntPtr.Zero, Linux.RTLD_LAZY); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + return MacOS.dlopen(IntPtr.Zero, MacOS.RTLD_LAZY); + } + } + + return IntPtr.Zero; + } + + // ReSharper disable InconsistentNaming + private static class MacOS + { + private const string SystemLibrary = "/usr/lib/libSystem.dylib"; + + public const int RTLD_LAZY = 1; + + [DllImport(SystemLibrary)] + public static extern IntPtr dlopen(IntPtr path, int mode); + } + + private static class Linux + { + // libdl.so was resulting in DllNotFoundException, for some reason... + // libcoreclr.so should work with both CoreCLR and the .NET Core version of Mono. + private const string SystemLibrary = "libcoreclr.so"; + + public const int RTLD_LAZY = 1; + + [DllImport(SystemLibrary)] + public static extern IntPtr dlopen(IntPtr path, int mode); + } + + private static class Win32 + { + private const string SystemLibrary = "Kernel32.dll"; + + [DllImport(SystemLibrary)] + public static extern IntPtr GetModuleHandle(IntPtr lpModuleName); + } + // ReSharper restore InconsistentNaming + } +} diff --git a/modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs b/modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs new file mode 100644 index 0000000000..1b969716aa --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Runtime.Loader; + +namespace GodotPlugins +{ + public class PluginLoadContext : AssemblyLoadContext + { + private readonly AssemblyDependencyResolver _resolver; + private readonly ICollection<string> _sharedAssemblies; + private readonly AssemblyLoadContext _mainLoadContext; + + public PluginLoadContext(string pluginPath, ICollection<string> sharedAssemblies, + AssemblyLoadContext mainLoadContext) + { + Console.WriteLine(pluginPath); + Console.Out.Flush(); + _resolver = new AssemblyDependencyResolver(pluginPath); + _sharedAssemblies = sharedAssemblies; + _mainLoadContext = mainLoadContext; + } + + protected override Assembly? Load(AssemblyName assemblyName) + { + if (assemblyName.Name == null) + return null; + + if (_sharedAssemblies.Contains(assemblyName.Name)) + return _mainLoadContext.LoadFromAssemblyName(assemblyName); + + string? assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName); + if (assemblyPath != null) + { + // Load in memory to prevent locking the file + using var assemblyFile = File.Open(assemblyPath, FileMode.Open, FileAccess.Read, FileShare.Read); + string pdbPath = Path.ChangeExtension(assemblyPath, ".pdb"); + + if (File.Exists(pdbPath)) + { + using var pdbFile = File.Open(pdbPath, FileMode.Open, FileAccess.Read, FileShare.Read); + return LoadFromStream(assemblyFile, pdbFile); + } + + return LoadFromStream(assemblyFile); + } + + return null; + } + + protected override IntPtr LoadUnmanagedDll(string unmanagedDllName) + { + string? libraryPath = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName); + if (libraryPath != null) + return LoadUnmanagedDllFromPath(libraryPath); + + return IntPtr.Zero; + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp.sln b/modules/mono/glue/GodotSharp/GodotSharp.sln index 4896d0a07d..fc4e6e91f1 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp.sln +++ b/modules/mono/glue/GodotSharp/GodotSharp.sln @@ -4,6 +4,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotSharp", "GodotSharp\Go EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotSharpEditor", "GodotSharpEditor\GodotSharpEditor.csproj", "{8FBEC238-D944-4074-8548-B3B524305905}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotPlugins", "GodotPlugins\GodotPlugins.csproj", "{944B77DB-497B-47F5-A1E3-81C35E3E9D4E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -18,5 +20,9 @@ Global {8FBEC238-D944-4074-8548-B3B524305905}.Debug|Any CPU.Build.0 = Debug|Any CPU {8FBEC238-D944-4074-8548-B3B524305905}.Release|Any CPU.ActiveCfg = Release|Any CPU {8FBEC238-D944-4074-8548-B3B524305905}.Release|Any CPU.Build.0 = Release|Any CPU + {944B77DB-497B-47F5-A1E3-81C35E3E9D4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {944B77DB-497B-47F5-A1E3-81C35E3E9D4E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {944B77DB-497B-47F5-A1E3-81C35E3E9D4E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {944B77DB-497B-47F5-A1E3-81C35E3E9D4E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs index a2a97e0a3e..2aa2ece803 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs @@ -95,7 +95,7 @@ namespace Godot.Collections public Array Duplicate(bool deep = false) { godot_array newArray; - NativeFuncs.godotsharp_array_duplicate(ref NativeValue, deep, out newArray); + NativeFuncs.godotsharp_array_duplicate(ref NativeValue, deep.ToGodotBool(), out newArray); return CreateTakingOwnershipOfDisposableValue(newArray); } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs index 16fde2a900..db27989bdb 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs @@ -6,118 +6,167 @@ namespace Godot.Bridge { internal static class CSharpInstanceBridge { - private static unsafe void Call(IntPtr godotObjectGCHandle, godot_string_name* method, - godot_variant** args, int argCount, godot_variant_call_error* ref_callError, godot_variant* r_ret) + [UnmanagedCallersOnly] + internal static unsafe godot_bool Call(IntPtr godotObjectGCHandle, godot_string_name* method, + godot_variant** args, int argCount, godot_variant_call_error* refCallError, godot_variant* ret) { - // Performance is not critical here as this will be replaced with source generators. - var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target; - - if (godotObject == null) + try { - *r_ret = default; - (*ref_callError).error = godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INSTANCE_IS_NULL; - return; + // Performance is not critical here as this will be replaced with source generators. + var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target; + + if (godotObject == null) + { + *ret = default; + (*refCallError).error = godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INSTANCE_IS_NULL; + return false.ToGodotBool(); + } + + using godot_string dest = default; + NativeFuncs.godotsharp_string_name_as_string(&dest, method); + string methodStr = Marshaling.mono_string_from_godot(dest); + + bool methodInvoked = godotObject.InternalGodotScriptCall(methodStr, args, argCount, out godot_variant retValue); + + if (!methodInvoked) + { + *ret = default; + // This is important, as it tells Object::call that no method was called. + // Otherwise, it would prevent Object::call from calling native methods. + (*refCallError).error = godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INVALID_METHOD; + return false.ToGodotBool(); + } + + *ret = retValue; + return true.ToGodotBool(); } - - using godot_string dest = default; - NativeFuncs.godotsharp_string_name_as_string(&dest, method); - string methodStr = Marshaling.mono_string_from_godot(dest); - - bool methodInvoked = godotObject.InternalGodotScriptCall(methodStr, args, argCount, out godot_variant outRet); - - if (!methodInvoked) + catch (Exception e) { - *r_ret = default; - // This is important, as it tells Object::call that no method was called. - // Otherwise, it would prevent Object::call from calling native methods. - (*ref_callError).error = godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INVALID_METHOD; - return; + ExceptionUtils.DebugPrintUnhandledException(e); + *ret = default; + return false.ToGodotBool(); } - - *r_ret = outRet; } - private static unsafe bool Set(IntPtr godotObjectGCHandle, godot_string_name* name, godot_variant* value) + [UnmanagedCallersOnly] + internal static unsafe godot_bool Set(IntPtr godotObjectGCHandle, godot_string_name* name, godot_variant* value) { - // Performance is not critical here as this will be replaced with source generators. - var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target; + try + { + // Performance is not critical here as this will be replaced with source generators. + var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target; - if (godotObject == null) - throw new InvalidOperationException(); + if (godotObject == null) + throw new InvalidOperationException(); - var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue( - NativeFuncs.godotsharp_string_name_new_copy(name)); + var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue( + NativeFuncs.godotsharp_string_name_new_copy(name)); - if (godotObject.InternalGodotScriptSetFieldOrPropViaReflection(nameManaged.ToString(), value)) - return true; + if (godotObject.InternalGodotScriptSetFieldOrPropViaReflection(nameManaged.ToString(), value)) + return true.ToGodotBool(); - object valueManaged = Marshaling.variant_to_mono_object(value); + object valueManaged = Marshaling.variant_to_mono_object(value); - return godotObject._Set(nameManaged, valueManaged); + return godotObject._Set(nameManaged, valueManaged).ToGodotBool(); + } + catch (Exception e) + { + ExceptionUtils.DebugPrintUnhandledException(e); + return false.ToGodotBool(); + } } - private static unsafe bool Get(IntPtr godotObjectGCHandle, godot_string_name* name, godot_variant* r_retValue) + [UnmanagedCallersOnly] + internal static unsafe godot_bool Get(IntPtr godotObjectGCHandle, godot_string_name* name, + godot_variant* outRet) { - // Performance is not critical here as this will be replaced with source generators. - var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target; + try + { + // Performance is not critical here as this will be replaced with source generators. + var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target; - if (godotObject == null) - throw new InvalidOperationException(); + if (godotObject == null) + throw new InvalidOperationException(); - var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue( - NativeFuncs.godotsharp_string_name_new_copy(name)); + var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue( + NativeFuncs.godotsharp_string_name_new_copy(name)); - if (godotObject.InternalGodotScriptGetFieldOrPropViaReflection(nameManaged.ToString(), - out godot_variant outRet)) - { - *r_retValue = outRet; - return true; - } + if (godotObject.InternalGodotScriptGetFieldOrPropViaReflection(nameManaged.ToString(), + out godot_variant outRetValue)) + { + *outRet = outRetValue; + return true.ToGodotBool(); + } - object ret = godotObject._Get(nameManaged); + object ret = godotObject._Get(nameManaged); - if (ret == null) + if (ret == null) + { + *outRet = default; + return false.ToGodotBool(); + } + + *outRet = Marshaling.mono_object_to_variant(ret); + return true.ToGodotBool(); + } + catch (Exception e) { - *r_retValue = default; - return false; + ExceptionUtils.DebugPrintUnhandledException(e); + *outRet = default; + return false.ToGodotBool(); } - - *r_retValue = Marshaling.mono_object_to_variant(ret); - return true; } - private static void CallDispose(IntPtr godotObjectGCHandle, bool okIfNull) + [UnmanagedCallersOnly] + internal static void CallDispose(IntPtr godotObjectGCHandle, godot_bool okIfNull) { - var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target; + try + { + var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target; - if (okIfNull) - godotObject?.Dispose(); - else - godotObject!.Dispose(); + if (okIfNull.ToBool()) + godotObject?.Dispose(); + else + godotObject!.Dispose(); + } + catch (Exception e) + { + ExceptionUtils.DebugPrintUnhandledException(e); + } } - private static unsafe void CallToString(IntPtr godotObjectGCHandle, godot_string* r_res, bool* r_valid) + [UnmanagedCallersOnly] + internal static unsafe void CallToString(IntPtr godotObjectGCHandle, godot_string* outRes, godot_bool* outValid) { - var self = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target; - - if (self == null) + try { - *r_res = default; - *r_valid = false; - return; + var self = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target; + + if (self == null) + { + *outRes = default; + *outValid = false.ToGodotBool(); + return; + } + + var resultStr = self.ToString(); + + if (resultStr == null) + { + *outRes = default; + *outValid = false.ToGodotBool(); + return; + } + + *outRes = Marshaling.mono_string_to_godot(resultStr); + *outValid = true.ToGodotBool(); } - - var resultStr = self.ToString(); - - if (resultStr == null) + catch (Exception e) { - *r_res = default; - *r_valid = false; - return; + ExceptionUtils.DebugPrintUnhandledException(e); + *outRes = default; + *outValid = false.ToGodotBool(); } - - *r_res = Marshaling.mono_string_to_godot(resultStr); - *r_valid = true; } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs index aa9e434b07..c6f2e8f77d 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs @@ -1,11 +1,22 @@ using System; using System.Runtime.InteropServices; +using Godot.NativeInterop; namespace Godot.Bridge { internal static class GCHandleBridge { - private static void FreeGCHandle(IntPtr gcHandlePtr) - => GCHandle.FromIntPtr(gcHandlePtr).Free(); + [UnmanagedCallersOnly] + internal static void FreeGCHandle(IntPtr gcHandlePtr) + { + try + { + GCHandle.FromIntPtr(gcHandlePtr).Free(); + } + catch (Exception e) + { + ExceptionUtils.DebugPrintUnhandledException(e); + } + } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs new file mode 100644 index 0000000000..1d19376cdd --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs @@ -0,0 +1,72 @@ +using System; +using System.Runtime.InteropServices; +using Godot.NativeInterop; + +namespace Godot.Bridge +{ + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct ManagedCallbacks + { + // @formatter:off + public delegate* unmanaged<IntPtr, godot_variant**, int, godot_bool*, void> SignalAwaiter_SignalCallback; + public delegate* unmanaged<IntPtr, godot_variant**, uint, godot_variant*, void> DelegateUtils_InvokeWithVariantArgs; + public delegate* unmanaged<IntPtr, IntPtr, godot_bool> DelegateUtils_DelegateEquals; + public delegate* unmanaged<void> ScriptManagerBridge_FrameCallback; + public delegate* unmanaged<godot_string_name*, IntPtr, IntPtr> ScriptManagerBridge_CreateManagedForGodotObjectBinding; + public delegate* unmanaged<IntPtr, IntPtr, godot_variant**, int, godot_bool> ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance; + public delegate* unmanaged<IntPtr, godot_string_name*, void> ScriptManagerBridge_GetScriptNativeName; + public delegate* unmanaged<IntPtr, IntPtr, void> ScriptManagerBridge_SetGodotObjectPtr; + public delegate* unmanaged<IntPtr, godot_string_name*, godot_variant**, int, godot_bool*, void> ScriptManagerBridge_RaiseEventSignal; + public delegate* unmanaged<IntPtr, godot_dictionary*, void> ScriptManagerBridge_GetScriptSignalList; + public delegate* unmanaged<IntPtr, godot_string*, godot_bool> ScriptManagerBridge_HasScriptSignal; + public delegate* unmanaged<IntPtr, godot_string*, godot_bool, godot_bool> ScriptManagerBridge_HasMethodUnknownParams; + public delegate* unmanaged<IntPtr, IntPtr, godot_bool> ScriptManagerBridge_ScriptIsOrInherits; + public delegate* unmanaged<IntPtr, godot_string*, godot_bool> ScriptManagerBridge_AddScriptBridge; + public delegate* unmanaged<IntPtr, void> ScriptManagerBridge_RemoveScriptBridge; + public delegate* unmanaged<IntPtr, godot_bool*, godot_dictionary*, void> ScriptManagerBridge_UpdateScriptClassInfo; + public delegate* unmanaged<IntPtr, IntPtr*, godot_bool, godot_bool> ScriptManagerBridge_SwapGCHandleForType; + public delegate* unmanaged<IntPtr, godot_string_name*, godot_variant**, int, godot_variant_call_error*, godot_variant*, godot_bool> CSharpInstanceBridge_Call; + public delegate* unmanaged<IntPtr, godot_string_name*, godot_variant*, godot_bool> CSharpInstanceBridge_Set; + public delegate* unmanaged<IntPtr, godot_string_name*, godot_variant*, godot_bool> CSharpInstanceBridge_Get; + public delegate* unmanaged<IntPtr, godot_bool, void> CSharpInstanceBridge_CallDispose; + public delegate* unmanaged<IntPtr, godot_string*, godot_bool*, void> CSharpInstanceBridge_CallToString; + public delegate* unmanaged<IntPtr, void> GCHandleBridge_FreeGCHandle; + public delegate* unmanaged<void> DebuggingUtils_InstallTraceListener; + public delegate* unmanaged<void> Dispatcher_InitializeDefaultGodotTaskScheduler; + // @formatter:on + + public static ManagedCallbacks Create() + { + return new() + { + // @formatter:off + SignalAwaiter_SignalCallback = &SignalAwaiter.SignalCallback, + DelegateUtils_InvokeWithVariantArgs = &DelegateUtils.InvokeWithVariantArgs, + DelegateUtils_DelegateEquals = &DelegateUtils.DelegateEquals, + ScriptManagerBridge_FrameCallback = &ScriptManagerBridge.FrameCallback, + ScriptManagerBridge_CreateManagedForGodotObjectBinding = &ScriptManagerBridge.CreateManagedForGodotObjectBinding, + ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance = &ScriptManagerBridge.CreateManagedForGodotObjectScriptInstance, + ScriptManagerBridge_GetScriptNativeName = &ScriptManagerBridge.GetScriptNativeName, + ScriptManagerBridge_SetGodotObjectPtr = &ScriptManagerBridge.SetGodotObjectPtr, + ScriptManagerBridge_RaiseEventSignal = &ScriptManagerBridge.RaiseEventSignal, + ScriptManagerBridge_GetScriptSignalList = &ScriptManagerBridge.GetScriptSignalList, + ScriptManagerBridge_HasScriptSignal = &ScriptManagerBridge.HasScriptSignal, + ScriptManagerBridge_HasMethodUnknownParams = &ScriptManagerBridge.HasMethodUnknownParams, + ScriptManagerBridge_ScriptIsOrInherits = &ScriptManagerBridge.ScriptIsOrInherits, + ScriptManagerBridge_AddScriptBridge = &ScriptManagerBridge.AddScriptBridge, + ScriptManagerBridge_RemoveScriptBridge = &ScriptManagerBridge.RemoveScriptBridge, + ScriptManagerBridge_UpdateScriptClassInfo = &ScriptManagerBridge.UpdateScriptClassInfo, + ScriptManagerBridge_SwapGCHandleForType = &ScriptManagerBridge.SwapGCHandleForType, + CSharpInstanceBridge_Call = &CSharpInstanceBridge.Call, + CSharpInstanceBridge_Set = &CSharpInstanceBridge.Set, + CSharpInstanceBridge_Get = &CSharpInstanceBridge.Get, + CSharpInstanceBridge_CallDispose = &CSharpInstanceBridge.CallDispose, + CSharpInstanceBridge_CallToString = &CSharpInstanceBridge.CallToString, + GCHandleBridge_FreeGCHandle = &GCHandleBridge.FreeGCHandle, + DebuggingUtils_InstallTraceListener = &DebuggingUtils.InstallTraceListener, + Dispatcher_InitializeDefaultGodotTaskScheduler = &Dispatcher.InitializeDefaultGodotTaskScheduler, + // @formatter:on + }; + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs index a39da68a02..9655887e52 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs @@ -27,99 +27,150 @@ namespace Godot.Bridge } }; + [UnmanagedCallersOnly] internal static void FrameCallback() { - Dispatcher.DefaultGodotTaskScheduler?.Activate(); + try + { + Dispatcher.DefaultGodotTaskScheduler?.Activate(); + } + catch (Exception e) + { + ExceptionUtils.DebugUnhandledException(e); + } } + [UnmanagedCallersOnly] internal static unsafe IntPtr CreateManagedForGodotObjectBinding(godot_string_name* nativeTypeName, IntPtr godotObject) { - Type nativeType = TypeGetProxyClass(nativeTypeName); - var obj = (Object)FormatterServices.GetUninitializedObject(nativeType); + try + { + Type nativeType = TypeGetProxyClass(nativeTypeName); + var obj = (Object)FormatterServices.GetUninitializedObject(nativeType); - obj.NativePtr = godotObject; + obj.NativePtr = godotObject; - var ctor = nativeType.GetConstructor( - BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, - null, Type.EmptyTypes, null); - _ = ctor!.Invoke(obj, null); + var ctor = nativeType.GetConstructor( + BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, + null, Type.EmptyTypes, null); + _ = ctor!.Invoke(obj, null); - return GCHandle.ToIntPtr(GCHandle.Alloc(obj)); + return GCHandle.ToIntPtr(GCHandle.Alloc(obj)); + } + catch (Exception e) + { + ExceptionUtils.DebugPrintUnhandledException(e); + return IntPtr.Zero; + } } - internal static unsafe void CreateManagedForGodotObjectScriptInstance(IntPtr scriptPtr, IntPtr godotObject, + [UnmanagedCallersOnly] + internal static unsafe godot_bool CreateManagedForGodotObjectScriptInstance(IntPtr scriptPtr, + IntPtr godotObject, godot_variant** args, int argCount) { - // Performance is not critical here as this will be replaced with source generators. - Type scriptType = _scriptBridgeMap[scriptPtr]; - var obj = (Object)FormatterServices.GetUninitializedObject(scriptType); + try + { + // Performance is not critical here as this will be replaced with source generators. + Type scriptType = _scriptBridgeMap[scriptPtr]; + var obj = (Object)FormatterServices.GetUninitializedObject(scriptType); - obj.NativePtr = godotObject; + obj.NativePtr = godotObject; - var ctor = scriptType - .GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) - .Where(c => c.GetParameters().Length == argCount) - .FirstOrDefault(); + var ctor = scriptType + .GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) + .Where(c => c.GetParameters().Length == argCount) + .FirstOrDefault(); - if (ctor == null) - { - if (argCount == 0) - { - throw new MissingMemberException( - $"Cannot create script instance. The class '{scriptType.FullName}' does not define a parameterless constructor."); - } - else + if (ctor == null) { - throw new MissingMemberException( - $"The class '{scriptType.FullName}' does not define a constructor that takes x parameters."); + if (argCount == 0) + { + throw new MissingMemberException( + $"Cannot create script instance. The class '{scriptType.FullName}' does not define a parameterless constructor."); + } + else + { + throw new MissingMemberException( + $"The class '{scriptType.FullName}' does not define a constructor that takes x parameters."); + } } - } - var parameters = ctor.GetParameters(); - int paramCount = parameters.Length; + var parameters = ctor.GetParameters(); + int paramCount = parameters.Length; - object[] invokeParams = new object[paramCount]; + object[] invokeParams = new object[paramCount]; - for (int i = 0; i < paramCount; i++) + for (int i = 0; i < paramCount; i++) + { + invokeParams[i] = Marshaling.variant_to_mono_object_of_type( + args[i], parameters[i].ParameterType); + } + + ctor.Invoke(obj, invokeParams); + return true.ToGodotBool(); + } + catch (Exception e) { - invokeParams[i] = Marshaling.variant_to_mono_object_of_type( - args[i], parameters[i].ParameterType); + ExceptionUtils.DebugPrintUnhandledException(e); + return false.ToGodotBool(); } - - ctor.Invoke(obj, invokeParams); } - private static unsafe void GetScriptNativeName(IntPtr scriptPtr, godot_string_name* r_res) + [UnmanagedCallersOnly] + internal static unsafe void GetScriptNativeName(IntPtr scriptPtr, godot_string_name* outRes) { - // Performance is not critical here as this will be replaced with source generators. - if (!_scriptBridgeMap.TryGetValue(scriptPtr, out var scriptType)) + try { - *r_res = default; - return; - } + // Performance is not critical here as this will be replaced with source generators. + if (!_scriptBridgeMap.TryGetValue(scriptPtr, out var scriptType)) + { + *outRes = default; + return; + } - var native = Object.InternalGetClassNativeBase(scriptType); + var native = Object.InternalGetClassNativeBase(scriptType); - var field = native?.GetField("NativeName", BindingFlags.DeclaredOnly | BindingFlags.Static | - BindingFlags.Public | BindingFlags.NonPublic); + var field = native?.GetField("NativeName", BindingFlags.DeclaredOnly | BindingFlags.Static | + BindingFlags.Public | BindingFlags.NonPublic); - if (field == null) - { - *r_res = default; - return; - } + if (field == null) + { + *outRes = default; + return; + } - var nativeName = (StringName)field.GetValue(null); + var nativeName = (StringName)field.GetValue(null); - *r_res = NativeFuncs.godotsharp_string_name_new_copy(nativeName.NativeValue); + if (nativeName == null) + { + *outRes = default; + return; + } + + *outRes = NativeFuncs.godotsharp_string_name_new_copy(nativeName.NativeValue); + } + catch (Exception e) + { + ExceptionUtils.DebugUnhandledException(e); + *outRes = default; + } } - private static void SetGodotObjectPtr(IntPtr gcHandlePtr, IntPtr newPtr) + [UnmanagedCallersOnly] + internal static void SetGodotObjectPtr(IntPtr gcHandlePtr, IntPtr newPtr) { - var target = (Object)GCHandle.FromIntPtr(gcHandlePtr).Target; - if (target != null) - target.NativePtr = newPtr; + try + { + var target = (Object)GCHandle.FromIntPtr(gcHandlePtr).Target; + if (target != null) + target.NativePtr = newPtr; + } + catch (Exception e) + { + ExceptionUtils.DebugUnhandledException(e); + } } private static unsafe Type TypeGetProxyClass(godot_string_name* nativeTypeName) @@ -152,7 +203,9 @@ namespace Godot.Bridge return wrapperType; } - internal static void LookupScriptsInAssembly(Assembly assembly) + // Called from GodotPlugins + // ReSharper disable once UnusedMember.Local + private static void LookupScriptsInAssembly(Assembly assembly) { static void LookupScriptForClass(Type type) { @@ -208,294 +261,398 @@ namespace Godot.Bridge } } + [UnmanagedCallersOnly] internal static unsafe void RaiseEventSignal(IntPtr ownerGCHandlePtr, - godot_string_name* eventSignalName, godot_variant** args, int argCount, bool* r_ownerIsNull) + godot_string_name* eventSignalName, godot_variant** args, int argCount, godot_bool* outOwnerIsNull) { - var owner = (Object)GCHandle.FromIntPtr(ownerGCHandlePtr).Target; - - if (owner == null) + try { - *r_ownerIsNull = true; - return; - } + var owner = (Object)GCHandle.FromIntPtr(ownerGCHandlePtr).Target; + + if (owner == null) + { + *outOwnerIsNull = true.ToGodotBool(); + return; + } - *r_ownerIsNull = false; + *outOwnerIsNull = false.ToGodotBool(); - owner.InternalRaiseEventSignal(eventSignalName, args, argCount); + owner.InternalRaiseEventSignal(eventSignalName, args, argCount); + } + catch (Exception e) + { + ExceptionUtils.DebugPrintUnhandledException(e); + *outOwnerIsNull = false.ToGodotBool(); + } } - internal static unsafe void GetScriptSignalList(IntPtr scriptPtr, godot_dictionary* r_retSignals) + [UnmanagedCallersOnly] + internal static unsafe void GetScriptSignalList(IntPtr scriptPtr, godot_dictionary* outRetSignals) { - // Performance is not critical here as this will be replaced with source generators. - using var signals = new Dictionary(); - - Type top = _scriptBridgeMap[scriptPtr]; - Type native = Object.InternalGetClassNativeBase(top); - - while (top != null && top != native) + try { - // Legacy signals + // Performance is not critical here as this will be replaced with source generators. + using var signals = new Dictionary(); + + Type top = _scriptBridgeMap[scriptPtr]; + Type native = Object.InternalGetClassNativeBase(top); - foreach (var signalDelegate in top - .GetNestedTypes(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public) - .Where(nestedType => typeof(Delegate).IsAssignableFrom(nestedType)) - .Where(@delegate => @delegate.GetCustomAttributes().OfType<SignalAttribute>().Any())) + while (top != null && top != native) { - var invokeMethod = signalDelegate.GetMethod("Invoke"); + // Legacy signals - if (invokeMethod == null) - throw new MissingMethodException(signalDelegate.FullName, "Invoke"); + foreach (var signalDelegate in top + .GetNestedTypes(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public) + .Where(nestedType => typeof(Delegate).IsAssignableFrom(nestedType)) + .Where(@delegate => @delegate.GetCustomAttributes().OfType<SignalAttribute>().Any())) + { + var invokeMethod = signalDelegate.GetMethod("Invoke"); - var signalParams = new Collections.Array(); + if (invokeMethod == null) + throw new MissingMethodException(signalDelegate.FullName, "Invoke"); - foreach (var parameters in invokeMethod.GetParameters()) - { - var paramType = Marshaling.managed_to_variant_type( - parameters.ParameterType, out bool nilIsVariant); - signalParams.Add(new Dictionary() + var signalParams = new Collections.Array(); + + foreach (var parameters in invokeMethod.GetParameters()) { - { "name", parameters.Name }, - { "type", paramType }, - { "nil_is_variant", nilIsVariant } - }); + var paramType = Marshaling.managed_to_variant_type( + parameters.ParameterType, out bool nilIsVariant); + signalParams.Add(new Dictionary() + { + { "name", parameters.Name }, + { "type", paramType }, + { "nil_is_variant", nilIsVariant } + }); + } + + signals.Add(signalDelegate.Name, signalParams); } - signals.Add(signalDelegate.Name, signalParams); - } + // Event signals - // Event signals + var foundEventSignals = top.GetEvents( + BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public) + .Where(ev => ev.GetCustomAttributes().OfType<SignalAttribute>().Any()) + .Select(ev => ev.Name); - var foundEventSignals = top.GetEvents( + var fields = top.GetFields( BindingFlags.DeclaredOnly | BindingFlags.Instance | - BindingFlags.NonPublic | BindingFlags.Public) - .Where(ev => ev.GetCustomAttributes().OfType<SignalAttribute>().Any()) - .Select(ev => ev.Name); - - var fields = top.GetFields( - BindingFlags.DeclaredOnly | BindingFlags.Instance | - BindingFlags.NonPublic | BindingFlags.Public); + BindingFlags.NonPublic | BindingFlags.Public); - foreach (var eventSignalField in fields - .Where(f => typeof(Delegate).IsAssignableFrom(f.FieldType)) - .Where(f => foundEventSignals.Contains(f.Name))) - { - var delegateType = eventSignalField.FieldType; - var invokeMethod = delegateType.GetMethod("Invoke"); + foreach (var eventSignalField in fields + .Where(f => typeof(Delegate).IsAssignableFrom(f.FieldType)) + .Where(f => foundEventSignals.Contains(f.Name))) + { + var delegateType = eventSignalField.FieldType; + var invokeMethod = delegateType.GetMethod("Invoke"); - if (invokeMethod == null) - throw new MissingMethodException(delegateType.FullName, "Invoke"); + if (invokeMethod == null) + throw new MissingMethodException(delegateType.FullName, "Invoke"); - var signalParams = new Collections.Array(); + var signalParams = new Collections.Array(); - foreach (var parameters in invokeMethod.GetParameters()) - { - var paramType = Marshaling.managed_to_variant_type( - parameters.ParameterType, out bool nilIsVariant); - signalParams.Add(new Dictionary() + foreach (var parameters in invokeMethod.GetParameters()) { - { "name", parameters.Name }, - { "type", paramType }, - { "nil_is_variant", nilIsVariant } - }); + var paramType = Marshaling.managed_to_variant_type( + parameters.ParameterType, out bool nilIsVariant); + signalParams.Add(new Dictionary() + { + { "name", parameters.Name }, + { "type", paramType }, + { "nil_is_variant", nilIsVariant } + }); + } + + signals.Add(eventSignalField.Name, signalParams); } - signals.Add(eventSignalField.Name, signalParams); + top = top.BaseType; } - top = top.BaseType; + *outRetSignals = NativeFuncs.godotsharp_dictionary_new_copy(signals.NativeValue); + } + catch (Exception e) + { + ExceptionUtils.DebugUnhandledException(e); + *outRetSignals = NativeFuncs.godotsharp_dictionary_new(); } - - *r_retSignals = NativeFuncs.godotsharp_dictionary_new_copy(signals.NativeValue); } - internal static unsafe bool HasScriptSignal(IntPtr scriptPtr, godot_string* signalName) + [UnmanagedCallersOnly] + internal static unsafe godot_bool HasScriptSignal(IntPtr scriptPtr, godot_string* signalName) { - // Performance is not critical here as this will be replaced with source generators. - using var signals = new Dictionary(); - - string signalNameStr = Marshaling.mono_string_from_godot(*signalName); + try + { + // Performance is not critical here as this will be replaced with source generators. + using var signals = new Dictionary(); - Type top = _scriptBridgeMap[scriptPtr]; - Type native = Object.InternalGetClassNativeBase(top); + string signalNameStr = Marshaling.mono_string_from_godot(*signalName); - while (top != null && top != native) - { - // Legacy signals + Type top = _scriptBridgeMap[scriptPtr]; + Type native = Object.InternalGetClassNativeBase(top); - if (top - .GetNestedTypes(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public) - .Where(nestedType => typeof(Delegate).IsAssignableFrom(nestedType)) - .Where(@delegate => @delegate.GetCustomAttributes().OfType<SignalAttribute>().Any()) - .Any(signalDelegate => signalDelegate.Name == signalNameStr) - ) + while (top != null && top != native) { - return true; - } + // Legacy signals + + if (top + .GetNestedTypes(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public) + .Where(nestedType => typeof(Delegate).IsAssignableFrom(nestedType)) + .Where(@delegate => @delegate.GetCustomAttributes().OfType<SignalAttribute>().Any()) + .Any(signalDelegate => signalDelegate.Name == signalNameStr) + ) + { + return true.ToGodotBool(); + } - // Event signals + // Event signals - if (top.GetEvents( - BindingFlags.DeclaredOnly | BindingFlags.Instance | - BindingFlags.NonPublic | BindingFlags.Public) - .Where(ev => ev.GetCustomAttributes().OfType<SignalAttribute>().Any()) - .Any(eventSignal => eventSignal.Name == signalNameStr) - ) - { - return true; + if (top.GetEvents( + BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public) + .Where(ev => ev.GetCustomAttributes().OfType<SignalAttribute>().Any()) + .Any(eventSignal => eventSignal.Name == signalNameStr) + ) + { + return true.ToGodotBool(); + } + + top = top.BaseType; } - top = top.BaseType; + return false.ToGodotBool(); + } + catch (Exception e) + { + ExceptionUtils.DebugUnhandledException(e); + return false.ToGodotBool(); } - - return false; } - internal static unsafe bool HasMethodUnknownParams(IntPtr scriptPtr, godot_string* method, bool deep) + [UnmanagedCallersOnly] + internal static unsafe godot_bool HasMethodUnknownParams(IntPtr scriptPtr, godot_string* method, + godot_bool deep) { - // Performance is not critical here as this will be replaced with source generators. - if (!_scriptBridgeMap.TryGetValue(scriptPtr, out var scriptType)) - return false; - - string methodStr = Marshaling.mono_string_from_godot(*method); - - if (deep) + try { - Type top = scriptType; - Type native = Object.InternalGetClassNativeBase(scriptType); + // Performance is not critical here as this will be replaced with source generators. + if (!_scriptBridgeMap.TryGetValue(scriptPtr, out var scriptType)) + return false.ToGodotBool(); - while (top != null && top != native) + string methodStr = Marshaling.mono_string_from_godot(*method); + + if (deep.ToBool()) { - var methodInfo = top.GetMethod(methodStr, - BindingFlags.DeclaredOnly | BindingFlags.Instance | - BindingFlags.NonPublic | BindingFlags.Public); + Type top = scriptType; + Type native = Object.InternalGetClassNativeBase(scriptType); - if (methodInfo != null) - return true; + while (top != null && top != native) + { + var methodInfo = top.GetMethod(methodStr, + BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public); - top = top.BaseType; - } + if (methodInfo != null) + return true.ToGodotBool(); + + top = top.BaseType; + } + + top = native; + Type typeOfSystemObject = typeof(System.Object); + while (top != null && top != typeOfSystemObject) + { + bool found = top.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public) + .Where(m => m.GetCustomAttributes(false).OfType<GodotMethodAttribute>() + .Where(a => a.MethodName == methodStr) + .Any()) + .Any(); + + if (found) + return true.ToGodotBool(); - return false; + top = top.BaseType; + } + + return false.ToGodotBool(); + } + else + { + var methodInfo = scriptType.GetMethod(methodStr, BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public); + return (methodInfo != null).ToGodotBool(); + } } - else + catch (Exception e) { - var methodInfo = scriptType.GetMethod(methodStr, BindingFlags.DeclaredOnly | BindingFlags.Instance | - BindingFlags.NonPublic | BindingFlags.Public); - return methodInfo != null; + ExceptionUtils.DebugUnhandledException(e); + return false.ToGodotBool(); } } - internal static bool ScriptIsOrInherits(IntPtr scriptPtr, IntPtr scriptPtrMaybeBase) + [UnmanagedCallersOnly] + internal static godot_bool ScriptIsOrInherits(IntPtr scriptPtr, IntPtr scriptPtrMaybeBase) { - if (!_scriptBridgeMap.TryGetValue(scriptPtr, out var scriptType)) - return false; + try + { + if (!_scriptBridgeMap.TryGetValue(scriptPtr, out var scriptType)) + return false.ToGodotBool(); - if (!_scriptBridgeMap.TryGetValue(scriptPtrMaybeBase, out var maybeBaseType)) - return false; + if (!_scriptBridgeMap.TryGetValue(scriptPtrMaybeBase, out var maybeBaseType)) + return false.ToGodotBool(); - return scriptType == maybeBaseType || maybeBaseType.IsAssignableFrom(scriptType); + return (scriptType == maybeBaseType || maybeBaseType.IsAssignableFrom(scriptType)).ToGodotBool(); + } + catch (Exception e) + { + ExceptionUtils.DebugUnhandledException(e); + return false.ToGodotBool(); + } } - internal static unsafe bool AddScriptBridge(IntPtr scriptPtr, godot_string* scriptPath) + [UnmanagedCallersOnly] + internal static unsafe godot_bool AddScriptBridge(IntPtr scriptPtr, godot_string* scriptPath) { - string scriptPathStr = Marshaling.mono_string_from_godot(*scriptPath); + try + { + string scriptPathStr = Marshaling.mono_string_from_godot(*scriptPath); - if (!_scriptLookupMap.TryGetValue(scriptPathStr, out var lookupInfo)) - return false; + if (!_scriptLookupMap.TryGetValue(scriptPathStr, out var lookupInfo)) + return false.ToGodotBool(); - _scriptBridgeMap.Add(scriptPtr, lookupInfo.ScriptType); + _scriptBridgeMap.Add(scriptPtr, lookupInfo.ScriptType); - return true; + return true.ToGodotBool(); + } + catch (Exception e) + { + ExceptionUtils.DebugUnhandledException(e); + return false.ToGodotBool(); + } } internal static void AddScriptBridgeWithType(IntPtr scriptPtr, Type scriptType) => _scriptBridgeMap.Add(scriptPtr, scriptType); + [UnmanagedCallersOnly] internal static void RemoveScriptBridge(IntPtr scriptPtr) - => _scriptBridgeMap.Remove(scriptPtr); - - internal static unsafe void UpdateScriptClassInfo(IntPtr scriptPtr, bool* r_tool, - godot_dictionary* r_rpcFunctionsDest) { - // Performance is not critical here as this will be replaced with source generators. - var scriptType = _scriptBridgeMap[scriptPtr]; - - *r_tool = scriptType.GetCustomAttributes(inherit: false) - .OfType<ToolAttribute>() - .Any(); + try + { + _scriptBridgeMap.Remove(scriptPtr); + } + catch (Exception e) + { + ExceptionUtils.DebugUnhandledException(e); + } + } - if (!*r_tool && scriptType.IsNested) + [UnmanagedCallersOnly] + internal static unsafe void UpdateScriptClassInfo(IntPtr scriptPtr, godot_bool* outTool, + godot_dictionary* outRpcFunctionsDest) + { + try { - *r_tool = scriptType.DeclaringType?.GetCustomAttributes(inherit: false) + // Performance is not critical here as this will be replaced with source generators. + var scriptType = _scriptBridgeMap[scriptPtr]; + + *outTool = scriptType.GetCustomAttributes(inherit: false) .OfType<ToolAttribute>() - .Any() ?? false; - } + .Any().ToGodotBool(); - if (!*r_tool && scriptType.Assembly.GetName().Name == "GodotTools") - *r_tool = true; + if (!(*outTool).ToBool() && scriptType.IsNested) + { + *outTool = (scriptType.DeclaringType?.GetCustomAttributes(inherit: false) + .OfType<ToolAttribute>() + .Any() ?? false).ToGodotBool(); + } - // RPC functions + if (!(*outTool).ToBool() && scriptType.Assembly.GetName().Name == "GodotTools") + *outTool = true.ToGodotBool(); - Dictionary<string, Dictionary> rpcFunctions = new(); + // RPC functions - Type top = _scriptBridgeMap[scriptPtr]; - Type native = Object.InternalGetClassNativeBase(top); + Dictionary<string, Dictionary> rpcFunctions = new(); - while (top != null && top != native) - { - foreach (var method in top.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | - BindingFlags.NonPublic | BindingFlags.Public)) + Type top = _scriptBridgeMap[scriptPtr]; + Type native = Object.InternalGetClassNativeBase(top); + + while (top != null && top != native) { - if (method.IsStatic) - continue; + foreach (var method in top.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | + BindingFlags.NonPublic | BindingFlags.Public)) + { + if (method.IsStatic) + continue; - string methodName = method.Name; + string methodName = method.Name; - if (rpcFunctions.ContainsKey(methodName)) - continue; + if (rpcFunctions.ContainsKey(methodName)) + continue; - var rpcAttr = method.GetCustomAttributes(inherit: false) - .OfType<RPCAttribute>().FirstOrDefault(); + var rpcAttr = method.GetCustomAttributes(inherit: false) + .OfType<RPCAttribute>().FirstOrDefault(); - if (rpcAttr == null) - continue; + if (rpcAttr == null) + continue; - var rpcConfig = new Dictionary(); + var rpcConfig = new Dictionary(); - rpcConfig["rpc_mode"] = (long)rpcAttr.Mode; - rpcConfig["call_local"] = rpcAttr.CallLocal; - rpcConfig["transfer_mode"] = (long)rpcAttr.TransferMode; - rpcConfig["channel"] = rpcAttr.TransferChannel; + rpcConfig["rpc_mode"] = (long)rpcAttr.Mode; + rpcConfig["call_local"] = rpcAttr.CallLocal; + rpcConfig["transfer_mode"] = (long)rpcAttr.TransferMode; + rpcConfig["channel"] = rpcAttr.TransferChannel; + + rpcFunctions.Add(methodName, rpcConfig); + } - rpcFunctions.Add(methodName, rpcConfig); + top = top.BaseType; } - top = top.BaseType; + *outRpcFunctionsDest = + NativeFuncs.godotsharp_dictionary_new_copy(((Dictionary)rpcFunctions).NativeValue); + } + catch (Exception e) + { + ExceptionUtils.DebugUnhandledException(e); + *outTool = false.ToGodotBool(); + *outRpcFunctionsDest = NativeFuncs.godotsharp_dictionary_new(); } - - *r_rpcFunctionsDest = NativeFuncs.godotsharp_dictionary_new_copy(((Dictionary)rpcFunctions).NativeValue); } - internal static unsafe bool SwapGCHandleForType(IntPtr oldGCHandlePtr, IntPtr* r_newGCHandlePtr, - bool createWeak) + [UnmanagedCallersOnly] + internal static unsafe godot_bool SwapGCHandleForType(IntPtr oldGCHandlePtr, IntPtr* outNewGCHandlePtr, + godot_bool createWeak) { - var oldGCHandle = GCHandle.FromIntPtr(oldGCHandlePtr); + try + { + var oldGCHandle = GCHandle.FromIntPtr(oldGCHandlePtr); - object target = oldGCHandle.Target; + object target = oldGCHandle.Target; - if (target == null) - { - oldGCHandle.Free(); - *r_newGCHandlePtr = IntPtr.Zero; - return false; // Called after the managed side was collected, so nothing to do here - } + if (target == null) + { + oldGCHandle.Free(); + *outNewGCHandlePtr = IntPtr.Zero; + return false.ToGodotBool(); // Called after the managed side was collected, so nothing to do here + } - // Release the current weak handle and replace it with a strong handle. - var newGCHandle = GCHandle.Alloc(target, createWeak ? GCHandleType.Weak : GCHandleType.Normal); + // Release the current weak handle and replace it with a strong handle. + var newGCHandle = GCHandle.Alloc(target, + createWeak.ToBool() ? GCHandleType.Weak : GCHandleType.Normal); - oldGCHandle.Free(); - *r_newGCHandlePtr = GCHandle.ToIntPtr(newGCHandle); - return true; + oldGCHandle.Free(); + *outNewGCHandlePtr = GCHandle.ToIntPtr(newGCHandle); + return true.ToGodotBool(); + } + catch (Exception e) + { + ExceptionUtils.DebugUnhandledException(e); + *outNewGCHandlePtr = IntPtr.Zero; + return false.ToGodotBool(); + } } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs index edfe3464ec..e446b3db1c 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs @@ -1,7 +1,9 @@ using System; using System.Diagnostics; using System.Reflection; +using System.Runtime.InteropServices; using System.Text; +using Godot.NativeInterop; namespace Godot { @@ -19,13 +21,23 @@ namespace Godot sb.Append(" "); } - public static void InstallTraceListener() + [UnmanagedCallersOnly] + internal static void InstallTraceListener() { - Trace.Listeners.Clear(); - Trace.Listeners.Add(new GodotTraceListener()); + try + { + Trace.Listeners.Clear(); + Trace.Listeners.Add(new GodotTraceListener()); + } + catch (Exception e) + { + ExceptionUtils.DebugPrintUnhandledException(e); + ExceptionUtils.PushError("Failed to install 'System.Diagnostics.Trace' listener."); + } } - public static void GetStackFrameInfo(StackFrame frame, out string fileName, out int fileLineNumber, out string methodDecl) + public static void GetStackFrameInfo(StackFrame frame, out string fileName, out int fileLineNumber, + out string methodDecl) { fileName = frame.GetFileName(); fileLineNumber = frame.GetFileLineNumber(); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs index 2a562d4d48..87c93e35f5 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs @@ -11,38 +11,56 @@ namespace Godot { internal static class DelegateUtils { - internal static bool DelegateEquals(IntPtr delegateGCHandleA, IntPtr delegateGCHandleB) + [UnmanagedCallersOnly] + internal static godot_bool DelegateEquals(IntPtr delegateGCHandleA, IntPtr delegateGCHandleB) { - var @delegateA = (Delegate)GCHandle.FromIntPtr(delegateGCHandleA).Target; - var @delegateB = (Delegate)GCHandle.FromIntPtr(delegateGCHandleB).Target; - return @delegateA == @delegateB; + try + { + var @delegateA = (Delegate)GCHandle.FromIntPtr(delegateGCHandleA).Target; + var @delegateB = (Delegate)GCHandle.FromIntPtr(delegateGCHandleB).Target; + return (@delegateA == @delegateB).ToGodotBool(); + } + catch (Exception e) + { + ExceptionUtils.DebugUnhandledException(e); + return false.ToGodotBool(); + } } + [UnmanagedCallersOnly] internal static unsafe void InvokeWithVariantArgs(IntPtr delegateGCHandle, godot_variant** args, uint argc, - godot_variant* ret) + godot_variant* outRet) { - // TODO: Optimize - var @delegate = (Delegate)GCHandle.FromIntPtr(delegateGCHandle).Target; - var managedArgs = new object[argc]; + try + { + // TODO: Optimize + var @delegate = (Delegate)GCHandle.FromIntPtr(delegateGCHandle).Target; + var managedArgs = new object[argc]; - var parameterInfos = @delegate.Method.GetParameters(); - var paramsLength = parameterInfos.Length; + var parameterInfos = @delegate!.Method.GetParameters(); + var paramsLength = parameterInfos.Length; - if (argc != paramsLength) - { - throw new InvalidOperationException( - $"The delegate expects {paramsLength} arguments, but received {argc}."); - } + if (argc != paramsLength) + { + throw new InvalidOperationException( + $"The delegate expects {paramsLength} arguments, but received {argc}."); + } - for (uint i = 0; i < argc; i++) - { - managedArgs[i] = Marshaling.variant_to_mono_object_of_type( - args[i], parameterInfos[i].ParameterType); - } + for (uint i = 0; i < argc; i++) + { + managedArgs[i] = Marshaling.variant_to_mono_object_of_type( + args[i], parameterInfos[i].ParameterType); + } - object invokeRet = @delegate.DynamicInvoke(managedArgs); + object invokeRet = @delegate.DynamicInvoke(managedArgs); - *ret = Marshaling.mono_object_to_variant(invokeRet); + *outRet = Marshaling.mono_object_to_variant(invokeRet); + } + catch (Exception e) + { + ExceptionUtils.DebugPrintUnhandledException(e); + *outRet = default; + } } // TODO: Check if we should be using BindingFlags.DeclaredOnly (would give better reflection performance). diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs index 8bc33837e6..c21d53b4d4 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs @@ -76,7 +76,7 @@ namespace Godot.Collections public Dictionary Duplicate(bool deep = false) { godot_dictionary newDictionary; - NativeFuncs.godotsharp_dictionary_duplicate(ref NativeValue, deep, out newDictionary); + NativeFuncs.godotsharp_dictionary_duplicate(ref NativeValue, deep.ToGodotBool(), out newDictionary); return CreateTakingOwnershipOfDisposableValue(newDictionary); } @@ -137,7 +137,7 @@ namespace Godot.Collections { using godot_variant variantKey = Marshaling.mono_object_to_variant(key); if (NativeFuncs.godotsharp_dictionary_try_get_value(ref NativeValue, &variantKey, - out godot_variant value)) + out godot_variant value).ToBool()) { using (value) return Marshaling.variant_to_mono_object(&value); @@ -165,7 +165,7 @@ namespace Godot.Collections { using godot_variant variantKey = Marshaling.mono_object_to_variant(key); - if (NativeFuncs.godotsharp_dictionary_contains_key(ref NativeValue, &variantKey)) + if (NativeFuncs.godotsharp_dictionary_contains_key(ref NativeValue, &variantKey).ToBool()) throw new ArgumentException("An element with the same key already exists", nameof(key)); using godot_variant variantValue = Marshaling.mono_object_to_variant(value); @@ -185,7 +185,7 @@ namespace Godot.Collections public unsafe bool Contains(object key) { using godot_variant variantKey = Marshaling.mono_object_to_variant(key); - return NativeFuncs.godotsharp_dictionary_contains_key(ref NativeValue, &variantKey); + return NativeFuncs.godotsharp_dictionary_contains_key(ref NativeValue, &variantKey).ToBool(); } /// <summary> @@ -432,7 +432,7 @@ namespace Godot.Collections { using godot_variant variantKey = Marshaling.mono_object_to_variant(key); if (NativeFuncs.godotsharp_dictionary_try_get_value(ref _underlyingDict.NativeValue, - &variantKey, out godot_variant value)) + &variantKey, out godot_variant value).ToBool()) { using (value) return (TValue)Marshaling.variant_to_mono_object_of_type(&value, TypeOfValues); @@ -513,7 +513,7 @@ namespace Godot.Collections public unsafe bool Remove(TKey key) { using godot_variant variantKey = Marshaling.mono_object_to_variant(key); - return NativeFuncs.godotsharp_dictionary_remove_key(ref _underlyingDict.NativeValue, &variantKey); + return NativeFuncs.godotsharp_dictionary_remove_key(ref _underlyingDict.NativeValue, &variantKey).ToBool(); } /// <summary> @@ -526,7 +526,7 @@ namespace Godot.Collections { using godot_variant variantKey = Marshaling.mono_object_to_variant(key); bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref _underlyingDict.NativeValue, - &variantKey, out godot_variant retValue); + &variantKey, out godot_variant retValue).ToBool(); using (retValue) { @@ -566,7 +566,7 @@ namespace Godot.Collections { using godot_variant variantKey = Marshaling.mono_object_to_variant(item.Key); bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref _underlyingDict.NativeValue, - &variantKey, out godot_variant retValue); + &variantKey, out godot_variant retValue).ToBool(); using (retValue) { @@ -574,7 +574,7 @@ namespace Godot.Collections return false; using godot_variant variantValue = Marshaling.mono_object_to_variant(item.Value); - return NativeFuncs.godotsharp_variant_equals(&variantValue, &retValue); + return NativeFuncs.godotsharp_variant_equals(&variantValue, &retValue).ToBool(); } } @@ -610,7 +610,7 @@ namespace Godot.Collections { using godot_variant variantKey = Marshaling.mono_object_to_variant(item.Key); bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref _underlyingDict.NativeValue, - &variantKey, out godot_variant retValue); + &variantKey, out godot_variant retValue).ToBool(); using (retValue) { @@ -618,8 +618,11 @@ namespace Godot.Collections return false; using godot_variant variantValue = Marshaling.mono_object_to_variant(item.Value); - if (NativeFuncs.godotsharp_variant_equals(&variantValue, &retValue)) - return NativeFuncs.godotsharp_dictionary_remove_key(ref _underlyingDict.NativeValue, &variantKey); + if (NativeFuncs.godotsharp_variant_equals(&variantValue, &retValue).ToBool()) + { + return NativeFuncs.godotsharp_dictionary_remove_key( + ref _underlyingDict.NativeValue, &variantKey).ToBool(); + } return false; } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs index 5f84bb530f..e8cfb8e1b1 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs @@ -1,12 +1,24 @@ +using System; +using System.Runtime.InteropServices; +using Godot.NativeInterop; + namespace Godot { public static class Dispatcher { internal static GodotTaskScheduler DefaultGodotTaskScheduler; - private static void InitializeDefaultGodotTaskScheduler() + [UnmanagedCallersOnly] + internal static void InitializeDefaultGodotTaskScheduler() { - DefaultGodotTaskScheduler = new GodotTaskScheduler(); + try + { + DefaultGodotTaskScheduler = new GodotTaskScheduler(); + } + catch (Exception e) + { + ExceptionUtils.DebugUnhandledException(e); + } } public static GodotSynchronizationContext SynchronizationContext => DefaultGodotTaskScheduler.Context; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs index b939da8778..17bca19fab 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs @@ -24,14 +24,16 @@ namespace Godot if (nativeBase) { // Native type - var field = typeOfT.GetField("NativeName", BindingFlags.DeclaredOnly | BindingFlags.Static | - BindingFlags.Public | BindingFlags.NonPublic); + var field = typeOfT.GetField("NativeName", + BindingFlags.DeclaredOnly | BindingFlags.Static | + BindingFlags.Public | BindingFlags.NonPublic); var nativeName = (StringName)field!.GetValue(null); godot_string_name nativeNameAux = nativeName.NativeValue; godot_array inputAux = array.NativeValue; godot_array filteredArray; - godotsharp_array_filter_godot_objects_by_native(&nativeNameAux, &inputAux, &filteredArray); + NativeFuncs.godotsharp_array_filter_godot_objects_by_native( + &nativeNameAux, &inputAux, &filteredArray); return Array<T>.CreateTakingOwnershipOfDisposableValue(filteredArray); } else @@ -39,7 +41,7 @@ namespace Godot // Custom derived type godot_array inputAux = array.NativeValue; godot_array filteredArray; - godotsharp_array_filter_godot_objects_by_non_native(&inputAux, &filteredArray); + NativeFuncs.godotsharp_array_filter_godot_objects_by_non_native(&inputAux, &filteredArray); var filteredArrayWrapped = Array.CreateTakingOwnershipOfDisposableValue(filteredArray); @@ -62,13 +64,5 @@ namespace Godot return resWrapped; } } - - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern unsafe void godotsharp_array_filter_godot_objects_by_native(godot_string_name* p_native_name, - godot_array* p_input, godot_array* r_output); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern unsafe void godotsharp_array_filter_godot_objects_by_non_native(godot_array* p_input, - godot_array* r_output); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs index f428100ff7..39271d3daf 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs @@ -30,7 +30,7 @@ namespace Godot { using var varBytes = Marshaling.mono_array_to_PackedByteArray(bytes); using godot_variant ret = default; - NativeFuncs.godotsharp_bytes2var(&varBytes, allowObjects, &ret); + NativeFuncs.godotsharp_bytes2var(&varBytes, allowObjects.ToGodotBool(), &ret); return Marshaling.variant_to_mono_object(&ret); } @@ -561,7 +561,7 @@ namespace Godot { using var variant = Marshaling.mono_object_to_variant(var); using godot_packed_byte_array varBytes = default; - NativeFuncs.godotsharp_var2bytes(&variant, fullObjects, &varBytes); + NativeFuncs.godotsharp_var2bytes(&variant, fullObjects.ToGodotBool(), &varBytes); using (varBytes) return Marshaling.PackedByteArray_to_mono_array(&varBytes); } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs new file mode 100644 index 0000000000..2830d9c527 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs @@ -0,0 +1,74 @@ +using System; + +namespace Godot.NativeInterop +{ + internal static class ExceptionUtils + { + public static void PushError(string message) + { + GD.PushError(message); + } + + private static void OnExceptionLoggerException(Exception loggerException, Exception exceptionToLog) + { + // This better not throw + PushError("Exception thrown when trying to log another exception..."); + PushError("Exception:"); + PushError(exceptionToLog.ToString()); + PushError("Logger exception:"); + PushError(loggerException.ToString()); + } + + public static void DebugPrintUnhandledException(Exception e) + { + try + { + // TODO Not implemented (debug_print_unhandled_exception) + GD.PushError(e.ToString()); + } + catch (Exception unexpected) + { + OnExceptionLoggerException(unexpected, e); + } + } + + public static void DebugSendUnhandledExceptionError(Exception e) + { + try + { + // TODO Not implemented (debug_send_unhandled_exception_error) + GD.PushError(e.ToString()); + } + catch (Exception unexpected) + { + OnExceptionLoggerException(unexpected, e); + } + } + + public static void DebugUnhandledException(Exception e) + { + try + { + // TODO Not implemented (debug_unhandled_exception) + GD.PushError(e.ToString()); + } + catch (Exception unexpected) + { + OnExceptionLoggerException(unexpected, e); + } + } + + public static void PrintUnhandledException(Exception e) + { + try + { + // TODO Not implemented (print_unhandled_exception) + GD.PushError(e.ToString()); + } + catch (Exception unexpected) + { + OnExceptionLoggerException(unexpected, e); + } + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs index d8931f8348..0942d8f722 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs @@ -1,26 +1,36 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; #if REAL_T_IS_DOUBLE using real_t = System.Double; #else using real_t = System.Single; - #endif +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace Godot.NativeInterop { - [StructLayout(LayoutKind.Sequential)] - // ReSharper disable once InconsistentNaming - public struct godot_bool + internal static class GodotBoolExtensions { - public byte _value; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe godot_bool ToGodotBool(this bool @bool) + { + return *(godot_bool*)&@bool; + } - public unsafe godot_bool(bool value) => _value = *(byte*)&value; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe bool ToBool(this godot_bool godotBool) + { + return *(bool*)&godotBool; + } + } - public static unsafe implicit operator bool(godot_bool godotBool) => *(bool*)&godotBool._value; - public static implicit operator godot_bool(bool @bool) => new godot_bool(@bool); + // Apparently a struct with a byte is not blittable? It crashes when calling a UnmanagedCallersOnly function ptr. + // ReSharper disable once InconsistentNaming + public enum godot_bool : byte + { + True = 1, + False = 0 } [StructLayout(LayoutKind.Sequential)] diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs index 5d53006140..5779421c69 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs @@ -16,13 +16,14 @@ namespace Godot.NativeInterop return null; IntPtr gcHandlePtr; - bool has_cs_script_instance = false; + godot_bool has_cs_script_instance = false.ToGodotBool(); // First try to get the tied managed instance from a CSharpInstance script instance unsafe { - gcHandlePtr = unmanaged_get_script_instance_managed(unmanaged, &has_cs_script_instance); + gcHandlePtr = NativeFuncs.godotsharp_internal_unmanaged_get_script_instance_managed( + unmanaged, &has_cs_script_instance); } if (gcHandlePtr != IntPtr.Zero) @@ -30,12 +31,12 @@ namespace Godot.NativeInterop // Otherwise, if the object has a CSharpInstance script instance, return null - if (has_cs_script_instance) + if (has_cs_script_instance.ToBool()) return null; // If it doesn't have a CSharpInstance script instance, try with native instance bindings - gcHandlePtr = unmanaged_get_instance_binding_managed(unmanaged); + gcHandlePtr = NativeFuncs.godotsharp_internal_unmanaged_get_instance_binding_managed(unmanaged); object target = gcHandlePtr != IntPtr.Zero ? GCHandle.FromIntPtr(gcHandlePtr).Target : null; @@ -44,22 +45,12 @@ namespace Godot.NativeInterop // If the native instance binding GC handle target was collected, create a new one - gcHandlePtr = unmanaged_instance_binding_create_managed(unmanaged, gcHandlePtr); + gcHandlePtr = NativeFuncs.godotsharp_internal_unmanaged_instance_binding_create_managed( + unmanaged, gcHandlePtr); return gcHandlePtr != IntPtr.Zero ? (Object)GCHandle.FromIntPtr(gcHandlePtr).Target : null; } - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern unsafe IntPtr unmanaged_get_script_instance_managed(IntPtr p_unmanaged, - bool* r_has_cs_script_instance); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern IntPtr unmanaged_get_instance_binding_managed(IntPtr p_unmanaged); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern IntPtr unmanaged_instance_binding_create_managed(IntPtr p_unmanaged, - IntPtr oldGCHandlePtr); - public static void TieManagedToUnmanaged(Object managed, IntPtr unmanaged, StringName nativeName, bool refCounted, Type type, Type nativeType) { @@ -70,30 +61,22 @@ namespace Godot.NativeInterop unsafe { godot_string_name nativeNameAux = nativeName.NativeValue; - internal_tie_native_managed_to_unmanaged(GCHandle.ToIntPtr(gcHandle), unmanaged, - &nativeNameAux, refCounted); + NativeFuncs.godotsharp_internal_tie_native_managed_to_unmanaged( + GCHandle.ToIntPtr(gcHandle), unmanaged, &nativeNameAux, refCounted.ToGodotBool()); } } else { - IntPtr scriptPtr = internal_new_csharp_script(); + IntPtr scriptPtr = NativeFuncs.godotsharp_internal_new_csharp_script(); ScriptManagerBridge.AddScriptBridgeWithType(scriptPtr, type); // IMPORTANT: This must be called after AddScriptWithTypeBridge - internal_tie_user_managed_to_unmanaged(GCHandle.ToIntPtr(gcHandle), unmanaged, - scriptPtr, refCounted); + NativeFuncs.godotsharp_internal_tie_user_managed_to_unmanaged( + GCHandle.ToIntPtr(gcHandle), unmanaged, scriptPtr, refCounted.ToGodotBool()); } } - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern unsafe void internal_tie_native_managed_to_unmanaged(IntPtr gcHandleIntPtr, - IntPtr unmanaged, godot_string_name* nativeName, bool refCounted); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void internal_tie_user_managed_to_unmanaged(IntPtr gcHandleIntPtr, - IntPtr unmanaged, IntPtr scriptPtr, bool refCounted); - public static void TieManagedToUnmanagedWithPreSetup(Object managed, IntPtr unmanaged, Type type, Type nativeType) { @@ -101,16 +84,10 @@ namespace Godot.NativeInterop return; var strongGCHandle = GCHandle.Alloc(managed, GCHandleType.Normal); - internal_tie_managed_to_unmanaged_with_pre_setup(GCHandle.ToIntPtr(strongGCHandle), unmanaged); + NativeFuncs.godotsharp_internal_tie_managed_to_unmanaged_with_pre_setup( + GCHandle.ToIntPtr(strongGCHandle), unmanaged); } - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void internal_tie_managed_to_unmanaged_with_pre_setup( - IntPtr gcHandleIntPtr, IntPtr unmanaged); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern IntPtr internal_new_csharp_script(); - public static unsafe Object EngineGetSingleton(string name) { using godot_string src = Marshaling.mono_string_to_godot(name); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs index eae644af85..74232425bb 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Reflection; @@ -420,44 +421,18 @@ namespace Godot.NativeInterop if (genericTypeDefinition == typeof(System.Collections.Generic.Dictionary<,>)) { // TODO: Validate key and value types are compatible with Variant -#if NET - Collections.IGenericGodotDictionary genericGodotDictionary = - IDictionaryToGenericGodotDictionary((dynamic)p_obj); -#else - var genericArguments = type.GetGenericArguments(); - - // With .NET Standard we need a package reference for Microsoft.CSharp in order to - // use dynamic, so we have this workaround for now until we switch to .NET 5/6. - var method = typeof(Marshaling).GetMethod(nameof(IDictionaryToGenericGodotDictionary), - BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.DeclaredOnly)! - .MakeGenericMethod(genericArguments[0], genericArguments[1]); + var godotDict = new Collections.Dictionary(); - var genericGodotDictionary = (Collections.IGenericGodotDictionary)method - .Invoke(null, new[] { p_obj }); -#endif + foreach (KeyValuePair<object, object> entry in (IDictionary)p_obj) + godotDict.Add(entry.Key, entry.Value); - var godotDict = genericGodotDictionary.UnderlyingDictionary; - if (godotDict == null) - return new godot_variant(); return VariantUtils.CreateFromDictionary(godotDict.NativeValue); } if (genericTypeDefinition == typeof(System.Collections.Generic.List<>)) { // TODO: Validate element type is compatible with Variant -#if NET - var nativeGodotArray = - (godot_array)mono_array_to_Array(System.Runtime.InteropServices.CollectionsMarshal.AsSpan((dynamic)p_obj)); -#else - // With .NET Standard we need a package reference for Microsoft.CSharp in order to - // use dynamic, so we have this workaround for now until we switch to .NET 5/6. - // Also CollectionsMarshal.AsSpan is not available with .NET Standard. - - var collection = (System.Collections.ICollection)p_obj; - var array = new object[collection.Count]; - collection.CopyTo(array, 0); - var nativeGodotArray = mono_array_to_Array(array); -#endif + var nativeGodotArray = mono_array_to_Array((IList)p_obj); return VariantUtils.CreateFromArray(&nativeGodotArray); } } @@ -478,9 +453,6 @@ namespace Godot.NativeInterop } } - private static Collections.Dictionary<TKey, TValue> IDictionaryToGenericGodotDictionary<TKey, TValue> - (IDictionary<TKey, TValue> dictionary) => new(dictionary); - public static unsafe string variant_to_mono_string(godot_variant* p_var) { switch ((*p_var)._type) @@ -855,7 +827,7 @@ namespace Godot.NativeInterop switch ((*p_var)._type) { case Variant.Type.Bool: - return (bool)(*p_var)._data._bool; + return (*p_var)._data._bool.ToBool(); case Variant.Type.Int: return (*p_var)._data._int; case Variant.Type.Float: @@ -1058,7 +1030,7 @@ namespace Godot.NativeInterop godot_string_name name; if (NativeFuncs.godotsharp_callable_get_data_for_marshalling( - p_callable, &delegateGCHandle, &godotObject, &name)) + p_callable, &delegateGCHandle, &godotObject, &name).ToBool()) { if (delegateGCHandle != IntPtr.Zero) { @@ -1141,15 +1113,37 @@ namespace Godot.NativeInterop return ret; } - public static godot_array mono_array_to_Array(Span<object> p_array) + public static godot_array mono_array_to_Array(object[] p_array) { - if (p_array.IsEmpty) + int length = p_array.Length; + + if (length == 0) return NativeFuncs.godotsharp_array_new(); using var array = new Collections.Array(); - array.Resize(p_array.Length); + array.Resize(length); - for (int i = 0; i < p_array.Length; i++) + for (int i = 0; i < length; i++) + array[i] = p_array[i]; + + godot_array src = array.NativeValue; + unsafe + { + return NativeFuncs.godotsharp_array_new_copy(&src); + } + } + + public static godot_array mono_array_to_Array(IList p_array) + { + int length = p_array.Count; + + if (length == 0) + return NativeFuncs.godotsharp_array_new(); + + using var array = new Collections.Array(); + array.Resize(length); + + for (int i = 0; i < length; i++) array[i] = p_array[i]; godot_array src = array.NativeValue; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs index 73ac837fe1..8bc785f375 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs @@ -20,20 +20,61 @@ namespace Godot.NativeInterop public static extern IntPtr godotsharp_method_bind_get_method(ref godot_string_name p_classname, char* p_methodname); -#if NET [DllImport(GodotDllName)] - public static extern delegate* unmanaged<IntPtr> godotsharp_get_class_constructor(ref godot_string_name p_classname); -#else - // Workaround until we switch to .NET 5/6 + public static extern delegate* unmanaged<IntPtr> godotsharp_get_class_constructor( + ref godot_string_name p_classname); + [DllImport(GodotDllName)] - public static extern IntPtr godotsharp_get_class_constructor(ref godot_string_name p_classname); + public static extern IntPtr godotsharp_engine_get_singleton(godot_string* p_name); [DllImport(GodotDllName)] - public static extern IntPtr godotsharp_invoke_class_constructor(IntPtr p_creation_func); -#endif + internal static extern void godotsharp_internal_object_disposed(IntPtr ptr); [DllImport(GodotDllName)] - public static extern IntPtr godotsharp_engine_get_singleton(godot_string* p_name); + internal static extern void godotsharp_internal_refcounted_disposed(IntPtr ptr, godot_bool isFinalizer); + + [DllImport(GodotDllName)] + internal static extern void godotsharp_internal_object_connect_event_signal(IntPtr obj, + godot_string_name* eventSignal); + + [DllImport(GodotDllName)] + internal static extern Error godotsharp_internal_signal_awaiter_connect(IntPtr source, + ref godot_string_name signal, + IntPtr target, IntPtr awaiterHandlePtr); + + [DllImport(GodotDllName)] + public static extern void godotsharp_internal_tie_native_managed_to_unmanaged(IntPtr gcHandleIntPtr, + IntPtr unmanaged, godot_string_name* nativeName, godot_bool refCounted); + + [DllImport(GodotDllName)] + public static extern void godotsharp_internal_tie_user_managed_to_unmanaged(IntPtr gcHandleIntPtr, + IntPtr unmanaged, IntPtr scriptPtr, godot_bool refCounted); + + [DllImport(GodotDllName)] + public static extern void godotsharp_internal_tie_managed_to_unmanaged_with_pre_setup( + IntPtr gcHandleIntPtr, IntPtr unmanaged); + + [DllImport(GodotDllName)] + public static extern IntPtr godotsharp_internal_unmanaged_get_script_instance_managed(IntPtr p_unmanaged, + godot_bool* r_has_cs_script_instance); + + [DllImport(GodotDllName)] + public static extern IntPtr godotsharp_internal_unmanaged_get_instance_binding_managed(IntPtr p_unmanaged); + + [DllImport(GodotDllName)] + public static extern IntPtr godotsharp_internal_unmanaged_instance_binding_create_managed(IntPtr p_unmanaged, + IntPtr oldGCHandlePtr); + + [DllImport(GodotDllName)] + public static extern IntPtr godotsharp_internal_new_csharp_script(); + + [DllImport(GodotDllName)] + public static extern void godotsharp_array_filter_godot_objects_by_native(godot_string_name* p_native_name, + godot_array* p_input, godot_array* r_output); + + [DllImport(GodotDllName)] + public static extern void godotsharp_array_filter_godot_objects_by_non_native(godot_array* p_input, + godot_array* r_output); [DllImport(GodotDllName)] public static extern void godotsharp_ref_destroy(ref godot_ref p_instance); @@ -509,12 +550,12 @@ namespace Godot.NativeInterop public static extern int godotsharp_node_path_get_subname_count(ref godot_node_path p_self); [DllImport(GodotDllName)] - public static extern bool godotsharp_node_path_is_absolute(ref godot_node_path p_self); + public static extern godot_bool godotsharp_node_path_is_absolute(ref godot_node_path p_self); // GD, etc [DllImport(GodotDllName)] - public static extern void godotsharp_bytes2var(godot_packed_byte_array* p_bytes, bool p_allow_objects, + public static extern void godotsharp_bytes2var(godot_packed_byte_array* p_bytes, godot_bool p_allow_objects, godot_variant* r_ret); [DllImport(GodotDllName)] @@ -578,7 +619,7 @@ namespace Godot.NativeInterop public static extern void godotsharp_str2var(godot_string* p_str, godot_variant* r_ret); [DllImport(GodotDllName)] - public static extern void godotsharp_var2bytes(godot_variant* what, bool fullObjects, + public static extern void godotsharp_var2bytes(godot_variant* what, godot_bool fullObjects, godot_packed_byte_array* bytes); [DllImport(GodotDllName)] diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs index 91ba864687..e52454a2e3 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs @@ -8,46 +8,46 @@ namespace Godot.NativeInterop public static class VariantUtils { public static godot_variant CreateFromRID(RID from) - => new() {_type = Variant.Type.Rid, _data = {_m_rid = from}}; + => new() { _type = Variant.Type.Rid, _data = { _m_rid = from } }; public static godot_variant CreateFromBool(bool from) - => new() {_type = Variant.Type.Bool, _data = {_bool = from}}; + => new() { _type = Variant.Type.Bool, _data = { _bool = from.ToGodotBool() } }; public static godot_variant CreateFromInt(long from) - => new() {_type = Variant.Type.Int, _data = {_int = from}}; + => new() { _type = Variant.Type.Int, _data = { _int = from } }; public static godot_variant CreateFromInt(ulong from) - => new() {_type = Variant.Type.Int, _data = {_int = (long)from}}; + => new() { _type = Variant.Type.Int, _data = { _int = (long)from } }; public static godot_variant CreateFromFloat(double from) - => new() {_type = Variant.Type.Float, _data = {_float = from}}; + => new() { _type = Variant.Type.Float, _data = { _float = from } }; public static godot_variant CreateFromVector2(Vector2 from) - => new() {_type = Variant.Type.Vector2, _data = {_m_vector2 = from}}; + => new() { _type = Variant.Type.Vector2, _data = { _m_vector2 = from } }; public static godot_variant CreateFromVector2i(Vector2i from) - => new() {_type = Variant.Type.Vector2i, _data = {_m_vector2i = from}}; + => new() { _type = Variant.Type.Vector2i, _data = { _m_vector2i = from } }; public static godot_variant CreateFromVector3(Vector3 from) - => new() {_type = Variant.Type.Vector3, _data = {_m_vector3 = from}}; + => new() { _type = Variant.Type.Vector3, _data = { _m_vector3 = from } }; public static godot_variant CreateFromVector3i(Vector3i from) - => new() {_type = Variant.Type.Vector3i, _data = {_m_vector3i = from}}; + => new() { _type = Variant.Type.Vector3i, _data = { _m_vector3i = from } }; public static godot_variant CreateFromRect2(Rect2 from) - => new() {_type = Variant.Type.Rect2, _data = {_m_rect2 = from}}; + => new() { _type = Variant.Type.Rect2, _data = { _m_rect2 = from } }; public static godot_variant CreateFromRect2i(Rect2i from) - => new() {_type = Variant.Type.Rect2i, _data = {_m_rect2i = from}}; + => new() { _type = Variant.Type.Rect2i, _data = { _m_rect2i = from } }; public static godot_variant CreateFromQuaternion(Quaternion from) - => new() {_type = Variant.Type.Quaternion, _data = {_m_quaternion = from}}; + => new() { _type = Variant.Type.Quaternion, _data = { _m_quaternion = from } }; public static godot_variant CreateFromColor(Color from) - => new() {_type = Variant.Type.Color, _data = {_m_color = from}}; + => new() { _type = Variant.Type.Color, _data = { _m_color = from } }; public static godot_variant CreateFromPlane(Plane from) - => new() {_type = Variant.Type.Plane, _data = {_m_plane = from}}; + => new() { _type = Variant.Type.Plane, _data = { _m_plane = from } }; public static unsafe godot_variant CreateFromTransform2D(Transform2D from) { @@ -100,15 +100,15 @@ namespace Godot.NativeInterop // Explicit name to make it very clear public static godot_variant CreateFromCallableTakingOwnershipOfDisposableValue(godot_callable from) - => new() {_type = Variant.Type.Callable, _data = {_m_callable = from}}; + => new() { _type = Variant.Type.Callable, _data = { _m_callable = from } }; // Explicit name to make it very clear public static godot_variant CreateFromSignalTakingOwnershipOfDisposableValue(godot_signal from) - => new() {_type = Variant.Type.Signal, _data = {_m_signal = from}}; + => new() { _type = Variant.Type.Signal, _data = { _m_signal = from } }; // Explicit name to make it very clear public static godot_variant CreateFromStringTakingOwnershipOfDisposableValue(godot_string from) - => new() {_type = Variant.Type.String, _data = {_m_string = from}}; + => new() { _type = Variant.Type.String, _data = { _m_string = from } }; public static unsafe godot_variant CreateFromPackedByteArray(godot_packed_byte_array* from) { @@ -223,61 +223,97 @@ namespace Godot.NativeInterop // We avoid the internal call if the stored type is the same we want. public static unsafe bool ConvertToBool(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Bool ? (*p_var)._data._bool : NativeFuncs.godotsharp_variant_as_bool(p_var); + => (*p_var)._type == Variant.Type.Bool ? + (*p_var)._data._bool.ToBool() : + NativeFuncs.godotsharp_variant_as_bool(p_var).ToBool(); public static unsafe char ConvertToChar(godot_variant* p_var) - => (char)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var)); + => (char)((*p_var)._type == Variant.Type.Int ? + (*p_var)._data._int : + NativeFuncs.godotsharp_variant_as_int(p_var)); public static unsafe sbyte ConvertToInt8(godot_variant* p_var) - => (sbyte)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var)); + => (sbyte)((*p_var)._type == Variant.Type.Int ? + (*p_var)._data._int : + NativeFuncs.godotsharp_variant_as_int(p_var)); public static unsafe Int16 ConvertToInt16(godot_variant* p_var) - => (Int16)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var)); + => (Int16)((*p_var)._type == Variant.Type.Int ? + (*p_var)._data._int : + NativeFuncs.godotsharp_variant_as_int(p_var)); public static unsafe Int32 ConvertToInt32(godot_variant* p_var) - => (Int32)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var)); + => (Int32)((*p_var)._type == Variant.Type.Int ? + (*p_var)._data._int : + NativeFuncs.godotsharp_variant_as_int(p_var)); public static unsafe Int64 ConvertToInt64(godot_variant* p_var) => (*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var); public static unsafe byte ConvertToUInt8(godot_variant* p_var) - => (byte)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var)); + => (byte)((*p_var)._type == Variant.Type.Int ? + (*p_var)._data._int : + NativeFuncs.godotsharp_variant_as_int(p_var)); public static unsafe UInt16 ConvertToUInt16(godot_variant* p_var) - => (UInt16)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var)); + => (UInt16)((*p_var)._type == Variant.Type.Int ? + (*p_var)._data._int : + NativeFuncs.godotsharp_variant_as_int(p_var)); public static unsafe UInt32 ConvertToUInt32(godot_variant* p_var) - => (UInt32)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var)); + => (UInt32)((*p_var)._type == Variant.Type.Int ? + (*p_var)._data._int : + NativeFuncs.godotsharp_variant_as_int(p_var)); public static unsafe UInt64 ConvertToUInt64(godot_variant* p_var) - => (UInt64)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var)); + => (UInt64)((*p_var)._type == Variant.Type.Int ? + (*p_var)._data._int : + NativeFuncs.godotsharp_variant_as_int(p_var)); public static unsafe float ConvertToFloat32(godot_variant* p_var) - => (float)((*p_var)._type == Variant.Type.Float ? (*p_var)._data._float : NativeFuncs.godotsharp_variant_as_float(p_var)); + => (float)((*p_var)._type == Variant.Type.Float ? + (*p_var)._data._float : + NativeFuncs.godotsharp_variant_as_float(p_var)); public static unsafe double ConvertToFloat64(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Float ? (*p_var)._data._float : NativeFuncs.godotsharp_variant_as_float(p_var); + => (*p_var)._type == Variant.Type.Float ? + (*p_var)._data._float : + NativeFuncs.godotsharp_variant_as_float(p_var); public static unsafe Vector2 ConvertToVector2(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Vector2 ? (*p_var)._data._m_vector2 : NativeFuncs.godotsharp_variant_as_vector2(p_var); + => (*p_var)._type == Variant.Type.Vector2 ? + (*p_var)._data._m_vector2 : + NativeFuncs.godotsharp_variant_as_vector2(p_var); public static unsafe Vector2i ConvertToVector2i(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Vector2i ? (*p_var)._data._m_vector2i : NativeFuncs.godotsharp_variant_as_vector2i(p_var); + => (*p_var)._type == Variant.Type.Vector2i ? + (*p_var)._data._m_vector2i : + NativeFuncs.godotsharp_variant_as_vector2i(p_var); public static unsafe Rect2 ConvertToRect2(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Rect2 ? (*p_var)._data._m_rect2 : NativeFuncs.godotsharp_variant_as_rect2(p_var); + => (*p_var)._type == Variant.Type.Rect2 ? + (*p_var)._data._m_rect2 : + NativeFuncs.godotsharp_variant_as_rect2(p_var); public static unsafe Rect2i ConvertToRect2i(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Rect2i ? (*p_var)._data._m_rect2i : NativeFuncs.godotsharp_variant_as_rect2i(p_var); + => (*p_var)._type == Variant.Type.Rect2i ? + (*p_var)._data._m_rect2i : + NativeFuncs.godotsharp_variant_as_rect2i(p_var); public static unsafe Transform2D ConvertToTransform2D(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Transform2d ? *(*p_var)._data._transform2d : NativeFuncs.godotsharp_variant_as_transform2d(p_var); + => (*p_var)._type == Variant.Type.Transform2d ? + *(*p_var)._data._transform2d : + NativeFuncs.godotsharp_variant_as_transform2d(p_var); public static unsafe Vector3 ConvertToVector3(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Vector3 ? (*p_var)._data._m_vector3 : NativeFuncs.godotsharp_variant_as_vector3(p_var); + => (*p_var)._type == Variant.Type.Vector3 ? + (*p_var)._data._m_vector3 : + NativeFuncs.godotsharp_variant_as_vector3(p_var); public static unsafe Vector3i ConvertToVector3i(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Vector3i ? (*p_var)._data._m_vector3i : NativeFuncs.godotsharp_variant_as_vector3i(p_var); + => (*p_var)._type == Variant.Type.Vector3i ? + (*p_var)._data._m_vector3i : + NativeFuncs.godotsharp_variant_as_vector3i(p_var); public static unsafe Vector4 ConvertToVector4(godot_variant* p_var) => (*p_var)._type == Variant.Type.Vector4 ? *(*p_var)._data._vector4 : NativeFuncs.godotsharp_variant_as_vector4(p_var); @@ -286,31 +322,45 @@ namespace Godot.NativeInterop => (*p_var)._type == Variant.Type.Vector4i ? *(*p_var)._data._vector4i : NativeFuncs.godotsharp_variant_as_vector4i(p_var); public static unsafe Basis ConvertToBasis(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Basis ? *(*p_var)._data._basis : NativeFuncs.godotsharp_variant_as_basis(p_var); + => (*p_var)._type == Variant.Type.Basis ? + *(*p_var)._data._basis : + NativeFuncs.godotsharp_variant_as_basis(p_var); public static unsafe Quaternion ConvertToQuaternion(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Quaternion ? (*p_var)._data._m_quaternion : NativeFuncs.godotsharp_variant_as_quaternion(p_var); + => (*p_var)._type == Variant.Type.Quaternion ? + (*p_var)._data._m_quaternion : + NativeFuncs.godotsharp_variant_as_quaternion(p_var); public static unsafe Transform3D ConvertToTransform3D(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Transform3d ? *(*p_var)._data._transform3d : NativeFuncs.godotsharp_variant_as_transform3d(p_var); + => (*p_var)._type == Variant.Type.Transform3d ? + *(*p_var)._data._transform3d : + NativeFuncs.godotsharp_variant_as_transform3d(p_var); public static unsafe Projection ConvertToProjection(godot_variant* p_var) => (*p_var)._type == Variant.Type.Projection ? *(*p_var)._data._projection : NativeFuncs.godotsharp_variant_as_projection(p_var); public static unsafe AABB ConvertToAABB(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Aabb ? *(*p_var)._data._aabb : NativeFuncs.godotsharp_variant_as_aabb(p_var); + => (*p_var)._type == Variant.Type.Aabb ? + *(*p_var)._data._aabb : + NativeFuncs.godotsharp_variant_as_aabb(p_var); public static unsafe Color ConvertToColor(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Color ? (*p_var)._data._m_color : NativeFuncs.godotsharp_variant_as_color(p_var); + => (*p_var)._type == Variant.Type.Color ? + (*p_var)._data._m_color : + NativeFuncs.godotsharp_variant_as_color(p_var); public static unsafe Plane ConvertToPlane(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Plane ? (*p_var)._data._m_plane : NativeFuncs.godotsharp_variant_as_plane(p_var); + => (*p_var)._type == Variant.Type.Plane ? + (*p_var)._data._m_plane : + NativeFuncs.godotsharp_variant_as_plane(p_var); public static unsafe IntPtr ConvertToGodotObject(godot_variant* p_var) => (*p_var)._type == Variant.Type.Object ? (*p_var)._data._m_obj_data.obj : IntPtr.Zero; public static unsafe RID ConvertToRID(godot_variant* p_var) - => (*p_var)._type == Variant.Type.Rid ? (*p_var)._data._m_rid : NativeFuncs.godotsharp_variant_as_rid(p_var); + => (*p_var)._type == Variant.Type.Rid ? + (*p_var)._data._m_rid : + NativeFuncs.godotsharp_variant_as_rid(p_var); public static unsafe godot_string_name ConvertToStringName(godot_variant* p_var) => (*p_var)._type == Variant.Type.StringName ? diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs index 824f29558f..b18606b47e 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs @@ -263,7 +263,7 @@ namespace Godot /// <returns>If the <see cref="NodePath"/> is an absolute path.</returns> public bool IsAbsolute() { - return NativeFuncs.godotsharp_node_path_is_absolute(ref NativeValue); + return NativeFuncs.godotsharp_node_path_is_absolute(ref NativeValue).ToBool(); } /// <summary> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs index 763483a11f..98266ffdfc 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs @@ -21,14 +21,11 @@ namespace Godot { if (NativePtr == IntPtr.Zero) { -#if NET unsafe { NativePtr = NativeCtor(); } -#else - NativePtr = _gd__invoke_class_constructor(NativeCtor); -#endif + InteropUtils.TieManagedToUnmanaged(this, NativePtr, NativeName, refCounted: false, GetType(), _cachedType); } @@ -58,7 +55,7 @@ namespace Godot { using var eventSignalName = new StringName(eventSignal.Name); godot_string_name eventSignalNameAux = eventSignalName.NativeValue; - godot_icall_Object_ConnectEventSignal(NativePtr, &eventSignalNameAux); + NativeFuncs.godotsharp_internal_object_connect_event_signal(NativePtr, &eventSignalNameAux); } } @@ -114,14 +111,14 @@ namespace Godot if (MemoryOwn) { MemoryOwn = false; - godot_icall_RefCounted_Disposed(NativePtr, !disposing); + NativeFuncs.godotsharp_internal_refcounted_disposed(NativePtr, (!disposing).ToGodotBool()); } else { - godot_icall_Object_Disposed(NativePtr); + NativeFuncs.godotsharp_internal_object_disposed(NativePtr); } - this.NativePtr = IntPtr.Zero; + NativePtr = IntPtr.Zero; } _disposed = true; @@ -391,7 +388,6 @@ namespace Godot return methodBind; } -#if NET internal static unsafe delegate* unmanaged<IntPtr> ClassDB_get_constructor(StringName type) { // for some reason the '??' operator doesn't support 'delegate*' @@ -402,30 +398,5 @@ namespace Godot return nativeConstructor; } -#else - internal static IntPtr ClassDB_get_constructor(StringName type) - { - // for some reason the '??' operator doesn't support 'delegate*' - var nativeConstructor = NativeFuncs.godotsharp_get_class_constructor(ref type.NativeValue); - - if (nativeConstructor == IntPtr.Zero) - throw new NativeConstructorNotFoundException(type); - - return nativeConstructor; - } - - internal static IntPtr _gd__invoke_class_constructor(IntPtr ctorFuncPtr) - => NativeFuncs.godotsharp_invoke_class_constructor(ctorFuncPtr); -#endif - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Object_Disposed(IntPtr ptr); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_RefCounted_Disposed(IntPtr ptr, bool isFinalizer); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern unsafe void godot_icall_Object_ConnectEventSignal(IntPtr obj, - godot_string_name* eventSignal); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs index fd6636e410..62dec81582 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs @@ -1,5 +1,4 @@ using System; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Godot.NativeInterop; @@ -13,14 +12,10 @@ namespace Godot public SignalAwaiter(Object source, StringName signal, Object target) { - godot_icall_SignalAwaiter_connect(Object.GetPtr(source), ref signal.NativeValue, + NativeFuncs.godotsharp_internal_signal_awaiter_connect(Object.GetPtr(source), ref signal.NativeValue, Object.GetPtr(target), GCHandle.ToIntPtr(GCHandle.Alloc(this))); } - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern Error godot_icall_SignalAwaiter_connect(IntPtr source, ref godot_string_name signal, - IntPtr target, IntPtr awaiterHandlePtr); - public bool IsCompleted => _completed; public void OnCompleted(Action action) @@ -32,30 +27,38 @@ namespace Godot public IAwaiter<object[]> GetAwaiter() => this; - internal static unsafe void SignalCallback(IntPtr awaiterGCHandlePtr, - godot_variant** args, int argCount, - bool* r_awaiterIsNull) + [UnmanagedCallersOnly] + internal static unsafe void SignalCallback(IntPtr awaiterGCHandlePtr, godot_variant** args, int argCount, + godot_bool* outAwaiterIsNull) { - var awaiter = (SignalAwaiter)GCHandle.FromIntPtr(awaiterGCHandlePtr).Target; - - if (awaiter == null) + try { - *r_awaiterIsNull = true; - return; - } + var awaiter = (SignalAwaiter)GCHandle.FromIntPtr(awaiterGCHandlePtr).Target; + + if (awaiter == null) + { + *outAwaiterIsNull = true.ToGodotBool(); + return; + } - *r_awaiterIsNull = false; + *outAwaiterIsNull = false.ToGodotBool(); - awaiter._completed = true; + awaiter._completed = true; - object[] signalArgs = new object[argCount]; + object[] signalArgs = new object[argCount]; - for (int i = 0; i < argCount; i++) - signalArgs[i] = Marshaling.variant_to_mono_object(args[i]); + for (int i = 0; i < argCount; i++) + signalArgs[i] = Marshaling.variant_to_mono_object(args[i]); - awaiter._result = signalArgs; + awaiter._result = signalArgs; - awaiter._action?.Invoke(); + awaiter._action?.Invoke(); + } + catch (Exception e) + { + ExceptionUtils.DebugPrintUnhandledException(e); + *outAwaiterIsNull = false.ToGodotBool(); + } } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj index 6a529de99b..763ded8809 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj @@ -4,7 +4,7 @@ <OutputPath>bin/$(Configuration)</OutputPath> <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath> <RootNamespace>Godot</RootNamespace> - <TargetFramework>netstandard2.1</TargetFramework> + <TargetFramework>net5.0</TargetFramework> <DocumentationFile>$(OutputPath)/$(AssemblyName).xml</DocumentationFile> <EnableDefaultItems>false</EnableDefaultItems> <AllowUnsafeBlocks>true</AllowUnsafeBlocks> @@ -34,6 +34,7 @@ <Compile Include="Core\Basis.cs" /> <Compile Include="Core\Bridge\CSharpInstanceBridge.cs" /> <Compile Include="Core\Bridge\GCHandleBridge.cs" /> + <Compile Include="Core\Bridge\ManagedCallbacks.cs" /> <Compile Include="Core\Bridge\ScriptManagerBridge.cs" /> <Compile Include="Core\Callable.cs" /> <Compile Include="Core\Color.cs" /> @@ -58,6 +59,7 @@ <Compile Include="Core\MarshalUtils.cs" /> <Compile Include="Core\Mathf.cs" /> <Compile Include="Core\MathfEx.cs" /> + <Compile Include="Core\NativeInterop\ExceptionUtils.cs" /> <Compile Include="Core\NativeInterop\InteropUtils.cs" /> <Compile Include="Core\NativeInterop\NativeFuncs.extended.cs" /> <Compile Include="Core\NativeInterop\VariantSpanHelpers.cs" /> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Properties/AssemblyInfo.cs b/modules/mono/glue/GodotSharp/GodotSharp/Properties/AssemblyInfo.cs index da6f293871..dbd98d0afb 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Properties/AssemblyInfo.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Properties/AssemblyInfo.cs @@ -1,3 +1,4 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("GodotSharpEditor")] +[assembly: InternalsVisibleTo("GodotPlugins")] diff --git a/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj b/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj index 1082c74448..c0c1b91dc9 100644 --- a/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj @@ -4,7 +4,7 @@ <OutputPath>bin/$(Configuration)</OutputPath> <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath> <RootNamespace>Godot</RootNamespace> - <TargetFramework>netstandard2.1</TargetFramework> + <TargetFramework>net5.0</TargetFramework> <DocumentationFile>$(OutputPath)/$(AssemblyName).xml</DocumentationFile> <EnableDefaultItems>false</EnableDefaultItems> <AllowUnsafeBlocks>true</AllowUnsafeBlocks> diff --git a/modules/mono/glue/base_object_glue.cpp b/modules/mono/glue/base_object_glue.cpp deleted file mode 100644 index dbf2ae84aa..0000000000 --- a/modules/mono/glue/base_object_glue.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/*************************************************************************/ -/* base_object_glue.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "core/object/class_db.h" -#include "core/object/ref_counted.h" -#include "core/string/string_name.h" - -#include "../csharp_script.h" -#include "../mono_gd/gd_mono_cache.h" -#include "../mono_gd/gd_mono_internals.h" -#include "../mono_gd/gd_mono_utils.h" -#include "../signal_awaiter_utils.h" - -void godot_icall_Object_Disposed(Object *p_ptr) { -#ifdef DEBUG_ENABLED - CRASH_COND(p_ptr == nullptr); -#endif - - if (p_ptr->get_script_instance()) { - CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(p_ptr->get_script_instance()); - if (cs_instance) { - if (!cs_instance->is_destructing_script_instance()) { - cs_instance->mono_object_disposed(); - p_ptr->set_script_instance(nullptr); - } - return; - } - } - - void *data = CSharpLanguage::get_existing_instance_binding(p_ptr); - - if (data) { - CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->get(); - if (script_binding.inited) { - MonoGCHandleData &gchandle = script_binding.gchandle; - if (!gchandle.is_released()) { - CSharpLanguage::release_script_gchandle(nullptr, gchandle); - script_binding.inited = false; - } - } - } -} - -void godot_icall_RefCounted_Disposed(Object *p_ptr, MonoBoolean p_is_finalizer) { -#ifdef DEBUG_ENABLED - CRASH_COND(p_ptr == nullptr); - // This is only called with RefCounted derived classes - CRASH_COND(!Object::cast_to<RefCounted>(p_ptr)); -#endif - - RefCounted *rc = static_cast<RefCounted *>(p_ptr); - - if (rc->get_script_instance()) { - CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(rc->get_script_instance()); - if (cs_instance) { - if (!cs_instance->is_destructing_script_instance()) { - bool delete_owner; - bool remove_script_instance; - - cs_instance->mono_object_disposed_baseref(p_is_finalizer, delete_owner, remove_script_instance); - - if (delete_owner) { - memdelete(rc); - } else if (remove_script_instance) { - rc->set_script_instance(nullptr); - } - } - return; - } - } - - // Unsafe refcount decrement. The managed instance also counts as a reference. - // See: CSharpLanguage::alloc_instance_binding_data(Object *p_object) - CSharpLanguage::get_singleton()->pre_unsafe_unreference(rc); - if (rc->unreference()) { - memdelete(rc); - } else { - void *data = CSharpLanguage::get_existing_instance_binding(rc); - - if (data) { - CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->get(); - if (script_binding.inited) { - MonoGCHandleData &gchandle = script_binding.gchandle; - if (!gchandle.is_released()) { - CSharpLanguage::release_script_gchandle(nullptr, gchandle); - script_binding.inited = false; - } - } - } - } -} - -void godot_icall_Object_ConnectEventSignal(Object *p_ptr, const StringName *p_event_signal) { - CSharpInstance *csharp_instance = CAST_CSHARP_INSTANCE(p_ptr->get_script_instance()); - if (csharp_instance) { - csharp_instance->connect_event_signal(*p_event_signal); - } -} - -int32_t godot_icall_SignalAwaiter_connect(Object *p_source, StringName *p_signal, Object *p_target, GCHandleIntPtr p_awaiter_handle_ptr) { - StringName signal = p_signal ? *p_signal : StringName(); - return (int32_t)gd_mono_connect_signal_awaiter(p_source, signal, p_target, p_awaiter_handle_ptr); -} - -void godot_register_object_icalls() { - GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_Disposed", godot_icall_Object_Disposed); - GDMonoUtils::add_internal_call("Godot.Object::godot_icall_RefCounted_Disposed", godot_icall_RefCounted_Disposed); - GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_ConnectEventSignal", godot_icall_Object_ConnectEventSignal); - GDMonoUtils::add_internal_call("Godot.SignalAwaiter::godot_icall_SignalAwaiter_connect", godot_icall_SignalAwaiter_connect); -} diff --git a/modules/mono/glue/placeholder_glue.cpp b/modules/mono/glue/placeholder_glue.cpp deleted file mode 100644 index 0dd904373e..0000000000 --- a/modules/mono/glue/placeholder_glue.cpp +++ /dev/null @@ -1,201 +0,0 @@ -/*************************************************************************/ -/* placeholder_glue.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef GLUE_HEADER_H -#define GLUE_HEADER_H - -#include "core/object/object.h" - -#include "../csharp_script.h" -#include "../mono_gd/gd_mono_cache.h" -#include "../mono_gd/gd_mono_internals.h" -#include "../mono_gd/gd_mono_utils.h" - -GCHandleIntPtr unmanaged_get_script_instance_managed(Object *p_unmanaged, bool *r_has_cs_script_instance) { -#ifdef DEBUG_ENABLED - CRASH_COND(!p_unmanaged); - CRASH_COND(!r_has_cs_script_instance); -#endif - - if (p_unmanaged->get_script_instance()) { - CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(p_unmanaged->get_script_instance()); - - if (cs_instance) { - *r_has_cs_script_instance = true; - return cs_instance->get_gchandle_intptr(); - } - } - - *r_has_cs_script_instance = false; - return GCHandleIntPtr(); -} - -GCHandleIntPtr unmanaged_get_instance_binding_managed(Object *p_unmanaged) { -#ifdef DEBUG_ENABLED - CRASH_COND(!p_unmanaged); -#endif - - void *data = CSharpLanguage::get_instance_binding(p_unmanaged); - ERR_FAIL_NULL_V(data, GCHandleIntPtr()); - CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->value(); - ERR_FAIL_COND_V(!script_binding.inited, GCHandleIntPtr()); - - return script_binding.gchandle.get_intptr(); -} - -GCHandleIntPtr unmanaged_instance_binding_create_managed(Object *p_unmanaged, GCHandleIntPtr p_old_gchandle) { -#ifdef DEBUG_ENABLED - CRASH_COND(!p_unmanaged); -#endif - - void *data = CSharpLanguage::get_instance_binding(p_unmanaged); - ERR_FAIL_NULL_V(data, GCHandleIntPtr()); - CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->value(); - ERR_FAIL_COND_V(!script_binding.inited, GCHandleIntPtr()); - - MonoGCHandleData &gchandle = script_binding.gchandle; - - // TODO: Possible data race? - CRASH_COND(gchandle.get_intptr().value != p_old_gchandle.value); - - CSharpLanguage::get_singleton()->release_script_gchandle(gchandle); - script_binding.inited = false; - - // Create a new one - -#ifdef DEBUG_ENABLED - CRASH_COND(script_binding.type_name == StringName()); -#endif - - bool parent_is_object_class = ClassDB::is_parent_class(p_unmanaged->get_class_name(), script_binding.type_name); - ERR_FAIL_COND_V_MSG(!parent_is_object_class, GCHandleIntPtr(), - "Type inherits from native type '" + script_binding.type_name + "', so it can't be instantiated in object of type: '" + p_unmanaged->get_class() + "'."); - - MonoException *exc = nullptr; - GCHandleIntPtr strong_gchandle = - GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_CreateManagedForGodotObjectBinding - .invoke(&script_binding.type_name, p_unmanaged, &exc); - - if (exc) { - GDMonoUtils::set_pending_exception(exc); - return GCHandleIntPtr(); - } - - ERR_FAIL_NULL_V(strong_gchandle.value, GCHandleIntPtr()); - - gchandle = MonoGCHandleData(strong_gchandle, gdmono::GCHandleType::STRONG_HANDLE); - script_binding.inited = true; - - // Tie managed to unmanaged - RefCounted *rc = Object::cast_to<RefCounted>(p_unmanaged); - - if (rc) { - // Unsafe refcount increment. The managed instance also counts as a reference. - // This way if the unmanaged world has no references to our owner - // but the managed instance is alive, the refcount will be 1 instead of 0. - // See: godot_icall_RefCounted_Dtor(MonoObject *p_obj, Object *p_ptr) - rc->reference(); - CSharpLanguage::get_singleton()->post_unsafe_reference(rc); - } - - return gchandle.get_intptr(); -} - -void godot_icall_InteropUtils_tie_native_managed_to_unmanaged(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged, const StringName *p_native_name, bool p_ref_counted) { - CSharpLanguage::tie_native_managed_to_unmanaged(p_gchandle_intptr, p_unmanaged, p_native_name, p_ref_counted); -} - -void godot_icall_InteropUtils_tie_user_managed_to_unmanaged(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged, CSharpScript *p_script, bool p_ref_counted) { - CSharpLanguage::tie_user_managed_to_unmanaged(p_gchandle_intptr, p_unmanaged, p_script, p_ref_counted); -} - -void godot_icall_InteropUtils_tie_managed_to_unmanaged_with_pre_setup(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged) { - CSharpLanguage::tie_managed_to_unmanaged_with_pre_setup(p_gchandle_intptr, p_unmanaged); -} - -CSharpScript *godot_icall_InteropUtils_internal_new_csharp_script() { - CSharpScript *script = memnew(CSharpScript); - CRASH_COND(!script); - return script; -} - -void godotsharp_array_filter_godot_objects_by_native(StringName *p_native_name, const Array *p_input, Array *r_output) { - memnew_placement(r_output, Array); - - for (int i = 0; i < p_input->size(); ++i) { - if (ClassDB::is_parent_class(((Object *)(*p_input)[i])->get_class(), *p_native_name)) { - r_output->push_back(p_input[i]); - } - } -} - -void godotsharp_array_filter_godot_objects_by_non_native(const Array *p_input, Array *r_output) { - memnew_placement(r_output, Array); - - for (int i = 0; i < p_input->size(); ++i) { - CSharpInstance *si = CAST_CSHARP_INSTANCE(((Object *)(*p_input)[i])->get_script_instance()); - - if (si != nullptr) { - r_output->push_back(p_input[i]); - } - } -} - -void godot_register_placeholder_icalls() { - GDMonoUtils::add_internal_call( - "Godot.NativeInterop.InteropUtils::unmanaged_get_script_instance_managed", - unmanaged_get_script_instance_managed); - GDMonoUtils::add_internal_call( - "Godot.NativeInterop.InteropUtils::unmanaged_get_instance_binding_managed", - unmanaged_get_instance_binding_managed); - GDMonoUtils::add_internal_call( - "Godot.NativeInterop.InteropUtils::unmanaged_instance_binding_create_managed", - unmanaged_instance_binding_create_managed); - GDMonoUtils::add_internal_call( - "Godot.NativeInterop.InteropUtils::internal_tie_native_managed_to_unmanaged", - godot_icall_InteropUtils_tie_native_managed_to_unmanaged); - GDMonoUtils::add_internal_call( - "Godot.NativeInterop.InteropUtils::internal_tie_user_managed_to_unmanaged", - godot_icall_InteropUtils_tie_user_managed_to_unmanaged); - GDMonoUtils::add_internal_call( - "Godot.NativeInterop.InteropUtils::internal_tie_managed_to_unmanaged_with_pre_setup", - godot_icall_InteropUtils_tie_managed_to_unmanaged_with_pre_setup); - GDMonoUtils::add_internal_call( - "Godot.NativeInterop.InteropUtils::internal_new_csharp_script", - godot_icall_InteropUtils_internal_new_csharp_script); - GDMonoUtils::add_internal_call( - "Godot.NativeInterop.SceneTree::godotsharp_array_filter_godot_objects_by_native", - godotsharp_array_filter_godot_objects_by_native); - GDMonoUtils::add_internal_call( - "Godot.NativeInterop.SceneTree::godotsharp_array_filter_godot_objects_by_non_native", - godotsharp_array_filter_godot_objects_by_non_native); -} - -#endif // GLUE_HEADER_H diff --git a/modules/mono/glue/runtime_interop.cpp b/modules/mono/glue/runtime_interop.cpp index 29d373e885..14e638d163 100644 --- a/modules/mono/glue/runtime_interop.cpp +++ b/modules/mono/glue/runtime_interop.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -37,7 +37,9 @@ #include "../interop_types.h" +#include "modules/mono/csharp_script.h" #include "modules/mono/managed_callable.h" +#include "modules/mono/mono_gd/gd_mono_cache.h" #include "modules/mono/signal_awaiter_utils.h" #ifdef __cplusplus @@ -75,14 +77,225 @@ GD_PINVOKE_EXPORT godotsharp_class_creation_func godotsharp_get_class_constructo return nullptr; } -GD_PINVOKE_EXPORT Object *godotsharp_invoke_class_constructor(godotsharp_class_creation_func p_creation_func) { - return p_creation_func(); -} - GD_PINVOKE_EXPORT Object *godotsharp_engine_get_singleton(const String *p_name) { return Engine::get_singleton()->get_singleton_object(*p_name); } +GD_PINVOKE_EXPORT void godotsharp_internal_object_disposed(Object *p_ptr) { +#ifdef DEBUG_ENABLED + CRASH_COND(p_ptr == nullptr); +#endif + + if (p_ptr->get_script_instance()) { + CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(p_ptr->get_script_instance()); + if (cs_instance) { + if (!cs_instance->is_destructing_script_instance()) { + cs_instance->mono_object_disposed(); + p_ptr->set_script_instance(nullptr); + } + return; + } + } + + void *data = CSharpLanguage::get_existing_instance_binding(p_ptr); + + if (data) { + CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->get(); + if (script_binding.inited) { + MonoGCHandleData &gchandle = script_binding.gchandle; + if (!gchandle.is_released()) { + CSharpLanguage::release_script_gchandle(nullptr, gchandle); + script_binding.inited = false; + } + } + } +} + +GD_PINVOKE_EXPORT void godotsharp_internal_refcounted_disposed(Object *p_ptr, bool p_is_finalizer) { +#ifdef DEBUG_ENABLED + CRASH_COND(p_ptr == nullptr); + // This is only called with RefCounted derived classes + CRASH_COND(!Object::cast_to<RefCounted>(p_ptr)); +#endif + + RefCounted *rc = static_cast<RefCounted *>(p_ptr); + + if (rc->get_script_instance()) { + CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(rc->get_script_instance()); + if (cs_instance) { + if (!cs_instance->is_destructing_script_instance()) { + bool delete_owner; + bool remove_script_instance; + + cs_instance->mono_object_disposed_baseref(p_is_finalizer, delete_owner, remove_script_instance); + + if (delete_owner) { + memdelete(rc); + } else if (remove_script_instance) { + rc->set_script_instance(nullptr); + } + } + return; + } + } + + // Unsafe refcount decrement. The managed instance also counts as a reference. + // See: CSharpLanguage::alloc_instance_binding_data(Object *p_object) + CSharpLanguage::get_singleton()->pre_unsafe_unreference(rc); + if (rc->unreference()) { + memdelete(rc); + } else { + void *data = CSharpLanguage::get_existing_instance_binding(rc); + + if (data) { + CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->get(); + if (script_binding.inited) { + MonoGCHandleData &gchandle = script_binding.gchandle; + if (!gchandle.is_released()) { + CSharpLanguage::release_script_gchandle(nullptr, gchandle); + script_binding.inited = false; + } + } + } + } +} + +GD_PINVOKE_EXPORT void godotsharp_internal_object_connect_event_signal(Object *p_ptr, const StringName *p_event_signal) { + CSharpInstance *csharp_instance = CAST_CSHARP_INSTANCE(p_ptr->get_script_instance()); + if (csharp_instance) { + csharp_instance->connect_event_signal(*p_event_signal); + } +} + +GD_PINVOKE_EXPORT int32_t godotsharp_internal_signal_awaiter_connect(Object *p_source, StringName *p_signal, Object *p_target, GCHandleIntPtr p_awaiter_handle_ptr) { + StringName signal = p_signal ? *p_signal : StringName(); + return (int32_t)gd_mono_connect_signal_awaiter(p_source, signal, p_target, p_awaiter_handle_ptr); +} + +GD_PINVOKE_EXPORT GCHandleIntPtr godotsharp_internal_unmanaged_get_script_instance_managed(Object *p_unmanaged, bool *r_has_cs_script_instance) { +#ifdef DEBUG_ENABLED + CRASH_COND(!p_unmanaged); + CRASH_COND(!r_has_cs_script_instance); +#endif + + if (p_unmanaged->get_script_instance()) { + CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(p_unmanaged->get_script_instance()); + + if (cs_instance) { + *r_has_cs_script_instance = true; + return cs_instance->get_gchandle_intptr(); + } + } + + *r_has_cs_script_instance = false; + return GCHandleIntPtr(); +} + +GD_PINVOKE_EXPORT GCHandleIntPtr godotsharp_internal_unmanaged_get_instance_binding_managed(Object *p_unmanaged) { +#ifdef DEBUG_ENABLED + CRASH_COND(!p_unmanaged); +#endif + + void *data = CSharpLanguage::get_instance_binding(p_unmanaged); + ERR_FAIL_NULL_V(data, GCHandleIntPtr()); + CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->value(); + ERR_FAIL_COND_V(!script_binding.inited, GCHandleIntPtr()); + + return script_binding.gchandle.get_intptr(); +} + +GD_PINVOKE_EXPORT GCHandleIntPtr godotsharp_internal_unmanaged_instance_binding_create_managed(Object *p_unmanaged, GCHandleIntPtr p_old_gchandle) { +#ifdef DEBUG_ENABLED + CRASH_COND(!p_unmanaged); +#endif + + void *data = CSharpLanguage::get_instance_binding(p_unmanaged); + ERR_FAIL_NULL_V(data, GCHandleIntPtr()); + CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->value(); + ERR_FAIL_COND_V(!script_binding.inited, GCHandleIntPtr()); + + MonoGCHandleData &gchandle = script_binding.gchandle; + + // TODO: Possible data race? + CRASH_COND(gchandle.get_intptr().value != p_old_gchandle.value); + + CSharpLanguage::get_singleton()->release_script_gchandle(gchandle); + script_binding.inited = false; + + // Create a new one + +#ifdef DEBUG_ENABLED + CRASH_COND(script_binding.type_name == StringName()); +#endif + + bool parent_is_object_class = ClassDB::is_parent_class(p_unmanaged->get_class_name(), script_binding.type_name); + ERR_FAIL_COND_V_MSG(!parent_is_object_class, GCHandleIntPtr(), + "Type inherits from native type '" + script_binding.type_name + "', so it can't be instantiated in object of type: '" + p_unmanaged->get_class() + "'."); + + GCHandleIntPtr strong_gchandle = + GDMonoCache::managed_callbacks.ScriptManagerBridge_CreateManagedForGodotObjectBinding( + &script_binding.type_name, p_unmanaged); + + ERR_FAIL_NULL_V(strong_gchandle.value, GCHandleIntPtr()); + + gchandle = MonoGCHandleData(strong_gchandle, gdmono::GCHandleType::STRONG_HANDLE); + script_binding.inited = true; + + // Tie managed to unmanaged + RefCounted *rc = Object::cast_to<RefCounted>(p_unmanaged); + + if (rc) { + // Unsafe refcount increment. The managed instance also counts as a reference. + // This way if the unmanaged world has no references to our owner + // but the managed instance is alive, the refcount will be 1 instead of 0. + // See: godot_icall_RefCounted_Dtor(MonoObject *p_obj, Object *p_ptr) + rc->reference(); + CSharpLanguage::get_singleton()->post_unsafe_reference(rc); + } + + return gchandle.get_intptr(); +} + +GD_PINVOKE_EXPORT void godotsharp_internal_tie_native_managed_to_unmanaged(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged, const StringName *p_native_name, bool p_ref_counted) { + CSharpLanguage::tie_native_managed_to_unmanaged(p_gchandle_intptr, p_unmanaged, p_native_name, p_ref_counted); +} + +GD_PINVOKE_EXPORT void godotsharp_internal_tie_user_managed_to_unmanaged(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged, CSharpScript *p_script, bool p_ref_counted) { + CSharpLanguage::tie_user_managed_to_unmanaged(p_gchandle_intptr, p_unmanaged, p_script, p_ref_counted); +} + +GD_PINVOKE_EXPORT void godotsharp_internal_tie_managed_to_unmanaged_with_pre_setup(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged) { + CSharpLanguage::tie_managed_to_unmanaged_with_pre_setup(p_gchandle_intptr, p_unmanaged); +} + +GD_PINVOKE_EXPORT CSharpScript *godotsharp_internal_new_csharp_script() { + CSharpScript *script = memnew(CSharpScript); + CRASH_COND(!script); + return script; +} + +GD_PINVOKE_EXPORT void godotsharp_array_filter_godot_objects_by_native(StringName *p_native_name, const Array *p_input, Array *r_output) { + memnew_placement(r_output, Array); + + for (int i = 0; i < p_input->size(); ++i) { + if (ClassDB::is_parent_class(((Object *)(*p_input)[i])->get_class(), *p_native_name)) { + r_output->push_back(p_input[i]); + } + } +} + +GD_PINVOKE_EXPORT void godotsharp_array_filter_godot_objects_by_non_native(const Array *p_input, Array *r_output) { + memnew_placement(r_output, Array); + + for (int i = 0; i < p_input->size(); ++i) { + CSharpInstance *si = CAST_CSHARP_INSTANCE(((Object *)(*p_input)[i])->get_script_instance()); + + if (si != nullptr) { + r_output->push_back(p_input[i]); + } + } +} + GD_PINVOKE_EXPORT void godotsharp_ref_destroy(Ref<RefCounted> *p_instance) { p_instance->~Ref(); } @@ -1003,8 +1216,8 @@ GD_PINVOKE_EXPORT void godotsharp_convert(const godot_variant *p_what, int32_t p if (ce.error != Callable::CallError::CALL_OK) { memnew_placement(r_ret, Variant); ERR_FAIL_MSG("Unable to convert parameter from '" + - Variant::get_type_name(reinterpret_cast<const Variant *>(p_what)->get_type()) + - "' to '" + Variant::get_type_name(Variant::Type(p_type)) + "'."); + Variant::get_type_name(reinterpret_cast<const Variant *>(p_what)->get_type()) + + "' to '" + Variant::get_type_name(Variant::Type(p_type)) + "'."); } memnew_placement(r_ret, Variant(ret)); } @@ -1028,11 +1241,23 @@ GD_PINVOKE_EXPORT void godotsharp_object_to_string(Object *p_ptr, godot_string * #endif // We need this to prevent the functions from being stripped. -void *godotsharp_pinvoke_funcs[164] = { +void *godotsharp_pinvoke_funcs[176] = { (void *)godotsharp_method_bind_get_method, (void *)godotsharp_get_class_constructor, - (void *)godotsharp_invoke_class_constructor, (void *)godotsharp_engine_get_singleton, + (void *)godotsharp_internal_object_disposed, + (void *)godotsharp_internal_refcounted_disposed, + (void *)godotsharp_internal_object_connect_event_signal, + (void *)godotsharp_internal_signal_awaiter_connect, + (void *)godotsharp_internal_unmanaged_get_script_instance_managed, + (void *)godotsharp_internal_unmanaged_get_instance_binding_managed, + (void *)godotsharp_internal_unmanaged_instance_binding_create_managed, + (void *)godotsharp_internal_tie_native_managed_to_unmanaged, + (void *)godotsharp_internal_tie_user_managed_to_unmanaged, + (void *)godotsharp_internal_tie_managed_to_unmanaged_with_pre_setup, + (void *)godotsharp_internal_new_csharp_script, + (void *)godotsharp_array_filter_godot_objects_by_native, + (void *)godotsharp_array_filter_godot_objects_by_non_native, (void *)godotsharp_ref_destroy, (void *)godotsharp_string_name_new_from_string, (void *)godotsharp_node_path_new_from_string, diff --git a/modules/mono/managed_callable.cpp b/modules/mono/managed_callable.cpp index e01d0c6e18..a1f94aa590 100644 --- a/modules/mono/managed_callable.cpp +++ b/modules/mono/managed_callable.cpp @@ -32,7 +32,6 @@ #include "csharp_script.h" #include "mono_gd/gd_mono_cache.h" -#include "mono_gd/gd_mono_utils.h" #ifdef GD_MONO_HOT_RELOAD SelfList<ManagedCallable>::List ManagedCallable::instances; @@ -52,12 +51,8 @@ bool ManagedCallable::compare_equal(const CallableCustom *p_a, const CallableCus } // Call Delegate's 'Equals' - MonoException *exc = nullptr; - MonoBoolean res = GDMonoCache::cached_data.methodthunk_DelegateUtils_DelegateEquals - .invoke(a->delegate_handle, - b->delegate_handle, &exc); - UNHANDLED_EXCEPTION(exc); - return (bool)res; + return GDMonoCache::managed_callbacks.DelegateUtils_DelegateEquals( + a->delegate_handle, b->delegate_handle); } bool ManagedCallable::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) { @@ -92,27 +87,15 @@ void ManagedCallable::call(const Variant **p_arguments, int p_argcount, Variant r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; // Can't find anything better r_return_value = Variant(); - MonoException *exc = nullptr; - GDMonoCache::cached_data.methodthunk_DelegateUtils_InvokeWithVariantArgs - .invoke(delegate_handle, p_arguments, - p_argcount, &r_return_value, &exc); + GDMonoCache::managed_callbacks.DelegateUtils_InvokeWithVariantArgs( + delegate_handle, p_arguments, p_argcount, &r_return_value); - if (exc) { - GDMonoUtils::set_pending_exception(exc); - } else { - r_call_error.error = Callable::CallError::CALL_OK; - } + r_call_error.error = Callable::CallError::CALL_OK; } void ManagedCallable::release_delegate_handle() { if (delegate_handle.value) { - MonoException *exc = nullptr; - GDMonoCache::cached_data.methodthunk_GCHandleBridge_FreeGCHandle.invoke(delegate_handle, &exc); - - if (exc) { - GDMonoUtils::debug_print_unhandled_exception(exc); - } - + GDMonoCache::managed_callbacks.GCHandleBridge_FreeGCHandle(delegate_handle); delegate_handle = GCHandleIntPtr(); } } diff --git a/modules/mono/managed_callable.h b/modules/mono/managed_callable.h index d0f6362d42..aa3344f4d5 100644 --- a/modules/mono/managed_callable.h +++ b/modules/mono/managed_callable.h @@ -31,8 +31,6 @@ #ifndef MANAGED_CALLABLE_H #define MANAGED_CALLABLE_H -#include <mono/metadata/object.h> - #include "core/os/mutex.h" #include "core/templates/self_list.h" #include "core/variant/callable.h" diff --git a/modules/mono/mono_gc_handle.cpp b/modules/mono/mono_gc_handle.cpp index f0a48483f6..9cf0a641b9 100644 --- a/modules/mono/mono_gc_handle.cpp +++ b/modules/mono/mono_gc_handle.cpp @@ -38,17 +38,13 @@ void MonoGCHandleData::release() { CRASH_COND(handle.value && GDMono::get_singleton() == nullptr); #endif - if (handle.value && !GDMonoCache::cached_data.methodthunk_GCHandleBridge_FreeGCHandle.is_null() && + if (handle.value && GDMonoCache::godot_api_cache_updated && GDMono::get_singleton()->is_runtime_initialized()) { free_gchandle(handle); handle.value = nullptr; } } void MonoGCHandleData::free_gchandle(GCHandleIntPtr p_gchandle) { - CRASH_COND(GDMonoCache::cached_data.methodthunk_GCHandleBridge_FreeGCHandle.is_null()); - MonoException *exc = nullptr; - GDMonoCache::cached_data.methodthunk_GCHandleBridge_FreeGCHandle.invoke(p_gchandle, &exc); - if (exc) { - GDMonoUtils::debug_unhandled_exception(exc); - } + CRASH_COND(!GDMonoCache::godot_api_cache_updated); + GDMonoCache::managed_callbacks.GCHandleBridge_FreeGCHandle(p_gchandle); } diff --git a/modules/mono/mono_gc_handle.h b/modules/mono/mono_gc_handle.h index 96553cf4f4..a921b24103 100644 --- a/modules/mono/mono_gc_handle.h +++ b/modules/mono/mono_gc_handle.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -42,9 +42,11 @@ enum class GCHandleType : char { }; } +extern "C" { struct GCHandleIntPtr { void *value = nullptr; }; +} static_assert(sizeof(GCHandleIntPtr) == sizeof(void *)); diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index ce4fc0c5a0..dcda799f32 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -30,13 +30,6 @@ #include "gd_mono.h" -#include <mono/metadata/environment.h> -#include <mono/metadata/exception.h> -#include <mono/metadata/mono-config.h> -#include <mono/metadata/mono-debug.h> -#include <mono/metadata/mono-gc.h> -#include <mono/metadata/profiler.h> - #include "core/config/project_settings.h" #include "core/debugger/engine_debugger.h" #include "core/io/dir_access.h" @@ -48,61 +41,28 @@ #include "../godotsharp_dirs.h" #include "../utils/path_utils.h" #include "gd_mono_cache.h" -#include "gd_mono_utils.h" +#include <nethost.h> + +#include <coreclr_delegates.h> +#include <hostfxr.h> + +#warning TODO mobile +#if 0 #ifdef ANDROID_ENABLED #include "android_mono_config.h" #include "support/android_support.h" #elif defined(IOS_ENABLED) #include "support/ios_support.h" #endif - -#if defined(TOOL_ENABLED) && defined(GD_MONO_SINGLE_APPDOMAIN) -// This will no longer be the case if we replace appdomains with AssemblyLoadContext -#error "Editor build requires support for multiple appdomains" -#endif - -#if defined(GD_MONO_HOT_RELOAD) && defined(GD_MONO_SINGLE_APPDOMAIN) -#error "Hot reloading requires multiple appdomains" #endif -// TODO: -// This has turned 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 = nullptr; namespace { -#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(); - - List<CharString> cmdline_args_utf8; - Vector<char *> main_args; - main_args.resize(cmdline_args.size() + 1); - - main_args.write[0] = execpath.ptrw(); - - int i = 1; - for (const String &E : cmdline_args) { - CharString &stored = cmdline_args_utf8.push_back(E.utf8())->get(); - main_args.write[i] = stored.ptrw(); - i++; - } - - mono_runtime_set_main_args(main_args.size(), main_args.ptrw()); -} - +#warning "TODO .NET debugging and profiling. What's needed?" +#if 0 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); @@ -165,258 +125,193 @@ void gd_mono_debug_init() { }; mono_jit_parse_options(2, (char **)options); } - -#endif // !defined(JAVASCRIPT_ENABLED) - -#if defined(JAVASCRIPT_ENABLED) -MonoDomain *gd_initialize_mono_runtime() { - const char *vfs_prefix = "managed"; - int enable_debugging = 0; - - // TODO: Provide a way to enable debugging on WASM release builds. -#ifdef DEBUG_ENABLED - enable_debugging = 1; #endif +} // namespace - mono_wasm_load_runtime(vfs_prefix, enable_debugging); - - return mono_get_root_domain(); -} +namespace { +hostfxr_initialize_for_runtime_config_fn hostfxr_initialize_for_runtime_config = nullptr; +hostfxr_get_runtime_delegate_fn hostfxr_get_runtime_delegate = nullptr; +hostfxr_close_fn hostfxr_close = nullptr; + +#ifdef _WIN32 +static_assert(sizeof(char_t) == sizeof(char16_t)); +using HostFxrCharString = Char16String; +#define HOSTFXR_STR(m_str) L##m_str #else -MonoDomain *gd_initialize_mono_runtime() { - gd_mono_debug_init(); +static_assert(sizeof(char_t) == sizeof(char)); +using HostFxrCharString = CharString; +#define HOSTFXR_STR(m_str) m_str +#endif -#if defined(IOS_ENABLED) || defined(ANDROID_ENABLED) - // I don't know whether this actually matters or not - const char *runtime_version = "mobile"; +HostFxrCharString str_to_hostfxr(const String &p_str) { +#ifdef _WIN32 + return p_str.utf16(); #else - const char *runtime_version = "v4.0.30319"; + return p_str.utf8(); #endif - - return mono_jit_init_version("GodotEngine.RootDomain", runtime_version); } -#endif -} // namespace -void GDMono::add_mono_shared_libs_dir_to_path() { - // TODO: Replace this with a mono_dl_fallback +String str_from_hostfxr(const char_t *p_buffer) { +#ifdef _WIN32 + return String::utf16((const char16_t *)p_buffer); +#else + return String::utf8((const char *)p_buffer); +#endif +} - // 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) - // @executable_path@/../lib - // @executable_path@/../Libraries (__MACH__ only) +const char_t *get_data(const HostFxrCharString &p_char_str) { + return (const char_t *)p_char_str.get_data(); +} - // This does not work when embedding Mono unless we use the same directory structure. - // To fix this we append the directory containing our shared libraries to PATH. +String find_hostfxr() { + const int HostApiBufferTooSmall = 0x80008098; -#if defined(WINDOWS_ENABLED) || defined(UNIX_ENABLED) - String path_var("PATH"); - String path_value = OS::get_singleton()->get_environment(path_var); + size_t buffer_size = 0; + int rc = get_hostfxr_path(nullptr, &buffer_size, nullptr); -#ifdef WINDOWS_ENABLED - path_value += ';'; + if (rc == HostApiBufferTooSmall) { + // Pre-allocate a large buffer for the path to hostfxr + Vector<char_t> buffer; + buffer.resize(buffer_size); - String bundled_bin_dir = GodotSharpDirs::get_data_mono_bin_dir(); -#ifdef TOOLS_ENABLED - if (DirAccess::exists(bundled_bin_dir)) { - path_value += bundled_bin_dir; - } else { - path_value += mono_reg_info.bin_dir; - } -#else - if (DirAccess::exists(bundled_bin_dir)) { - path_value += bundled_bin_dir; - } -#endif // TOOLS_ENABLED + rc = get_hostfxr_path(buffer.ptrw(), &buffer_size, nullptr); -#else - path_value += ':'; + if (rc != 0) { + return String(); + } - String bundled_lib_dir = GodotSharpDirs::get_data_mono_lib_dir(); - if (DirAccess::exists(bundled_lib_dir)) { - path_value += bundled_lib_dir; - } else { - // TODO: Do we need to add the lib dir when using the system installed Mono on Unix platforms? + return str_from_hostfxr(buffer.ptr()); } -#endif // WINDOWS_ENABLED - OS::get_singleton()->set_environment(path_var, path_value); -#endif // WINDOWS_ENABLED || UNIX_ENABLED + return String(); } -void GDMono::determine_mono_dirs(String &r_assembly_rootdir, String &r_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(); +// Forward declarations +bool load_hostfxr() { + String hostfxr_path = find_hostfxr(); - if (mono_reg_info.assembly_dir.length() && DirAccess::exists(mono_reg_info.assembly_dir)) { - r_assembly_rootdir = mono_reg_info.assembly_dir; + if (hostfxr_path.is_empty()) { + return false; } - if (mono_reg_info.config_dir.length() && DirAccess::exists(mono_reg_info.config_dir)) { - r_config_dir = mono_reg_info.config_dir; - } -#elif defined(MACOS_ENABLED) - const char *c_assembly_rootdir = mono_assembly_getrootdir(); - const char *c_config_dir = mono_get_config_dir(); - - if (!c_assembly_rootdir || !c_config_dir || !DirAccess::exists(c_assembly_rootdir) || !DirAccess::exists(c_config_dir)) { - Vector<const char *> locations; - locations.push_back("/Library/Frameworks/Mono.framework/Versions/Current/"); - locations.push_back("/usr/local/var/homebrew/linked/mono/"); - - for (int i = 0; i < locations.size(); i++) { - String hint_assembly_rootdir = path::join(locations[i], "lib"); - String hint_mscorlib_path = path::join(hint_assembly_rootdir, "mono", "4.5", "mscorlib.dll"); - String hint_config_dir = path::join(locations[i], "etc"); - - if (FileAccess::exists(hint_mscorlib_path) && DirAccess::exists(hint_config_dir)) { - r_assembly_rootdir = hint_assembly_rootdir; - r_config_dir = hint_config_dir; - break; - } - } - } -#endif + print_verbose("Found hostfxr: " + hostfxr_path); - if (DirAccess::exists(bundled_assembly_rootdir)) { - r_assembly_rootdir = bundled_assembly_rootdir; - } + void *lib = nullptr; + Error err = OS::get_singleton()->open_dynamic_library(hostfxr_path, lib); + // TODO: Clean up lib handle when shutting down - if (DirAccess::exists(bundled_config_dir)) { - r_config_dir = bundled_config_dir; + if (err != OK) { + return false; } -#ifdef WINDOWS_ENABLED - if (r_assembly_rootdir.is_empty() || r_config_dir.is_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); - } -#endif // WINDOWS_ENABLED + void *symbol = nullptr; -#else - // Export templates always use the bundled directories - r_assembly_rootdir = bundled_assembly_rootdir; - r_config_dir = bundled_config_dir; -#endif -} + err = OS::get_singleton()->get_dynamic_library_symbol_handle(lib, "hostfxr_initialize_for_runtime_config", symbol); + ERR_FAIL_COND_V(err != OK, false); + hostfxr_initialize_for_runtime_config = (hostfxr_initialize_for_runtime_config_fn)symbol; -void GDMono::initialize() { - ERR_FAIL_NULL(Engine::get_singleton()); + err = OS::get_singleton()->get_dynamic_library_symbol_handle(lib, "hostfxr_get_runtime_delegate", symbol); + ERR_FAIL_COND_V(err != OK, false); + hostfxr_get_runtime_delegate = (hostfxr_get_runtime_delegate_fn)symbol; - print_verbose("Mono: Initializing module..."); + err = OS::get_singleton()->get_dynamic_library_symbol_handle(lib, "hostfxr_close", symbol); + ERR_FAIL_COND_V(err != OK, false); + hostfxr_close = (hostfxr_close_fn)symbol; - char *runtime_build_info = mono_get_runtime_build_info(); - print_verbose("Mono JIT compiler version " + String(runtime_build_info)); - mono_free(runtime_build_info); + return (hostfxr_initialize_for_runtime_config && + hostfxr_get_runtime_delegate && + hostfxr_close); +} - _init_godot_api_hashes(); - _init_exception_policy(); +load_assembly_and_get_function_pointer_fn initialize_hostfxr(const char_t *p_config_path) { + hostfxr_handle cxt = nullptr; + int rc = hostfxr_initialize_for_runtime_config(p_config_path, nullptr, &cxt); + if (rc != 0 || cxt == nullptr) { + hostfxr_close(cxt); + ERR_FAIL_V_MSG(nullptr, "hostfxr_initialize_for_runtime_config failed"); + } - GDMonoLog::get_singleton()->initialize(); + void *load_assembly_and_get_function_pointer = nullptr; -#if !defined(JAVASCRIPT_ENABLED) - String assembly_rootdir; - String config_dir; - determine_mono_dirs(assembly_rootdir, config_dir); + rc = hostfxr_get_runtime_delegate(cxt, + hdt_load_assembly_and_get_function_pointer, &load_assembly_and_get_function_pointer); + if (rc != 0 || load_assembly_and_get_function_pointer == nullptr) { + ERR_FAIL_V_MSG(nullptr, "hostfxr_get_runtime_delegate failed"); + } - // Leak if we call mono_set_dirs more than once - mono_set_dirs(assembly_rootdir.length() ? assembly_rootdir.utf8().get_data() : nullptr, - config_dir.length() ? config_dir.utf8().get_data() : nullptr); + hostfxr_close(cxt); - add_mono_shared_libs_dir_to_path(); -#endif + return (load_assembly_and_get_function_pointer_fn)load_assembly_and_get_function_pointer; +} +} // namespace -#ifdef ANDROID_ENABLED - mono_config_parse_memory(get_godot_android_mono_config().utf8().get_data()); -#else - mono_config_parse(nullptr); -#endif +static bool _on_core_api_assembly_loaded() { + if (!GDMonoCache::godot_api_cache_updated) { + return false; + } -#if defined(ANDROID_ENABLED) - gdmono::android::support::initialize(); -#elif defined(IOS_ENABLED) - gdmono::ios::support::initialize(); + GDMonoCache::managed_callbacks.Dispatcher_InitializeDefaultGodotTaskScheduler(); + +#ifdef DEBUG_ENABLED + // Install the trace listener now before the project assembly is loaded + GDMonoCache::managed_callbacks.DebuggingUtils_InstallTraceListener(); #endif - GDMonoAssembly::initialize(); + return true; +} -#if !defined(JAVASCRIPT_ENABLED) - gd_mono_profiler_init(); -#endif +void GDMono::initialize() { + ERR_FAIL_NULL(Engine::get_singleton()); - mono_install_unhandled_exception_hook(&unhandled_exception_hook, nullptr); + print_verbose(".NET: Initializing module..."); -#ifndef TOOLS_ENABLED - // 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").is_empty()) { - print_verbose("Mono: Skipping runtime initialization because 'mscorlib.dll' could not be found"); - return; - } -#endif + _init_godot_api_hashes(); -#if !defined(NO_MONO_THREADS_SUSPEND_WORKAROUND) - // FIXME: Temporary workaround. See: https://github.com/godotengine/godot/issues/29812 - if (!OS::get_singleton()->has_environment("MONO_THREADS_SUSPEND")) { - OS::get_singleton()->set_environment("MONO_THREADS_SUSPEND", "preemptive"); + if (!load_hostfxr()) { + ERR_FAIL_MSG(".NET: Failed to load hostfxr"); } -#endif - // NOTE: Internal calls must be registered after the Mono runtime initialization. - // Otherwise registration fails with the error: 'assertion 'hash != nullptr' failed'. + auto config_path = str_to_hostfxr(GodotSharpDirs::get_api_assemblies_dir() + .plus_file("GodotPlugins.runtimeconfig.json")); - root_domain = gd_initialize_mono_runtime(); - ERR_FAIL_NULL_MSG(root_domain, "Mono: Failed to initialize runtime."); + load_assembly_and_get_function_pointer_fn load_assembly_and_get_function_pointer = + initialize_hostfxr(get_data(config_path)); + ERR_FAIL_NULL(load_assembly_and_get_function_pointer); - GDMonoUtils::set_main_thread(GDMonoUtils::get_current_thread()); + runtime_initialized = true; -#if !defined(JAVASCRIPT_ENABLED) - gd_mono_setup_runtime_main_args(); // Required for System.Environment.GetCommandLineArgs -#endif + print_verbose(".NET: hostfxr initialized"); - runtime_initialized = true; + auto godot_plugins_path = str_to_hostfxr(GodotSharpDirs::get_api_assemblies_dir() + .plus_file("GodotPlugins.dll")); - print_verbose("Mono: Runtime initialized"); + using godot_plugins_initialize_fn = bool (*)(bool, PluginCallbacks *, GDMonoCache::ManagedCallbacks *); + godot_plugins_initialize_fn godot_plugins_initialize = nullptr; -#if defined(ANDROID_ENABLED) - gdmono::android::support::register_internal_calls(); -#endif + int rc = load_assembly_and_get_function_pointer(get_data(godot_plugins_path), + HOSTFXR_STR("GodotPlugins.Main, GodotPlugins"), + HOSTFXR_STR("Initialize"), + UNMANAGEDCALLERSONLY_METHOD, + nullptr, + (void **)&godot_plugins_initialize); + ERR_FAIL_COND_MSG(rc != 0, ".NET: Failed to get Godot.Plugins Initialize function pointer"); - // mscorlib assembly MUST be present at initialization - bool corlib_loaded = _load_corlib_assembly(); - ERR_FAIL_COND_MSG(!corlib_loaded, "Mono: Failed to load mscorlib assembly."); + PluginCallbacks aux_plugin_callbacks; + GDMonoCache::ManagedCallbacks managed_callbacks; + bool init_ok = godot_plugins_initialize(Engine::get_singleton()->is_editor_hint(), + &aux_plugin_callbacks, &managed_callbacks); + ERR_FAIL_COND_MSG(!init_ok, ".NET: Call to Godot.Plugins Initialize failed"); -#ifndef GD_MONO_SINGLE_APPDOMAIN - Error domain_load_err = _load_scripts_domain(); - ERR_FAIL_COND_MSG(domain_load_err != OK, "Mono: Failed to load scripts domain."); -#else - scripts_domain = root_domain; -#endif + GDMonoCache::update_godot_api_cache(managed_callbacks); + plugin_callbacks = aux_plugin_callbacks; - _register_internal_calls(); + print_verbose(".NET: GodotPlugins initialized"); - print_verbose("Mono: INITIALIZED"); + _on_core_api_assembly_loaded(); } void GDMono::initialize_load_assemblies() { - // Load assemblies. The API and tools assemblies are required, - // the application is aborted if these assemblies cannot be loaded. - - if (!_try_load_api_assemblies()) { - CRASH_NOW_MSG("Failed to load one of the API assemblies."); - } - #if defined(TOOLS_ENABLED) - bool tool_assemblies_loaded = _load_tools_assemblies(); - CRASH_COND_MSG(!tool_assemblies_loaded, "Mono: Failed to load '" TOOLS_ASM_NAME "' assemblies."); - if (Engine::get_singleton()->is_project_manager_hint()) { return; } @@ -427,20 +322,11 @@ void GDMono::initialize_load_assemblies() { // we're running in the editor, it may just happen to be it wasn't built yet. if (!_load_project_assembly()) { if (OS::get_singleton()->is_stdout_verbose()) { - print_error("Mono: Failed to load project assembly"); + print_error(".NET: Failed to load project assembly"); } } } -void godot_register_object_icalls(); -void godot_register_placeholder_icalls(); - -void GDMono::_register_internal_calls() { - // Registers internal calls that were not generated. - godot_register_object_icalls(); - godot_register_placeholder_icalls(); -} - void GDMono::_init_godot_api_hashes() { #ifdef DEBUG_METHODS_ENABLED get_api_core_hash(); @@ -451,216 +337,22 @@ void GDMono::_init_godot_api_hashes() { #endif // DEBUG_METHODS_ENABLED } -void GDMono::_init_exception_policy() { - PropertyInfo exc_policy_prop = PropertyInfo(Variant::INT, "mono/runtime/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(int32_t p_domain_id, GDMonoAssembly *p_assembly) { - assemblies[p_domain_id][p_assembly->get_name()] = p_assembly; -} - -GDMonoAssembly *GDMono::get_loaded_assembly(const String &p_name) { - if (p_name == "mscorlib" && corlib_assembly) { - return corlib_assembly; - } - - MonoDomain *domain = mono_domain_get(); - int32_t domain_id = domain ? mono_domain_get_id(domain) : 0; - GDMonoAssembly **result = assemblies[domain_id].getptr(p_name); - return result ? *result : nullptr; -} - -bool GDMono::load_assembly(const String &p_name, GDMonoAssembly **r_assembly) { -#ifdef DEBUG_ENABLED - CRASH_COND(!r_assembly); -#endif - - MonoAssemblyName *aname = mono_assembly_name_new(p_name.utf8()); - bool result = load_assembly(p_name, aname, r_assembly); - mono_assembly_name_free(aname); - mono_free(aname); - - return result; -} - -bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly) { -#ifdef DEBUG_ENABLED - CRASH_COND(!r_assembly); -#endif - - return load_assembly(p_name, p_aname, r_assembly, GDMonoAssembly::get_default_search_dirs()); -} - -bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, const Vector<String> &p_search_dirs) { -#ifdef DEBUG_ENABLED - CRASH_COND(!r_assembly); -#endif - - print_verbose("Mono: Loading assembly " + p_name + "..."); - - GDMonoAssembly *assembly = GDMonoAssembly::load(p_name, p_aname, /* refonly: */ false, p_search_dirs); - - if (!assembly) { - return false; - } - - *r_assembly = assembly; - - print_verbose("Mono: Assembly " + p_name + " loaded from path: " + (*r_assembly)->get_path()); - - return true; -} - -bool GDMono::load_assembly_from(const String &p_name, const String &p_path, GDMonoAssembly **r_assembly) { - CRASH_COND(!r_assembly); - - print_verbose("Mono: Loading assembly " + p_name + "..."); - - GDMonoAssembly *assembly = GDMonoAssembly::load_from(p_name, p_path, /* refonly: */ false); - - if (!assembly) { - return false; - } - - *r_assembly = assembly; - - print_verbose("Mono: Assembly " + p_name + " loaded from path: " + (*r_assembly)->get_path()); - - return true; -} - -bool GDMono::_load_corlib_assembly() { - if (corlib_assembly) { - return true; - } - - return load_assembly("mscorlib", &corlib_assembly); -} - -bool GDMono::_load_core_api_assembly(GDMonoAssembly **r_loaded_api_assembly, const String &p_config) { - if (*r_loaded_api_assembly) { - return true; - } - - return load_assembly(CORE_API_ASSEMBLY_NAME, r_loaded_api_assembly); -} - -#ifdef TOOLS_ENABLED -bool GDMono::_load_editor_api_assembly(GDMonoAssembly **r_loaded_api_assembly, const String &p_config) { - if (*r_loaded_api_assembly) { - return true; - } - - return load_assembly(EDITOR_API_ASSEMBLY_NAME, r_loaded_api_assembly); -} -#endif - -bool GDMono::_try_load_api_assemblies() { - String config = get_expected_api_build_config(); - - if (!_load_core_api_assembly(&core_api_assembly, config)) { - if (OS::get_singleton()->is_stdout_verbose()) { - print_error("Mono: Failed to load Core API assembly"); - } - return false; - } - -#ifdef TOOLS_ENABLED - if (!_load_editor_api_assembly(&editor_api_assembly, config)) { - if (OS::get_singleton()->is_stdout_verbose()) { - print_error("Mono: Failed to load Editor API assembly"); - } - return false; - } -#endif - - return _on_core_api_assembly_loaded(); -} - -bool GDMono::_on_core_api_assembly_loaded() { - GDMonoCache::update_godot_api_cache(); - - if (!GDMonoCache::cached_data.godot_api_cache_updated) { - return false; - } - - get_singleton()->_install_trace_listener(); - - return true; -} - -#ifdef TOOLS_ENABLED -bool GDMono::_load_tools_assemblies() { - if (tools_assembly && tools_project_editor_assembly) { - return true; - } - - return load_assembly(TOOLS_ASM_NAME, &tools_assembly) && - load_assembly(TOOLS_PROJECT_EDITOR_ASM_NAME, &tools_project_editor_assembly); -} -#endif - bool GDMono::_load_project_assembly() { - if (project_assembly) { - return true; - } - String appname = ProjectSettings::get_singleton()->get("application/config/name"); String appname_safe = OS::get_singleton()->get_safe_dir_name(appname); if (appname_safe.is_empty()) { appname_safe = "UnnamedProject"; } - bool success = load_assembly(appname_safe, &project_assembly); + String assembly_path = GodotSharpDirs::get_res_temp_assemblies_dir() + .plus_file(appname_safe + ".dll"); + assembly_path = ProjectSettings::get_singleton()->globalize_path(assembly_path); - if (success) { - mono_assembly_set_main(project_assembly->get_assembly()); - MonoException *exc = nullptr; - GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_LookupScriptsInAssembly.invoke( - mono_assembly_get_object(mono_domain_get(), project_assembly->get_assembly()), &exc); - if (exc) { - GDMonoUtils::debug_print_unhandled_exception(exc); - } - } - - return success; -} - -void GDMono::_install_trace_listener() { -#ifdef DEBUG_ENABLED - // Install the trace listener now before the project assembly is loaded - MonoException *exc = nullptr; - GDMonoCache::cached_data.methodthunk_DebuggingUtils_InstallTraceListener.invoke(&exc); - if (exc) { - GDMonoUtils::debug_print_unhandled_exception(exc); - ERR_PRINT("Failed to install 'System.Diagnostics.Trace' listener."); - } -#endif -} - -#ifndef GD_MONO_SINGLE_APPDOMAIN -Error GDMono::_load_scripts_domain() { - ERR_FAIL_COND_V(scripts_domain != nullptr, ERR_BUG); - - print_verbose("Mono: Loading scripts domain..."); - - scripts_domain = GDMonoUtils::create_domain("GodotEngine.Domain.Scripts"); - - ERR_FAIL_NULL_V_MSG(scripts_domain, ERR_CANT_CREATE, "Mono: Could not create scripts app domain."); - - mono_domain_set(scripts_domain, true); - - return OK; + return plugin_callbacks.LoadProjectAssemblyCallback(assembly_path.utf16()); } +#warning TODO hot-reload +#if 0 Error GDMono::_unload_scripts_domain() { ERR_FAIL_NULL_V(scripts_domain, ERR_BUG); @@ -682,10 +374,6 @@ Error GDMono::_unload_scripts_domain() { mono_gc_collect(mono_gc_max_generation()); - GDMonoCache::clear_godot_api_cache(); - - _domain_assemblies_cleanup(mono_domain_get_id(scripts_domain)); - core_api_assembly = nullptr; #ifdef TOOLS_ENABLED editor_api_assembly = nullptr; @@ -694,7 +382,6 @@ Error GDMono::_unload_scripts_domain() { project_assembly = nullptr; #ifdef TOOLS_ENABLED tools_assembly = nullptr; - tools_project_editor_assembly = nullptr; #endif MonoDomain *domain = scripts_domain; @@ -713,7 +400,6 @@ Error GDMono::_unload_scripts_domain() { return OK; } -#endif #ifdef GD_MONO_HOT_RELOAD Error GDMono::reload_scripts_domain() { @@ -750,51 +436,10 @@ Error GDMono::reload_scripts_domain() { return OK; } #endif - -#ifndef GD_MONO_SINGLE_APPDOMAIN -Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) { - CRASH_COND(p_domain == nullptr); - CRASH_COND(p_domain == GDMono::get_singleton()->get_scripts_domain()); // Should use _unload_scripts_domain() instead - - String domain_name = mono_domain_get_friendly_name(p_domain); - - print_verbose("Mono: Unloading domain '" + domain_name + "'..."); - - if (mono_domain_get() == p_domain) { - mono_domain_set(root_domain, true); - } - - if (!mono_domain_finalize(p_domain, 2000)) { - ERR_PRINT("Mono: Domain finalization timeout."); - } - - mono_gc_collect(mono_gc_max_generation()); - - _domain_assemblies_cleanup(mono_domain_get_id(p_domain)); - - MonoException *exc = nullptr; - mono_domain_try_unload(p_domain, (MonoObject **)&exc); - - if (exc) { - ERR_PRINT("Exception thrown when unloading domain '" + domain_name + "'."); - GDMonoUtils::debug_print_unhandled_exception(exc); - return FAILED; - } - - return OK; -} #endif -void GDMono::_domain_assemblies_cleanup(int32_t p_domain_id) { - HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[p_domain_id]; - - for (const KeyValue<String, GDMonoAssembly *> &E : domain_assemblies) { - memdelete(E.value); - } - - assemblies.erase(p_domain_id); -} - +#warning TODO Reimplement in C# +#if 0 void GDMono::unhandled_exception_hook(MonoObject *p_exc, void *) { // This method will be called by the runtime when a thrown exception is not handled. // It won't be called when we manually treat a thrown exception as unhandled. @@ -811,89 +456,37 @@ void GDMono::unhandled_exception_hook(MonoObject *p_exc, void *) { GD_UNREACHABLE(); } +#endif GDMono::GDMono() { singleton = this; - gdmono_log = memnew(GDMonoLog); - runtime_initialized = false; finalizing_scripts_domain = false; - root_domain = nullptr; - scripts_domain = nullptr; - - corlib_assembly = nullptr; - project_assembly = nullptr; -#ifdef TOOLS_ENABLED - tools_assembly = nullptr; - tools_project_editor_assembly = nullptr; -#endif - api_core_hash = 0; #ifdef TOOLS_ENABLED api_editor_hash = 0; #endif - - unhandled_exception_policy = POLICY_TERMINATE_APP; } GDMono::~GDMono() { if (is_runtime_initialized()) { -#ifndef GD_MONO_SINGLE_APPDOMAIN +#warning "TODO assembly unloading for cleanup of disposables (including managed RefCounteds)" +#if 0 if (scripts_domain) { Error err = _unload_scripts_domain(); if (err != OK) { ERR_PRINT("Mono: Failed to unload scripts domain."); } } -#else - CRASH_COND(scripts_domain != root_domain); - - print_verbose("Mono: Finalizing scripts domain..."); - - if (mono_domain_get() != root_domain) { - mono_domain_set(root_domain, true); - } - - finalizing_scripts_domain = true; - - if (!mono_domain_finalize(root_domain, 2000)) { - ERR_PRINT("Mono: Domain finalization timeout."); - } - - finalizing_scripts_domain = false; - - mono_gc_collect(mono_gc_max_generation()); - - GDMonoCache::clear_godot_api_cache(); - - _domain_assemblies_cleanup(mono_domain_get_id(root_domain)); - - core_api_assembly.assembly = nullptr; - - project_assembly = nullptr; - - root_domain = nullptr; - scripts_domain = nullptr; - - // Leave the rest to 'mono_jit_cleanup' -#endif - - for (const KeyValue<int32_t, HashMap<String, GDMonoAssembly *>> &E : assemblies) { - const HashMap<String, GDMonoAssembly *> &domain_assemblies = E.value; - - for (const KeyValue<String, GDMonoAssembly *> &F : domain_assemblies) { - memdelete(F.value); - } - } - assemblies.clear(); print_verbose("Mono: Runtime cleanup..."); mono_jit_cleanup(root_domain); print_verbose("Mono: Finalized"); +#endif runtime_initialized = false; } @@ -902,10 +495,6 @@ GDMono::~GDMono() { gdmono::android::support::cleanup(); #endif - if (gdmono_log) { - memdelete(gdmono_log); - } - singleton = nullptr; } @@ -913,62 +502,7 @@ namespace mono_bind { GodotSharp *GodotSharp::singleton = nullptr; -void GodotSharp::attach_thread() { - GDMonoUtils::attach_current_thread(); -} - -void GodotSharp::detach_thread() { - GDMonoUtils::detach_current_thread(); -} - -int32_t GodotSharp::get_domain_id() { - MonoDomain *domain = mono_domain_get(); - ERR_FAIL_NULL_V(domain, -1); - return mono_domain_get_id(domain); -} - -int32_t GodotSharp::get_scripts_domain_id() { - ERR_FAIL_NULL_V_MSG(GDMono::get_singleton(), - -1, "The Mono runtime is not initialized"); - MonoDomain *domain = GDMono::get_singleton()->get_scripts_domain(); - ERR_FAIL_NULL_V(domain, -1); - return mono_domain_get_id(domain); -} - -bool GodotSharp::is_scripts_domain_loaded() { - return GDMono::get_singleton() != nullptr && - GDMono::get_singleton()->is_runtime_initialized() && - GDMono::get_singleton()->get_scripts_domain() != nullptr; -} - -bool GodotSharp::_is_domain_finalizing_for_unload(int32_t p_domain_id) { - return is_domain_finalizing_for_unload(p_domain_id); -} - -bool GodotSharp::is_domain_finalizing_for_unload(int32_t p_domain_id) { - return is_domain_finalizing_for_unload(mono_domain_get_by_id(p_domain_id)); -} - -bool GodotSharp::is_domain_finalizing_for_unload(MonoDomain *p_domain) { - GDMono *gd_mono = GDMono::get_singleton(); - - ERR_FAIL_COND_V_MSG(!gd_mono || !gd_mono->is_runtime_initialized(), - false, "The Mono runtime is not initialized"); - - ERR_FAIL_NULL_V(p_domain, true); - - if (p_domain == gd_mono->get_scripts_domain() && gd_mono->is_finalizing_scripts_domain()) { - return true; - } - - return mono_domain_is_unloading(p_domain); -} - -bool GodotSharp::is_runtime_shutting_down() { - return mono_runtime_is_shutting_down(); -} - -bool GodotSharp::is_runtime_initialized() { +bool GodotSharp::_is_runtime_initialized() { return GDMono::get_singleton() != nullptr && GDMono::get_singleton()->is_runtime_initialized(); } @@ -984,16 +518,7 @@ void GodotSharp::_reload_assemblies(bool p_soft_reload) { } void GodotSharp::_bind_methods() { - ClassDB::bind_method(D_METHOD("attach_thread"), &GodotSharp::attach_thread); - ClassDB::bind_method(D_METHOD("detach_thread"), &GodotSharp::detach_thread); - - ClassDB::bind_method(D_METHOD("get_domain_id"), &GodotSharp::get_domain_id); - ClassDB::bind_method(D_METHOD("get_scripts_domain_id"), &GodotSharp::get_scripts_domain_id); - ClassDB::bind_method(D_METHOD("is_scripts_domain_loaded"), &GodotSharp::is_scripts_domain_loaded); - ClassDB::bind_method(D_METHOD("is_domain_finalizing_for_unload", "domain_id"), &GodotSharp::_is_domain_finalizing_for_unload); - - ClassDB::bind_method(D_METHOD("is_runtime_shutting_down"), &GodotSharp::is_runtime_shutting_down); - ClassDB::bind_method(D_METHOD("is_runtime_initialized"), &GodotSharp::is_runtime_initialized); + ClassDB::bind_method(D_METHOD("is_runtime_initialized"), &GodotSharp::_is_runtime_initialized); ClassDB::bind_method(D_METHOD("_reload_assemblies"), &GodotSharp::_reload_assemblies); } diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h index ac99aecabd..fee1cab9c7 100644 --- a/modules/mono/mono_gd/gd_mono.h +++ b/modules/mono/mono_gd/gd_mono.h @@ -34,82 +34,35 @@ #include "core/io/config_file.h" #include "../godotsharp_defs.h" -#include "gd_mono_assembly.h" -#include "gd_mono_log.h" - -#ifdef WINDOWS_ENABLED -#include "../utils/mono_reg_utils.h" -#endif class GDMono { -public: - enum UnhandledExceptionPolicy { - POLICY_TERMINATE_APP, - POLICY_LOG_ERROR - }; - -private: bool runtime_initialized; bool finalizing_scripts_domain; - UnhandledExceptionPolicy unhandled_exception_policy; - - MonoDomain *root_domain = nullptr; - MonoDomain *scripts_domain = nullptr; - - HashMap<int32_t, HashMap<String, GDMonoAssembly *>> assemblies; - - GDMonoAssembly *corlib_assembly = nullptr; - GDMonoAssembly *project_assembly = nullptr; -#ifdef TOOLS_ENABLED - GDMonoAssembly *tools_assembly = nullptr; - GDMonoAssembly *tools_project_editor_assembly = nullptr; -#endif - - GDMonoAssembly *core_api_assembly; - GDMonoAssembly *editor_api_assembly; - - bool _load_core_api_assembly(GDMonoAssembly **r_loaded_api_assembly, const String &p_config); -#ifdef TOOLS_ENABLED - bool _load_editor_api_assembly(GDMonoAssembly **r_loaded_api_assembly, const String &p_config); -#endif - - static bool _on_core_api_assembly_loaded(); - - bool _load_corlib_assembly(); -#ifdef TOOLS_ENABLED - bool _load_tools_assemblies(); -#endif bool _load_project_assembly(); bool _try_load_api_assemblies(); - void _install_trace_listener(); - - void _register_internal_calls(); - -#ifndef GD_MONO_SINGLE_APPDOMAIN - Error _load_scripts_domain(); Error _unload_scripts_domain(); -#endif - - void _domain_assemblies_cleanup(int32_t p_domain_id); uint64_t api_core_hash; #ifdef TOOLS_ENABLED uint64_t api_editor_hash; #endif void _init_godot_api_hashes(); - void _init_exception_policy(); - - GDMonoLog *gdmono_log = nullptr; -#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED) - MonoRegInfo mono_reg_info; + friend class CSharpLanguage; +#ifdef WIN32 +#define GD_CLR_STDCALL __stdcall +#else +#define GD_CLR_STDCALL #endif - - void add_mono_shared_libs_dir_to_path(); - void determine_mono_dirs(String &r_assembly_rootdir, String &r_config_dir); + struct PluginCallbacks { + using FuncLoadProjectAssemblyCallback = bool(GD_CLR_STDCALL *)(const char16_t *); + using FuncLoadToolsAssemblyCallback = Object *(GD_CLR_STDCALL *)(const char16_t *); + FuncLoadProjectAssemblyCallback LoadProjectAssemblyCallback = nullptr; + FuncLoadToolsAssemblyCallback LoadToolsAssemblyCallback = nullptr; + } plugin_callbacks; protected: static GDMono *singleton; @@ -146,42 +99,13 @@ public: static GDMono *get_singleton() { return singleton; } - [[noreturn]] static void unhandled_exception_hook(MonoObject *p_exc, void *p_user_data); - - UnhandledExceptionPolicy get_unhandled_exception_policy() const { return unhandled_exception_policy; } - - // Do not use these, unless you know what you're doing - void add_assembly(int32_t p_domain_id, GDMonoAssembly *p_assembly); - GDMonoAssembly *get_loaded_assembly(const String &p_name); - - _FORCE_INLINE_ bool is_runtime_initialized() const { return runtime_initialized && !mono_runtime_is_shutting_down() /* stays true after shutdown finished */; } - + _FORCE_INLINE_ bool is_runtime_initialized() const { return runtime_initialized; } _FORCE_INLINE_ bool is_finalizing_scripts_domain() { return finalizing_scripts_domain; } - _FORCE_INLINE_ MonoDomain *get_scripts_domain() { return scripts_domain; } - - _FORCE_INLINE_ GDMonoAssembly *get_corlib_assembly() const { return corlib_assembly; } - _FORCE_INLINE_ GDMonoAssembly *get_core_api_assembly() const { return core_api_assembly; } - _FORCE_INLINE_ GDMonoAssembly *get_project_assembly() const { return project_assembly; } -#ifdef TOOLS_ENABLED - _FORCE_INLINE_ GDMonoAssembly *get_tools_assembly() const { return tools_assembly; } -#endif - -#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED) - const MonoRegInfo &get_mono_reg_info() { return mono_reg_info; } -#endif - #ifdef GD_MONO_HOT_RELOAD Error reload_scripts_domain(); #endif - bool load_assembly(const String &p_name, GDMonoAssembly **r_assembly); - bool load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly); - bool load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, const Vector<String> &p_search_dirs); - bool load_assembly_from(const String &p_name, const String &p_path, GDMonoAssembly **r_assembly); - - Error finalize_and_unload_domain(MonoDomain *p_domain); - void initialize(); void initialize_load_assemblies(); @@ -189,52 +113,6 @@ public: ~GDMono(); }; -namespace gdmono { - -class ScopeDomain { - MonoDomain *prev_domain = nullptr; - -public: - ScopeDomain(MonoDomain *p_domain) { - prev_domain = mono_domain_get(); - if (prev_domain != p_domain) { - mono_domain_set(p_domain, false); - } else { - prev_domain = nullptr; - } - } - - ~ScopeDomain() { - if (prev_domain) { - mono_domain_set(prev_domain, false); - } - } -}; - -class ScopeExitDomainUnload { - MonoDomain *domain = nullptr; - -public: - ScopeExitDomainUnload(MonoDomain *p_domain) : - domain(p_domain) { - } - - ~ScopeExitDomainUnload() { - if (domain) { - GDMono::get_singleton()->finalize_and_unload_domain(domain); - } - } -}; -} // namespace gdmono - -#define _GDMONO_SCOPE_DOMAIN_(m_mono_domain) \ - gdmono::ScopeDomain __gdmono__scope__domain__(m_mono_domain); \ - (void)__gdmono__scope__domain__; - -#define _GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(m_mono_domain) \ - gdmono::ScopeExitDomainUnload __gdmono__scope__exit__domain__unload__(m_mono_domain); \ - (void)__gdmono__scope__exit__domain__unload__; - namespace mono_bind { class GodotSharp : public Object { @@ -242,9 +120,8 @@ class GodotSharp : public Object { friend class GDMono; - bool _is_domain_finalizing_for_unload(int32_t p_domain_id); - void _reload_assemblies(bool p_soft_reload); + bool _is_runtime_initialized(); protected: static GodotSharp *singleton; @@ -253,20 +130,6 @@ protected: public: static GodotSharp *get_singleton() { return singleton; } - void attach_thread(); - void detach_thread(); - - int32_t get_domain_id(); - int32_t get_scripts_domain_id(); - - bool is_scripts_domain_loaded(); - - bool is_domain_finalizing_for_unload(int32_t p_domain_id); - bool is_domain_finalizing_for_unload(MonoDomain *p_domain); - - bool is_runtime_shutting_down(); - bool is_runtime_initialized(); - GodotSharp(); ~GodotSharp(); }; diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp deleted file mode 100644 index 605216d331..0000000000 --- a/modules/mono/mono_gd/gd_mono_assembly.cpp +++ /dev/null @@ -1,388 +0,0 @@ -/*************************************************************************/ -/* gd_mono_assembly.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "gd_mono_assembly.h" - -#include <mono/metadata/mono-debug.h> -#include <mono/metadata/tokentype.h> - -#include "core/config/project_settings.h" -#include "core/io/file_access.h" -#include "core/io/file_access_pack.h" -#include "core/os/os.h" -#include "core/templates/list.h" - -#include "../godotsharp_dirs.h" -#include "gd_mono.h" -#include "gd_mono_cache.h" - -Vector<String> GDMonoAssembly::search_dirs; - -void GDMonoAssembly::fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config, const String &p_custom_bcl_dir) { - String framework_dir; - - if (!p_custom_bcl_dir.is_empty()) { - framework_dir = p_custom_bcl_dir; - } else if (mono_assembly_getrootdir()) { - framework_dir = String::utf8(mono_assembly_getrootdir()).plus_file("mono").plus_file("4.5"); - } - - if (!framework_dir.is_empty()) { - r_search_dirs.push_back(framework_dir); - 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.is_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 { - r_search_dirs.push_back(GodotSharpDirs::get_res_temp_assemblies_dir()); - } - - if (p_custom_config.is_empty()) { - r_search_dirs.push_back(GodotSharpDirs::get_api_assemblies_dir()); - } else { - String api_config = p_custom_config == "ExportRelease" ? "Release" : "Debug"; - r_search_dirs.push_back(GodotSharpDirs::get_api_assemblies_base_dir().plus_file(api_config)); - } - - r_search_dirs.push_back(GodotSharpDirs::get_api_assemblies_base_dir()); - r_search_dirs.push_back(OS::get_singleton()->get_resource_dir()); - r_search_dirs.push_back(OS::get_singleton()->get_executable_path().get_base_dir()); - -#ifdef TOOLS_ENABLED - r_search_dirs.push_back(GodotSharpDirs::get_data_editor_tools_dir()); -#endif -} - -// This is how these assembly loading hooks work: -// -// - The 'search' hook checks if the assembly has already been loaded, to avoid loading again. -// - The 'preload' hook does the actual loading and is only called if the -// 'search' hook didn't find the assembly in the list of loaded assemblies. -// - The 'load' hook is called after the assembly has been loaded. Its job is to add the -// assembly to the list of loaded assemblies so that the 'search' hook can look it up. - -void GDMonoAssembly::assembly_load_hook(MonoAssembly *assembly, [[maybe_unused]] void *user_data) { - String name = String::utf8(mono_assembly_name_get_name(mono_assembly_get_name(assembly))); - - MonoImage *image = mono_assembly_get_image(assembly); - - GDMonoAssembly *gdassembly = memnew(GDMonoAssembly(name, image, assembly)); - -#ifdef GD_MONO_HOT_RELOAD - String path = String::utf8(mono_image_get_filename(image)); - if (FileAccess::exists(path)) { - gdassembly->modified_time = FileAccess::get_modified_time(path); - } -#endif - - MonoDomain *domain = mono_domain_get(); - GDMono::get_singleton()->add_assembly(domain ? mono_domain_get_id(domain) : 0, gdassembly); -} - -MonoAssembly *GDMonoAssembly::assembly_search_hook(MonoAssemblyName *aname, void *user_data) { - return GDMonoAssembly::_search_hook(aname, user_data, false); -} - -MonoAssembly *GDMonoAssembly::assembly_refonly_search_hook(MonoAssemblyName *aname, void *user_data) { - return GDMonoAssembly::_search_hook(aname, user_data, true); -} - -MonoAssembly *GDMonoAssembly::assembly_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data) { - return GDMonoAssembly::_preload_hook(aname, assemblies_path, user_data, false); -} - -MonoAssembly *GDMonoAssembly::assembly_refonly_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data) { - return GDMonoAssembly::_preload_hook(aname, assemblies_path, user_data, true); -} - -MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, [[maybe_unused]] void *user_data, bool refonly) { - String name = String::utf8(mono_assembly_name_get_name(aname)); - bool has_extension = name.ends_with(".dll") || name.ends_with(".exe"); - - GDMonoAssembly *loaded_asm = GDMono::get_singleton()->get_loaded_assembly(has_extension ? name.get_basename() : name); - if (loaded_asm) { - return loaded_asm->get_assembly(); - } - - return nullptr; -} - -MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **, [[maybe_unused]] void *user_data, bool refonly) { - String name = String::utf8(mono_assembly_name_get_name(aname)); - return _load_assembly_search(name, aname, refonly, search_dirs); -} - -MonoAssembly *GDMonoAssembly::_load_assembly_search(const String &p_name, MonoAssemblyName *p_aname, bool p_refonly, const Vector<String> &p_search_dirs) { - MonoAssembly *res = nullptr; - String path; - - bool has_extension = p_name.ends_with(".dll") || p_name.ends_with(".exe"); - - for (int i = 0; i < p_search_dirs.size(); i++) { - const String &search_dir = p_search_dirs[i]; - - if (has_extension) { - path = search_dir.plus_file(p_name); - if (FileAccess::exists(path)) { - res = _real_load_assembly_from(path, p_refonly, p_aname); - if (res != nullptr) { - return res; - } - } - } else { - path = search_dir.plus_file(p_name + ".dll"); - if (FileAccess::exists(path)) { - res = _real_load_assembly_from(path, p_refonly, p_aname); - if (res != nullptr) { - return res; - } - } - - path = search_dir.plus_file(p_name + ".exe"); - if (FileAccess::exists(path)) { - res = _real_load_assembly_from(path, p_refonly, p_aname); - if (res != nullptr) { - return res; - } - } - } - } - - return nullptr; -} - -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(); -} - -void GDMonoAssembly::initialize() { - fill_search_dirs(search_dirs); - - mono_install_assembly_search_hook(&assembly_search_hook, nullptr); - mono_install_assembly_refonly_search_hook(&assembly_refonly_search_hook, nullptr); - mono_install_assembly_preload_hook(&assembly_preload_hook, nullptr); - mono_install_assembly_refonly_preload_hook(&assembly_refonly_preload_hook, nullptr); - mono_install_assembly_load_hook(&assembly_load_hook, nullptr); -} - -MonoAssembly *GDMonoAssembly::_real_load_assembly_from(const String &p_path, bool p_refonly, MonoAssemblyName *p_aname) { - Vector<uint8_t> data = FileAccess::get_file_as_array(p_path); - ERR_FAIL_COND_V_MSG(data.is_empty(), nullptr, "Could read the assembly in the specified location"); - - String image_filename; - -#ifdef ANDROID_ENABLED - if (p_path.begins_with("res://")) { - image_filename = p_path.substr(6, p_path.length()); - } else { - image_filename = ProjectSettings::get_singleton()->globalize_path(p_path); - } -#else - // FIXME: globalize_path does not work on exported games - image_filename = ProjectSettings::get_singleton()->globalize_path(p_path); -#endif - - MonoImageOpenStatus status = MONO_IMAGE_OK; - - MonoImage *image = mono_image_open_from_data_with_name( - (char *)&data[0], data.size(), - true, &status, p_refonly, - image_filename.utf8()); - - ERR_FAIL_COND_V_MSG(status != MONO_IMAGE_OK || !image, nullptr, "Failed to open assembly image from memory: '" + p_path + "'."); - - if (p_aname != nullptr) { - // Check assembly version - const MonoTableInfo *table = mono_image_get_table_info(image, MONO_TABLE_ASSEMBLY); - - ERR_FAIL_NULL_V(table, nullptr); - - if (mono_table_info_get_rows(table)) { - uint32_t cols[MONO_ASSEMBLY_SIZE]; - mono_metadata_decode_row(table, 0, cols, MONO_ASSEMBLY_SIZE); - - // Not sure about .NET's policy. We will only ensure major and minor are equal, and ignore build and revision. - uint16_t major = cols[MONO_ASSEMBLY_MAJOR_VERSION]; - uint16_t minor = cols[MONO_ASSEMBLY_MINOR_VERSION]; - - uint16_t required_minor; - uint16_t required_major = mono_assembly_name_get_version(p_aname, &required_minor, nullptr, nullptr); - - if (required_major != 0) { - if (major != required_major && minor != required_minor) { - mono_image_close(image); - return nullptr; - } - } - } - } - -#ifdef DEBUG_ENABLED - Vector<uint8_t> pdb_data; - String pdb_path(p_path + ".pdb"); - - if (!FileAccess::exists(pdb_path)) { - pdb_path = p_path.get_basename() + ".pdb"; // without .dll - - if (!FileAccess::exists(pdb_path)) { - goto no_pdb; - } - } - - pdb_data = FileAccess::get_file_as_array(pdb_path); - - // mono_debug_close_image doesn't seem to be needed - mono_debug_open_image_from_memory(image, &pdb_data[0], pdb_data.size()); - -no_pdb: - -#endif - - bool need_manual_load_hook = mono_image_get_assembly(image) != nullptr; // Re-using an existing image with an assembly loaded - - status = MONO_IMAGE_OK; - - MonoAssembly *assembly = mono_assembly_load_from_full(image, image_filename.utf8().get_data(), &status, p_refonly); - - ERR_FAIL_COND_V_MSG(status != MONO_IMAGE_OK || !assembly, nullptr, "Failed to load assembly for image"); - - if (need_manual_load_hook) { - // For some reason if an assembly survived domain reloading (maybe because it's referenced somewhere else), - // the mono internal search hook don't detect it, yet mono_image_open_from_data_with_name re-uses the image - // and assembly, and mono_assembly_load_from_full doesn't call the load hook. We need to call it manually. - String name = String::utf8(mono_assembly_name_get_name(mono_assembly_get_name(assembly))); - bool has_extension = name.ends_with(".dll") || name.ends_with(".exe"); - GDMonoAssembly *loaded_asm = GDMono::get_singleton()->get_loaded_assembly(has_extension ? name.get_basename() : name); - if (!loaded_asm) { - assembly_load_hook(assembly, nullptr); - } - } - - // Decrement refcount which was previously incremented by mono_image_open_from_data_with_name - mono_image_close(image); - - return assembly; -} - -void GDMonoAssembly::unload() { - ERR_FAIL_NULL(image); // Should not be called if already unloaded - - assembly = nullptr; - image = nullptr; -} - -String GDMonoAssembly::get_path() const { - return String::utf8(mono_image_get_filename(image)); -} - -GDMonoAssembly *GDMonoAssembly::load(const String &p_name, MonoAssemblyName *p_aname, bool p_refonly, const Vector<String> &p_search_dirs) { - if (GDMono::get_singleton()->get_corlib_assembly() && (p_name == "mscorlib" || p_name == "mscorlib.dll")) { - return GDMono::get_singleton()->get_corlib_assembly(); - } - - // We need to manually call the search hook in this case, as it won't be called in the next step - MonoAssembly *assembly = mono_assembly_invoke_search_hook(p_aname); - - if (!assembly) { - assembly = _load_assembly_search(p_name, p_aname, p_refonly, p_search_dirs); - if (!assembly) { - return nullptr; - } - } - - GDMonoAssembly *loaded_asm = GDMono::get_singleton()->get_loaded_assembly(p_name); - ERR_FAIL_NULL_V_MSG(loaded_asm, nullptr, "Loaded assembly missing from table. Did we not receive the load hook?"); - ERR_FAIL_COND_V(loaded_asm->get_assembly() != assembly, nullptr); - - return loaded_asm; -} - -GDMonoAssembly *GDMonoAssembly::load_from(const String &p_name, const String &p_path, bool p_refonly) { - if (p_name == "mscorlib" || p_name == "mscorlib.dll") { - return GDMono::get_singleton()->get_corlib_assembly(); - } - - // We need to manually call the search hook in this case, as it won't be called in the next step - MonoAssemblyName *aname = mono_assembly_name_new(p_name.utf8()); - MonoAssembly *assembly = mono_assembly_invoke_search_hook(aname); - mono_assembly_name_free(aname); - mono_free(aname); - - if (!assembly) { - assembly = _real_load_assembly_from(p_path, p_refonly); - if (!assembly) { - return nullptr; - } - } - - GDMonoAssembly *loaded_asm = GDMono::get_singleton()->get_loaded_assembly(p_name); - ERR_FAIL_NULL_V_MSG(loaded_asm, nullptr, "Loaded assembly missing from table. Did we not receive the load hook?"); - - return loaded_asm; -} - -GDMonoAssembly::~GDMonoAssembly() { - if (image) { - unload(); - } -} diff --git a/modules/mono/mono_gd/gd_mono_assembly.h b/modules/mono/mono_gd/gd_mono_assembly.h deleted file mode 100644 index f67a3e0f25..0000000000 --- a/modules/mono/mono_gd/gd_mono_assembly.h +++ /dev/null @@ -1,97 +0,0 @@ -/*************************************************************************/ -/* gd_mono_assembly.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef GD_MONO_ASSEMBLY_H -#define GD_MONO_ASSEMBLY_H - -#include <mono/jit/jit.h> -#include <mono/metadata/assembly.h> - -#include "core/string/ustring.h" -#include "core/templates/hash_map.h" -#include "core/templates/rb_map.h" -#include "gd_mono_utils.h" - -class GDMonoAssembly { - String name; - MonoImage *image = nullptr; - MonoAssembly *assembly = nullptr; - -#ifdef GD_MONO_HOT_RELOAD - uint64_t modified_time = 0; -#endif - - static Vector<String> search_dirs; - - static void assembly_load_hook(MonoAssembly *assembly, void *user_data); - static MonoAssembly *assembly_search_hook(MonoAssemblyName *aname, void *user_data); - static MonoAssembly *assembly_refonly_search_hook(MonoAssemblyName *aname, void *user_data); - static MonoAssembly *assembly_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data); - static MonoAssembly *assembly_refonly_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data); - - static MonoAssembly *_search_hook(MonoAssemblyName *aname, void *user_data, bool refonly); - static MonoAssembly *_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data, bool refonly); - - static MonoAssembly *_real_load_assembly_from(const String &p_path, bool p_refonly, MonoAssemblyName *p_aname = nullptr); - static MonoAssembly *_load_assembly_search(const String &p_name, MonoAssemblyName *p_aname, bool p_refonly, const Vector<String> &p_search_dirs); - - friend class GDMono; - static void initialize(); - -public: - void unload(); - - _FORCE_INLINE_ MonoImage *get_image() const { return image; } - _FORCE_INLINE_ MonoAssembly *get_assembly() const { return assembly; } - _FORCE_INLINE_ String get_name() const { return name; } - -#ifdef GD_MONO_HOT_RELOAD - _FORCE_INLINE_ uint64_t get_modified_time() const { return modified_time; } -#endif - - String get_path() const; - - 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 const Vector<String> &get_default_search_dirs() { return search_dirs; } - - static GDMonoAssembly *load(const String &p_name, MonoAssemblyName *p_aname, bool p_refonly, const Vector<String> &p_search_dirs); - static GDMonoAssembly *load_from(const String &p_name, const String &p_path, bool p_refonly); - - GDMonoAssembly(const String &p_name, MonoImage *p_image, MonoAssembly *p_assembly) : - name(p_name), - image(p_image), - assembly(p_assembly) { - } - ~GDMonoAssembly(); -}; - -#endif // GD_MONO_ASSEMBLY_H diff --git a/modules/mono/mono_gd/gd_mono_cache.cpp b/modules/mono/mono_gd/gd_mono_cache.cpp index 37c8e7d021..17addfb49d 100644 --- a/modules/mono/mono_gd/gd_mono_cache.cpp +++ b/modules/mono/mono_gd/gd_mono_cache.cpp @@ -30,82 +30,47 @@ #include "gd_mono_cache.h" -#include "gd_mono.h" -#include "gd_mono_utils.h" +#include "core/error/error_macros.h" namespace GDMonoCache { -CachedData cached_data; - -static MonoMethod *get_mono_method(MonoClass *p_mono_class, const char *p_method_name, int p_param_count) { - ERR_FAIL_NULL_V(p_mono_class, nullptr); - return mono_class_get_method_from_name(p_mono_class, p_method_name, p_param_count); -} - -static MonoClass *get_mono_class(GDMonoAssembly *p_assembly, const char *p_namespace, const char *p_name) { - ERR_FAIL_NULL_V(p_assembly->get_image(), nullptr); - return mono_class_from_name(p_assembly->get_image(), p_namespace, p_name); -} - -void update_godot_api_cache() { -#define CACHE_METHOD_THUNK_AND_CHECK_IMPL(m_var, m_val) \ - { \ - CRASH_COND(!m_var.is_null()); \ - val = m_val; \ - ERR_FAIL_COND_MSG(val == nullptr, "Mono Cache: Method for member " #m_var " is null."); \ - m_var.set_from_method(val); \ - ERR_FAIL_COND_MSG(m_var.is_null(), "Mono Cache: Member " #m_var " is null."); \ - } - -#define CACHE_METHOD_THUNK_AND_CHECK(m_class, m_method, m_val) CACHE_METHOD_THUNK_AND_CHECK_IMPL(cached_data.methodthunk_##m_class##_##m_method, m_val) - -#define GODOT_API_CLASS(m_class) (get_mono_class(GDMono::get_singleton()->get_core_api_assembly(), BINDINGS_NAMESPACE, #m_class)) -#define GODOT_API_BRIDGE_CLASS(m_class) (get_mono_class(GDMono::get_singleton()->get_core_api_assembly(), BINDINGS_NAMESPACE_BRIDGE, #m_class)) - - MonoMethod *val = nullptr; - - CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, SignalCallback, get_mono_method(GODOT_API_CLASS(SignalAwaiter), "SignalCallback", 4)); - - CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, InvokeWithVariantArgs, get_mono_method(GODOT_API_CLASS(DelegateUtils), "InvokeWithVariantArgs", 4)); - CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, DelegateEquals, get_mono_method(GODOT_API_CLASS(DelegateUtils), "DelegateEquals", 2)); - - CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, FrameCallback, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "FrameCallback", 0)); - CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, CreateManagedForGodotObjectBinding, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "CreateManagedForGodotObjectBinding", 2)); - CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, CreateManagedForGodotObjectScriptInstance, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "CreateManagedForGodotObjectScriptInstance", 4)); - CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, GetScriptNativeName, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "GetScriptNativeName", 2)); - CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, LookupScriptsInAssembly, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "LookupScriptsInAssembly", 1)); - CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, SetGodotObjectPtr, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "SetGodotObjectPtr", 2)); - CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, RaiseEventSignal, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "RaiseEventSignal", 5)); - CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, GetScriptSignalList, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "GetScriptSignalList", 2)); - CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, HasScriptSignal, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "HasScriptSignal", 2)); - CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, HasMethodUnknownParams, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "HasMethodUnknownParams", 3)); - CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, ScriptIsOrInherits, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "ScriptIsOrInherits", 2)); - CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, AddScriptBridge, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "AddScriptBridge", 2)); - CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, RemoveScriptBridge, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "RemoveScriptBridge", 1)); - CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, UpdateScriptClassInfo, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "UpdateScriptClassInfo", 3)); - CACHE_METHOD_THUNK_AND_CHECK(ScriptManagerBridge, SwapGCHandleForType, get_mono_method(GODOT_API_BRIDGE_CLASS(ScriptManagerBridge), "SwapGCHandleForType", 3)); - - CACHE_METHOD_THUNK_AND_CHECK(CSharpInstanceBridge, Call, get_mono_method(GODOT_API_BRIDGE_CLASS(CSharpInstanceBridge), "Call", 6)); - CACHE_METHOD_THUNK_AND_CHECK(CSharpInstanceBridge, Set, get_mono_method(GODOT_API_BRIDGE_CLASS(CSharpInstanceBridge), "Set", 3)); - CACHE_METHOD_THUNK_AND_CHECK(CSharpInstanceBridge, Get, get_mono_method(GODOT_API_BRIDGE_CLASS(CSharpInstanceBridge), "Get", 3)); - CACHE_METHOD_THUNK_AND_CHECK(CSharpInstanceBridge, CallDispose, get_mono_method(GODOT_API_BRIDGE_CLASS(CSharpInstanceBridge), "CallDispose", 2)); - CACHE_METHOD_THUNK_AND_CHECK(CSharpInstanceBridge, CallToString, get_mono_method(GODOT_API_BRIDGE_CLASS(CSharpInstanceBridge), "CallToString", 3)); - - CACHE_METHOD_THUNK_AND_CHECK(GCHandleBridge, FreeGCHandle, get_mono_method(GODOT_API_BRIDGE_CLASS(GCHandleBridge), "FreeGCHandle", 1)); - - CACHE_METHOD_THUNK_AND_CHECK(DebuggingUtils, InstallTraceListener, get_mono_method(GODOT_API_CLASS(DebuggingUtils), "InstallTraceListener", 0)); - - MonoException *exc = nullptr; - MonoMethod *init_default_godot_task_scheduler = - get_mono_method(GODOT_API_CLASS(Dispatcher), "InitializeDefaultGodotTaskScheduler", 0); - ERR_FAIL_COND_MSG(init_default_godot_task_scheduler == nullptr, - "Mono Cache: InitializeDefaultGodotTaskScheduler is null."); - mono_runtime_invoke(init_default_godot_task_scheduler, nullptr, nullptr, (MonoObject **)&exc); - - if (exc) { - GDMonoUtils::debug_unhandled_exception(exc); - } - - cached_data.godot_api_cache_updated = true; +ManagedCallbacks managed_callbacks; +bool godot_api_cache_updated = false; + +void update_godot_api_cache(const ManagedCallbacks &p_managed_callbacks) { +#define CHECK_CALLBACK_NOT_NULL_IMPL(m_var, m_class, m_method) ERR_FAIL_COND_MSG(m_var == nullptr, \ + "Mono Cache: Managed callback for '" #m_class "_" #m_method "' is null.") + +#define CHECK_CALLBACK_NOT_NULL(m_class, m_method) CHECK_CALLBACK_NOT_NULL_IMPL(p_managed_callbacks.m_class##_##m_method, m_class, m_method) + + CHECK_CALLBACK_NOT_NULL(SignalAwaiter, SignalCallback); + CHECK_CALLBACK_NOT_NULL(DelegateUtils, InvokeWithVariantArgs); + CHECK_CALLBACK_NOT_NULL(DelegateUtils, DelegateEquals); + CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, FrameCallback); + CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, CreateManagedForGodotObjectBinding); + CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, CreateManagedForGodotObjectScriptInstance); + CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, GetScriptNativeName); + CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, SetGodotObjectPtr); + CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, RaiseEventSignal); + CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, GetScriptSignalList); + CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, HasScriptSignal); + CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, HasMethodUnknownParams); + CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, ScriptIsOrInherits); + CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, AddScriptBridge); + CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, RemoveScriptBridge); + CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, UpdateScriptClassInfo); + CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, SwapGCHandleForType); + CHECK_CALLBACK_NOT_NULL(CSharpInstanceBridge, Call); + CHECK_CALLBACK_NOT_NULL(CSharpInstanceBridge, Set); + CHECK_CALLBACK_NOT_NULL(CSharpInstanceBridge, Get); + CHECK_CALLBACK_NOT_NULL(CSharpInstanceBridge, CallDispose); + CHECK_CALLBACK_NOT_NULL(CSharpInstanceBridge, CallToString); + CHECK_CALLBACK_NOT_NULL(GCHandleBridge, FreeGCHandle); + CHECK_CALLBACK_NOT_NULL(DebuggingUtils, InstallTraceListener); + CHECK_CALLBACK_NOT_NULL(Dispatcher, InitializeDefaultGodotTaskScheduler); + + managed_callbacks = p_managed_callbacks; + + 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 index 5785347938..56bf4cef94 100644 --- a/modules/mono/mono_gd/gd_mono_cache.h +++ b/modules/mono/mono_gd/gd_mono_cache.h @@ -31,56 +31,88 @@ #ifndef GD_MONO_CACHE_H #define GD_MONO_CACHE_H -#include "gd_mono_method_thunk.h" +#include <stdint.h> + +#include "../csharp_script.h" +#include "../mono_gc_handle.h" +#include "core/object/object.h" +#include "core/string/string_name.h" +#include "core/string/ustring.h" +#include "core/variant/callable.h" +#include "core/variant/dictionary.h" +#include "core/variant/variant.h" class CSharpScript; namespace GDMonoCache { -struct CachedData { - // Mono method thunks require structs to be boxed, even if passed by ref (out, ref, in). - // As such we need to use pointers instead for now, instead of by ref parameters. - - GDMonoMethodThunk<GCHandleIntPtr, const Variant **, int, bool *> methodthunk_SignalAwaiter_SignalCallback; - - GDMonoMethodThunk<GCHandleIntPtr, const Variant **, uint32_t, const Variant *> methodthunk_DelegateUtils_InvokeWithVariantArgs; - GDMonoMethodThunkR<bool, GCHandleIntPtr, GCHandleIntPtr> methodthunk_DelegateUtils_DelegateEquals; - - GDMonoMethodThunk<> methodthunk_ScriptManagerBridge_FrameCallback; - GDMonoMethodThunkR<GCHandleIntPtr, const StringName *, Object *> methodthunk_ScriptManagerBridge_CreateManagedForGodotObjectBinding; - GDMonoMethodThunk<const CSharpScript *, Object *, const Variant **, int> methodthunk_ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance; - GDMonoMethodThunk<const CSharpScript *, StringName *> methodthunk_ScriptManagerBridge_GetScriptNativeName; - GDMonoMethodThunk<MonoReflectionAssembly *> methodthunk_ScriptManagerBridge_LookupScriptsInAssembly; - GDMonoMethodThunk<GCHandleIntPtr, Object *> methodthunk_ScriptManagerBridge_SetGodotObjectPtr; - GDMonoMethodThunk<GCHandleIntPtr, const StringName *, const Variant **, int, bool *> methodthunk_ScriptManagerBridge_RaiseEventSignal; - GDMonoMethodThunk<const CSharpScript *, Dictionary *> methodthunk_ScriptManagerBridge_GetScriptSignalList; - GDMonoMethodThunkR<bool, const CSharpScript *, const String *> methodthunk_ScriptManagerBridge_HasScriptSignal; - GDMonoMethodThunkR<bool, const CSharpScript *, const String *, bool> methodthunk_ScriptManagerBridge_HasMethodUnknownParams; - GDMonoMethodThunkR<bool, const CSharpScript *, const CSharpScript *> methodthunk_ScriptManagerBridge_ScriptIsOrInherits; - GDMonoMethodThunkR<bool, const CSharpScript *, const String *> methodthunk_ScriptManagerBridge_AddScriptBridge; - GDMonoMethodThunk<const CSharpScript *> methodthunk_ScriptManagerBridge_RemoveScriptBridge; - GDMonoMethodThunk<const CSharpScript *, bool *, Dictionary *> methodthunk_ScriptManagerBridge_UpdateScriptClassInfo; - GDMonoMethodThunkR<bool, GCHandleIntPtr, GCHandleIntPtr *, bool> methodthunk_ScriptManagerBridge_SwapGCHandleForType; - - GDMonoMethodThunk<GCHandleIntPtr, const StringName *, const Variant **, int, Callable::CallError *, Variant *> methodthunk_CSharpInstanceBridge_Call; - GDMonoMethodThunkR<bool, GCHandleIntPtr, const StringName *, const Variant *> methodthunk_CSharpInstanceBridge_Set; - GDMonoMethodThunkR<bool, GCHandleIntPtr, const StringName *, Variant *> methodthunk_CSharpInstanceBridge_Get; - GDMonoMethodThunk<GCHandleIntPtr, bool> methodthunk_CSharpInstanceBridge_CallDispose; - GDMonoMethodThunk<GCHandleIntPtr, String *, bool *> methodthunk_CSharpInstanceBridge_CallToString; - - GDMonoMethodThunk<GCHandleIntPtr> methodthunk_GCHandleBridge_FreeGCHandle; - - GDMonoMethodThunk<> methodthunk_DebuggingUtils_InstallTraceListener; - - bool godot_api_cache_updated = false; +#ifdef WIN32 +#define GD_CLR_STDCALL __stdcall +#else +#define GD_CLR_STDCALL +#endif + +struct ManagedCallbacks { + using FuncSignalAwaiter_SignalCallback = void(GD_CLR_STDCALL *)(GCHandleIntPtr, const Variant **, int32_t, bool *); + using FuncDelegateUtils_InvokeWithVariantArgs = void(GD_CLR_STDCALL *)(GCHandleIntPtr, const Variant **, uint32_t, const Variant *); + using FuncDelegateUtils_DelegateEquals = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, GCHandleIntPtr); + using FuncScriptManagerBridge_FrameCallback = void(GD_CLR_STDCALL *)(); + using FuncScriptManagerBridge_CreateManagedForGodotObjectBinding = GCHandleIntPtr(GD_CLR_STDCALL *)(const StringName *, Object *); + using FuncScriptManagerBridge_CreateManagedForGodotObjectScriptInstance = bool(GD_CLR_STDCALL *)(const CSharpScript *, Object *, const Variant **, int); + using FuncScriptManagerBridge_GetScriptNativeName = void(GD_CLR_STDCALL *)(const CSharpScript *, StringName *); + using FuncScriptManagerBridge_SetGodotObjectPtr = void(GD_CLR_STDCALL *)(GCHandleIntPtr, Object *); + using FuncScriptManagerBridge_RaiseEventSignal = void(GD_CLR_STDCALL *)(GCHandleIntPtr, const StringName *, const Variant **, int, bool *); + using FuncScriptManagerBridge_GetScriptSignalList = void(GD_CLR_STDCALL *)(const CSharpScript *, Dictionary *); + using FuncScriptManagerBridge_HasScriptSignal = bool(GD_CLR_STDCALL *)(const CSharpScript *, const String *); + using FuncScriptManagerBridge_HasMethodUnknownParams = bool(GD_CLR_STDCALL *)(const CSharpScript *, const String *, bool); + using FuncScriptManagerBridge_ScriptIsOrInherits = bool(GD_CLR_STDCALL *)(const CSharpScript *, const CSharpScript *); + using FuncScriptManagerBridge_AddScriptBridge = bool(GD_CLR_STDCALL *)(const CSharpScript *, const String *); + using FuncScriptManagerBridge_RemoveScriptBridge = void(GD_CLR_STDCALL *)(const CSharpScript *); + using FuncScriptManagerBridge_UpdateScriptClassInfo = void(GD_CLR_STDCALL *)(const CSharpScript *, bool *, Dictionary *); + using FuncScriptManagerBridge_SwapGCHandleForType = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, GCHandleIntPtr *, bool); + using FuncCSharpInstanceBridge_Call = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, const StringName *, const Variant **, int, Callable::CallError *, Variant *); + using FuncCSharpInstanceBridge_Set = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, const StringName *, const Variant *); + using FuncCSharpInstanceBridge_Get = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, const StringName *, Variant *); + using FuncCSharpInstanceBridge_CallDispose = void(GD_CLR_STDCALL *)(GCHandleIntPtr, bool); + using FuncCSharpInstanceBridge_CallToString = void(GD_CLR_STDCALL *)(GCHandleIntPtr, String *, bool *); + using FuncGCHandleBridge_FreeGCHandle = void(GD_CLR_STDCALL *)(GCHandleIntPtr); + using FuncDebuggingUtils_InstallTraceListener = void(GD_CLR_STDCALL *)(); + using FuncDispatcher_InitializeDefaultGodotTaskScheduler = void(GD_CLR_STDCALL *)(); + + FuncSignalAwaiter_SignalCallback SignalAwaiter_SignalCallback; + FuncDelegateUtils_InvokeWithVariantArgs DelegateUtils_InvokeWithVariantArgs; + FuncDelegateUtils_DelegateEquals DelegateUtils_DelegateEquals; + FuncScriptManagerBridge_FrameCallback ScriptManagerBridge_FrameCallback; + FuncScriptManagerBridge_CreateManagedForGodotObjectBinding ScriptManagerBridge_CreateManagedForGodotObjectBinding; + FuncScriptManagerBridge_CreateManagedForGodotObjectScriptInstance ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance; + FuncScriptManagerBridge_GetScriptNativeName ScriptManagerBridge_GetScriptNativeName; + FuncScriptManagerBridge_SetGodotObjectPtr ScriptManagerBridge_SetGodotObjectPtr; + FuncScriptManagerBridge_RaiseEventSignal ScriptManagerBridge_RaiseEventSignal; + FuncScriptManagerBridge_GetScriptSignalList ScriptManagerBridge_GetScriptSignalList; + FuncScriptManagerBridge_HasScriptSignal ScriptManagerBridge_HasScriptSignal; + FuncScriptManagerBridge_HasMethodUnknownParams ScriptManagerBridge_HasMethodUnknownParams; + FuncScriptManagerBridge_ScriptIsOrInherits ScriptManagerBridge_ScriptIsOrInherits; + FuncScriptManagerBridge_AddScriptBridge ScriptManagerBridge_AddScriptBridge; + FuncScriptManagerBridge_RemoveScriptBridge ScriptManagerBridge_RemoveScriptBridge; + FuncScriptManagerBridge_UpdateScriptClassInfo ScriptManagerBridge_UpdateScriptClassInfo; + FuncScriptManagerBridge_SwapGCHandleForType ScriptManagerBridge_SwapGCHandleForType; + FuncCSharpInstanceBridge_Call CSharpInstanceBridge_Call; + FuncCSharpInstanceBridge_Set CSharpInstanceBridge_Set; + FuncCSharpInstanceBridge_Get CSharpInstanceBridge_Get; + FuncCSharpInstanceBridge_CallDispose CSharpInstanceBridge_CallDispose; + FuncCSharpInstanceBridge_CallToString CSharpInstanceBridge_CallToString; + FuncGCHandleBridge_FreeGCHandle GCHandleBridge_FreeGCHandle; + FuncDebuggingUtils_InstallTraceListener DebuggingUtils_InstallTraceListener; + FuncDispatcher_InitializeDefaultGodotTaskScheduler Dispatcher_InitializeDefaultGodotTaskScheduler; }; -extern CachedData cached_data; +extern ManagedCallbacks managed_callbacks; +extern bool godot_api_cache_updated; -void update_godot_api_cache(); +void update_godot_api_cache(const ManagedCallbacks &p_managed_callbacks); inline void clear_godot_api_cache() { - cached_data = CachedData(); + managed_callbacks = ManagedCallbacks(); } } // namespace GDMonoCache diff --git a/modules/mono/mono_gd/gd_mono_internals.cpp b/modules/mono/mono_gd/gd_mono_internals.cpp deleted file mode 100644 index 7b5fdef8a3..0000000000 --- a/modules/mono/mono_gd/gd_mono_internals.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/*************************************************************************/ -/* gd_mono_internals.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "gd_mono_internals.h" - -#include "../csharp_script.h" -#include "../utils/macros.h" -#include "gd_mono_utils.h" - -#include "core/debugger/engine_debugger.h" -#include "core/debugger/script_debugger.h" - -#include <mono/metadata/exception.h> - -namespace GDMonoInternals { -void unhandled_exception(MonoException *p_exc) { - mono_print_unhandled_exception((MonoObject *)p_exc); - gd_unhandled_exception_event(p_exc); - - if (GDMono::get_singleton()->get_unhandled_exception_policy() == GDMono::POLICY_TERMINATE_APP) { - // Too bad 'mono_invoke_unhandled_exception_hook' is not exposed to embedders - mono_unhandled_exception((MonoObject *)p_exc); - GDMono::unhandled_exception_hook((MonoObject *)p_exc, nullptr); - GD_UNREACHABLE(); - } else { -#ifdef DEBUG_ENABLED - GDMonoUtils::debug_send_unhandled_exception_error(p_exc); - if (EngineDebugger::is_active()) { - EngineDebugger::get_singleton()->poll_events(false); - } -#endif - } -} - -void gd_unhandled_exception_event(MonoException *p_exc) { - MonoImage *mono_image = GDMono::get_singleton()->get_core_api_assembly()->get_image(); - - MonoClass *gd_klass = mono_class_from_name(mono_image, "Godot", "GD"); - MonoMethod *unhandled_exception_method = mono_class_get_method_from_name(gd_klass, "OnUnhandledException", 1); - void *args[1]; - args[0] = p_exc; - mono_runtime_invoke(unhandled_exception_method, nullptr, (void **)args, nullptr); -} -} // namespace GDMonoInternals diff --git a/modules/mono/mono_gd/gd_mono_internals.h b/modules/mono/mono_gd/gd_mono_internals.h deleted file mode 100644 index 858c395b78..0000000000 --- a/modules/mono/mono_gd/gd_mono_internals.h +++ /dev/null @@ -1,52 +0,0 @@ -/*************************************************************************/ -/* gd_mono_internals.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef GD_MONO_INTERNALS_H -#define GD_MONO_INTERNALS_H - -#include <mono/jit/jit.h> - -#include "../utils/macros.h" - -#include "core/object/class_db.h" - -class CSharpScript; - -namespace GDMonoInternals { -/** - * Do not call this function directly. - * Use GDMonoUtils::debug_unhandled_exception(MonoException *) instead. - */ -void unhandled_exception(MonoException *p_exc); - -void gd_unhandled_exception_event(MonoException *p_exc); -} // namespace GDMonoInternals - -#endif // GD_MONO_INTERNALS_H diff --git a/modules/mono/mono_gd/gd_mono_log.cpp b/modules/mono/mono_gd/gd_mono_log.cpp deleted file mode 100644 index 6ea3c5539e..0000000000 --- a/modules/mono/mono_gd/gd_mono_log.cpp +++ /dev/null @@ -1,209 +0,0 @@ -/*************************************************************************/ -/* gd_mono_log.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "gd_mono_log.h" - -#include <stdlib.h> // abort - -#include "core/io/dir_access.h" -#include "core/os/os.h" - -#include "../godotsharp_dirs.h" -#include "../utils/string_utils.h" - -static CharString get_default_log_level() { -#ifdef DEBUG_ENABLED - return String("info").utf8(); -#else - return String("warning").utf8(); -#endif -} - -GDMonoLog *GDMonoLog::singleton = nullptr; - -#ifdef GD_MONO_LOG_ENABLED - -static int get_log_level_id(const char *p_log_level) { - const char *valid_log_levels[] = { "error", "critical", "warning", "message", "info", "debug", nullptr }; - - int i = 0; - while (valid_log_levels[i]) { - if (!strcmp(valid_log_levels[i], p_log_level)) { - return i; - } - i++; - } - - return -1; -} - -static String make_text(const char *log_domain, const char *log_level, const char *message) { - String text(message); - text += " (in domain "; - text += log_domain; - if (log_level) { - text += ", "; - text += log_level; - } - text += ")"; - return text; -} - -void GDMonoLog::mono_log_callback(const char *log_domain, const char *log_level, const char *message, mono_bool fatal, void *) { - if (GDMonoLog::get_singleton()->log_level_id >= get_log_level_id(log_level)) { - String text = make_text(log_domain, log_level, message); - text += "\n"; - - GDMonoLog::get_singleton()->log_file->seek_end(); - GDMonoLog::get_singleton()->log_file->store_string(text); - } - - if (fatal) { - String text = make_text(log_domain, log_level, message); - ERR_PRINT("Mono: FATAL ERROR '" + text + "', ABORTING! Logfile: '" + GDMonoLog::get_singleton()->log_file_path + "'."); - // Make sure to flush before aborting - GDMonoLog::get_singleton()->log_file->flush(); - GDMonoLog::get_singleton()->log_file.unref(); - - abort(); - } -} - -bool GDMonoLog::_try_create_logs_dir(const String &p_logs_dir) { - if (!DirAccess::exists(p_logs_dir)) { - Ref<DirAccess> diraccess = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - ERR_FAIL_COND_V(diraccess.is_null(), false); - Error logs_mkdir_err = diraccess->make_dir_recursive(p_logs_dir); - ERR_FAIL_COND_V_MSG(logs_mkdir_err != OK, false, "Failed to create mono logs directory."); - } - - return true; -} - -void GDMonoLog::_delete_old_log_files(const String &p_logs_dir) { - static const uint64_t MAX_SECS = 5 * 86400; // 5 days - - Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - ERR_FAIL_COND(da.is_null()); - - Error err = da->change_dir(p_logs_dir); - ERR_FAIL_COND_MSG(err != OK, "Cannot change directory to '" + p_logs_dir + "'."); - - ERR_FAIL_COND(da->list_dir_begin() != OK); - - String current = da->get_next(); - while (!current.is_empty()) { - if (da->current_is_dir() || !current.ends_with(".txt")) { - current = da->get_next(); - continue; - } - - uint64_t modified_time = FileAccess::get_modified_time(da->get_current_dir().plus_file(current)); - - if (OS::get_singleton()->get_unix_time() - modified_time > MAX_SECS) { - da->remove(current); - } - current = da->get_next(); - } - - da->list_dir_end(); -} - -void GDMonoLog::initialize() { - CharString log_level = OS::get_singleton()->get_environment("GODOT_MONO_LOG_LEVEL").utf8(); - - if (log_level.length() != 0 && get_log_level_id(log_level.get_data()) == -1) { - ERR_PRINT(String() + "Mono: Ignoring invalid log level (GODOT_MONO_LOG_LEVEL): '" + log_level.get_data() + "'."); - log_level = CharString(); - } - - if (log_level.length() == 0) { - log_level = get_default_log_level(); - } - - String logs_dir = GodotSharpDirs::get_mono_logs_dir(); - - if (_try_create_logs_dir(logs_dir)) { - _delete_old_log_files(logs_dir); - - OS::Date date_now = OS::get_singleton()->get_date(); - OS::Time time_now = OS::get_singleton()->get_time(); - - String log_file_name = str_format("%04d-%02d-%02d_%02d.%02d.%02d", - (int)date_now.year, (int)date_now.month, (int)date_now.day, - (int)time_now.hour, (int)time_now.minute, (int)time_now.second); - - log_file_name += str_format("_%d", OS::get_singleton()->get_process_id()); - - log_file_name += ".log"; - - log_file_path = logs_dir.plus_file(log_file_name); - - log_file = FileAccess::open(log_file_path, FileAccess::WRITE); - if (log_file.is_null()) { - ERR_PRINT("Mono: Cannot create log file at: " + log_file_path); - } - } - - mono_trace_set_level_string(log_level.get_data()); - log_level_id = get_log_level_id(log_level.get_data()); - - if (log_file.is_valid()) { - OS::get_singleton()->print("Mono: Log file is: '%s'\n", log_file_path.utf8().get_data()); - mono_trace_set_log_handler(mono_log_callback, this); - } else { - OS::get_singleton()->printerr("Mono: No log file, using default log handler\n"); - } -} - -GDMonoLog::GDMonoLog() { - singleton = this; -} - -GDMonoLog::~GDMonoLog() { - singleton = nullptr; -} - -#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 = nullptr; -} - -#endif // !defined(JAVASCRIPT_ENABLED) diff --git a/modules/mono/mono_gd/gd_mono_log.h b/modules/mono/mono_gd/gd_mono_log.h deleted file mode 100644 index 93ba6a410e..0000000000 --- a/modules/mono/mono_gd/gd_mono_log.h +++ /dev/null @@ -1,71 +0,0 @@ -/*************************************************************************/ -/* gd_mono_log.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef GD_MONO_LOG_H -#define GD_MONO_LOG_H - -#include <mono/utils/mono-logger.h> - -#include "core/typedefs.h" - -#if !defined(JAVASCRIPT_ENABLED) && !defined(IOS_ENABLED) -// We have custom mono log callbacks for WASM and iOS -#define GD_MONO_LOG_ENABLED -#endif - -#ifdef GD_MONO_LOG_ENABLED -#include "core/io/file_access.h" -#endif - -class GDMonoLog { -#ifdef GD_MONO_LOG_ENABLED - int log_level_id = -1; - - Ref<FileAccess> log_file; - String log_file_path; - - 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: - _FORCE_INLINE_ static GDMonoLog *get_singleton() { return singleton; } - - void initialize(); - - GDMonoLog(); - ~GDMonoLog(); -}; - -#endif // GD_MONO_LOG_H diff --git a/modules/mono/mono_gd/gd_mono_method_thunk.h b/modules/mono/mono_gd/gd_mono_method_thunk.h deleted file mode 100644 index aa84a7f2d0..0000000000 --- a/modules/mono/mono_gd/gd_mono_method_thunk.h +++ /dev/null @@ -1,126 +0,0 @@ -/*************************************************************************/ -/* gd_mono_method_thunk.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef GD_MONO_METHOD_THUNK_H -#define GD_MONO_METHOD_THUNK_H - -#include <mono/jit/jit.h> -#include <mono/metadata/attrdefs.h> -#include <type_traits> - -#include "core/error/error_macros.h" -#include "gd_mono_utils.h" - -#ifdef WIN32 -#define GD_MONO_STDCALL __stdcall -#else -#define GD_MONO_STDCALL -#endif - -template <class... ParamTypes> -struct GDMonoMethodThunk { - typedef void(GD_MONO_STDCALL *M)(ParamTypes... p_args, MonoException **); - - M mono_method_thunk = nullptr; - -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; - } - - bool is_null() { - return mono_method_thunk == nullptr; - } - - void set_from_method(MonoMethod *p_mono_method) { -#ifdef DEBUG_ENABLED - CRASH_COND(p_mono_method == nullptr); - - MonoMethodSignature *method_sig = mono_method_signature(p_mono_method); - MonoType *ret_type = mono_signature_get_return_type(method_sig); - int ret_type_encoding = ret_type ? mono_type_get_type(ret_type) : MONO_TYPE_VOID; - - CRASH_COND(ret_type_encoding != MONO_TYPE_VOID); - - bool is_static = mono_method_get_flags(p_mono_method, nullptr) & MONO_METHOD_ATTR_STATIC; - CRASH_COND(!is_static); - - uint32_t parameters_count = mono_signature_get_param_count(method_sig); - CRASH_COND(parameters_count != sizeof...(ParamTypes)); -#endif - mono_method_thunk = (M)mono_method_get_unmanaged_thunk(p_mono_method); - } - - GDMonoMethodThunk() {} -}; - -template <class R, class... ParamTypes> -struct GDMonoMethodThunkR { - typedef R(GD_MONO_STDCALL *M)(ParamTypes... p_args, MonoException **); - - M mono_method_thunk = nullptr; - -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; - } - - bool is_null() { - return mono_method_thunk == nullptr; - } - - void set_from_method(MonoMethod *p_mono_method) { -#ifdef DEBUG_ENABLED - CRASH_COND(p_mono_method == nullptr); - - MonoMethodSignature *method_sig = mono_method_signature(p_mono_method); - MonoType *ret_type = mono_signature_get_return_type(method_sig); - int ret_type_encoding = ret_type ? mono_type_get_type(ret_type) : MONO_TYPE_VOID; - - CRASH_COND(ret_type_encoding == MONO_TYPE_VOID); - - bool is_static = mono_method_get_flags(p_mono_method, nullptr) & MONO_METHOD_ATTR_STATIC; - CRASH_COND(!is_static); - - uint32_t parameters_count = mono_signature_get_param_count(method_sig); - CRASH_COND(parameters_count != sizeof...(ParamTypes)); -#endif - mono_method_thunk = (M)mono_method_get_unmanaged_thunk(p_mono_method); - } - - GDMonoMethodThunkR() {} -}; - -#endif // GD_MONO_METHOD_THUNK_H diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp deleted file mode 100644 index 3e2f4b05d5..0000000000 --- a/modules/mono/mono_gd/gd_mono_utils.cpp +++ /dev/null @@ -1,253 +0,0 @@ -/*************************************************************************/ -/* gd_mono_utils.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "gd_mono_utils.h" - -#include <mono/metadata/debug-helpers.h> -#include <mono/metadata/exception.h> - -#include "core/debugger/engine_debugger.h" -#include "core/debugger/script_debugger.h" -#include "core/io/dir_access.h" -#include "core/object/ref_counted.h" -#include "core/os/mutex.h" -#include "core/os/os.h" - -#ifdef TOOLS_ENABLED -#include "editor/debugger/editor_debugger_node.h" -#endif - -#include "../csharp_script.h" -#include "../utils/macros.h" -#include "gd_mono.h" -#include "gd_mono_cache.h" - -namespace GDMonoUtils { - -void set_main_thread(MonoThread *p_thread) { - mono_thread_set_main(p_thread); -} - -MonoThread *attach_current_thread() { - ERR_FAIL_COND_V(!GDMono::get_singleton()->is_runtime_initialized(), nullptr); - MonoDomain *scripts_domain = GDMono::get_singleton()->get_scripts_domain(); -#ifndef GD_MONO_SINGLE_APPDOMAIN - MonoThread *mono_thread = mono_thread_attach(scripts_domain ? scripts_domain : mono_get_root_domain()); -#else - // The scripts domain is the root domain - MonoThread *mono_thread = mono_thread_attach(scripts_domain); -#endif - ERR_FAIL_NULL_V(mono_thread, nullptr); - return mono_thread; -} - -void detach_current_thread() { - ERR_FAIL_COND(!GDMono::get_singleton()->is_runtime_initialized()); - MonoThread *mono_thread = mono_thread_current(); - ERR_FAIL_NULL(mono_thread); - mono_thread_detach(mono_thread); -} - -void detach_current_thread(MonoThread *p_mono_thread) { - ERR_FAIL_COND(!GDMono::get_singleton()->is_runtime_initialized()); - ERR_FAIL_NULL(p_mono_thread); - mono_thread_detach(p_mono_thread); -} - -MonoThread *get_current_thread() { - return mono_thread_current(); -} - -bool is_thread_attached() { - return mono_domain_get() != nullptr; -} - -MonoDomain *create_domain(const String &p_friendly_name) { - print_verbose("Mono: Creating domain '" + p_friendly_name + "'..."); - - MonoDomain *domain = mono_domain_create_appdomain((char *)p_friendly_name.utf8().get_data(), nullptr); - - if (domain) { - // Workaround to avoid this exception: - // System.Configuration.ConfigurationErrorsException: Error Initializing the configuration system. - // ---> System.ArgumentException: The 'ExeConfigFilename' argument cannot be null. - mono_domain_set_config(domain, ".", ""); - } - - return domain; -} - -// TODO: -// Implement all of the disabled exception logging below. Once we move to .NET 6. -// It will have to be done from C# as UnmanagedCallersOnly doesn't allow throwing. - -#warning TODO -#if 0 -String get_exception_name_and_message(MonoException *p_exc) { - String res; - - MonoClass *klass = mono_object_get_class((MonoObject *)p_exc); - MonoType *type = mono_class_get_type(klass); - - char *full_name = mono_type_full_name(type); - res += full_name; - mono_free(full_name); - - res += ": "; - - MonoProperty *prop = mono_class_get_property_from_name(klass, "Message"); - MonoString *msg = (MonoString *)property_get_value(prop, (MonoObject *)p_exc, nullptr, nullptr); - res += GDMonoMarshal::mono_string_to_godot(msg); - - return res; -} -#endif - -void debug_print_unhandled_exception(MonoException *p_exc) { - print_unhandled_exception(p_exc); - debug_send_unhandled_exception_error(p_exc); -} - -void debug_send_unhandled_exception_error(MonoException *p_exc) { -#ifdef DEBUG_ENABLED - if (!EngineDebugger::is_active()) { -#ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint()) { -#warning TODO -#if 0 - ERR_PRINT(GDMonoUtils::get_exception_name_and_message(p_exc)); -#endif - } -#endif - return; - } - - static thread_local bool _recursion_flag_ = false; - if (_recursion_flag_) { - return; - } - _recursion_flag_ = true; - SCOPE_EXIT { _recursion_flag_ = false; }; - - ScriptLanguage::StackInfo separator; - separator.file = String(); - separator.func = "--- " + RTR("End of inner exception stack trace") + " ---"; - separator.line = 0; - - Vector<ScriptLanguage::StackInfo> si; - String exc_msg; - -#warning TODO -#if 0 - while (p_exc != nullptr) { - GDMonoClass *st_klass = CACHED_CLASS(System_Diagnostics_StackTrace); - MonoObject *stack_trace = mono_object_new(mono_domain_get(), st_klass->get_mono_ptr()); - - MonoBoolean need_file_info = true; - void *ctor_args[2] = { p_exc, &need_file_info }; - - MonoException *unexpected_exc = nullptr; - CACHED_METHOD(System_Diagnostics_StackTrace, ctor_Exception_bool)->invoke_raw(stack_trace, ctor_args, &unexpected_exc); - - if (unexpected_exc) { - GDMonoInternals::unhandled_exception(unexpected_exc); - return; - } - - Vector<ScriptLanguage::StackInfo> _si; - if (stack_trace != nullptr) { - _si = CSharpLanguage::get_singleton()->stack_trace_get_info(stack_trace); - for (int i = _si.size() - 1; i >= 0; i--) { - si.insert(0, _si[i]); - } - } - - exc_msg += (exc_msg.length() > 0 ? " ---> " : "") + GDMonoUtils::get_exception_name_and_message(p_exc); - - GDMonoClass *exc_class = GDMono::get_singleton()->get_class(mono_get_exception_class()); - GDMonoProperty *inner_exc_prop = exc_class->get_property("InnerException"); - CRASH_COND(inner_exc_prop == nullptr); - - MonoObject *inner_exc = inner_exc_prop->get_value((MonoObject *)p_exc); - if (inner_exc != nullptr) { - si.insert(0, separator); - } - - p_exc = (MonoException *)inner_exc; - } -#endif - - String file = si.size() ? si[0].file : __FILE__; - String func = si.size() ? si[0].func : FUNCTION_STR; - int line = si.size() ? si[0].line : __LINE__; - String error_msg = "Unhandled exception"; - - EngineDebugger::get_script_debugger()->send_error(func, file, line, error_msg, exc_msg, true, ERR_HANDLER_ERROR, si); -#endif -} - -void debug_unhandled_exception(MonoException *p_exc) { - GDMonoInternals::unhandled_exception(p_exc); // prints the exception as well -} - -void print_unhandled_exception(MonoException *p_exc) { - mono_print_unhandled_exception((MonoObject *)p_exc); -} - -void set_pending_exception(MonoException *p_exc) { -#ifdef NO_PENDING_EXCEPTIONS - debug_unhandled_exception(p_exc); -#else - if (get_runtime_invoke_count() == 0) { - debug_unhandled_exception(p_exc); - return; - } - - if (!mono_runtime_set_pending_exception(p_exc, false)) { - ERR_PRINT("Exception thrown from managed code, but it could not be set as pending:"); - GDMonoUtils::debug_print_unhandled_exception(p_exc); - } -#endif -} - -thread_local int current_invoke_count = 0; - -ScopeThreadAttach::ScopeThreadAttach() { - if (likely(GDMono::get_singleton()->is_runtime_initialized()) && unlikely(!mono_domain_get())) { - mono_thread = GDMonoUtils::attach_current_thread(); - } -} - -ScopeThreadAttach::~ScopeThreadAttach() { - if (unlikely(mono_thread)) { - GDMonoUtils::detach_current_thread(mono_thread); - } -} -} // namespace GDMonoUtils diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h deleted file mode 100644 index 16fc3cc757..0000000000 --- a/modules/mono/mono_gd/gd_mono_utils.h +++ /dev/null @@ -1,144 +0,0 @@ -/*************************************************************************/ -/* gd_mono_utils.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef GD_MONO_UTILS_H -#define GD_MONO_UTILS_H - -#include <mono/metadata/threads.h> - -#include "../mono_gc_handle.h" -#include "../utils/macros.h" -#ifdef JAVASCRIPT_ENABLED -#include "gd_mono_wasm_m2n.h" -#endif - -#include "core/object/class_db.h" -#include "core/object/ref_counted.h" - -#define UNHANDLED_EXCEPTION(m_exc) \ - if (unlikely(m_exc != nullptr)) { \ - GDMonoUtils::debug_unhandled_exception(m_exc); \ - GD_UNREACHABLE(); \ - } else \ - ((void)0) - -namespace GDMonoUtils { - -namespace Marshal { -bool type_has_flags_attribute(MonoReflectionType *p_reftype); -} // namespace Marshal - -_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); -} - -void set_main_thread(MonoThread *p_thread); -MonoThread *attach_current_thread(); -void detach_current_thread(); -void detach_current_thread(MonoThread *p_mono_thread); -MonoThread *get_current_thread(); -bool is_thread_attached(); - -MonoDomain *create_domain(const String &p_friendly_name); - -String get_exception_name_and_message(MonoException *p_exc); - -void debug_print_unhandled_exception(MonoException *p_exc); -void debug_send_unhandled_exception_error(MonoException *p_exc); -void debug_unhandled_exception(MonoException *p_exc); -void print_unhandled_exception(MonoException *p_exc); - -/** - * Sets the exception as pending. The exception will be thrown when returning to managed code. - * If no managed method is being invoked by the runtime, the exception will be treated as - * an unhandled exception and the method will not return. - */ -void set_pending_exception(MonoException *p_exc); - -extern thread_local int current_invoke_count; - -_FORCE_INLINE_ int get_runtime_invoke_count() { - return current_invoke_count; -} - -_FORCE_INLINE_ int &get_runtime_invoke_count_ref() { - return current_invoke_count; -} - -uint64_t unbox_enum_value(MonoObject *p_boxed, MonoType *p_enum_basetype, bool &r_error); - -struct ScopeThreadAttach { - ScopeThreadAttach(); - ~ScopeThreadAttach(); - -private: - MonoThread *mono_thread = nullptr; -}; - -template <typename... P> -void add_internal_call(const char *p_name, void (*p_func)(P...)) { -#ifdef JAVASCRIPT_ENABLED - GDMonoWasmM2n::ICallTrampolines<P...>::add(); -#endif - mono_add_internal_call(p_name, (void *)p_func); -} - -template <typename R, typename... P> -void add_internal_call(const char *p_name, R (*p_func)(P...)) { -#ifdef JAVASCRIPT_ENABLED - GDMonoWasmM2n::ICallTrampolinesR<R, P...>::add(); -#endif - mono_add_internal_call(p_name, (void *)p_func); -} -} // namespace GDMonoUtils - -#define GD_MONO_BEGIN_RUNTIME_INVOKE \ - int &_runtime_invoke_count_ref = GDMonoUtils::get_runtime_invoke_count_ref(); \ - _runtime_invoke_count_ref += 1; \ - ((void)0) - -#define GD_MONO_END_RUNTIME_INVOKE \ - _runtime_invoke_count_ref -= 1; \ - ((void)0) - -#define GD_MONO_SCOPE_THREAD_ATTACH \ - GDMonoUtils::ScopeThreadAttach __gdmono__scope__thread__attach__; \ - (void)__gdmono__scope__thread__attach__; \ - ((void)0) - -#ifdef DEBUG_ENABLED -#define GD_MONO_ASSERT_THREAD_ATTACHED \ - CRASH_COND(!GDMonoUtils::is_thread_attached()); \ - ((void)0) -#else -#define GD_MONO_ASSERT_THREAD_ATTACHED ((void)0) -#endif - -#endif // GD_MONO_UTILS_H diff --git a/modules/mono/mono_gd/gd_mono_wasm_m2n.cpp b/modules/mono/mono_gd/gd_mono_wasm_m2n.cpp deleted file mode 100644 index dbfca2dc0c..0000000000 --- a/modules/mono/mono_gd/gd_mono_wasm_m2n.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/*************************************************************************/ -/* gd_mono_wasm_m2n.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "gd_mono_wasm_m2n.h" - -#ifdef JAVASCRIPT_ENABLED - -#include "core/templates/oa_hash_map.h" - -typedef mono_bool (*GodotMonoM2nIcallTrampolineDispatch)(const char *cookie, void *target_func, Mono_InterpMethodArguments *margs); - -// This extern function is implemented in our patched version of Mono -MONO_API void godot_mono_register_m2n_icall_trampoline_dispatch_hook(GodotMonoM2nIcallTrampolineDispatch hook); - -namespace GDMonoWasmM2n { - -struct HashMapCookieComparator { - static bool compare(const char *p_lhs, const char *p_rhs) { - return strcmp(p_lhs, p_rhs) == 0; - } -}; - -// The default hasher supports 'const char *' C Strings, but we need a custom comparator -OAHashMap<const char *, TrampolineFunc, HashMapHasherDefault, HashMapCookieComparator> trampolines; - -void set_trampoline(const char *cookies, GDMonoWasmM2n::TrampolineFunc trampoline_func) { - trampolines.set(cookies, trampoline_func); -} - -mono_bool trampoline_dispatch_hook(const char *cookie, void *target_func, Mono_InterpMethodArguments *margs) { - TrampolineFunc *trampoline_func = trampolines.lookup_ptr(cookie); - - if (!trampoline_func) { - return false; - } - - (*trampoline_func)(target_func, margs); - return true; -} - -bool initialized = false; - -void lazy_initialize() { - // Doesn't need to be thread safe - if (!initialized) { - initialized = true; - godot_mono_register_m2n_icall_trampoline_dispatch_hook(&trampoline_dispatch_hook); - } -} -} // namespace GDMonoWasmM2n - -#endif diff --git a/modules/mono/mono_gd/gd_mono_wasm_m2n.h b/modules/mono/mono_gd/gd_mono_wasm_m2n.h deleted file mode 100644 index 83e2750e5a..0000000000 --- a/modules/mono/mono_gd/gd_mono_wasm_m2n.h +++ /dev/null @@ -1,263 +0,0 @@ -/*************************************************************************/ -/* gd_mono_wasm_m2n.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef GD_MONO_WASM_M2N_H -#define GD_MONO_WASM_M2N_H - -#ifdef JAVASCRIPT_ENABLED - -#include "core/string/ustring.h" -#include "core/typedefs.h" - -#include <mono/metadata/loader.h> -#include <mono/utils/mono-publib.h> -#include <stdexcept> -#include <type_traits> - -extern "C" { - -struct Mono_InterpMethodArguments { - size_t ilen; - void **iargs; - size_t flen; - double *fargs = nullptr; - void **retval; - size_t is_float_ret; - //#ifdef TARGET_WASM - void *sig = nullptr; - //#endif -}; -} // extern "C" - -namespace GDMonoWasmM2n { - -template <typename T, size_t Size> -struct array { - T elems[Size]; -}; - -template <typename T> -constexpr char get_m2n_cookie_impl() { -#define M2N_REG_COOKIE(m_type, m_cookie) \ - if constexpr (std::is_same_v<m_type, T>) { \ - return m_cookie; \ - } - - M2N_REG_COOKIE(MonoBoolean, 'I'); - M2N_REG_COOKIE(int8_t, 'I'); - M2N_REG_COOKIE(uint8_t, 'I'); - M2N_REG_COOKIE(int16_t, 'I'); - M2N_REG_COOKIE(uint16_t, 'I'); - M2N_REG_COOKIE(int32_t, 'I'); - M2N_REG_COOKIE(uint32_t, 'I'); - M2N_REG_COOKIE(int64_t, 'L'); - M2N_REG_COOKIE(uint64_t, 'L'); - M2N_REG_COOKIE(float, 'F'); - M2N_REG_COOKIE(double, 'D'); - - if constexpr (std::is_pointer_v<T>) { - if constexpr (sizeof(void *) == 4) { - return 'I'; - } else { - return 'L'; - } - } - - if constexpr (std::is_void_v<T>) { - return 'V'; - } - - return 'X'; - -#undef M2N_REG_COOKIE -} - -template <typename T> -constexpr char get_m2n_cookie() { - constexpr char cookie = get_m2n_cookie_impl<T>(); - static_assert(cookie != 'X', "Type not supported in internal call signature."); - return cookie; -} - -template <typename... T> -constexpr array<const char, sizeof...(T) + 2> get_m2n_cookies() { - return array<const char, sizeof...(T) + 2>{ 'V', get_m2n_cookie<T>()..., '\0' }; -} - -template <typename R, typename... T> -constexpr array<const char, sizeof...(T) + 2> get_m2n_cookies_r() { - return array<const char, sizeof...(T) + 2>{ get_m2n_cookie<R>(), get_m2n_cookie<T>()..., '\0' }; -} - -template <typename T> -constexpr size_t calc_m2n_index(size_t &r_int_idx, size_t &r_float_idx) { - constexpr char cookie = get_m2n_cookie<T>(); - - static_assert(cookie == 'I' || cookie == 'L' || cookie == 'F' || cookie == 'D'); - - if constexpr (cookie == 'I' || cookie == 'L') { - size_t ret = r_int_idx; - r_int_idx += cookie == 'I' ? 1 : 2; - return ret; - } else { - size_t ret = r_float_idx; - r_float_idx += cookie == 'F' ? 1 : 2; - return ret; - } -} - -template <typename... P> -constexpr array<size_t, sizeof...(P)> get_indices_for_type() { - size_t int_idx = 0; - size_t float_idx = 0; - return array<size_t, sizeof...(P)>{ calc_m2n_index<P>(int_idx, float_idx)... }; -} - -constexpr size_t fidx(size_t p_x) { - if constexpr (sizeof(void *) == 4) { - return p_x * 2; - } else { - return p_x; - } -} - -template <typename T> -T m2n_arg_cast(Mono_InterpMethodArguments *p_margs, size_t p_idx) { - constexpr char cookie = get_m2n_cookie<T>(); - - static_assert(cookie == 'I' || cookie == 'L' || cookie == 'F' || cookie == 'D'); - - if constexpr (cookie == 'I') { - return (T)(size_t)p_margs->iargs[p_idx]; - } else if constexpr (cookie == 'L') { - static_assert(std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t> || - (sizeof(void *) == 8 && std::is_pointer_v<T>), - "Invalid type for cookie 'L'."); - - union { - T l; - struct { - int32_t lo; - int32_t hi; - } pair; - } p; - - p.pair.lo = (int32_t)(size_t)p_margs->iargs[p_idx]; - p.pair.hi = (int32_t)(size_t)p_margs->iargs[p_idx + 1]; - - return p.l; - } else if constexpr (cookie == 'F') { - return *reinterpret_cast<float *>(&p_margs->fargs[fidx(p_idx)]); - } else if constexpr (cookie == 'D') { - return (T)p_margs->fargs[p_idx]; - } -} - -template <typename... P, size_t... Is> -void m2n_trampoline_with_idx_seq(void *p_target_func, Mono_InterpMethodArguments *p_margs, IndexSequence<Is...>) { - constexpr array<size_t, sizeof...(P)> indices = get_indices_for_type<P...>(); - typedef void (*Func)(P...); - Func func = (Func)p_target_func; - func(m2n_arg_cast<P>(p_margs, indices.elems[Is])...); -} - -template <typename R, typename... P, size_t... Is> -void m2n_trampoline_with_idx_seq_r(void *p_target_func, Mono_InterpMethodArguments *p_margs, IndexSequence<Is...>) { - constexpr array<size_t, sizeof...(P)> indices = get_indices_for_type<P...>(); - typedef R (*Func)(P...); - Func func = (Func)p_target_func; - R res = func(m2n_arg_cast<P>(p_margs, indices.elems[Is])...); - *reinterpret_cast<R *>(p_margs->retval) = res; -} - -inline void m2n_trampoline_with_idx_seq_0(void *p_target_func, Mono_InterpMethodArguments *p_margs) { - typedef void (*Func)(); - Func func = (Func)p_target_func; - func(); -} - -template <typename R> -void m2n_trampoline_with_idx_seq_r0(void *p_target_func, Mono_InterpMethodArguments *p_margs) { - typedef R (*Func)(); - Func func = (Func)p_target_func; - R res = func(); - *reinterpret_cast<R *>(p_margs->retval) = res; -} - -template <typename... P> -void m2n_trampoline(void *p_target_func, Mono_InterpMethodArguments *p_margs) { - if constexpr (sizeof...(P) == 0) { - m2n_trampoline_with_idx_seq_0(p_target_func, p_margs); - } else { - m2n_trampoline_with_idx_seq<P...>(p_target_func, p_margs, BuildIndexSequence<sizeof...(P)>{}); - } -} - -template <typename R, typename... P> -void m2n_trampoline_r(void *p_target_func, Mono_InterpMethodArguments *p_margs) { - if constexpr (sizeof...(P) == 0) { - m2n_trampoline_with_idx_seq_r0<R>(p_target_func, p_margs); - } else { - m2n_trampoline_with_idx_seq_r<R, P...>(p_target_func, p_margs, BuildIndexSequence<sizeof...(P)>{}); - } -} - -typedef void (*TrampolineFunc)(void *p_target_func, Mono_InterpMethodArguments *p_margs); - -void set_trampoline(const char *cookies, TrampolineFunc trampoline_func); - -void lazy_initialize(); - -template <typename... P> -struct ICallTrampolines { - static constexpr auto cookies = get_m2n_cookies<P...>(); - - static void add() { - lazy_initialize(); - set_trampoline(cookies.elems, &m2n_trampoline<P...>); - } -}; - -template <typename R, typename... P> -struct ICallTrampolinesR { - static constexpr auto cookies = get_m2n_cookies_r<R, P...>(); - - static void add() { - lazy_initialize(); - set_trampoline(cookies.elems, &m2n_trampoline_r<R, P...>); - } -}; - -void initialize(); -} // namespace GDMonoWasmM2n - -#endif - -#endif // GD_MONO_WASM_M2N_H diff --git a/modules/mono/signal_awaiter_utils.cpp b/modules/mono/signal_awaiter_utils.cpp index b71e5f392b..55d2138674 100644 --- a/modules/mono/signal_awaiter_utils.cpp +++ b/modules/mono/signal_awaiter_utils.cpp @@ -32,7 +32,6 @@ #include "csharp_script.h" #include "mono_gd/gd_mono_cache.h" -#include "mono_gd/gd_mono_utils.h" Error gd_mono_connect_signal_awaiter(Object *p_source, const StringName &p_signal, Object *p_target, GCHandleIntPtr p_awaiter_handle_ptr) { ERR_FAIL_NULL_V(p_source, ERR_INVALID_DATA); @@ -104,14 +103,8 @@ void SignalAwaiterCallable::call(const Variant **p_arguments, int p_argcount, Va "Resumed after await, but class instance is gone."); #endif - MonoException *exc = nullptr; bool awaiter_is_null = false; - GDMonoCache::cached_data.methodthunk_SignalAwaiter_SignalCallback.invoke(awaiter_handle.get_intptr(), p_arguments, p_argcount, &awaiter_is_null, &exc); - - if (exc) { - GDMonoUtils::set_pending_exception(exc); - ERR_FAIL(); - } + GDMonoCache::managed_callbacks.SignalAwaiter_SignalCallback(awaiter_handle.get_intptr(), p_arguments, p_argcount, &awaiter_is_null); if (awaiter_is_null) { r_call_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL; @@ -192,16 +185,10 @@ void EventSignalCallable::call(const Variant **p_arguments, int p_argcount, Vari GCHandleIntPtr owner_gchandle_intptr = csharp_instance->get_gchandle_intptr(); - MonoException *exc = nullptr; bool awaiter_is_null = false; - GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_RaiseEventSignal.invoke( + GDMonoCache::managed_callbacks.ScriptManagerBridge_RaiseEventSignal( owner_gchandle_intptr, &event_signal_name, - p_arguments, p_argcount, &awaiter_is_null, &exc); - - if (exc) { - GDMonoUtils::set_pending_exception(exc); - ERR_FAIL(); - } + p_arguments, p_argcount, &awaiter_is_null); if (awaiter_is_null) { r_call_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL; diff --git a/modules/mono/utils/mono_reg_utils.cpp b/modules/mono/utils/mono_reg_utils.cpp deleted file mode 100644 index 8e37e6943c..0000000000 --- a/modules/mono/utils/mono_reg_utils.cpp +++ /dev/null @@ -1,242 +0,0 @@ -/*************************************************************************/ -/* mono_reg_utils.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "mono_reg_utils.h" -#include "core/io/dir_access.h" - -#ifdef WINDOWS_ENABLED - -#include "core/os/os.h" - -#define WIN32_LEAN_AND_MEAN -#include <windows.h> - -namespace MonoRegUtils { - -template <int> -REGSAM bitness_sam_impl(); - -template <> -REGSAM bitness_sam_impl<4>() { - return KEY_WOW64_64KEY; -} - -template <> -REGSAM bitness_sam_impl<8>() { - return KEY_WOW64_32KEY; -} - -REGSAM _get_bitness_sam() { - return bitness_sam_impl<sizeof(size_t)>(); -} - -LONG _RegOpenKey(HKEY hKey, LPCWSTR lpSubKey, PHKEY phkResult) { - LONG res = RegOpenKeyExW(hKey, lpSubKey, 0, KEY_READ, phkResult); - - if (res != ERROR_SUCCESS) { - res = RegOpenKeyExW(hKey, lpSubKey, 0, KEY_READ | _get_bitness_sam(), phkResult); - } - - return res; -} - -LONG _RegKeyQueryString(HKEY hKey, const String &p_value_name, String &r_value) { - Vector<WCHAR> buffer; - buffer.resize(512); - DWORD dwBufferSize = buffer.size(); - - LONG res = RegQueryValueExW(hKey, (LPCWSTR)(p_value_name.utf16().get_data()), 0, nullptr, (LPBYTE)buffer.ptr(), &dwBufferSize); - - if (res == ERROR_MORE_DATA) { - // dwBufferSize now contains the actual size - buffer.resize(dwBufferSize); - res = RegQueryValueExW(hKey, (LPCWSTR)(p_value_name.utf16().get_data()), 0, nullptr, (LPBYTE)buffer.ptr(), &dwBufferSize); - } - - if (res == ERROR_SUCCESS) { - r_value = String(buffer.ptr(), buffer.size()); - } else { - r_value = String(); - } - - return res; -} - -LONG _find_mono_in_reg(const String &p_subkey, MonoRegInfo &r_info, bool p_old_reg = false) { - HKEY hKey; - LONG res = _RegOpenKey(HKEY_LOCAL_MACHINE, (LPCWSTR)(p_subkey.utf16().get_data()), &hKey); - - if (res != ERROR_SUCCESS) { - goto cleanup; - } - - if (!p_old_reg) { - res = _RegKeyQueryString(hKey, "Version", r_info.version); - if (res != ERROR_SUCCESS) { - goto cleanup; - } - } - - res = _RegKeyQueryString(hKey, "SdkInstallRoot", r_info.install_root_dir); - if (res != ERROR_SUCCESS) { - goto cleanup; - } - - res = _RegKeyQueryString(hKey, "FrameworkAssemblyDirectory", r_info.assembly_dir); - if (res != ERROR_SUCCESS) { - goto cleanup; - } - - res = _RegKeyQueryString(hKey, "MonoConfigDir", r_info.config_dir); - if (res != ERROR_SUCCESS) { - goto cleanup; - } - - if (r_info.install_root_dir.ends_with("\\")) { - r_info.bin_dir = r_info.install_root_dir + "bin"; - } else { - r_info.bin_dir = r_info.install_root_dir + "\\bin"; - } - -cleanup: - RegCloseKey(hKey); - return res; -} - -LONG _find_mono_in_reg_old(const String &p_subkey, MonoRegInfo &r_info) { - String default_clr; - - HKEY hKey; - LONG res = _RegOpenKey(HKEY_LOCAL_MACHINE, (LPCWSTR)(p_subkey.utf16().get_data()), &hKey); - - if (res != ERROR_SUCCESS) { - goto cleanup; - } - - res = _RegKeyQueryString(hKey, "DefaultCLR", default_clr); - - if (res == ERROR_SUCCESS && default_clr.length()) { - r_info.version = default_clr; - res = _find_mono_in_reg(p_subkey + "\\" + default_clr, r_info, true); - } - -cleanup: - RegCloseKey(hKey); - return res; -} - -MonoRegInfo find_mono() { - MonoRegInfo info; - - if (_find_mono_in_reg("Software\\Mono", info) == ERROR_SUCCESS) { - return info; - } - - if (_find_mono_in_reg_old("Software\\Novell\\Mono", info) == ERROR_SUCCESS) { - return info; - } - - return MonoRegInfo(); -} - -String find_msbuild_tools_path() { - String msbuild_tools_path; - - // Try to find 15.0 with vswhere - - String vswhere_path = OS::get_singleton()->get_environment(sizeof(size_t) == 8 ? "ProgramFiles(x86)" : "ProgramFiles"); - vswhere_path += "\\Microsoft Visual Studio\\Installer\\vswhere.exe"; - - List<String> vswhere_args; - vswhere_args.push_back("-latest"); - vswhere_args.push_back("-products"); - vswhere_args.push_back("*"); - vswhere_args.push_back("-requires"); - vswhere_args.push_back("Microsoft.Component.MSBuild"); - - String output; - int exit_code; - OS::get_singleton()->execute(vswhere_path, vswhere_args, &output, &exit_code); - - if (exit_code == 0) { - Vector<String> lines = output.split("\n"); - - for (int i = 0; i < lines.size(); i++) { - const String &line = lines[i]; - int sep_idx = line.find(":"); - - if (sep_idx > 0) { - String key = line.substr(0, sep_idx); // No need to trim - - if (key == "installationPath") { - String val = line.substr(sep_idx + 1, line.length()).strip_edges(); - - ERR_BREAK(val.is_empty()); - - if (!val.ends_with("\\")) { - val += "\\"; - } - - // Since VS2019, the directory is simply named "Current" - String msbuild_dir = val + "MSBuild\\Current\\Bin"; - if (DirAccess::exists(msbuild_dir)) { - return msbuild_dir; - } - - // Directory name "15.0" is used in VS 2017 - return val + "MSBuild\\15.0\\Bin"; - } - } - } - } - - // Try to find 14.0 in the Registry - - HKEY hKey; - LONG res = _RegOpenKey(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\MSBuild\\ToolsVersions\\14.0", &hKey); - - if (res != ERROR_SUCCESS) { - goto cleanup; - } - - res = _RegKeyQueryString(hKey, "MSBuildToolsPath", msbuild_tools_path); - - if (res != ERROR_SUCCESS) { - goto cleanup; - } - -cleanup: - RegCloseKey(hKey); - - return msbuild_tools_path; -} -} // namespace MonoRegUtils - -#endif // WINDOWS_ENABLED diff --git a/modules/mono/utils/mono_reg_utils.h b/modules/mono/utils/mono_reg_utils.h deleted file mode 100644 index 5be60d4930..0000000000 --- a/modules/mono/utils/mono_reg_utils.h +++ /dev/null @@ -1,54 +0,0 @@ -/*************************************************************************/ -/* mono_reg_utils.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef MONO_REG_UTILS_H -#define MONO_REG_UTILS_H - -#ifdef WINDOWS_ENABLED - -#include "core/string/ustring.h" - -struct MonoRegInfo { - String version; - String install_root_dir; - String assembly_dir; - String config_dir; - String bin_dir; -}; - -namespace MonoRegUtils { - -MonoRegInfo find_mono(); -String find_msbuild_tools_path(); -} // namespace MonoRegUtils - -#endif // WINDOWS_ENABLED - -#endif // MONO_REG_UTILS_H |