C#: Begin move to .NET Core

We're targeting .NET 5 for now to make development easier while
.NET 6 is not yet released.

TEMPORARY REGRESSIONS
---------------------

Assembly unloading is not implemented yet. As such, many Godot
resources are leaked at exit. This will be re-implemented later
together with assembly hot-reloading.
This commit is contained in:
Ignacio Roldán Etcheverry 2021-09-12 20:23:05 +02:00
parent 513ee857a9
commit f9a67ee9da
96 changed files with 2475 additions and 5615 deletions

View file

@ -1433,20 +1433,6 @@
<member name="memory/limits/multithreaded_server/rid_pool_prealloc" type="int" setter="" getter="" default="60">
This is used by servers when used in multi-threading mode (servers and visual). RIDs are preallocated to avoid stalling the server requesting them on threads. If servers get stalled too often when loading resources in a thread, increase this number.
</member>
<member name="mono/debugger_agent/port" type="int" setter="" getter="" default="23685">
</member>
<member name="mono/debugger_agent/wait_for_debugger" type="bool" setter="" getter="" default="false">
</member>
<member name="mono/debugger_agent/wait_timeout" type="int" setter="" getter="" default="3000">
</member>
<member name="mono/profiler/args" type="String" setter="" getter="" default="&quot;log:calls,alloc,sample,output=output.mlpd&quot;">
</member>
<member name="mono/profiler/enabled" type="bool" setter="" getter="" default="false">
</member>
<member name="mono/runtime/unhandled_exception_policy" type="int" setter="" getter="" default="0">
The policy to use for unhandled Mono (C#) exceptions. The default "Terminate Application" exits the project as soon as an unhandled exception is thrown. "Log Error" logs an error message to the console instead, and will not interrupt the project execution when an unhandled exception is thrown.
[b]Note:[/b] The unhandled exception policy is always set to "Log Error" in the editor, which also includes C# [code]tool[/code] scripts running within the editor as well as editor plugin code.
</member>
<member name="navigation/2d/default_cell_size" type="int" setter="" getter="" default="1">
Default cell size for 2D navigation maps. See [method NavigationServer2D.map_set_cell_size].
</member>

View file

@ -2255,18 +2255,6 @@ bool Main::start() {
ERR_FAIL_COND_V_MSG(da.is_null(), false, "Argument supplied to --doctool must be a valid directory path.");
}
#ifndef MODULE_MONO_ENABLED
// Hack to define Mono-specific project settings even on non-Mono builds,
// so that we don't lose their descriptions and default values in DocData.
// Default values should be synced with mono_gd/gd_mono.cpp.
GLOBAL_DEF("mono/debugger_agent/port", 23685);
GLOBAL_DEF("mono/debugger_agent/wait_for_debugger", false);
GLOBAL_DEF("mono/debugger_agent/wait_timeout", 3000);
GLOBAL_DEF("mono/profiler/args", "log:calls,alloc,sample,output=output.mlpd");
GLOBAL_DEF("mono/profiler/enabled", false);
GLOBAL_DEF("mono/runtime/unhandled_exception_policy", 0);
#endif
Error err;
DocTools doc;
doc.generate(doc_base);

View file

@ -818,18 +818,21 @@ def generate_vs_project(env, num_jobs):
module_configs = ModuleConfigs()
if env.get("module_mono_enabled"):
import modules.mono.build_scripts.mono_reg_utils as mono_reg
import modules.mono.build_scripts.mono_configure as mono_configure
mono_root = env.get("mono_prefix") or mono_reg.find_mono_root_dir(env["bits"])
if mono_root:
app_host_dir = mono_configure.find_dotnet_app_host_dir(env)
if app_host_dir and os.path.isdir(app_host_dir):
mono_defines = [("NETHOST_USE_AS_STATIC",)]
if env["tools"]:
mono_defines += [("GD_MONO_HOT_RELOAD",)]
module_configs.add_mode(
"mono",
includes=os.path.join(mono_root, "include", "mono-2.0"),
cli_args="module_mono_enabled=yes mono_glue=yes",
defines=[("MONO_GLUE_ENABLED",)],
includes=app_host_dir,
cli_args="module_mono_enabled=yes",
defines=mono_defines,
)
else:
print("Mono installation directory not found. Generated project will not have build variants for Mono.")
print(".NET App Host directory not found. Generated project will not have build variants for .NET.")
env["MSVSBUILDCOM"] = module_configs.build_commandline("scons")
env["MSVSREBUILDCOM"] = module_configs.build_commandline("scons vsproj=yes")

View file

@ -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)

View file

@ -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)
)

View file

