diff options
Diffstat (limited to 'modules')
93 files changed, 2369 insertions, 5486 deletions
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 |