@ -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>

View file

@ -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"])
app_host_dir = find_dotnet_app_host_dir(env)
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)
# 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.
# 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")
env.Append(LIBPATH=[app_host_dir])
env_mono.Prepend(CPPPATH=app_host_dir)
libnethost_path = os.path.join(app_host_dir, "libnethost.lib" if os.name == "nt" else "libnethost.a")
if env["platform"] == "windows":
mono_root = mono_prefix
env_mono.Append(CPPDEFINES=["NETHOST_USE_AS_STATIC"])
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"))
lib_suffixes = [".lib"]
if not env.msvc:
# MingW supports both '.a' and '.lib'
lib_suffixes.insert(0, ".a")
if mono_static:
if env.msvc:
mono_static_lib_name = "libmono-static-sgen"
else:
mono_static_lib_name = "libmonosgen-2.0"
mono_static_lib_file = find_file_in_dir(mono_lib_path, [mono_static_lib_name], extensions=lib_suffixes)
if not mono_static_lib_file:
raise RuntimeError("Could not find static mono library in: " + mono_lib_path)
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"])
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
# is_macos = is_apple and not is_ios
sharedlib_ext = ".dylib" if is_apple else ".so"
# if is_ios and not is_ios_sim:
# env_mono.Append(CPPDEFINES=["IOS_DEVICE"])
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)
if is_apple:
env.Append(LINKFLAGS=["-Wl,-force_load," + libnethost_path])
else:
assert not mono_static
# TODO: Add option to force using pkg-config
print("Mono root directory not found. Using pkg-config instead")
env.ParseConfig("pkg-config monosgen-2 --libs")
env_mono.ParseConfig("pkg-config monosgen-2 --cflags")
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
if not mono_so_file:
raise RuntimeError("Could not find mono shared library in: " + str(tmpenv["LIBPATH"]))
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)
if not tools_enabled:
if is_desktop(env["platform"]):
if not mono_root:
mono_root = (
subprocess.check_output(["pkg-config", "mono-2", "--variable=prefix"]).decode("utf8").strip()
)
make_template_dir(env, mono_root)
elif is_android:
# Compress Android Mono Config
from . import make_android_mono_config
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/")
# 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
if copy_mono_root:
if not mono_root:
mono_root = subprocess.check_output(["pkg-config", "mono-2", "--variable=prefix"]).decode("utf8").strip()
if tools_enabled:
# Only supported for editor builds.
copy_mono_root_files(env, mono_root, mono_bcl)
env.Append(LINKFLAGS=["-Wl,-whole-archive", libnethost_path, "-Wl,-no-whole-archive"])
def make_template_dir(env, mono_root):
from shutil import rmtree
def find_dotnet_app_host_dir(env):
dotnet_root = env["dotnet_root"]
platform = env["platform"]
target = env["target"]
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")
template_dir_name = ""
print("Found .NET Core Sdk root directory: " + dotnet_root)
assert is_desktop(platform)
dotnet_cmd = os.path.join(dotnet_root, "dotnet.exe" if os.name == "nt" else "dotnet")
template_dir_name = "data.mono.%s.%s.%s" % (platform, env["bits"], target)
runtime_identifier = determine_runtime_identifier(env)
output_dir = Dir("#bin").abspath
template_dir = os.path.join(output_dir, template_dir_name)
# 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)
template_mono_root_dir = os.path.join(template_dir, "Mono")
app_host_dir = os.path.join(
dotnet_root,
"packs",
"Microsoft.NETCore.App.Host." + runtime_identifier,
app_host_version,
"runtimes",
runtime_identifier,
"native",
)
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)
return app_host_dir
def copy_mono_root_files(env, mono_root, mono_bcl):
from glob import glob
from shutil import copy
from shutil import rmtree
def determine_runtime_identifier(env):
names_map = {
"windows": "win",
"macos": "osx",
"linuxbsd": "linux",
"server": "linux", # FIXME: Is server linux only, or also macos?
}
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)
# 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")
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")
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 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)
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
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")
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 copy_mono_shared_libs(env, mono_root, target_mono_root_dir):
from shutil import copy
def copy_if_exists(src, dst):
if os.path.isfile(src):
copy(src, dst)
# .NET RID architectures: x86, x64, arm, or arm64
platform = env["platform"]
if platform == "windows":
src_mono_bin_dir = os.path.join(mono_root, "bin")
target_mono_bin_dir = os.path.join(target_mono_root_dir, "bin")
if not os.path.isdir(target_mono_bin_dir):
os.makedirs(target_mono_bin_dir)
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)
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:
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)
raise NotImplementedError()
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, "..")
def find_app_host_version(dotnet_cmd, search_version):
import subprocess
try:
lines = subprocess.check_output([dotnet_cmd, "--list-runtimes"]).splitlines()
for line_bytes in lines:
line = line_bytes.decode("utf-8")
if not line.startswith("Microsoft.NETCore.App "):
continue
parts = line.split(" ")
if len(parts) < 2:
continue
version = parts[1]
# Look for 6.0.0 or 6.0.0-*
if version.startswith(search_version + "."):
return version
except (subprocess.CalledProcessError, OSError):
pass
return ""
ENV_PATH_SEP = ";" if os.name == "nt" else ":"
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
for dir in search_dirs:
path = os.path.join(dir, name)
if is_windows:
for extension in windows_exts:
path_with_ext = path + extension
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
return ""

View file

@ -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 ""

View file

@ -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 [

View file

@ -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();
// Load GodotTools and initialize GodotSharpEditor
// Initialize GodotSharpEditor
MonoClass *editor_klass = mono_class_from_name(
GDMono::get_singleton()->get_tools_assembly()->get_image(),
"GodotTools", "GodotSharpEditor");
CRASH_COND(editor_klass == nullptr);
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);
bool ok = GDMonoCache::managed_callbacks.CSharpInstanceBridge_Call(
gchandle.get_intptr(), &method, nullptr, 0, &call_error, &ret);
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)));
// 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);
}
}

View file

@ -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,8 +471,11 @@ 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);

View file

@ -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>

View file

@ -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

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>

View file

@ -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; }

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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);

View file

@ -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;

View file

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<Import Project="GenerateGodotNupkgsVersions.targets" />
</Project>

View file

@ -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}"

View file

@ -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);

View file

@ -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;

View file

@ -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?

View file

@ -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))

View file

@ -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);
}
}

View file

@ -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)
{

View file

@ -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>

View file

@ -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();

View file

@ -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

View file

@ -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);
}
}
}

View file

@ -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);
}
}

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -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;

View file

@ -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);
});
}

View file

@ -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;

View file

@ -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,
};

View file

@ -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

View file

@ -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

View file

@ -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>

View file

@ -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
}
}

View file

@ -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;
}
}
}

View file

@ -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

View file

@ -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);
}

View file

@ -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;
if (godotObject == null)
throw new InvalidOperationException();
var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue(
NativeFuncs.godotsharp_string_name_new_copy(name));
if (godotObject.InternalGodotScriptGetFieldOrPropViaReflection(nameManaged.ToString(),
out godot_variant outRet))
try
{
*r_retValue = outRet;
return true;
// 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();
var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue(
NativeFuncs.godotsharp_string_name_new_copy(name));
if (godotObject.InternalGodotScriptGetFieldOrPropViaReflection(nameManaged.ToString(),
out godot_variant outRetValue))
{
*outRet = outRetValue;
return true.ToGodotBool();
}
object ret = godotObject._Get(nameManaged);
if (ret == null)
{
*outRet = default;
return false.ToGodotBool();
}
*outRet = Marshaling.mono_object_to_variant(ret);
return true.ToGodotBool();
}
object ret = godotObject._Get(nameManaged);
if (ret == null)
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;
}
}
}

View file

@ -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);
}
}
}
}

View file

@ -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
};
}
}
}

View file

@ -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);
obj.NativePtr = godotObject;
var ctor = scriptType
.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Where(c => c.GetParameters().Length == argCount)
.FirstOrDefault();
if (ctor == null)
try
{
if (argCount == 0)
// 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;
var ctor = scriptType
.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Where(c => c.GetParameters().Length == argCount)
.FirstOrDefault();
if (ctor == null)
{
throw new MissingMemberException(
$"Cannot create script instance. The class '{scriptType.FullName}' does not define a parameterless constructor.");
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.");
}
}
else
var parameters = ctor.GetParameters();
int paramCount = parameters.Length;
object[] invokeParams = new object[paramCount];
for (int i = 0; i < paramCount; i++)
{
throw new MissingMemberException(
$"The class '{scriptType.FullName}' does not define a constructor that takes x parameters.");
invokeParams[i] = Marshaling.variant_to_mono_object_of_type(
args[i], parameters[i].ParameterType);
}
ctor.Invoke(obj, invokeParams);
return true.ToGodotBool();
}
var parameters = ctor.GetParameters();
int paramCount = parameters.Length;
object[] invokeParams = new object[paramCount];
for (int i = 0; i < paramCount; i++)
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 field = native?.GetField("NativeName", BindingFlags.DeclaredOnly | BindingFlags.Static |
BindingFlags.Public | BindingFlags.NonPublic);
if (field == null)
{
*outRes = default;
return;
}
var nativeName = (StringName)field.GetValue(null);
if (nativeName == null)
{
*outRes = default;
return;
}
*outRes = NativeFuncs.godotsharp_string_name_new_copy(nativeName.NativeValue);
}
var native = Object.InternalGetClassNativeBase(scriptType);
var field = native?.GetField("NativeName", BindingFlags.DeclaredOnly | BindingFlags.Static |
BindingFlags.Public | BindingFlags.NonPublic);
if (field == null)
catch (Exception e)
{
*r_res = default;
return;
ExceptionUtils.DebugUnhandledException(e);
*outRes = default;
}
var nativeName = (StringName)field.GetValue(null);
*r_res = NativeFuncs.godotsharp_string_name_new_copy(nativeName.NativeValue);
}
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;
}
*outOwnerIsNull = false.ToGodotBool();
owner.InternalRaiseEventSignal(eventSignalName, args, argCount);
}
catch (Exception e)
{
ExceptionUtils.DebugPrintUnhandledException(e);
*outOwnerIsNull = false.ToGodotBool();
}
*r_ownerIsNull = false;
owner.InternalRaiseEventSignal(eventSignalName, args, argCount);
}
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();
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");
if (invokeMethod == null)
throw new MissingMethodException(signalDelegate.FullName, "Invoke");
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()
{
{ "name", parameters.Name },
{ "type", paramType },
{ "nil_is_variant", nilIsVariant }
});
}
signals.Add(signalDelegate.Name, signalParams);
}
// 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 fields = top.GetFields(
BindingFlags.DeclaredOnly | BindingFlags.Instance |
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");
if (invokeMethod == null)
throw new MissingMethodException(delegateType.FullName, "Invoke");
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()
{
{ "name", parameters.Name },
{ "type", paramType },
{ "nil_is_variant", nilIsVariant }
});
}
signals.Add(eventSignalField.Name, signalParams);
}
top = top.BaseType;
}
*r_retSignals = NativeFuncs.godotsharp_dictionary_new_copy(signals.NativeValue);
}
internal static unsafe 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);
Type top = _scriptBridgeMap[scriptPtr];
Type native = Object.InternalGetClassNativeBase(top);
while (top != null && top != native)
{
// 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;
}
// 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;
}
top = top.BaseType;
}
return false;
}
internal static unsafe bool HasMethodUnknownParams(IntPtr scriptPtr, godot_string* method, 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)
{
Type top = scriptType;
Type native = Object.InternalGetClassNativeBase(scriptType);
Type top = _scriptBridgeMap[scriptPtr];
Type native = Object.InternalGetClassNativeBase(top);
while (top != null && top != native)
{
var methodInfo = top.GetMethod(methodStr,
// Legacy signals
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");
if (invokeMethod == null)
throw new MissingMethodException(signalDelegate.FullName, "Invoke");
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()
{
{ "name", parameters.Name },
{ "type", paramType },
{ "nil_is_variant", nilIsVariant }
});
}
signals.Add(signalDelegate.Name, signalParams);
}
// 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 fields = top.GetFields(
BindingFlags.DeclaredOnly | BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public);
if (methodInfo != null)
return true;
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");
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()
{
{ "name", parameters.Name },
{ "type", paramType },
{ "nil_is_variant", nilIsVariant }
});
}
signals.Add(eventSignalField.Name, signalParams);
}
top = top.BaseType;
}
return false;
*outRetSignals = NativeFuncs.godotsharp_dictionary_new_copy(signals.NativeValue);
}
else
catch (Exception e)
{
var methodInfo = scriptType.GetMethod(methodStr, BindingFlags.DeclaredOnly | BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public);
return methodInfo != null;
ExceptionUtils.DebugUnhandledException(e);
*outRetSignals = NativeFuncs.godotsharp_dictionary_new();
}
}
internal static bool ScriptIsOrInherits(IntPtr scriptPtr, IntPtr scriptPtrMaybeBase)
[UnmanagedCallersOnly]
internal static unsafe godot_bool HasScriptSignal(IntPtr scriptPtr, godot_string* signalName)
{
if (!_scriptBridgeMap.TryGetValue(scriptPtr, out var scriptType))
return false;
try
{
// Performance is not critical here as this will be replaced with source generators.
using var signals = new Dictionary();
if (!_scriptBridgeMap.TryGetValue(scriptPtrMaybeBase, out var maybeBaseType))
return false;
string signalNameStr = Marshaling.mono_string_from_godot(*signalName);
return scriptType == maybeBaseType || maybeBaseType.IsAssignableFrom(scriptType);
Type top = _scriptBridgeMap[scriptPtr];
Type native = Object.InternalGetClassNativeBase(top);
while (top != null && top != native)
{
// 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
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;
}
return false.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 HasMethodUnknownParams(IntPtr scriptPtr, godot_string* method,
godot_bool deep)
{
string scriptPathStr = Marshaling.mono_string_from_godot(*scriptPath);
try
{
// Performance is not critical here as this will be replaced with source generators.
if (!_scriptBridgeMap.TryGetValue(scriptPtr, out var scriptType))
return false.ToGodotBool();
if (!_scriptLookupMap.TryGetValue(scriptPathStr, out var lookupInfo))
return false;
string methodStr = Marshaling.mono_string_from_godot(*method);
_scriptBridgeMap.Add(scriptPtr, lookupInfo.ScriptType);
if (deep.ToBool())
{
Type top = scriptType;
Type native = Object.InternalGetClassNativeBase(scriptType);
return true;
while (top != null && top != native)
{
var methodInfo = top.GetMethod(methodStr,
BindingFlags.DeclaredOnly | BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public);
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();
top = top.BaseType;
}
return false.ToGodotBool();
}
else
{
var methodInfo = scriptType.GetMethod(methodStr, BindingFlags.DeclaredOnly | BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public);
return (methodInfo != null).ToGodotBool();
}
}
catch (Exception e)
{
ExceptionUtils.DebugUnhandledException(e);
return false.ToGodotBool();
}
}
[UnmanagedCallersOnly]
internal static godot_bool ScriptIsOrInherits(IntPtr scriptPtr, IntPtr scriptPtrMaybeBase)
{
try
{
if (!_scriptBridgeMap.TryGetValue(scriptPtr, out var scriptType))
return false.ToGodotBool();
if (!_scriptBridgeMap.TryGetValue(scriptPtrMaybeBase, out var maybeBaseType))
return false.ToGodotBool();
return (scriptType == maybeBaseType || maybeBaseType.IsAssignableFrom(scriptType)).ToGodotBool();
}
catch (Exception e)
{
ExceptionUtils.DebugUnhandledException(e);
return false.ToGodotBool();
}
}
[UnmanagedCallersOnly]
internal static unsafe godot_bool AddScriptBridge(IntPtr scriptPtr, godot_string* scriptPath)
{
try
{
string scriptPathStr = Marshaling.mono_string_from_godot(*scriptPath);
if (!_scriptLookupMap.TryGetValue(scriptPathStr, out var lookupInfo))
return false.ToGodotBool();
_scriptBridgeMap.Add(scriptPtr, lookupInfo.ScriptType);
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();
if (!*r_tool && scriptType.IsNested)
try
{
*r_tool = scriptType.DeclaringType?.GetCustomAttributes(inherit: false)
.OfType<ToolAttribute>()
.Any() ?? false;
_scriptBridgeMap.Remove(scriptPtr);
}
if (!*r_tool && scriptType.Assembly.GetName().Name == "GodotTools")
*r_tool = true;
// RPC functions
Dictionary<string, Dictionary> rpcFunctions = new();
Type top = _scriptBridgeMap[scriptPtr];
Type native = Object.InternalGetClassNativeBase(top);
while (top != null && top != native)
catch (Exception e)
{
foreach (var method in top.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public))
{
if (method.IsStatic)
continue;
string methodName = method.Name;
if (rpcFunctions.ContainsKey(methodName))
continue;
var rpcAttr = method.GetCustomAttributes(inherit: false)
.OfType<RPCAttribute>().FirstOrDefault();
if (rpcAttr == null)
continue;
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;
rpcFunctions.Add(methodName, rpcConfig);
}
top = top.BaseType;
ExceptionUtils.DebugUnhandledException(e);
}
*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 void UpdateScriptClassInfo(IntPtr scriptPtr, godot_bool* outTool,
godot_dictionary* outRpcFunctionsDest)
{
var oldGCHandle = GCHandle.FromIntPtr(oldGCHandlePtr);
object target = oldGCHandle.Target;
if (target == null)
try
{
oldGCHandle.Free();
*r_newGCHandlePtr = IntPtr.Zero;
return false; // Called after the managed side was collected, so nothing to do here
// 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().ToGodotBool();
if (!(*outTool).ToBool() && scriptType.IsNested)
{
*outTool = (scriptType.DeclaringType?.GetCustomAttributes(inherit: false)
.OfType<ToolAttribute>()
.Any() ?? false).ToGodotBool();
}
if (!(*outTool).ToBool() && scriptType.Assembly.GetName().Name == "GodotTools")
*outTool = true.ToGodotBool();
// RPC functions
Dictionary<string, Dictionary> rpcFunctions = new();
Type top = _scriptBridgeMap[scriptPtr];
Type native = Object.InternalGetClassNativeBase(top);
while (top != null && top != native)
{
foreach (var method in top.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public))
{
if (method.IsStatic)
continue;
string methodName = method.Name;
if (rpcFunctions.ContainsKey(methodName))
continue;
var rpcAttr = method.GetCustomAttributes(inherit: false)
.OfType<RPCAttribute>().FirstOrDefault();
if (rpcAttr == null)
continue;
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;
rpcFunctions.Add(methodName, rpcConfig);
}
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();
}
}
// Release the current weak handle and replace it with a strong handle.
var newGCHandle = GCHandle.Alloc(target, createWeak ? GCHandleType.Weak : GCHandleType.Normal);
[UnmanagedCallersOnly]
internal static unsafe godot_bool SwapGCHandleForType(IntPtr oldGCHandlePtr, IntPtr* outNewGCHandlePtr,
godot_bool createWeak)
{
try
{
var oldGCHandle = GCHandle.FromIntPtr(oldGCHandlePtr);
oldGCHandle.Free();
*r_newGCHandlePtr = GCHandle.ToIntPtr(newGCHandle);
return true;
object target = oldGCHandle.Target;
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.ToBool() ? GCHandleType.Weak : GCHandleType.Normal);
oldGCHandle.Free();
*outNewGCHandlePtr = GCHandle.ToIntPtr(newGCHandle);
return true.ToGodotBool();
}
catch (Exception e)
{
ExceptionUtils.DebugUnhandledException(e);
*outNewGCHandlePtr = IntPtr.Zero;
return false.ToGodotBool();
}
}
}
}

View file

@ -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();

View file

@ -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];
var parameterInfos = @delegate.Method.GetParameters();
var paramsLength = parameterInfos.Length;
if (argc != paramsLength)
try
{
throw new InvalidOperationException(
$"The delegate expects {paramsLength} arguments, but received {argc}.");
}
// TODO: Optimize
var @delegate = (Delegate)GCHandle.FromIntPtr(delegateGCHandle).Target;
var managedArgs = new object[argc];
for (uint i = 0; i < argc; i++)
var parameterInfos = @delegate!.Method.GetParameters();
var paramsLength = parameterInfos.Length;
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);
}
object invokeRet = @delegate.DynamicInvoke(managedArgs);
*outRet = Marshaling.mono_object_to_variant(invokeRet);
}
catch (Exception e)
{
managedArgs[i] = Marshaling.variant_to_mono_object_of_type(
args[i], parameterInfos[i].ParameterType);
ExceptionUtils.DebugPrintUnhandledException(e);
*outRet = default;
}
object invokeRet = @delegate.DynamicInvoke(managedArgs);
*ret = Marshaling.mono_object_to_variant(invokeRet);
}
// TODO: Check if we should be using BindingFlags.DeclaredOnly (would give better reflection performance).

View file

@ -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;
}

View file

@ -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;

View file

@ -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);
}
}

View file

@ -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);
}

View file

@ -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);
}
}
}
}

View file

@ -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)]

View file

@ -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);

View file

@ -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();
var godotDict = new Collections.Dictionary();
// 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]);
foreach (KeyValuePair<object, object> entry in (IDictionary)p_obj)
godotDict.Add(entry.Key, entry.Value);
var genericGodotDictionary = (Collections.IGenericGodotDictionary)method
.Invoke(null, new[] { p_obj });
#endif
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;

View file

@ -20,21 +20,62 @@ 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
[DllImport(GodotDllName)]
public static extern IntPtr godotsharp_get_class_constructor(ref godot_string_name p_classname);
[DllImport(GodotDllName)]
public static extern IntPtr godotsharp_invoke_class_constructor(IntPtr p_creation_func);
#endif
public static extern delegate* unmanaged<IntPtr> godotsharp_get_class_constructor(
ref godot_string_name p_classname);
[DllImport(GodotDllName)]
public static extern IntPtr godotsharp_engine_get_singleton(godot_string* p_name);
[DllImport(GodotDllName)]
internal static extern void godotsharp_internal_object_disposed(IntPtr ptr);
[DllImport(GodotDllName)]
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)]

View file

@ -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 ?

View file

@ -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>

View file

@ -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);
}
}

View file

@ -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;
}
*outAwaiterIsNull = false.ToGodotBool();
awaiter._completed = true;
object[] signalArgs = new object[argCount];
for (int i = 0; i < argCount; i++)
signalArgs[i] = Marshaling.variant_to_mono_object(args[i]);
awaiter._result = signalArgs;
awaiter._action?.Invoke();
}
catch (Exception e)
{
ExceptionUtils.DebugPrintUnhandledException(e);
*outAwaiterIsNull = false.ToGodotBool();
}
*r_awaiterIsNull = false;
awaiter._completed = true;
object[] signalArgs = new object[argCount];
for (int i = 0; i < argCount; i++)
signalArgs[i] = Marshaling.variant_to_mono_object(args[i]);
awaiter._result = signalArgs;
awaiter._action?.Invoke();
}
}
}

View file

@ -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" />

View file

@ -1,3 +1,4 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("GodotSharpEditor")]
[assembly: InternalsVisibleTo("GodotPlugins")]

View file

@ -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>

View file

@ -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);
}

View file

@ -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

View file

@ -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,

View file

@ -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();
}
}

View file

@ -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"

View file

@ -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);
}

View file

@ -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 *));

View file

@ -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
mono_wasm_load_runtime(vfs_prefix, enable_debugging);
return mono_get_root_domain();
}
#else
MonoDomain *gd_initialize_mono_runtime() {
gd_mono_debug_init();
#if defined(IOS_ENABLED) || defined(ANDROID_ENABLED)
// I don't know whether this actually matters or not
const char *runtime_version = "mobile";
#else
const char *runtime_version = "v4.0.30319";
#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
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;
// 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)
// 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.
#if defined(WINDOWS_ENABLED) || defined(UNIX_ENABLED)
String path_var("PATH");
String path_value = OS::get_singleton()->get_environment(path_var);
#ifdef WINDOWS_ENABLED
path_value += ';';
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;
}
#ifdef _WIN32
static_assert(sizeof(char_t) == sizeof(char16_t));
using HostFxrCharString = Char16String;
#define HOSTFXR_STR(m_str) L##m_str
#else
if (DirAccess::exists(bundled_bin_dir)) {
path_value += bundled_bin_dir;
}
#endif // TOOLS_ENABLED
static_assert(sizeof(char_t) == sizeof(char));
using HostFxrCharString = CharString;
#define HOSTFXR_STR(m_str) m_str
#endif
HostFxrCharString str_to_hostfxr(const String &p_str) {
#ifdef _WIN32
return p_str.utf16();
#else
path_value += ':';
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?
}
#endif // WINDOWS_ENABLED
OS::get_singleton()->set_environment(path_var, path_value);
#endif // WINDOWS_ENABLED || UNIX_ENABLED
return p_str.utf8();
#endif
}
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();
if (mono_reg_info.assembly_dir.length() && DirAccess::exists(mono_reg_info.assembly_dir)) {
r_assembly_rootdir = mono_reg_info.assembly_dir;
}
if (mono_reg_info.config_dir.length() && DirAccess::exists(mono_reg_info.config_dir)) {
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
if (DirAccess::exists(bundled_assembly_rootdir)) {
r_assembly_rootdir = bundled_assembly_rootdir;
}
if (DirAccess::exists(bundled_config_dir)) {
r_config_dir = bundled_config_dir;
}
#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
String str_from_hostfxr(const char_t *p_buffer) {
#ifdef _WIN32
return String::utf16((const char16_t *)p_buffer);
#else
// Export templates always use the bundled directories
r_assembly_rootdir = bundled_assembly_rootdir;
r_config_dir = bundled_config_dir;
return String::utf8((const char *)p_buffer);
#endif
}
const char_t *get_data(const HostFxrCharString &p_char_str) {
return (const char_t *)p_char_str.get_data();
}
String find_hostfxr() {
const int HostApiBufferTooSmall = 0x80008098;
size_t buffer_size = 0;
int rc = get_hostfxr_path(nullptr, &buffer_size, nullptr);
if (rc == HostApiBufferTooSmall) {
// Pre-allocate a large buffer for the path to hostfxr
Vector<char_t> buffer;
buffer.resize(buffer_size);
rc = get_hostfxr_path(buffer.ptrw(), &buffer_size, nullptr);
if (rc != 0) {
return String();
}
return str_from_hostfxr(buffer.ptr());
}
return String();
}
// Forward declarations
bool load_hostfxr() {
String hostfxr_path = find_hostfxr();
if (hostfxr_path.is_empty()) {
return false;
}
print_verbose("Found hostfxr: " + hostfxr_path);
void *lib = nullptr;
Error err = OS::get_singleton()->open_dynamic_library(hostfxr_path, lib);
// TODO: Clean up lib handle when shutting down
if (err != OK) {
return false;
}
void *symbol = nullptr;
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;
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;
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;
return (hostfxr_initialize_for_runtime_config &&
hostfxr_get_runtime_delegate &&
hostfxr_close);
}
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");
}
void *load_assembly_and_get_function_pointer = nullptr;
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");
}
hostfxr_close(cxt);
return (load_assembly_and_get_function_pointer_fn)load_assembly_and_get_function_pointer;
}
} // namespace
static bool _on_core_api_assembly_loaded() {
if (!GDMonoCache::godot_api_cache_updated) {
return false;
}
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
return true;
}
void GDMono::initialize() {
ERR_FAIL_NULL(Engine::get_singleton());
print_verbose("Mono: Initializing module...");
char *runtime_build_info = mono_get_runtime_build_info();
print_verbose("Mono JIT compiler version " + String(runtime_build_info));
mono_free(runtime_build_info);
print_verbose(".NET: Initializing module...");
_init_godot_api_hashes();
_init_exception_policy();
GDMonoLog::get_singleton()->initialize();
#if !defined(JAVASCRIPT_ENABLED)
String assembly_rootdir;
String config_dir;
determine_mono_dirs(assembly_rootdir, config_dir);
// Leak if we call mono_set_dirs more than once
mono_set_dirs(assembly_rootdir.length() ? assembly_rootdir.utf8().get_data() : nullptr,
config_dir.length() ? config_dir.utf8().get_data() : nullptr);
add_mono_shared_libs_dir_to_path();
#endif
#ifdef ANDROID_ENABLED
mono_config_parse_memory(get_godot_android_mono_config().utf8().get_data());
#else
mono_config_parse(nullptr);
#endif
#if defined(ANDROID_ENABLED)
gdmono::android::support::initialize();
#elif defined(IOS_ENABLED)
gdmono::ios::support::initialize();
#endif
GDMonoAssembly::initialize();
#if !defined(JAVASCRIPT_ENABLED)
gd_mono_profiler_init();
#endif
mono_install_unhandled_exception_hook(&unhandled_exception_hook, nullptr);
#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;
if (!load_hostfxr()) {
ERR_FAIL_MSG(".NET: Failed to load hostfxr");
}
#endif
#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");
}
#endif
auto config_path = str_to_hostfxr(GodotSharpDirs::get_api_assemblies_dir()
.plus_file("GodotPlugins.runtimeconfig.json"));
// NOTE: Internal calls must be registered after the Mono runtime initialization.
// Otherwise registration fails with the error: 'assertion 'hash != nullptr' failed'.
root_domain = gd_initialize_mono_runtime();
ERR_FAIL_NULL_MSG(root_domain, "Mono: Failed to initialize runtime.");
GDMonoUtils::set_main_thread(GDMonoUtils::get_current_thread());
#if !defined(JAVASCRIPT_ENABLED)
gd_mono_setup_runtime_main_args(); // Required for System.Environment.GetCommandLineArgs
#endif
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);
runtime_initialized = true;
print_verbose("Mono: Runtime initialized");
print_verbose(".NET: hostfxr initialized");
#if defined(ANDROID_ENABLED)
gdmono::android::support::register_internal_calls();
#endif
auto godot_plugins_path = str_to_hostfxr(GodotSharpDirs::get_api_assemblies_dir()
.plus_file("GodotPlugins.dll"));
// 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.");
using godot_plugins_initialize_fn = bool (*)(bool, PluginCallbacks *, GDMonoCache::ManagedCallbacks *);
godot_plugins_initialize_fn godot_plugins_initialize = nullptr;
#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
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");
_register_internal_calls();
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");
print_verbose("Mono: INITIALIZED");
GDMonoCache::update_godot_api_cache(managed_callbacks);
plugin_callbacks = aux_plugin_callbacks;
print_verbose(".NET: GodotPlugins 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);
}

View file

@ -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();
};

View file

@ -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();
}
}

View file

@ -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

View file

@ -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;
ManagedCallbacks managed_callbacks;
bool godot_api_cache_updated = false;
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);
}
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.")
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);
}
#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)
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."); \
}
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);
#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)
managed_callbacks = p_managed_callbacks;
#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;
godot_api_cache_updated = true;
}
} // namespace GDMonoCache

View file

@ -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.
#ifdef WIN32
#define GD_CLR_STDCALL __stdcall
#else
#define GD_CLR_STDCALL
#endif
GDMonoMethodThunk<GCHandleIntPtr, const Variant **, int, bool *> methodthunk_SignalAwaiter_SignalCallback;
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 *)();
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;
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

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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