Mono/C#: WebAssembly support

This commit is contained in:
Ignacio Etcheverry 2019-11-10 17:10:38 +01:00
parent 14e52f7aee
commit de7c2ad21b
37 changed files with 1318 additions and 988 deletions

View file

@ -44,9 +44,33 @@ def copy_file(src_dir, dst_dir, name):
copy(src_path, dst_dir)
def is_desktop(platform):
return platform in ['windows', 'osx', 'x11', 'server', 'uwp', 'haiku']
def is_unix_like(platform):
return platform in ['osx', 'x11', 'server', 'android', 'haiku']
def module_supports_tools_on(platform):
return platform not in ['android', 'javascript']
def find_wasm_src_dir(mono_root):
hint_dirs = [
os.path.join(mono_root, 'src'),
os.path.join(mono_root, '../src'),
]
for hint_dir in hint_dirs:
if os.path.isfile(os.path.join(hint_dir, 'driver.c')):
return hint_dir
return ''
def configure(env, env_mono):
bits = env['bits']
is_android = env['platform'] == 'android'
is_javascript = env['platform'] == 'javascript'
tools_enabled = env['tools']
mono_static = env['mono_static']
@ -63,17 +87,21 @@ def configure(env, env_mono):
env_mono.Append(CPPDEFINES=['NO_PENDING_EXCEPTIONS'])
if is_android and not env['android_arch'] in android_arch_dirs:
raise RuntimeError('This module does not support for the specified \'android_arch\': ' + env['android_arch'])
raise RuntimeError('This module does not support the specified \'android_arch\': ' + env['android_arch'])
if is_android and tools_enabled:
# TODO: Implement this. We have to add the data directory to the apk, concretely the Api and Tools folders.
raise RuntimeError('This module does not currently support building for android with tools enabled')
if tools_enabled and not module_supports_tools_on(env['platform']):
# TODO:
# Android: We have to add the data directory to the apk, concretely the Api and Tools folders.
raise RuntimeError('This module does not currently support building for this platform with tools enabled')
if is_android and mono_static:
# When static linking and doing something that requires libmono-native, we get a dlopen error as libmono-native seems to depend on libmonosgen-2.0
raise RuntimeError('Linking Mono statically is not currently supported on Android')
# Android: When static linking and doing something that requires libmono-native, we get a dlopen error as libmono-native seems to depend on libmonosgen-2.0
raise RuntimeError('Statically linking Mono is not currently supported on this platform')
if (os.getenv('MONO32_PREFIX') or os.getenv('MONO64_PREFIX')) and not mono_prefix:
if is_javascript:
mono_static = True
if not mono_prefix and (os.getenv('MONO32_PREFIX') or os.getenv('MONO64_PREFIX')):
print("WARNING: The environment variables 'MONO32_PREFIX' and 'MONO64_PREFIX' are deprecated; use the 'mono_prefix' SCons parameter instead")
if env['platform'] == 'windows':
@ -143,7 +171,7 @@ def configure(env, env_mono):
mono_lib_path = ''
mono_so_name = ''
if not mono_root and is_android:
if not mono_root and (is_android or is_javascript):
raise RuntimeError("Mono installation directory not found; specify one manually with the 'mono_prefix' SCons parameter")
if not mono_root and is_apple:
@ -167,7 +195,7 @@ def configure(env, env_mono):
mono_lib_path = os.path.join(mono_root, 'lib')
env.Append(LIBPATH=mono_lib_path)
env.Append(LIBPATH=[mono_lib_path])
env_mono.Prepend(CPPPATH=os.path.join(mono_root, 'include', 'mono-2.0'))
mono_lib = find_file_in_dir(mono_lib_path, mono_lib_names, prefix='lib', extension='.a')
@ -183,7 +211,30 @@ def configure(env, env_mono):
if is_apple:
env.Append(LINKFLAGS=['-Wl,-force_load,' + mono_lib_file])
else:
assert is_desktop(env['platform']) or is_android or is_javascript
env.Append(LINKFLAGS=['-Wl,-whole-archive', mono_lib_file, '-Wl,-no-whole-archive'])
if is_javascript:
env.Append(LIBS=['mono-icall-table', 'mono-native', 'mono-ilgen', 'mono-ee-interp'])
wasm_src_dir = os.path.join(mono_root, 'src')
if not os.path.isdir(wasm_src_dir):
raise RuntimeError('Could not find mono wasm src directory')
# Ideally this should be defined only for 'driver.c', but I can't fight scons for another 2 hours
env_mono.Append(CPPDEFINES=['CORE_BINDINGS'])
env_mono.add_source_files(env.modules_sources, [
os.path.join(wasm_src_dir, 'driver.c'),
os.path.join(wasm_src_dir, 'zlib-helper.c'),
os.path.join(wasm_src_dir, 'corebindings.c')
])
env.Append(LINKFLAGS=[
'--js-library', os.path.join(wasm_src_dir, 'library_mono.js'),
'--js-library', os.path.join(wasm_src_dir, 'binding_support.js'),
'--js-library', os.path.join(wasm_src_dir, 'dotnet_support.js')
])
else:
env.Append(LIBS=[mono_lib])
@ -191,6 +242,8 @@ def configure(env, env_mono):
env.Append(LIBS=['iconv', 'pthread'])
elif is_android:
pass # Nothing
elif is_javascript:
env.Append(LIBS=['m', 'rt', 'dl', 'pthread'])
else:
env.Append(LIBS=['m', 'rt', 'dl', 'pthread'])
@ -230,19 +283,22 @@ def configure(env, env_mono):
env.Append(LINKFLAGS='-rdynamic')
if not tools_enabled and not is_android:
if not mono_root:
mono_root = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip()
if not tools_enabled:
if is_desktop(env['platform']):
if not mono_root:
mono_root = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip()
make_template_dir(env, mono_root)
elif not tools_enabled and is_android:
# Compress Android Mono Config
from . import make_android_mono_config
config_file_path = os.path.join(mono_root, 'etc', 'mono', 'config')
make_android_mono_config.generate_compressed_config(config_file_path, 'mono_gd/')
make_template_dir(env, mono_root)
elif is_android:
# Compress Android Mono Config
from . import make_android_mono_config
config_file_path = os.path.join(mono_root, 'etc', 'mono', 'config')
make_android_mono_config.generate_compressed_config(config_file_path, 'mono_gd/')
# Copy the required shared libraries
copy_mono_shared_libs(env, mono_root, None)
# Copy the required shared libraries
copy_mono_shared_libs(env, mono_root, None)
elif is_javascript:
pass # No data directory for this platform
if copy_mono_root:
if not mono_root:
@ -251,7 +307,7 @@ def configure(env, env_mono):
if tools_enabled:
copy_mono_root_files(env, mono_root)
else:
print("Ignoring option: 'copy_mono_root'. Only available for builds with 'tools' enabled.")
print("Ignoring option: 'copy_mono_root'; only available for builds with 'tools' enabled.")
def make_template_dir(env, mono_root):
@ -262,10 +318,9 @@ def make_template_dir(env, mono_root):
template_dir_name = ''
if platform in ['windows', 'osx', 'x11', 'android', 'server']:
template_dir_name = 'data.mono.%s.%s.%s' % (platform, env['bits'], target)
else:
assert False
assert is_desktop(platform)
template_dir_name = 'data.mono.%s.%s.%s' % (platform, env['bits'], target)
output_dir = Dir('#bin').abspath
template_dir = os.path.join(output_dir, template_dir_name)
@ -278,7 +333,7 @@ def make_template_dir(env, mono_root):
# Copy etc/mono/
template_mono_config_dir = os.path.join(template_mono_root_dir, 'etc', 'mono')
copy_mono_etc_dir(mono_root, template_mono_config_dir, env['platform'])
copy_mono_etc_dir(mono_root, template_mono_config_dir, platform)
# Copy the required shared libraries
@ -386,7 +441,7 @@ def copy_mono_shared_libs(env, mono_root, target_mono_root_dir):
if platform == 'osx':
# TODO: Make sure nothing is missing
copy(os.path.join(mono_root, 'lib', 'libMonoPosixHelper.dylib'), target_mono_lib_dir)
elif platform == 'x11' or platform == 'android' or platform == 'server':
elif is_unix_like(platform):
lib_file_names = [lib_name + '.so' for lib_name in [
'libmono-btls-shared', 'libmono-ee-interp', 'libmono-native', 'libMonoPosixHelper',
'libmono-profiler-aot', 'libmono-profiler-coverage', 'libmono-profiler-log', 'libMonoSupportW'

View file

@ -1,70 +0,0 @@
diff --git a/libgc/include/private/gcconfig.h b/libgc/include/private/gcconfig.h
index e2bdf13ac3e..f962200ba4e 100644
--- a/libgc/include/private/gcconfig.h
+++ b/libgc/include/private/gcconfig.h
@@ -2255,6 +2255,14 @@
# define GETPAGESIZE() getpagesize()
# endif
+#if defined(HOST_ANDROID) && !(__ANDROID_API__ >= 23) \
+ && ((defined(MIPS) && (CPP_WORDSZ == 32)) \
+ || defined(ARM32) || defined(I386) /* but not x32 */)
+ /* tkill() exists only on arm32/mips(32)/x86. */
+ /* NDK r11+ deprecates tkill() but keeps it for Mono clients. */
+# define USE_TKILL_ON_ANDROID
+#endif
+
# if defined(SUNOS5) || defined(DRSNX) || defined(UTS4)
/* OS has SVR4 generic features. Probably others also qualify. */
# define SVR4
diff --git a/libgc/pthread_stop_world.c b/libgc/pthread_stop_world.c
index f93ce26b562..4a49a6d578c 100644
--- a/libgc/pthread_stop_world.c
+++ b/libgc/pthread_stop_world.c
@@ -336,7 +336,7 @@ void GC_push_all_stacks()
pthread_t GC_stopping_thread;
int GC_stopping_pid;
-#ifdef HOST_ANDROID
+#ifdef USE_TKILL_ON_ANDROID
static
int android_thread_kill(pid_t tid, int sig)
{
diff --git a/mono/metadata/threads.c b/mono/metadata/threads.c
index ad9b8823f8f..3542b32b540 100644
--- a/mono/metadata/threads.c
+++ b/mono/metadata/threads.c
@@ -77,8 +77,12 @@ mono_native_thread_join_handle (HANDLE thread_handle, gboolean close_handle);
#include <zircon/syscalls.h>
#endif
-#if defined(HOST_ANDROID) && !defined(TARGET_ARM64) && !defined(TARGET_AMD64)
-#define USE_TKILL_ON_ANDROID 1
+#if defined(HOST_ANDROID) && !(__ANDROID_API__ >= 23) \
+ && ((defined(MIPS) && (CPP_WORDSZ == 32)) \
+ || defined(ARM32) || defined(I386) /* but not x32 */)
+ /* tkill() exists only on arm32/mips(32)/x86. */
+ /* NDK r11+ deprecates tkill() but keeps it for Mono clients. */
+# define USE_TKILL_ON_ANDROID
#endif
#ifdef HOST_ANDROID
diff --git a/mono/utils/mono-threads-posix.c b/mono/utils/mono-threads-posix.c
index 3e4bf93de5f..79c9f731fe7 100644
--- a/mono/utils/mono-threads-posix.c
+++ b/mono/utils/mono-threads-posix.c
@@ -31,8 +31,12 @@
#include <errno.h>
-#if defined(HOST_ANDROID) && !defined(TARGET_ARM64) && !defined(TARGET_AMD64)
-#define USE_TKILL_ON_ANDROID 1
+#if defined(HOST_ANDROID) && !(__ANDROID_API__ >= 23) \
+ && ((defined(MIPS) && (CPP_WORDSZ == 32)) \
+ || defined(ARM32) || defined(I386) /* but not x32 */)
+ /* tkill() exists only on arm32/mips(32)/x86. */
+ /* NDK r11+ deprecates tkill() but keeps it for Mono clients. */
+# define USE_TKILL_ON_ANDROID
#endif
#ifdef USE_TKILL_ON_ANDROID

View file

@ -1,10 +1,11 @@
def can_build(env, platform):
if platform in ['javascript']:
return False # Not yet supported
return True
def configure(env):
if env['platform'] not in ['windows', 'osx', 'x11', 'server', 'android', 'haiku', 'javascript']:
raise RuntimeError('This module does not currently support building for this platform')
env.use_ptrcall = True
env.add_module_version_string('mono')
@ -18,6 +19,13 @@ def configure(env):
envvars.Add(BoolVariable('xbuild_fallback', 'If MSBuild is not found, fallback to xbuild', False))
envvars.Update(env)
if env['platform'] == 'javascript':
# Mono wasm already has zlib builtin, so we need this workaround to avoid symbol collisions
print('Compiling with Mono wasm disables \'builtin_zlib\'')
env['builtin_zlib'] = False
thirdparty_zlib_dir = "#thirdparty/zlib/"
env.Prepend(CPPPATH=[thirdparty_zlib_dir])
def get_doc_classes():
return [

View file

@ -50,8 +50,10 @@
#include "editor/editor_internal_calls.h"
#include "godotsharp_dirs.h"
#include "mono_gd/gd_mono_cache.h"
#include "mono_gd/gd_mono_class.h"
#include "mono_gd/gd_mono_marshal.h"
#include "mono_gd/gd_mono_utils.h"
#include "signal_awaiter_utils.h"
#include "utils/macros.h"
#include "utils/mutex_utils.h"
@ -545,7 +547,7 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info()
#ifdef DEBUG_ENABLED
_TLS_RECURSION_GUARD_V_(Vector<StackInfo>());
if (!gdmono->is_runtime_initialized() || !GDMono::get_singleton()->get_core_api_assembly() || !GDMonoUtils::mono_cache.corlib_cache_updated)
if (!gdmono->is_runtime_initialized() || !GDMono::get_singleton()->get_core_api_assembly() || !GDMonoCache::cached_data.corlib_cache_updated)
return Vector<StackInfo>();
MonoObject *stack_trace = mono_object_new(mono_domain_get(), CACHED_CLASS(System_Diagnostics_StackTrace)->get_mono_ptr());
@ -571,7 +573,7 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObjec
MonoException *exc = NULL;
MonoArray *frames = invoke_method_thunk(CACHED_METHOD_THUNK(System_Diagnostics_StackTrace, GetFrames), p_stack_trace, &exc);
MonoArray *frames = CACHED_METHOD_THUNK(System_Diagnostics_StackTrace, GetFrames).invoke(p_stack_trace, &exc);
if (exc) {
GDMonoUtils::debug_print_unhandled_exception(exc);
@ -583,8 +585,6 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObjec
if (frame_count <= 0)
return Vector<StackInfo>();
GDMonoUtils::DebugUtils_StackFrameInfo get_sf_info = CACHED_METHOD_THUNK(DebuggingUtils, GetStackFrameInfo);
Vector<StackInfo> si;
si.resize(frame_count);
@ -595,7 +595,7 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObjec
MonoString *file_name;
int file_line_num;
MonoString *method_decl;
invoke_method_thunk(get_sf_info, frame, &file_name, &file_line_num, &method_decl, &exc);
CACHED_METHOD_THUNK(DebuggingUtils, GetStackFrameInfo).invoke(frame, &file_name, &file_line_num, &method_decl, &exc);
if (exc) {
GDMonoUtils::debug_print_unhandled_exception(exc);
@ -618,14 +618,14 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObjec
void CSharpLanguage::frame() {
if (gdmono && gdmono->is_runtime_initialized() && gdmono->get_core_api_assembly() != NULL) {
const Ref<MonoGCHandle> &task_scheduler_handle = GDMonoUtils::mono_cache.task_scheduler_handle;
const Ref<MonoGCHandle> &task_scheduler_handle = GDMonoCache::cached_data.task_scheduler_handle;
if (task_scheduler_handle.is_valid()) {
MonoObject *task_scheduler = task_scheduler_handle->get_target();
if (task_scheduler) {
MonoException *exc = NULL;
invoke_method_thunk(CACHED_METHOD_THUNK(GodotTaskScheduler, Activate), task_scheduler, &exc);
CACHED_METHOD_THUNK(GodotTaskScheduler, Activate).invoke(task_scheduler, &exc);
if (exc) {
GDMonoUtils::debug_unhandled_exception(exc);
@ -1079,7 +1079,7 @@ bool CSharpLanguage::overrides_external_editor() {
void CSharpLanguage::thread_enter() {
#if 0
if (mono->is_runtime_initialized()) {
if (gdmono->is_runtime_initialized()) {
GDMonoUtils::attach_current_thread();
}
#endif
@ -1088,7 +1088,7 @@ void CSharpLanguage::thread_enter() {
void CSharpLanguage::thread_exit() {
#if 0
if (mono->is_runtime_initialized()) {
if (gdmono->is_runtime_initialized()) {
GDMonoUtils::detach_current_thread();
}
#endif

View file

@ -91,7 +91,7 @@ namespace GodotTools.Build
{
var result = new List<string>();
if (OS.IsOSX())
if (OS.IsOSX)
{
result.Add("/Library/Frameworks/Mono.framework/Versions/Current/bin/");
result.Add("/usr/local/var/homebrew/linked/mono/bin/");

View file

@ -0,0 +1,211 @@
using Godot;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using GodotTools.Core;
using GodotTools.Internals;
using Directory = GodotTools.Utils.Directory;
using File = GodotTools.Utils.File;
using OS = GodotTools.Utils.OS;
using Path = System.IO.Path;
namespace GodotTools.Export
{
public class ExportPlugin : EditorExportPlugin
{
private void AddFile(string srcPath, string dstPath, bool remap = false)
{
AddFile(dstPath, File.ReadAllBytes(srcPath), remap);
}
public override void _ExportFile(string path, string type, string[] features)
{
base._ExportFile(path, type, features);
if (type != Internal.CSharpLanguageType)
return;
if (Path.GetExtension(path) != $".{Internal.CSharpLanguageExtension}")
throw new ArgumentException($"Resource of type {Internal.CSharpLanguageType} has an invalid file extension: {path}", nameof(path));
// TODO What if the source file is not part of the game's C# project
bool includeScriptsContent = (bool) ProjectSettings.GetSetting("mono/export/include_scripts_content");
if (!includeScriptsContent)
{
// We don't want to include the source code on exported games
AddFile(path, new byte[] { }, remap: false);
Skip();
}
}
public override void _ExportBegin(string[] features, bool isDebug, string path, int flags)
{
base._ExportBegin(features, isDebug, path, flags);
try
{
_ExportBeginImpl(features, isDebug, path, flags);
// TODO: Handle _ExportBeginImpl return value. Do something on error once _ExportBegin supports failing.
}
catch (Exception e)
{
GD.PushError($"Failed to export project: {e.Message}");
Console.Error.WriteLine(e);
// TODO: Do something on error once _ExportBegin supports failing.
}
}
private bool _ExportBeginImpl(string[] features, bool isDebug, string path, int flags)
{
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
return true;
string platform = DeterminePlatformFromFeatures(features);
if (platform == null)
throw new NotSupportedException("Target platform not supported");
// TODO Right now there is no way to stop the export process with an error
string buildConfig = isDebug ? "Debug" : "Release";
string scriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, $"scripts_metadata.{(isDebug ? "debug" : "release")}");
CsProjOperations.GenerateScriptsMetadata(GodotSharpDirs.ProjectCsProjPath, scriptsMetadataPath);
AddFile(scriptsMetadataPath, scriptsMetadataPath);
// Turn export features into defines
var godotDefines = features;
if (!BuildManager.BuildProjectBlocking(buildConfig, godotDefines))
{
GD.PushError("Failed to build project");
return false;
}
// Add dependency assemblies
var dependencies = new Godot.Collections.Dictionary<string, string>();
var projectDllName = (string) ProjectSettings.GetSetting("application/config/name");
if (projectDllName.Empty())
{
projectDllName = "UnnamedProject";
}
string projectDllSrcDir = Path.Combine(GodotSharpDirs.ResTempAssembliesBaseDir, buildConfig);
string projectDllSrcPath = Path.Combine(projectDllSrcDir, $"{projectDllName}.dll");
dependencies[projectDllName] = projectDllSrcPath;
{
string templatesDir = Internal.FullTemplatesDir;
string platformBclDir = Path.Combine(templatesDir, $"{platform}-bcl", platform);
string customBclDir = Directory.Exists(platformBclDir) ? platformBclDir : string.Empty;
internal_GetExportedAssemblyDependencies(projectDllName, projectDllSrcPath, buildConfig, customBclDir, dependencies);
}
string apiConfig = isDebug ? "Debug" : "Release";
string resAssembliesDir = Path.Combine(GodotSharpDirs.ResAssembliesBaseDir, apiConfig);
foreach (var dependency in dependencies)
{
string dependSrcPath = dependency.Value;
string dependDstPath = Path.Combine(resAssembliesDir, dependSrcPath.GetFile());
AddFile(dependSrcPath, dependDstPath);
}
// Mono specific export template extras (data dir)
ExportDataDirectory(features, platform, isDebug, path);
return true;
}
private static void ExportDataDirectory(ICollection<string> features, string platform, bool debug, string path)
{
if (!PlatformHasTemplateDir(platform))
return;
string bits = features.Contains("64") ? "64" : "32";
string target = debug ? "release_debug" : "release";
string TemplateDirName() => $"data.mono.{platform}.{bits}.{target}";
string templateDirPath = Path.Combine(Internal.FullTemplatesDir, TemplateDirName());
if (!Directory.Exists(templateDirPath))
{
templateDirPath = null;
if (debug)
{
target = "debug"; // Support both 'release_debug' and 'debug' for the template data directory name
templateDirPath = Path.Combine(Internal.FullTemplatesDir, TemplateDirName());
if (!Directory.Exists(templateDirPath))
templateDirPath = null;
}
}
if (templateDirPath == null)
throw new FileNotFoundException("Data template directory not found");
string outputDir = new FileInfo(path).Directory?.FullName ??
throw new FileNotFoundException("Base directory not found");
string outputDataDir = Path.Combine(outputDir, DataDirName);
if (Directory.Exists(outputDataDir))
Directory.Delete(outputDataDir, recursive: true); // Clean first
Directory.CreateDirectory(outputDataDir);
foreach (string dir in Directory.GetDirectories(templateDirPath, "*", SearchOption.AllDirectories))
{
Directory.CreateDirectory(Path.Combine(outputDataDir, dir.Substring(templateDirPath.Length + 1)));
}
foreach (string file in Directory.GetFiles(templateDirPath, "*", SearchOption.AllDirectories))
{
File.Copy(file, Path.Combine(outputDataDir, file.Substring(templateDirPath.Length + 1)));
}
}
private static bool PlatformHasTemplateDir(string platform)
{
// OSX export templates are contained in a zip, so we place our custom template inside it and let Godot do the rest.
return !new[] {OS.Platforms.OSX, OS.Platforms.Android, OS.Platforms.HTML5}.Contains(platform);
}
private static string DeterminePlatformFromFeatures(IEnumerable<string> features)
{
foreach (var feature in features)
{
if (OS.PlatformNameMap.TryGetValue(feature, out string platform))
return platform;
}
return null;
}
private static string DataDirName
{
get
{
var appName = (string) ProjectSettings.GetSetting("application/config/name");
string appNameSafe = appName.ToSafeDirName(allowDirSeparator: false);
return $"data_{appNameSafe}";
}
}
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void internal_GetExportedAssemblyDependencies(string projectDllName, string projectDllSrcPath,
string buildConfig, string customBclDir, Godot.Collections.Dictionary<string, string> dependencies);
}
}

View file

@ -1,4 +1,5 @@
using Godot;
using GodotTools.Export;
using GodotTools.Utils;
using System;
using System.Collections.Generic;
@ -225,7 +226,7 @@ namespace GodotTools
bool osxAppBundleInstalled = false;
if (OS.IsOSX())
if (OS.IsOSX)
{
// The package path is '/Applications/Visual Studio Code.app'
const string vscodeBundleId = "com.microsoft.VSCode";
@ -265,7 +266,7 @@ namespace GodotTools
string command;
if (OS.IsOSX())
if (OS.IsOSX)
{
if (!osxAppBundleInstalled && _vsCodePath.Empty())
{
@ -420,7 +421,7 @@ namespace GodotTools
settingsHintStr += $",MonoDevelop:{(int) ExternalEditorId.MonoDevelop}" +
$",Visual Studio Code:{(int) ExternalEditorId.VsCode}";
}
else if (OS.IsOSX())
else if (OS.IsOSX)
{
settingsHintStr += $",Visual Studio:{(int) ExternalEditorId.VisualStudioForMac}" +
$",MonoDevelop:{(int) ExternalEditorId.MonoDevelop}" +
@ -441,7 +442,7 @@ namespace GodotTools
});
// Export plugin
var exportPlugin = new GodotSharpExport();
var exportPlugin = new ExportPlugin();
AddExportPlugin(exportPlugin);
exportPluginWeak = WeakRef(exportPlugin);
@ -461,7 +462,7 @@ namespace GodotTools
// Otherwise, if the GC disposes it at a later time, EditorExportPlatformAndroid
// will be freed after EditorSettings already was, and its device polling thread
// will try to access the EditorSettings singleton, resulting in null dereferencing.
(exportPluginWeak.GetRef() as GodotSharpExport)?.Dispose();
(exportPluginWeak.GetRef() as ExportPlugin)?.Dispose();
exportPluginWeak.Dispose();
}

View file

@ -1,197 +0,0 @@
using Godot;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using GodotTools.Core;
using GodotTools.Internals;
using Directory = GodotTools.Utils.Directory;
using File = GodotTools.Utils.File;
using Path = System.IO.Path;
namespace GodotTools
{
public class GodotSharpExport : EditorExportPlugin
{
private void AddFile(string srcPath, string dstPath, bool remap = false)
{
AddFile(dstPath.Replace("\\", "/"), File.ReadAllBytes(srcPath), remap);
}
public override void _ExportFile(string path, string type, string[] features)
{
base._ExportFile(path, type, features);
if (type != Internal.CSharpLanguageType)
return;
if (Path.GetExtension(path) != $".{Internal.CSharpLanguageExtension}")
throw new ArgumentException($"Resource of type {Internal.CSharpLanguageType} has an invalid file extension: {path}", nameof(path));
// TODO What if the source file is not part of the game's C# project
bool includeScriptsContent = (bool) ProjectSettings.GetSetting("mono/export/include_scripts_content");
if (!includeScriptsContent)
{
// We don't want to include the source code on exported games
AddFile(path, new byte[] { }, remap: false);
Skip();
}
}
public override void _ExportBegin(string[] features, bool isDebug, string path, int flags)
{
base._ExportBegin(features, isDebug, path, flags);
try
{
_ExportBeginImpl(features, isDebug, path, flags);
}
catch (Exception e)
{
GD.PushError($"Failed to export project. Exception message: {e.Message}");
Console.Error.WriteLine(e);
}
}
public void _ExportBeginImpl(string[] features, bool isDebug, string path, int flags)
{
// TODO Right now there is no way to stop the export process with an error
if (File.Exists(GodotSharpDirs.ProjectSlnPath))
{
string buildConfig = isDebug ? "Debug" : "Release";
string scriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, $"scripts_metadata.{(isDebug ? "debug" : "release")}");
CsProjOperations.GenerateScriptsMetadata(GodotSharpDirs.ProjectCsProjPath, scriptsMetadataPath);
AddFile(scriptsMetadataPath, scriptsMetadataPath);
// Turn export features into defines
var godotDefines = features;
if (!BuildManager.BuildProjectBlocking(buildConfig, godotDefines))
{
GD.PushError("Failed to build project");
return;
}
// Add dependency assemblies
var dependencies = new Godot.Collections.Dictionary<string, string>();
var projectDllName = (string) ProjectSettings.GetSetting("application/config/name");
if (projectDllName.Empty())
{
projectDllName = "UnnamedProject";
}
string projectDllSrcDir = Path.Combine(GodotSharpDirs.ResTempAssembliesBaseDir, buildConfig);
string projectDllSrcPath = Path.Combine(projectDllSrcDir, $"{projectDllName}.dll");
dependencies[projectDllName] = projectDllSrcPath;
{
string templatesDir = Internal.FullTemplatesDir;
string androidBclDir = Path.Combine(templatesDir, "android-bcl");
string customLibDir = features.Contains("Android") && Directory.Exists(androidBclDir) ? androidBclDir : string.Empty;
GetExportedAssemblyDependencies(projectDllName, projectDllSrcPath, buildConfig, customLibDir, dependencies);
}
string apiConfig = isDebug ? "Debug" : "Release";
string resAssembliesDir = Path.Combine(GodotSharpDirs.ResAssembliesBaseDir, apiConfig);
foreach (var dependency in dependencies)
{
string dependSrcPath = dependency.Value;
string dependDstPath = Path.Combine(resAssembliesDir, dependSrcPath.GetFile());
AddFile(dependSrcPath, dependDstPath);
}
}
// Mono specific export template extras (data dir)
ExportDataDirectory(features, isDebug, path);
}
private static void ExportDataDirectory(IEnumerable<string> features, bool debug, string path)
{
var featureSet = new HashSet<string>(features);
if (!PlatformHasTemplateDir(featureSet))
return;
string templateDirName = "data.mono";
if (featureSet.Contains("Windows"))
{
templateDirName += ".windows";
templateDirName += featureSet.Contains("64") ? ".64" : ".32";
}
else if (featureSet.Contains("X11"))
{
templateDirName += ".x11";
templateDirName += featureSet.Contains("64") ? ".64" : ".32";
}
else
{
throw new NotSupportedException("Target platform not supported");
}
templateDirName += debug ? ".release_debug" : ".release";
string templateDirPath = Path.Combine(Internal.FullTemplatesDir, templateDirName);
if (!Directory.Exists(templateDirPath))
throw new FileNotFoundException("Data template directory not found");
string outputDir = new FileInfo(path).Directory?.FullName ??
throw new FileNotFoundException("Base directory not found");
string outputDataDir = Path.Combine(outputDir, DataDirName);
if (Directory.Exists(outputDataDir))
Directory.Delete(outputDataDir, recursive: true); // Clean first
Directory.CreateDirectory(outputDataDir);
foreach (string dir in Directory.GetDirectories(templateDirPath, "*", SearchOption.AllDirectories))
{
Directory.CreateDirectory(Path.Combine(outputDataDir, dir.Substring(templateDirPath.Length + 1)));
}
foreach (string file in Directory.GetFiles(templateDirPath, "*", SearchOption.AllDirectories))
{
File.Copy(file, Path.Combine(outputDataDir, file.Substring(templateDirPath.Length + 1)));
}
}
private static bool PlatformHasTemplateDir(IEnumerable<string> featureSet)
{
// OSX export templates are contained in a zip, so we place
// our custom template inside it and let Godot do the rest.
return !featureSet.Any(f => new[] {"OSX", "Android"}.Contains(f));
}
private static string DataDirName
{
get
{
var appName = (string) ProjectSettings.GetSetting("application/config/name");
string appNameSafe = appName.ToSafeDirName(allowDirSeparator: false);
return $"data_{appNameSafe}";
}
}
private static void GetExportedAssemblyDependencies(string projectDllName, string projectDllSrcPath,
string buildConfig, string customLibDir, Godot.Collections.Dictionary<string, string> dependencies) =>
internal_GetExportedAssemblyDependencies(projectDllName, projectDllSrcPath, buildConfig, customLibDir, dependencies);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void internal_GetExportedAssemblyDependencies(string projectDllName, string projectDllSrcPath,
string buildConfig, string customLibDir, Godot.Collections.Dictionary<string, string> dependencies);
}
}

View file

@ -40,6 +40,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Build\MsBuildFinder.cs" />
<Compile Include="Export\ExportPlugin.cs" />
<Compile Include="ExternalEditorId.cs" />
<Compile Include="Ides\GodotIdeManager.cs" />
<Compile Include="Ides\GodotIdeServer.cs" />
@ -63,7 +64,6 @@
<Compile Include="BuildInfo.cs" />
<Compile Include="BuildTab.cs" />
<Compile Include="BottomPanel.cs" />
<Compile Include="GodotSharpExport.cs" />
<Compile Include="CsProjOperations.cs" />
<Compile Include="Utils\CollectionExtensions.cs" />
</ItemGroup>

View file

@ -79,7 +79,7 @@ namespace GodotTools.Ides
{
MonoDevelop.Instance GetMonoDevelopInstance(string solutionPath)
{
if (Utils.OS.IsOSX() && editor == ExternalEditorId.VisualStudioForMac)
if (Utils.OS.IsOSX && editor == ExternalEditorId.VisualStudioForMac)
{
vsForMacInstance = vsForMacInstance ??
new MonoDevelop.Instance(solutionPath, MonoDevelop.EditorId.VisualStudioForMac);

View file

@ -24,7 +24,7 @@ namespace GodotTools.Ides.MonoDevelop
string command;
if (OS.IsOSX())
if (OS.IsOSX)
{
string bundleId = BundleIds[editorId];
@ -81,7 +81,7 @@ namespace GodotTools.Ides.MonoDevelop
public Instance(string solutionFile, EditorId editorId)
{
if (editorId == EditorId.VisualStudioForMac && !OS.IsOSX())
if (editorId == EditorId.VisualStudioForMac && !OS.IsOSX)
throw new InvalidOperationException($"{nameof(EditorId.VisualStudioForMac)} not supported on this platform");
this.solutionFile = solutionFile;
@ -93,7 +93,7 @@ namespace GodotTools.Ides.MonoDevelop
static Instance()
{
if (OS.IsOSX())
if (OS.IsOSX)
{
ExecutableNames = new Dictionary<EditorId, string>
{

View file

@ -1,56 +1,78 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
namespace GodotTools.Utils
{
[SuppressMessage("ReSharper", "InconsistentNaming")]
public static class OS
{
[MethodImpl(MethodImplOptions.InternalCall)]
extern static string GetPlatformName();
static extern string GetPlatformName();
const string HaikuName = "Haiku";
const string OSXName = "OSX";
const string ServerName = "Server";
const string UWPName = "UWP";
const string WindowsName = "Windows";
const string X11Name = "X11";
public static bool IsHaiku()
public static class Names
{
return HaikuName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
public const string Windows = "Windows";
public const string OSX = "OSX";
public const string X11 = "X11";
public const string Server = "Server";
public const string UWP = "UWP";
public const string Haiku = "Haiku";
public const string Android = "Android";
public const string HTML5 = "HTML5";
}
public static bool IsOSX()
public static class Platforms
{
return OSXName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
public const string Windows = "windows";
public const string OSX = "osx";
public const string X11 = "x11";
public const string Server = "server";
public const string UWP = "uwp";
public const string Haiku = "haiku";
public const string Android = "android";
public const string HTML5 = "javascript";
}
public static bool IsServer()
public static readonly Dictionary<string, string> PlatformNameMap = new Dictionary<string, string>
{
return ServerName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
[Names.Windows] = Platforms.Windows,
[Names.OSX] = Platforms.OSX,
[Names.X11] = Platforms.X11,
[Names.Server] = Platforms.Server,
[Names.UWP] = Platforms.UWP,
[Names.Haiku] = Platforms.Haiku,
[Names.Android] = Platforms.Android,
[Names.HTML5] = Platforms.HTML5
};
private static bool IsOS(string name)
{
return name.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
}
public static bool IsUWP()
{
return UWPName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
}
public static bool IsWindows() => IsOS(Names.Windows);
public static bool IsWindows()
{
return WindowsName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
}
public static bool IsOSX => IsOS(Names.OSX);
public static bool IsX11()
{
return X11Name.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
}
public static bool IsX11 => IsOS(Names.X11);
public static bool IsServer => IsOS(Names.Server);
public static bool IsUWP => IsOS(Names.UWP);
public static bool IsHaiku => IsOS(Names.Haiku);
public static bool IsAndroid => IsOS(Names.Android);
public static bool IsHTML5 => IsOS(Names.HTML5);
private static bool? _isUnixCache;
private static readonly string[] UnixPlatforms = {HaikuName, OSXName, ServerName, X11Name};
private static readonly string[] UnixPlatforms = {Names.OSX, Names.X11, Names.Server, Names.Haiku, Names.Android};
public static bool IsUnix()
{

View file

@ -219,15 +219,15 @@ int32_t godot_icall_ScriptClassParser_ParseFile(MonoString *p_filepath, MonoObje
return err;
}
uint32_t godot_icall_GodotSharpExport_GetExportedAssemblyDependencies(MonoString *p_project_dll_name, MonoString *p_project_dll_src_path,
MonoString *p_build_config, MonoString *p_custom_lib_dir, MonoObject *r_dependencies) {
uint32_t godot_icall_ExportPlugin_GetExportedAssemblyDependencies(MonoString *p_project_dll_name, MonoString *p_project_dll_src_path,
MonoString *p_build_config, MonoString *p_custom_bcl_dir, MonoObject *r_dependencies) {
String project_dll_name = GDMonoMarshal::mono_string_to_godot(p_project_dll_name);
String project_dll_src_path = GDMonoMarshal::mono_string_to_godot(p_project_dll_src_path);
String build_config = GDMonoMarshal::mono_string_to_godot(p_build_config);
String custom_lib_dir = GDMonoMarshal::mono_string_to_godot(p_custom_lib_dir);
String custom_bcl_dir = GDMonoMarshal::mono_string_to_godot(p_custom_bcl_dir);
Dictionary dependencies = GDMonoMarshal::mono_object_to_variant(r_dependencies);
return GodotSharpExport::get_exported_assembly_dependencies(project_dll_name, project_dll_src_path, build_config, custom_lib_dir, dependencies);
return GodotSharpExport::get_exported_assembly_dependencies(project_dll_name, project_dll_src_path, build_config, custom_bcl_dir, dependencies);
}
MonoString *godot_icall_Internal_UpdateApiAssembliesFromPrebuilt(MonoString *p_config) {
@ -411,8 +411,8 @@ void register_editor_internal_calls() {
// ScriptClassParser
mono_add_internal_call("GodotTools.Internals.ScriptClassParser::internal_ParseFile", (void *)godot_icall_ScriptClassParser_ParseFile);
// GodotSharpExport
mono_add_internal_call("GodotTools.GodotSharpExport::internal_GetExportedAssemblyDependencies", (void *)godot_icall_GodotSharpExport_GetExportedAssemblyDependencies);
// ExportPlugin
mono_add_internal_call("GodotTools.Export.ExportPlugin::internal_GetExportedAssemblyDependencies", (void *)godot_icall_ExportPlugin_GetExportedAssemblyDependencies);
// Internals
mono_add_internal_call("GodotTools.Internals.Internal::internal_UpdateApiAssembliesFromPrebuilt", (void *)godot_icall_Internal_UpdateApiAssembliesFromPrebuilt);

View file

@ -36,6 +36,7 @@
#include "../mono_gd/gd_mono.h"
#include "../mono_gd/gd_mono_assembly.h"
#include "../mono_gd/gd_mono_cache.h"
namespace GodotSharpExport {

View file

@ -36,6 +36,7 @@
#include "core/string_name.h"
#include "../csharp_script.h"
#include "../mono_gd/gd_mono_cache.h"
#include "../mono_gd/gd_mono_class.h"
#include "../mono_gd/gd_mono_internals.h"
#include "../mono_gd/gd_mono_utils.h"

View file

@ -34,6 +34,7 @@
#include <mono/metadata/exception.h>
#include "../mono_gd/gd_mono_cache.h"
#include "../mono_gd/gd_mono_class.h"
#include "../mono_gd/gd_mono_utils.h"

View file

@ -39,6 +39,7 @@
#include "core/variant.h"
#include "core/variant_parser.h"
#include "../mono_gd/gd_mono_cache.h"
#include "../mono_gd/gd_mono_utils.h"
MonoObject *godot_icall_GD_bytes2var(MonoArray *p_bytes, MonoBoolean p_allow_objects) {
@ -211,7 +212,7 @@ MonoString *godot_icall_GD_var2str(MonoObject *p_var) {
}
MonoObject *godot_icall_DefaultGodotTaskScheduler() {
return GDMonoUtils::mono_cache.task_scheduler_handle->get_target();
return GDMonoCache::cached_data.task_scheduler_handle->get_target();
}
void godot_register_gd_icalls() {

View file

@ -130,7 +130,11 @@ private:
res_temp_assemblies_base_dir = res_temp_dir.plus_file("bin");
res_temp_assemblies_dir = res_temp_assemblies_base_dir.plus_file(_get_expected_build_config());
#ifdef JAVASCRIPT_ENABLED
mono_user_dir = "user://";
#else
mono_user_dir = _get_mono_user_dir();
#endif
mono_logs_dir = mono_user_dir.plus_file("mono_logs");
#ifdef TOOLS_ENABLED

View file

@ -46,6 +46,7 @@
#include "../csharp_script.h"
#include "../godotsharp_dirs.h"
#include "../utils/path_utils.h"
#include "gd_mono_cache.h"
#include "gd_mono_class.h"
#include "gd_mono_marshal.h"
#include "gd_mono_utils.h"
@ -56,13 +57,26 @@
#ifdef ANDROID_ENABLED
#include "android_mono_config.h"
#include "gd_mono_android.h"
#endif
// TODO:
// This has turn into a gigantic mess. There's too much going on here. Too much #ifdef as well.
// It's just painful to read... It needs to be re-structured. Please, clean this up, future me.
GDMono *GDMono::singleton = NULL;
namespace {
void setup_runtime_main_args() {
#if defined(JAVASCRIPT_ENABLED)
extern "C" {
void mono_wasm_load_runtime(const char *managed_path, int enable_debugging);
}
#endif
#if !defined(JAVASCRIPT_ENABLED)
void gd_mono_setup_runtime_main_args() {
CharString execpath = OS::get_singleton()->get_executable_path().utf8();
List<String> cmdline_args = OS::get_singleton()->get_cmdline_args();
@ -83,7 +97,7 @@ void setup_runtime_main_args() {
mono_runtime_set_main_args(main_args.size(), main_args.ptrw());
}
void gdmono_profiler_init() {
void gd_mono_profiler_init() {
String profiler_args = GLOBAL_DEF("mono/profiler/args", "log:calls,alloc,sample,output=output.mlpd");
bool profiler_enabled = GLOBAL_DEF("mono/profiler/enabled", false);
if (profiler_enabled) {
@ -91,9 +105,9 @@ void gdmono_profiler_init() {
}
}
#ifdef DEBUG_ENABLED
#if defined(DEBUG_ENABLED)
bool _wait_for_debugger_msecs(uint32_t p_msecs) {
bool gd_mono_wait_for_debugger_msecs(uint32_t p_msecs) {
do {
if (mono_is_debugger_attached())
@ -115,7 +129,7 @@ bool _wait_for_debugger_msecs(uint32_t p_msecs) {
return mono_is_debugger_attached();
}
void gdmono_debug_init() {
void gd_mono_debug_init() {
mono_debug_init(MONO_DEBUG_FORMAT_MONO);
@ -151,11 +165,37 @@ void gdmono_debug_init() {
mono_jit_parse_options(2, (char **)options);
}
#endif // defined(DEBUG_ENABLED)
#endif // !defined(JAVASCRIPT_ENABLED)
#if defined(JAVASCRIPT_ENABLED)
MonoDomain *gd_initialize_mono_runtime() {
const char *vfs_prefix = "managed";
int enable_debugging = 0;
#ifdef DEBUG_ENABLED
enable_debugging = 1;
#endif
mono_wasm_load_runtime(vfs_prefix, enable_debugging);
return mono_get_root_domain();
}
#else
MonoDomain *gd_initialize_mono_runtime() {
#ifdef DEBUG_ENABLED
gd_mono_debug_init();
#endif
return mono_jit_init_version("GodotEngine.RootDomain", "v4.0.30319");
}
#endif
} // namespace
void GDMono::add_mono_shared_libs_dir_to_path() {
// TODO: Replace this with a mono_dl_fallback
// By default Mono seems to search shared libraries in the following directories:
// Current working directory, @executable_path@ and PATH
// The parent directory of the image file (assembly where the dllimport method is declared)
@ -279,11 +319,17 @@ void GDMono::initialize() {
config_dir = bundled_config_dir;
#endif // TOOLS_ENABLED
#if !defined(JAVASCRIPT_ENABLED)
// Leak if we call mono_set_dirs more than once
mono_set_dirs(assembly_rootdir.length() ? assembly_rootdir.utf8().get_data() : NULL,
config_dir.length() ? config_dir.utf8().get_data() : NULL);
add_mono_shared_libs_dir_to_path();
#endif
#if defined(ANDROID_ENABLED)
GDMonoAndroid::register_android_dl_fallback();
#endif
{
PropertyInfo exc_policy_prop = PropertyInfo(Variant::INT, "mono/unhandled_exception_policy", PROPERTY_HINT_ENUM,
@ -299,10 +345,8 @@ void GDMono::initialize() {
GDMonoAssembly::initialize();
gdmono_profiler_init();
#ifdef DEBUG_ENABLED
gdmono_debug_init();
#if !defined(JAVASCRIPT_ENABLED)
gd_mono_profiler_init();
#endif
#ifdef ANDROID_ENABLED
@ -326,12 +370,14 @@ void GDMono::initialize() {
}
#endif
root_domain = mono_jit_init_version("GodotEngine.RootDomain", "v4.0.30319");
root_domain = gd_initialize_mono_runtime();
ERR_FAIL_NULL_MSG(root_domain, "Mono: Failed to initialize runtime.");
GDMonoUtils::set_main_thread(GDMonoUtils::get_current_thread());
setup_runtime_main_args(); // Required for System.Environment.GetCommandLineArgs
#if !defined(JAVASCRIPT_ENABLED)
gd_mono_setup_runtime_main_args(); // Required for System.Environment.GetCommandLineArgs
#endif
runtime_initialized = true;
@ -344,8 +390,8 @@ void GDMono::initialize() {
Error domain_load_err = _load_scripts_domain();
ERR_FAIL_COND_MSG(domain_load_err != OK, "Mono: Failed to load scripts domain.");
#ifdef DEBUG_ENABLED
bool debugger_attached = _wait_for_debugger_msecs(500);
#if defined(DEBUG_ENABLED) && !defined(JAVASCRIPT_ENABLED)
bool debugger_attached = gd_mono_wait_for_debugger_msecs(500);
if (!debugger_attached && OS::get_singleton()->is_stdout_verbose())
print_error("Mono: Debugger wait timeout");
#endif
@ -381,7 +427,7 @@ void GDMono::initialize_load_assemblies() {
}
bool GDMono::_are_api_assemblies_out_of_sync() {
bool out_of_sync = core_api_assembly.assembly && (core_api_assembly.out_of_sync || !GDMonoUtils::mono_cache.godot_api_cache_updated);
bool out_of_sync = core_api_assembly.assembly && (core_api_assembly.out_of_sync || !GDMonoCache::cached_data.godot_api_cache_updated);
#ifdef TOOLS_ENABLED
if (!out_of_sync)
out_of_sync = editor_api_assembly.assembly && editor_api_assembly.out_of_sync;
@ -561,7 +607,7 @@ bool GDMono::_load_corlib_assembly() {
bool success = load_assembly("mscorlib", &corlib_assembly);
if (success)
GDMonoUtils::update_corlib_cache();
GDMonoCache::update_corlib_cache();
return success;
}
@ -834,9 +880,9 @@ bool GDMono::_try_load_api_assemblies(LoadedApiAssembly &r_core_api_assembly, Lo
}
bool GDMono::_on_core_api_assembly_loaded() {
GDMonoUtils::update_godot_api_cache();
GDMonoCache::update_godot_api_cache();
if (!GDMonoUtils::mono_cache.godot_api_cache_updated)
if (!GDMonoCache::cached_data.godot_api_cache_updated)
return false;
get_singleton()->_install_trace_listener();
@ -884,7 +930,7 @@ void GDMono::_load_api_assemblies() {
if (_are_api_assemblies_out_of_sync()) {
if (core_api_assembly.out_of_sync) {
ERR_PRINT("The assembly '" CORE_API_ASSEMBLY_NAME "' is out of sync.");
} else if (!GDMonoUtils::mono_cache.godot_api_cache_updated) {
} else if (!GDMonoCache::cached_data.godot_api_cache_updated) {
ERR_PRINT("The loaded assembly '" CORE_API_ASSEMBLY_NAME "' is in sync, but the cache update failed.");
}
@ -984,7 +1030,7 @@ Error GDMono::_unload_scripts_domain() {
mono_gc_collect(mono_gc_max_generation());
GDMonoUtils::clear_godot_api_cache();
GDMonoCache::clear_godot_api_cache();
_domain_assemblies_cleanup(mono_domain_get_id(scripts_domain));

View file

@ -39,6 +39,7 @@
#include "core/project_settings.h"
#include "../godotsharp_dirs.h"
#include "gd_mono_cache.h"
#include "gd_mono_class.h"
bool GDMonoAssembly::no_search = false;

View file

@ -0,0 +1,282 @@
#include "gd_mono_cache.h"
#include "gd_mono.h"
#include "gd_mono_class.h"
#include "gd_mono_marshal.h"
#include "gd_mono_method.h"
#include "gd_mono_utils.h"
namespace GDMonoCache {
CachedData cached_data;
#define CACHE_AND_CHECK(m_var, m_val) \
{ \
CRASH_COND(m_var != NULL); \
m_var = m_val; \
ERR_FAIL_COND_MSG(m_var == NULL, "Mono Cache: Member " #m_var " is null."); \
}
#define CACHE_CLASS_AND_CHECK(m_class, m_val) CACHE_AND_CHECK(cached_data.class_##m_class, m_val)
#define CACHE_NS_CLASS_AND_CHECK(m_ns, m_class, m_val) CACHE_AND_CHECK(cached_data.class_##m_ns##_##m_class, m_val)
#define CACHE_RAW_MONO_CLASS_AND_CHECK(m_class, m_val) CACHE_AND_CHECK(cached_data.rawclass_##m_class, m_val)
#define CACHE_FIELD_AND_CHECK(m_class, m_field, m_val) CACHE_AND_CHECK(cached_data.field_##m_class##_##m_field, m_val)
#define CACHE_METHOD_AND_CHECK(m_class, m_method, m_val) CACHE_AND_CHECK(cached_data.method_##m_class##_##m_method, m_val)
#define CACHE_PROPERTY_AND_CHECK(m_class, m_property, m_val) CACHE_AND_CHECK(cached_data.property_##m_class##_##m_property, m_val)
#define CACHE_METHOD_THUNK_AND_CHECK_IMPL(m_var, m_val) \
{ \
CRASH_COND(!m_var.is_null()); \
ERR_FAIL_COND_MSG(m_val == NULL, "Mono Cache: Method for member " #m_var " is null."); \
m_var.set_from_method(m_val); \
ERR_FAIL_COND_MSG(m_var.is_null(), "Mono Cache: Member " #m_var " is null."); \
}
#define CACHE_METHOD_THUNK_AND_CHECK(m_class, m_method, m_val) CACHE_METHOD_THUNK_AND_CHECK_IMPL(cached_data.methodthunk_##m_class##_##m_method, m_val)
void CachedData::clear_corlib_cache() {
corlib_cache_updated = false;
class_MonoObject = NULL;
class_bool = NULL;
class_int8_t = NULL;
class_int16_t = NULL;
class_int32_t = NULL;
class_int64_t = NULL;
class_uint8_t = NULL;
class_uint16_t = NULL;
class_uint32_t = NULL;
class_uint64_t = NULL;
class_float = NULL;
class_double = NULL;
class_String = NULL;
class_IntPtr = NULL;
class_System_Collections_IEnumerable = NULL;
class_System_Collections_IDictionary = NULL;
#ifdef DEBUG_ENABLED
class_System_Diagnostics_StackTrace = NULL;
methodthunk_System_Diagnostics_StackTrace_GetFrames.nullify();
method_System_Diagnostics_StackTrace_ctor_bool = NULL;
method_System_Diagnostics_StackTrace_ctor_Exception_bool = NULL;
#endif
class_KeyNotFoundException = NULL;
}
void CachedData::clear_godot_api_cache() {
godot_api_cache_updated = false;
rawclass_Dictionary = NULL;
class_Vector2 = NULL;
class_Rect2 = NULL;
class_Transform2D = NULL;
class_Vector3 = NULL;
class_Basis = NULL;
class_Quat = NULL;
class_Transform = NULL;
class_AABB = NULL;
class_Color = NULL;
class_Plane = NULL;
class_NodePath = NULL;
class_RID = NULL;
class_GodotObject = NULL;
class_GodotResource = NULL;
class_Node = NULL;
class_Control = NULL;
class_Spatial = NULL;
class_WeakRef = NULL;
class_Array = NULL;
class_Dictionary = NULL;
class_MarshalUtils = NULL;
class_ISerializationListener = NULL;
#ifdef DEBUG_ENABLED
class_DebuggingUtils = NULL;
methodthunk_DebuggingUtils_GetStackFrameInfo.nullify();
#endif
class_ExportAttribute = NULL;
field_ExportAttribute_hint = NULL;
field_ExportAttribute_hintString = NULL;
class_SignalAttribute = NULL;
class_ToolAttribute = NULL;
class_RemoteAttribute = NULL;
class_SyncAttribute = NULL;
class_MasterAttribute = NULL;
class_PuppetAttribute = NULL;
class_SlaveAttribute = NULL;
class_RemoteSyncAttribute = NULL;
class_MasterSyncAttribute = NULL;
class_PuppetSyncAttribute = NULL;
class_GodotMethodAttribute = NULL;
field_GodotMethodAttribute_methodName = NULL;
field_GodotObject_ptr = NULL;
field_NodePath_ptr = NULL;
field_Image_ptr = NULL;
field_RID_ptr = NULL;
methodthunk_GodotObject_Dispose.nullify();
methodthunk_Array_GetPtr.nullify();
methodthunk_Dictionary_GetPtr.nullify();
methodthunk_SignalAwaiter_SignalCallback.nullify();
methodthunk_SignalAwaiter_FailureCallback.nullify();
methodthunk_GodotTaskScheduler_Activate.nullify();
// Start of MarshalUtils methods
methodthunk_MarshalUtils_TypeIsGenericArray.nullify();
methodthunk_MarshalUtils_TypeIsGenericDictionary.nullify();
methodthunk_MarshalUtils_ArrayGetElementType.nullify();
methodthunk_MarshalUtils_DictionaryGetKeyValueTypes.nullify();
methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType.nullify();
methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType.nullify();
methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType_with_info.nullify();
methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType_with_info.nullify();
methodthunk_MarshalUtils_MakeGenericArrayType.nullify();
methodthunk_MarshalUtils_MakeGenericDictionaryType.nullify();
methodthunk_MarshalUtils_EnumerableToArray.nullify();
methodthunk_MarshalUtils_IDictionaryToDictionary.nullify();
methodthunk_MarshalUtils_GenericIDictionaryToDictionary.nullify();
// End of MarshalUtils methods
task_scheduler_handle = Ref<MonoGCHandle>();
}
#define GODOT_API_CLASS(m_class) (GDMono::get_singleton()->get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, #m_class))
#define GODOT_API_NS_CLASS(m_ns, m_class) (GDMono::get_singleton()->get_core_api_assembly()->get_class(m_ns, #m_class))
void update_corlib_cache() {
CACHE_CLASS_AND_CHECK(MonoObject, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_object_class()));
CACHE_CLASS_AND_CHECK(bool, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_boolean_class()));
CACHE_CLASS_AND_CHECK(int8_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_sbyte_class()));
CACHE_CLASS_AND_CHECK(int16_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_int16_class()));
CACHE_CLASS_AND_CHECK(int32_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_int32_class()));
CACHE_CLASS_AND_CHECK(int64_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_int64_class()));
CACHE_CLASS_AND_CHECK(uint8_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_byte_class()));
CACHE_CLASS_AND_CHECK(uint16_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_uint16_class()));
CACHE_CLASS_AND_CHECK(uint32_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_uint32_class()));
CACHE_CLASS_AND_CHECK(uint64_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_uint64_class()));
CACHE_CLASS_AND_CHECK(float, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_single_class()));
CACHE_CLASS_AND_CHECK(double, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_double_class()));
CACHE_CLASS_AND_CHECK(String, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_string_class()));
CACHE_CLASS_AND_CHECK(IntPtr, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_intptr_class()));
CACHE_CLASS_AND_CHECK(System_Collections_IEnumerable, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections", "IEnumerable"));
CACHE_CLASS_AND_CHECK(System_Collections_IDictionary, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections", "IDictionary"));
#ifdef DEBUG_ENABLED
CACHE_CLASS_AND_CHECK(System_Diagnostics_StackTrace, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Diagnostics", "StackTrace"));
CACHE_METHOD_THUNK_AND_CHECK(System_Diagnostics_StackTrace, GetFrames, CACHED_CLASS(System_Diagnostics_StackTrace)->get_method("GetFrames"));
CACHE_METHOD_AND_CHECK(System_Diagnostics_StackTrace, ctor_bool, CACHED_CLASS(System_Diagnostics_StackTrace)->get_method_with_desc("System.Diagnostics.StackTrace:.ctor(bool)", true));
CACHE_METHOD_AND_CHECK(System_Diagnostics_StackTrace, ctor_Exception_bool, CACHED_CLASS(System_Diagnostics_StackTrace)->get_method_with_desc("System.Diagnostics.StackTrace:.ctor(System.Exception,bool)", true));
#endif
CACHE_CLASS_AND_CHECK(KeyNotFoundException, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections.Generic", "KeyNotFoundException"));
cached_data.corlib_cache_updated = true;
}
void update_godot_api_cache() {
CACHE_CLASS_AND_CHECK(Vector2, GODOT_API_CLASS(Vector2));
CACHE_CLASS_AND_CHECK(Rect2, GODOT_API_CLASS(Rect2));
CACHE_CLASS_AND_CHECK(Transform2D, GODOT_API_CLASS(Transform2D));
CACHE_CLASS_AND_CHECK(Vector3, GODOT_API_CLASS(Vector3));
CACHE_CLASS_AND_CHECK(Basis, GODOT_API_CLASS(Basis));
CACHE_CLASS_AND_CHECK(Quat, GODOT_API_CLASS(Quat));
CACHE_CLASS_AND_CHECK(Transform, GODOT_API_CLASS(Transform));
CACHE_CLASS_AND_CHECK(AABB, GODOT_API_CLASS(AABB));
CACHE_CLASS_AND_CHECK(Color, GODOT_API_CLASS(Color));
CACHE_CLASS_AND_CHECK(Plane, GODOT_API_CLASS(Plane));
CACHE_CLASS_AND_CHECK(NodePath, GODOT_API_CLASS(NodePath));
CACHE_CLASS_AND_CHECK(RID, GODOT_API_CLASS(RID));
CACHE_CLASS_AND_CHECK(GodotObject, GODOT_API_CLASS(Object));
CACHE_CLASS_AND_CHECK(GodotResource, GODOT_API_CLASS(Resource));
CACHE_CLASS_AND_CHECK(Node, GODOT_API_CLASS(Node));
CACHE_CLASS_AND_CHECK(Control, GODOT_API_CLASS(Control));
CACHE_CLASS_AND_CHECK(Spatial, GODOT_API_CLASS(Spatial));
CACHE_CLASS_AND_CHECK(WeakRef, GODOT_API_CLASS(WeakRef));
CACHE_CLASS_AND_CHECK(Array, GODOT_API_NS_CLASS(BINDINGS_NAMESPACE_COLLECTIONS, Array));
CACHE_CLASS_AND_CHECK(Dictionary, GODOT_API_NS_CLASS(BINDINGS_NAMESPACE_COLLECTIONS, Dictionary));
CACHE_CLASS_AND_CHECK(MarshalUtils, GODOT_API_CLASS(MarshalUtils));
CACHE_CLASS_AND_CHECK(ISerializationListener, GODOT_API_CLASS(ISerializationListener));
#ifdef DEBUG_ENABLED
CACHE_CLASS_AND_CHECK(DebuggingUtils, GODOT_API_CLASS(DebuggingUtils));
#endif
// Attributes
CACHE_CLASS_AND_CHECK(ExportAttribute, GODOT_API_CLASS(ExportAttribute));
CACHE_FIELD_AND_CHECK(ExportAttribute, hint, CACHED_CLASS(ExportAttribute)->get_field("hint"));
CACHE_FIELD_AND_CHECK(ExportAttribute, hintString, CACHED_CLASS(ExportAttribute)->get_field("hintString"));
CACHE_CLASS_AND_CHECK(SignalAttribute, GODOT_API_CLASS(SignalAttribute));
CACHE_CLASS_AND_CHECK(ToolAttribute, GODOT_API_CLASS(ToolAttribute));
CACHE_CLASS_AND_CHECK(RemoteAttribute, GODOT_API_CLASS(RemoteAttribute));
CACHE_CLASS_AND_CHECK(SyncAttribute, GODOT_API_CLASS(SyncAttribute));
CACHE_CLASS_AND_CHECK(MasterAttribute, GODOT_API_CLASS(MasterAttribute));
CACHE_CLASS_AND_CHECK(PuppetAttribute, GODOT_API_CLASS(PuppetAttribute));
CACHE_CLASS_AND_CHECK(SlaveAttribute, GODOT_API_CLASS(SlaveAttribute));
CACHE_CLASS_AND_CHECK(RemoteSyncAttribute, GODOT_API_CLASS(RemoteSyncAttribute));
CACHE_CLASS_AND_CHECK(MasterSyncAttribute, GODOT_API_CLASS(MasterSyncAttribute));
CACHE_CLASS_AND_CHECK(PuppetSyncAttribute, GODOT_API_CLASS(PuppetSyncAttribute));
CACHE_CLASS_AND_CHECK(GodotMethodAttribute, GODOT_API_CLASS(GodotMethodAttribute));
CACHE_FIELD_AND_CHECK(GodotMethodAttribute, methodName, CACHED_CLASS(GodotMethodAttribute)->get_field("methodName"));
CACHE_FIELD_AND_CHECK(GodotObject, ptr, CACHED_CLASS(GodotObject)->get_field(BINDINGS_PTR_FIELD));
CACHE_FIELD_AND_CHECK(NodePath, ptr, CACHED_CLASS(NodePath)->get_field(BINDINGS_PTR_FIELD));
CACHE_FIELD_AND_CHECK(RID, ptr, CACHED_CLASS(RID)->get_field(BINDINGS_PTR_FIELD));
CACHE_METHOD_THUNK_AND_CHECK(GodotObject, Dispose, CACHED_CLASS(GodotObject)->get_method("Dispose", 0));
CACHE_METHOD_THUNK_AND_CHECK(Array, GetPtr, GODOT_API_NS_CLASS(BINDINGS_NAMESPACE_COLLECTIONS, Array)->get_method("GetPtr", 0));
CACHE_METHOD_THUNK_AND_CHECK(Dictionary, GetPtr, GODOT_API_NS_CLASS(BINDINGS_NAMESPACE_COLLECTIONS, Dictionary)->get_method("GetPtr", 0));
CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, SignalCallback, GODOT_API_CLASS(SignalAwaiter)->get_method("SignalCallback", 1));
CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, FailureCallback, GODOT_API_CLASS(SignalAwaiter)->get_method("FailureCallback", 0));
CACHE_METHOD_THUNK_AND_CHECK(GodotTaskScheduler, Activate, GODOT_API_CLASS(GodotTaskScheduler)->get_method("Activate", 0));
// Start of MarshalUtils methods
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericArray, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericArray", 1));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericDictionary, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericDictionary", 1));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, ArrayGetElementType, GODOT_API_CLASS(MarshalUtils)->get_method("ArrayGetElementType", 2));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, DictionaryGetKeyValueTypes, GODOT_API_CLASS(MarshalUtils)->get_method("DictionaryGetKeyValueTypes", 3));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIEnumerableIsAssignableFromType, GODOT_API_CLASS(MarshalUtils)->get_method("GenericIEnumerableIsAssignableFromType", 1));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIDictionaryIsAssignableFromType, GODOT_API_CLASS(MarshalUtils)->get_method("GenericIDictionaryIsAssignableFromType", 1));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIEnumerableIsAssignableFromType_with_info, GODOT_API_CLASS(MarshalUtils)->get_method("GenericIEnumerableIsAssignableFromType", 2));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIDictionaryIsAssignableFromType_with_info, GODOT_API_CLASS(MarshalUtils)->get_method("GenericIDictionaryIsAssignableFromType", 3));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, MakeGenericArrayType, GODOT_API_CLASS(MarshalUtils)->get_method("MakeGenericArrayType", 1));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, MakeGenericDictionaryType, GODOT_API_CLASS(MarshalUtils)->get_method("MakeGenericDictionaryType", 2));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, EnumerableToArray, GODOT_API_CLASS(MarshalUtils)->get_method("EnumerableToArray", 2));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, IDictionaryToDictionary, GODOT_API_CLASS(MarshalUtils)->get_method("IDictionaryToDictionary", 2));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIDictionaryToDictionary, GODOT_API_CLASS(MarshalUtils)->get_method("GenericIDictionaryToDictionary", 2));
// End of MarshalUtils methods
#ifdef DEBUG_ENABLED
CACHE_METHOD_THUNK_AND_CHECK(DebuggingUtils, GetStackFrameInfo, GODOT_API_CLASS(DebuggingUtils)->get_method("GetStackFrameInfo", 4));
#endif
// TODO Move to CSharpLanguage::init() and do handle disposal
MonoObject *task_scheduler = mono_object_new(mono_domain_get(), GODOT_API_CLASS(GodotTaskScheduler)->get_mono_ptr());
GDMonoUtils::runtime_object_init(task_scheduler, GODOT_API_CLASS(GodotTaskScheduler));
cached_data.task_scheduler_handle = MonoGCHandle::create_strong(task_scheduler);
cached_data.godot_api_cache_updated = true;
}
} // namespace GDMonoCache

View file

@ -0,0 +1,174 @@
#ifndef GD_MONO_CACHE_H
#define GD_MONO_CACHE_H
#include "gd_mono_header.h"
#include "gd_mono_method_thunk.h"
namespace GDMonoCache {
struct CachedData {
// -----------------------------------------------
// corlib classes
// Let's use the no-namespace format for these too
GDMonoClass *class_MonoObject;
GDMonoClass *class_bool;
GDMonoClass *class_int8_t;
GDMonoClass *class_int16_t;
GDMonoClass *class_int32_t;
GDMonoClass *class_int64_t;
GDMonoClass *class_uint8_t;
GDMonoClass *class_uint16_t;
GDMonoClass *class_uint32_t;
GDMonoClass *class_uint64_t;
GDMonoClass *class_float;
GDMonoClass *class_double;
GDMonoClass *class_String;
GDMonoClass *class_IntPtr;
GDMonoClass *class_System_Collections_IEnumerable;
GDMonoClass *class_System_Collections_IDictionary;
#ifdef DEBUG_ENABLED
GDMonoClass *class_System_Diagnostics_StackTrace;
GDMonoMethodThunkR<MonoArray *, MonoObject *> methodthunk_System_Diagnostics_StackTrace_GetFrames;
GDMonoMethod *method_System_Diagnostics_StackTrace_ctor_bool;
GDMonoMethod *method_System_Diagnostics_StackTrace_ctor_Exception_bool;
#endif
GDMonoClass *class_KeyNotFoundException;
MonoClass *rawclass_Dictionary;
// -----------------------------------------------
GDMonoClass *class_Vector2;
GDMonoClass *class_Rect2;
GDMonoClass *class_Transform2D;
GDMonoClass *class_Vector3;
GDMonoClass *class_Basis;
GDMonoClass *class_Quat;
GDMonoClass *class_Transform;
GDMonoClass *class_AABB;
GDMonoClass *class_Color;
GDMonoClass *class_Plane;
GDMonoClass *class_NodePath;
GDMonoClass *class_RID;
GDMonoClass *class_GodotObject;
GDMonoClass *class_GodotResource;
GDMonoClass *class_Node;
GDMonoClass *class_Control;
GDMonoClass *class_Spatial;
GDMonoClass *class_WeakRef;
GDMonoClass *class_Array;
GDMonoClass *class_Dictionary;
GDMonoClass *class_MarshalUtils;
GDMonoClass *class_ISerializationListener;
#ifdef DEBUG_ENABLED
GDMonoClass *class_DebuggingUtils;
GDMonoMethodThunk<MonoObject *, MonoString **, int *, MonoString **> methodthunk_DebuggingUtils_GetStackFrameInfo;
#endif
GDMonoClass *class_ExportAttribute;
GDMonoField *field_ExportAttribute_hint;
GDMonoField *field_ExportAttribute_hintString;
GDMonoClass *class_SignalAttribute;
GDMonoClass *class_ToolAttribute;
GDMonoClass *class_RemoteAttribute;
GDMonoClass *class_SyncAttribute;
GDMonoClass *class_RemoteSyncAttribute;
GDMonoClass *class_MasterSyncAttribute;
GDMonoClass *class_PuppetSyncAttribute;
GDMonoClass *class_MasterAttribute;
GDMonoClass *class_PuppetAttribute;
GDMonoClass *class_SlaveAttribute;
GDMonoClass *class_GodotMethodAttribute;
GDMonoField *field_GodotMethodAttribute_methodName;
GDMonoField *field_GodotObject_ptr;
GDMonoField *field_NodePath_ptr;
GDMonoField *field_Image_ptr;
GDMonoField *field_RID_ptr;
GDMonoMethodThunk<MonoObject *> methodthunk_GodotObject_Dispose;
GDMonoMethodThunkR<Array *, MonoObject *> methodthunk_Array_GetPtr;
GDMonoMethodThunkR<Dictionary *, MonoObject *> methodthunk_Dictionary_GetPtr;
GDMonoMethodThunk<MonoObject *, MonoArray *> methodthunk_SignalAwaiter_SignalCallback;
GDMonoMethodThunk<MonoObject *> methodthunk_SignalAwaiter_FailureCallback;
GDMonoMethodThunk<MonoObject *> methodthunk_GodotTaskScheduler_Activate;
// Start of MarshalUtils methods
GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericArray;
GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericDictionary;
GDMonoMethodThunk<MonoReflectionType *, MonoReflectionType **> methodthunk_MarshalUtils_ArrayGetElementType;
GDMonoMethodThunk<MonoReflectionType *, MonoReflectionType **, MonoReflectionType **> methodthunk_MarshalUtils_DictionaryGetKeyValueTypes;
GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType;
GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType;
GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *, MonoReflectionType **> methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType_with_info;
GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *, MonoReflectionType **, MonoReflectionType **> methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType_with_info;
GDMonoMethodThunkR<MonoReflectionType *, MonoReflectionType *> methodthunk_MarshalUtils_MakeGenericArrayType;
GDMonoMethodThunkR<MonoReflectionType *, MonoReflectionType *, MonoReflectionType *> methodthunk_MarshalUtils_MakeGenericDictionaryType;
GDMonoMethodThunk<MonoObject *, Array *> methodthunk_MarshalUtils_EnumerableToArray;
GDMonoMethodThunk<MonoObject *, Dictionary *> methodthunk_MarshalUtils_IDictionaryToDictionary;
GDMonoMethodThunk<MonoObject *, Dictionary *> methodthunk_MarshalUtils_GenericIDictionaryToDictionary;
// End of MarshalUtils methods
Ref<MonoGCHandle> task_scheduler_handle;
bool corlib_cache_updated;
bool godot_api_cache_updated;
void clear_corlib_cache();
void clear_godot_api_cache();
CachedData() {
clear_corlib_cache();
clear_godot_api_cache();
}
};
extern CachedData cached_data;
void update_corlib_cache();
void update_godot_api_cache();
inline void clear_corlib_cache() {
cached_data.clear_corlib_cache();
}
inline void clear_godot_api_cache() {
cached_data.clear_godot_api_cache();
}
_FORCE_INLINE_ bool tools_godot_api_check() {
#ifdef TOOLS_ENABLED
return cached_data.godot_api_cache_updated;
#else
return true; // Assume it's updated if this was called, otherwise it's a bug
#endif
}
} // namespace GDMonoCache
#define CACHED_CLASS(m_class) (GDMonoCache::cached_data.class_##m_class)
#define CACHED_CLASS_RAW(m_class) (GDMonoCache::cached_data.class_##m_class->get_mono_ptr())
#define CACHED_RAW_MONO_CLASS(m_class) (GDMonoCache::cached_data.rawclass_##m_class)
#define CACHED_FIELD(m_class, m_field) (GDMonoCache::cached_data.field_##m_class##_##m_field)
#define CACHED_METHOD(m_class, m_method) (GDMonoCache::cached_data.method_##m_class##_##m_method)
#define CACHED_METHOD_THUNK(m_class, m_method) (GDMonoCache::cached_data.methodthunk_##m_class##_##m_method)
#define CACHED_PROPERTY(m_class, m_property) (GDMonoCache::cached_data.property_##m_class##_##m_property)
#ifdef REAL_T_IS_DOUBLE
#define REAL_T_MONOCLASS CACHED_CLASS_RAW(double)
#else
#define REAL_T_MONOCLASS CACHED_CLASS_RAW(float)
#endif
#endif // GD_MONO_CACHE_H

View file

@ -33,6 +33,7 @@
#include <mono/metadata/attrdefs.h>
#include "gd_mono_assembly.h"
#include "gd_mono_cache.h"
#include "gd_mono_marshal.h"
String GDMonoClass::get_full_name(MonoClass *p_mono_class) {
@ -332,12 +333,6 @@ GDMonoMethod *GDMonoClass::get_method_with_desc(const String &p_description, boo
return get_method(method);
}
void *GDMonoClass::get_method_thunk(const StringName &p_name, int p_params_count) {
GDMonoMethod *method = get_method(p_name, p_params_count);
return method ? method->get_thunk() : NULL;
}
GDMonoField *GDMonoClass::get_field(const StringName &p_name) {
Map<StringName, GDMonoField *>::Element *result = fields.find(p_name);

View file

@ -144,8 +144,6 @@ public:
GDMonoMethod *get_method(MonoMethod *p_raw_method, const StringName &p_name, int p_params_count);
GDMonoMethod *get_method_with_desc(const String &p_description, bool p_include_namespace);
void *get_method_thunk(const StringName &p_name, int p_params_count = 0);
GDMonoField *get_field(const StringName &p_name);
const Vector<GDMonoField *> &get_all_fields();

View file

@ -32,8 +32,10 @@
#include <mono/metadata/attrdefs.h>
#include "gd_mono_cache.h"
#include "gd_mono_class.h"
#include "gd_mono_marshal.h"
#include "gd_mono_utils.h"
void GDMonoField::set_value_raw(MonoObject *p_object, void *p_ptr) {
mono_field_set_value(p_object, mono_field, &p_ptr);
@ -337,7 +339,7 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
}
if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
if (GDMonoUtils::tools_godot_api_check()) {
if (GDMonoCache::tools_godot_api_check()) {
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array));
mono_field_set_value(p_object, mono_field, managed);
break;
@ -491,7 +493,7 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
}
if (type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
if (GDMonoUtils::tools_godot_api_check()) {
if (GDMonoCache::tools_godot_api_check()) {
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array));
mono_field_set_value(p_object, mono_field, managed);
break;

View file

@ -33,6 +33,12 @@
#include "core/int_types.h"
#ifdef WIN32
#define GD_MONO_STDCALL __stdcall
#else
#define GD_MONO_STDCALL
#endif
class GDMonoAssembly;
class GDMonoClass;
class GDMonoField;

View file

@ -30,7 +30,6 @@
#include "gd_mono_log.h"
#include <mono/utils/mono-logger.h>
#include <stdlib.h> // abort
#include "core/os/dir_access.h"
@ -39,7 +38,19 @@
#include "../godotsharp_dirs.h"
#include "../utils/string_utils.h"
static int log_level_get_id(const char *p_log_level) {
static CharString get_default_log_level() {
#ifdef DEBUG_ENABLED
return String("info").utf8();
#else
return String("warning").utf8();
#endif
}
GDMonoLog *GDMonoLog::singleton = NULL;
#if !defined(JAVASCRIPT_ENABLED)
static int get_log_level_id(const char *p_log_level) {
const char *valid_log_levels[] = { "error", "critical", "warning", "message", "info", "debug", NULL };
@ -53,11 +64,11 @@ static int log_level_get_id(const char *p_log_level) {
return -1;
}
static void mono_log_callback(const char *log_domain, const char *log_level, const char *message, mono_bool fatal, void *user_data) {
void GDMonoLog::mono_log_callback(const char *log_domain, const char *log_level, const char *message, mono_bool fatal, void *) {
FileAccess *f = GDMonoLog::get_singleton()->get_log_file();
FileAccess *f = GDMonoLog::get_singleton()->log_file;
if (GDMonoLog::get_singleton()->get_log_level_id() >= log_level_get_id(log_level)) {
if (GDMonoLog::get_singleton()->log_level_id >= get_log_level_id(log_level)) {
String text(message);
text += " (in domain ";
text += log_domain;
@ -72,7 +83,7 @@ static void mono_log_callback(const char *log_domain, const char *log_level, con
}
if (fatal) {
ERR_PRINTS("Mono: FATAL ERROR, ABORTING! Logfile: '" + GDMonoLog::get_singleton()->get_log_file_path() + "'.");
ERR_PRINTS("Mono: FATAL ERROR, ABORTING! Logfile: '" + GDMonoLog::get_singleton()->log_file_path + "'.");
// Make sure to flush before aborting
f->flush();
f->close();
@ -82,8 +93,6 @@ static void mono_log_callback(const char *log_domain, const char *log_level, con
}
}
GDMonoLog *GDMonoLog::singleton = NULL;
bool GDMonoLog::_try_create_logs_dir(const String &p_logs_dir) {
if (!DirAccess::exists(p_logs_dir)) {
@ -129,17 +138,13 @@ void GDMonoLog::initialize() {
CharString log_level = OS::get_singleton()->get_environment("GODOT_MONO_LOG_LEVEL").utf8();
if (log_level.length() != 0 && log_level_get_id(log_level.get_data()) == -1) {
if (log_level.length() != 0 && get_log_level_id(log_level.get_data()) == -1) {
ERR_PRINTS(String() + "Mono: Ignoring invalid log level (GODOT_MONO_LOG_LEVEL): '" + log_level.get_data() + "'.");
log_level = CharString();
}
if (log_level.length() == 0) {
#ifdef DEBUG_ENABLED
log_level = String("info").utf8();
#else
log_level = String("warning").utf8();
#endif
log_level = get_default_log_level();
}
String logs_dir = GodotSharpDirs::get_mono_logs_dir();
@ -149,11 +154,14 @@ void GDMonoLog::initialize() {
OS::Date date_now = OS::get_singleton()->get_date();
OS::Time time_now = OS::get_singleton()->get_time();
int pid = OS::get_singleton()->get_process_id();
String log_file_name = str_format("%d_%02d_%02d %02d.%02d.%02d (%d).txt",
String log_file_name = str_format("%d_%02d_%02d %02d.%02d.%02d",
date_now.year, date_now.month, date_now.day,
time_now.hour, time_now.min, time_now.sec, pid);
time_now.hour, time_now.min, time_now.sec);
log_file_name += str_format(" (%d)", OS::get_singleton()->get_process_id());
log_file_name += ".txt";
log_file_path = logs_dir.plus_file(log_file_name);
@ -164,7 +172,7 @@ void GDMonoLog::initialize() {
}
mono_trace_set_level_string(log_level.get_data());
log_level_id = log_level_get_id(log_level.get_data());
log_level_id = get_log_level_id(log_level.get_data());
if (log_file) {
OS::get_singleton()->print("Mono: Logfile is: %s\n", log_file_path.utf8().get_data());
@ -190,3 +198,22 @@ GDMonoLog::~GDMonoLog() {
memdelete(log_file);
}
}
#else
void GDMonoLog::initialize() {
CharString log_level = get_default_log_level();
mono_trace_set_level_string(log_level.get_data());
}
GDMonoLog::GDMonoLog() {
singleton = this;
}
GDMonoLog::~GDMonoLog() {
singleton = NULL;
}
#endif // !defined(JAVASCRIPT_ENABLED)

View file

@ -31,10 +31,17 @@
#ifndef GD_MONO_LOG_H
#define GD_MONO_LOG_H
#include <mono/utils/mono-logger.h>
#include "core/typedefs.h"
#if !defined(JAVASCRIPT_ENABLED)
#include "core/os/file_access.h"
#endif
class GDMonoLog {
#if !defined(JAVASCRIPT_ENABLED)
int log_level_id;
FileAccess *log_file;
@ -43,6 +50,9 @@ class GDMonoLog {
bool _try_create_logs_dir(const String &p_logs_dir);
void _delete_old_log_files(const String &p_logs_dir);
static void mono_log_callback(const char *log_domain, const char *log_level, const char *message, mono_bool fatal, void *user_data);
#endif
static GDMonoLog *singleton;
public:
@ -50,10 +60,6 @@ public:
void initialize();
_FORCE_INLINE_ FileAccess *get_log_file() { return log_file; }
_FORCE_INLINE_ String get_log_file_path() { return log_file_path; }
_FORCE_INLINE_ int get_log_level_id() { return log_level_id; }
GDMonoLog();
~GDMonoLog();
};

View file

@ -31,6 +31,7 @@
#include "gd_mono_marshal.h"
#include "gd_mono.h"
#include "gd_mono_cache.h"
#include "gd_mono_class.h"
namespace GDMonoMarshal {
@ -556,7 +557,7 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
}
if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
if (GDMonoUtils::tools_godot_api_check()) {
if (GDMonoCache::tools_godot_api_check()) {
return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array));
} else {
return (MonoObject *)GDMonoMarshal::Array_to_mono_array(p_var->operator Array());
@ -683,7 +684,7 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
}
if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
if (GDMonoUtils::tools_godot_api_check()) {
if (GDMonoCache::tools_godot_api_check()) {
return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array));
} else {
return (MonoObject *)GDMonoMarshal::Array_to_mono_array(p_var->operator Array());
@ -834,14 +835,14 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
if (CACHED_CLASS(Array) == type_class) {
MonoException *exc = NULL;
Array *ptr = invoke_method_thunk(CACHED_METHOD_THUNK(Array, GetPtr), p_obj, &exc);
Array *ptr = CACHED_METHOD_THUNK(Array, GetPtr).invoke(p_obj, &exc);
UNHANDLED_EXCEPTION(exc);
return ptr ? Variant(*ptr) : Variant();
}
if (CACHED_CLASS(Dictionary) == type_class) {
MonoException *exc = NULL;
Dictionary *ptr = invoke_method_thunk(CACHED_METHOD_THUNK(Dictionary, GetPtr), p_obj, &exc);
Dictionary *ptr = CACHED_METHOD_THUNK(Dictionary, GetPtr).invoke(p_obj, &exc);
UNHANDLED_EXCEPTION(exc);
return ptr ? Variant(*ptr) : Variant();
}

View file

@ -43,6 +43,11 @@ T unbox(MonoObject *p_obj) {
return *(T *)mono_object_unbox(p_obj);
}
template <typename T>
T *unbox_addr(MonoObject *p_obj) {
return (T *)mono_object_unbox(p_obj);
}
#define BOX_DOUBLE(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(double), &x)
#define BOX_FLOAT(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(float), &x)
#define BOX_INT64(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(int64_t), &x)

View file

@ -30,8 +30,10 @@
#include "gd_mono_method.h"
#include "gd_mono_cache.h"
#include "gd_mono_class.h"
#include "gd_mono_marshal.h"
#include "gd_mono_utils.h"
#include <mono/metadata/attrdefs.h>
@ -99,10 +101,6 @@ IMonoClassMember::Visibility GDMonoMethod::get_visibility() {
}
}
void *GDMonoMethod::get_thunk() {
return mono_method_get_unmanaged_thunk(mono_method);
}
MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc) {
if (get_return_type().type_encoding != MONO_TYPE_VOID || get_parameters_count() > 0) {
MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), get_parameters_count());

View file

@ -71,11 +71,11 @@ public:
virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) GD_FINAL;
void fetch_attributes();
_FORCE_INLINE_ MonoMethod *get_mono_ptr() { return mono_method; }
_FORCE_INLINE_ int get_parameters_count() { return params_count; }
_FORCE_INLINE_ ManagedType get_return_type() { return return_type; }
void *get_thunk();
MonoObject *invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc = NULL);
MonoObject *invoke(MonoObject *p_object, MonoException **r_exc = NULL);
MonoObject *invoke_raw(MonoObject *p_object, void **p_params, MonoException **r_exc = NULL);

View file

@ -0,0 +1,302 @@
#ifndef GD_MONO_METHOD_THUNK_H
#define GD_MONO_METHOD_THUNK_H
#include <type_traits>
#include "gd_mono_class.h"
#include "gd_mono_header.h"
#include "gd_mono_marshal.h"
#include "gd_mono_method.h"
#include "gd_mono_utils.h"
#if !defined(JAVASCRIPT_ENABLED)
#define HAVE_METHOD_THUNKS
#endif
#ifdef HAVE_METHOD_THUNKS
template <class... ParamTypes>
struct GDMonoMethodThunk {
typedef void(GD_MONO_STDCALL *M)(ParamTypes... p_args, MonoException **);
M mono_method_thunk;
public:
_FORCE_INLINE_ void invoke(ParamTypes... p_args, MonoException **r_exc) {
GD_MONO_BEGIN_RUNTIME_INVOKE;
mono_method_thunk(p_args..., r_exc);
GD_MONO_END_RUNTIME_INVOKE;
}
_FORCE_INLINE_ bool is_null() {
return mono_method_thunk == NULL;
}
_FORCE_INLINE_ void nullify() {
mono_method_thunk = NULL;
}
_FORCE_INLINE_ void set_from_method(GDMonoMethod *p_mono_method) {
#ifdef DEBUG_ENABLED
CRASH_COND(p_mono_method == NULL);
CRASH_COND(p_mono_method->get_return_type().type_encoding != MONO_TYPE_VOID);
if (p_mono_method->is_static()) {
CRASH_COND(p_mono_method->get_parameters_count() != sizeof...(ParamTypes));
} else {
CRASH_COND(p_mono_method->get_parameters_count() != (sizeof...(ParamTypes) - 1));
}
#endif
mono_method_thunk = (M)mono_method_get_unmanaged_thunk(p_mono_method->get_mono_ptr());
}
GDMonoMethodThunk() :
mono_method_thunk(NULL) {
}
explicit GDMonoMethodThunk(GDMonoMethod *p_mono_method) {
set_from_method(p_mono_method);
}
};
template <class R, class... ParamTypes>
struct GDMonoMethodThunkR {
typedef R(GD_MONO_STDCALL *M)(ParamTypes... p_args, MonoException **);
M mono_method_thunk;
public:
_FORCE_INLINE_ R invoke(ParamTypes... p_args, MonoException **r_exc) {
GD_MONO_BEGIN_RUNTIME_INVOKE;
R r = mono_method_thunk(p_args..., r_exc);
GD_MONO_END_RUNTIME_INVOKE;
return r;
}
_FORCE_INLINE_ bool is_null() {
return mono_method_thunk == NULL;
}
_FORCE_INLINE_ void nullify() {
mono_method_thunk = NULL;
}
_FORCE_INLINE_ void set_from_method(GDMonoMethod *p_mono_method) {
#ifdef DEBUG_ENABLED
CRASH_COND(p_mono_method == NULL);
CRASH_COND(p_mono_method->get_return_type().type_encoding == MONO_TYPE_VOID);
if (p_mono_method->is_static()) {
CRASH_COND(p_mono_method->get_parameters_count() != sizeof...(ParamTypes));
} else {
CRASH_COND(p_mono_method->get_parameters_count() != (sizeof...(ParamTypes) - 1));
}
#endif
mono_method_thunk = (M)mono_method_get_unmanaged_thunk(p_mono_method->get_mono_ptr());
}
GDMonoMethodThunkR() :
mono_method_thunk(NULL) {
}
explicit GDMonoMethodThunkR(GDMonoMethod *p_mono_method) {
#ifdef DEBUG_ENABLED
CRASH_COND(p_mono_method == NULL);
#endif
mono_method_thunk = (M)mono_method_get_unmanaged_thunk(p_mono_method->get_mono_ptr());
}
};
#else
template <unsigned int ThunkParamCount, class P1, class... ParamTypes>
struct VariadicInvokeMonoMethodImpl {
static void invoke(GDMonoMethod *p_mono_method, P1 p_arg1, ParamTypes... p_args, MonoException **r_exc) {
if (p_mono_method->is_static()) {
void *args[ThunkParamCount] = { p_arg1, p_args... };
p_mono_method->invoke_raw(NULL, args, r_exc);
} else {
void *args[ThunkParamCount] = { p_args... };
p_mono_method->invoke_raw((MonoObject *)p_arg1, args, r_exc);
}
}
};
template <unsigned int ThunkParamCount, class... ParamTypes>
struct VariadicInvokeMonoMethod {
static void invoke(GDMonoMethod *p_mono_method, ParamTypes... p_args, MonoException **r_exc) {
VariadicInvokeMonoMethodImpl<ThunkParamCount, ParamTypes...>::invoke(p_mono_method, p_args..., r_exc);
}
};
template <>
struct VariadicInvokeMonoMethod<0> {
static void invoke(GDMonoMethod *p_mono_method, MonoException **r_exc) {
#ifdef DEBUG_ENABLED
CRASH_COND(!p_mono_method->is_static());
#endif
p_mono_method->invoke_raw(NULL, NULL, r_exc);
}
};
template <class P1>
struct VariadicInvokeMonoMethod<1, P1> {
static void invoke(GDMonoMethod *p_mono_method, P1 p_arg1, MonoException **r_exc) {
if (p_mono_method->is_static()) {
void *args[1] = { p_arg1 };
p_mono_method->invoke_raw(NULL, args, r_exc);
} else {
p_mono_method->invoke_raw((MonoObject *)p_arg1, NULL, r_exc);
}
}
};
template <class R>
R unbox_if_needed(MonoObject *p_val, const ManagedType &, typename std::enable_if<!std::is_pointer<R>::value>::type * = 0) {
return GDMonoMarshal::unbox<R>(p_val);
}
template <class R>
R unbox_if_needed(MonoObject *p_val, const ManagedType &p_type, typename std::enable_if<std::is_pointer<R>::value>::type * = 0) {
if (mono_class_is_valuetype(p_type.type_class->get_mono_ptr())) {
return GDMonoMarshal::unbox<R>(p_val);
} else {
// If it's not a value type, we assume 'R' is a pointer to 'MonoObject' or a compatible type, like 'MonoException'.
return (R)p_val;
}
}
template <unsigned int ThunkParamCount, class R, class P1, class... ParamTypes>
struct VariadicInvokeMonoMethodRImpl {
static R invoke(GDMonoMethod *p_mono_method, P1 p_arg1, ParamTypes... p_args, MonoException **r_exc) {
if (p_mono_method->is_static()) {
void *args[ThunkParamCount] = { p_arg1, p_args... };
MonoObject *r = p_mono_method->invoke_raw(NULL, args, r_exc);
return unbox_if_needed<R>(r, p_mono_method->get_return_type());
} else {
void *args[ThunkParamCount] = { p_args... };
MonoObject *r = p_mono_method->invoke_raw((MonoObject *)p_arg1, args, r_exc);
return unbox_if_needed<R>(r, p_mono_method->get_return_type());
}
}
};
template <unsigned int ThunkParamCount, class R, class... ParamTypes>
struct VariadicInvokeMonoMethodR {
static R invoke(GDMonoMethod *p_mono_method, ParamTypes... p_args, MonoException **r_exc) {
return VariadicInvokeMonoMethodRImpl<ThunkParamCount, R, ParamTypes...>::invoke(p_mono_method, p_args..., r_exc);
}
};
template <class R>
struct VariadicInvokeMonoMethodR<0, R> {
static R invoke(GDMonoMethod *p_mono_method, MonoException **r_exc) {
#ifdef DEBUG_ENABLED
CRASH_COND(!p_mono_method->is_static());
#endif
MonoObject *r = p_mono_method->invoke_raw(NULL, NULL, r_exc);
return unbox_if_needed<R>(r, p_mono_method->get_return_type());
}
};
template <class R, class P1>
struct VariadicInvokeMonoMethodR<1, R, P1> {
static R invoke(GDMonoMethod *p_mono_method, P1 p_arg1, MonoException **r_exc) {
if (p_mono_method->is_static()) {
void *args[1] = { p_arg1 };
MonoObject *r = p_mono_method->invoke_raw(NULL, args, r_exc);
return unbox_if_needed<R>(r, p_mono_method->get_return_type());
} else {
MonoObject *r = p_mono_method->invoke_raw((MonoObject *)p_arg1, NULL, r_exc);
return unbox_if_needed<R>(r, p_mono_method->get_return_type());
}
}
};
template <class... ParamTypes>
struct GDMonoMethodThunk {
GDMonoMethod *mono_method;
public:
_FORCE_INLINE_ void invoke(ParamTypes... p_args, MonoException **r_exc) {
VariadicInvokeMonoMethod<sizeof...(ParamTypes), ParamTypes...>::invoke(mono_method, p_args..., r_exc);
}
_FORCE_INLINE_ bool is_null() {
return mono_method == NULL;
}
_FORCE_INLINE_ void nullify() {
mono_method = NULL;
}
_FORCE_INLINE_ void set_from_method(GDMonoMethod *p_mono_method) {
#ifdef DEBUG_ENABLED
CRASH_COND(p_mono_method == NULL);
CRASH_COND(p_mono_method->get_return_type().type_encoding != MONO_TYPE_VOID);
if (p_mono_method->is_static()) {
CRASH_COND(p_mono_method->get_parameters_count() != sizeof...(ParamTypes));
} else {
CRASH_COND(p_mono_method->get_parameters_count() != (sizeof...(ParamTypes) - 1));
}
#endif
mono_method = p_mono_method;
}
GDMonoMethodThunk() :
mono_method(NULL) {
}
explicit GDMonoMethodThunk(GDMonoMethod *p_mono_method) {
set_from_method(p_mono_method);
}
};
template <class R, class... ParamTypes>
struct GDMonoMethodThunkR {
GDMonoMethod *mono_method;
public:
_FORCE_INLINE_ R invoke(ParamTypes... p_args, MonoException **r_exc) {
return VariadicInvokeMonoMethodR<sizeof...(ParamTypes), R, ParamTypes...>::invoke(mono_method, p_args..., r_exc);
}
_FORCE_INLINE_ bool is_null() {
return mono_method == NULL;
}
_FORCE_INLINE_ void nullify() {
mono_method = NULL;
}
_FORCE_INLINE_ void set_from_method(GDMonoMethod *p_mono_method) {
#ifdef DEBUG_ENABLED
CRASH_COND(p_mono_method == NULL);
CRASH_COND(p_mono_method->get_return_type().type_encoding == MONO_TYPE_VOID);
if (p_mono_method->is_static()) {
CRASH_COND(p_mono_method->get_parameters_count() != sizeof...(ParamTypes));
} else {
CRASH_COND(p_mono_method->get_parameters_count() != (sizeof...(ParamTypes) - 1));
}
#endif
mono_method = p_mono_method;
}
GDMonoMethodThunkR() :
mono_method(NULL) {
}
explicit GDMonoMethodThunkR(GDMonoMethod *p_mono_method) {
set_from_method(p_mono_method);
}
};
#endif
#endif // GD_MONO_METHOD_THUNK_H

View file

@ -30,8 +30,10 @@
#include "gd_mono_property.h"
#include "gd_mono_cache.h"
#include "gd_mono_class.h"
#include "gd_mono_marshal.h"
#include "gd_mono_utils.h"
#include <mono/metadata/attrdefs.h>

View file

@ -45,273 +45,13 @@
#include "../utils/macros.h"
#include "../utils/mutex_utils.h"
#include "gd_mono.h"
#include "gd_mono_cache.h"
#include "gd_mono_class.h"
#include "gd_mono_marshal.h"
#include "gd_mono_method_thunk.h"
namespace GDMonoUtils {
MonoCache mono_cache;
#define CACHE_AND_CHECK(m_var, m_val) \
{ \
CRASH_COND(m_var != NULL); \
m_var = m_val; \
ERR_FAIL_COND_MSG(!m_var, "Mono Cache: Member " #m_var " is null."); \
}
#define CACHE_CLASS_AND_CHECK(m_class, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.class_##m_class, m_val)
#define CACHE_NS_CLASS_AND_CHECK(m_ns, m_class, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.class_##m_ns##_##m_class, m_val)
#define CACHE_RAW_MONO_CLASS_AND_CHECK(m_class, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.rawclass_##m_class, m_val)
#define CACHE_FIELD_AND_CHECK(m_class, m_field, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.field_##m_class##_##m_field, m_val)
#define CACHE_METHOD_AND_CHECK(m_class, m_method, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.method_##m_class##_##m_method, m_val)
#define CACHE_METHOD_THUNK_AND_CHECK(m_class, m_method, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.methodthunk_##m_class##_##m_method, m_val)
#define CACHE_PROPERTY_AND_CHECK(m_class, m_property, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.property_##m_class##_##m_property, m_val)
void MonoCache::clear_corlib_cache() {
corlib_cache_updated = false;
class_MonoObject = NULL;
class_bool = NULL;
class_int8_t = NULL;
class_int16_t = NULL;
class_int32_t = NULL;
class_int64_t = NULL;
class_uint8_t = NULL;
class_uint16_t = NULL;
class_uint32_t = NULL;
class_uint64_t = NULL;
class_float = NULL;
class_double = NULL;
class_String = NULL;
class_IntPtr = NULL;
class_System_Collections_IEnumerable = NULL;
class_System_Collections_IDictionary = NULL;
#ifdef DEBUG_ENABLED
class_System_Diagnostics_StackTrace = NULL;
methodthunk_System_Diagnostics_StackTrace_GetFrames = NULL;
method_System_Diagnostics_StackTrace_ctor_bool = NULL;
method_System_Diagnostics_StackTrace_ctor_Exception_bool = NULL;
#endif
class_KeyNotFoundException = NULL;
}
void MonoCache::clear_godot_api_cache() {
godot_api_cache_updated = false;
rawclass_Dictionary = NULL;
class_Vector2 = NULL;
class_Rect2 = NULL;
class_Transform2D = NULL;
class_Vector3 = NULL;
class_Basis = NULL;
class_Quat = NULL;
class_Transform = NULL;
class_AABB = NULL;
class_Color = NULL;
class_Plane = NULL;
class_NodePath = NULL;
class_RID = NULL;
class_GodotObject = NULL;
class_GodotResource = NULL;
class_Node = NULL;
class_Control = NULL;
class_Spatial = NULL;
class_WeakRef = NULL;
class_Array = NULL;
class_Dictionary = NULL;
class_MarshalUtils = NULL;
class_ISerializationListener = NULL;
#ifdef DEBUG_ENABLED
class_DebuggingUtils = NULL;
methodthunk_DebuggingUtils_GetStackFrameInfo = NULL;
#endif
class_ExportAttribute = NULL;
field_ExportAttribute_hint = NULL;
field_ExportAttribute_hintString = NULL;
class_SignalAttribute = NULL;
class_ToolAttribute = NULL;
class_RemoteAttribute = NULL;
class_SyncAttribute = NULL;
class_MasterAttribute = NULL;
class_PuppetAttribute = NULL;
class_SlaveAttribute = NULL;
class_RemoteSyncAttribute = NULL;
class_MasterSyncAttribute = NULL;
class_PuppetSyncAttribute = NULL;
class_GodotMethodAttribute = NULL;
field_GodotMethodAttribute_methodName = NULL;
field_GodotObject_ptr = NULL;
field_NodePath_ptr = NULL;
field_Image_ptr = NULL;
field_RID_ptr = NULL;
methodthunk_GodotObject_Dispose = NULL;
methodthunk_Array_GetPtr = NULL;
methodthunk_Dictionary_GetPtr = NULL;
methodthunk_SignalAwaiter_SignalCallback = NULL;
methodthunk_SignalAwaiter_FailureCallback = NULL;
methodthunk_GodotTaskScheduler_Activate = NULL;
// Start of MarshalUtils methods
methodthunk_MarshalUtils_TypeIsGenericArray = NULL;
methodthunk_MarshalUtils_TypeIsGenericDictionary = NULL;
methodthunk_MarshalUtils_ArrayGetElementType = NULL;
methodthunk_MarshalUtils_DictionaryGetKeyValueTypes = NULL;
methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType = NULL;
methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType = NULL;
methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType_with_info = NULL;
methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType_with_info = NULL;
methodthunk_MarshalUtils_MakeGenericArrayType = NULL;
methodthunk_MarshalUtils_MakeGenericDictionaryType = NULL;
methodthunk_MarshalUtils_EnumerableToArray = NULL;
methodthunk_MarshalUtils_IDictionaryToDictionary = NULL;
methodthunk_MarshalUtils_GenericIDictionaryToDictionary = NULL;
// End of MarshalUtils methods
task_scheduler_handle = Ref<MonoGCHandle>();
}
#define GODOT_API_CLASS(m_class) (GDMono::get_singleton()->get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, #m_class))
#define GODOT_API_NS_CLAS(m_ns, m_class) (GDMono::get_singleton()->get_core_api_assembly()->get_class(m_ns, #m_class))
void update_corlib_cache() {
CACHE_CLASS_AND_CHECK(MonoObject, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_object_class()));
CACHE_CLASS_AND_CHECK(bool, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_boolean_class()));
CACHE_CLASS_AND_CHECK(int8_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_sbyte_class()));
CACHE_CLASS_AND_CHECK(int16_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_int16_class()));
CACHE_CLASS_AND_CHECK(int32_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_int32_class()));
CACHE_CLASS_AND_CHECK(int64_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_int64_class()));
CACHE_CLASS_AND_CHECK(uint8_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_byte_class()));
CACHE_CLASS_AND_CHECK(uint16_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_uint16_class()));
CACHE_CLASS_AND_CHECK(uint32_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_uint32_class()));
CACHE_CLASS_AND_CHECK(uint64_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_uint64_class()));
CACHE_CLASS_AND_CHECK(float, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_single_class()));
CACHE_CLASS_AND_CHECK(double, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_double_class()));
CACHE_CLASS_AND_CHECK(String, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_string_class()));
CACHE_CLASS_AND_CHECK(IntPtr, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_intptr_class()));
CACHE_CLASS_AND_CHECK(System_Collections_IEnumerable, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections", "IEnumerable"));
CACHE_CLASS_AND_CHECK(System_Collections_IDictionary, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections", "IDictionary"));
#ifdef DEBUG_ENABLED
CACHE_CLASS_AND_CHECK(System_Diagnostics_StackTrace, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Diagnostics", "StackTrace"));
CACHE_METHOD_THUNK_AND_CHECK(System_Diagnostics_StackTrace, GetFrames, (StackTrace_GetFrames)CACHED_CLASS(System_Diagnostics_StackTrace)->get_method_thunk("GetFrames"));
CACHE_METHOD_AND_CHECK(System_Diagnostics_StackTrace, ctor_bool, CACHED_CLASS(System_Diagnostics_StackTrace)->get_method_with_desc("System.Diagnostics.StackTrace:.ctor(bool)", true));
CACHE_METHOD_AND_CHECK(System_Diagnostics_StackTrace, ctor_Exception_bool, CACHED_CLASS(System_Diagnostics_StackTrace)->get_method_with_desc("System.Diagnostics.StackTrace:.ctor(System.Exception,bool)", true));
#endif
CACHE_CLASS_AND_CHECK(KeyNotFoundException, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections.Generic", "KeyNotFoundException"));
mono_cache.corlib_cache_updated = true;
}
void update_godot_api_cache() {
CACHE_CLASS_AND_CHECK(Vector2, GODOT_API_CLASS(Vector2));
CACHE_CLASS_AND_CHECK(Rect2, GODOT_API_CLASS(Rect2));
CACHE_CLASS_AND_CHECK(Transform2D, GODOT_API_CLASS(Transform2D));
CACHE_CLASS_AND_CHECK(Vector3, GODOT_API_CLASS(Vector3));
CACHE_CLASS_AND_CHECK(Basis, GODOT_API_CLASS(Basis));
CACHE_CLASS_AND_CHECK(Quat, GODOT_API_CLASS(Quat));
CACHE_CLASS_AND_CHECK(Transform, GODOT_API_CLASS(Transform));
CACHE_CLASS_AND_CHECK(AABB, GODOT_API_CLASS(AABB));
CACHE_CLASS_AND_CHECK(Color, GODOT_API_CLASS(Color));
CACHE_CLASS_AND_CHECK(Plane, GODOT_API_CLASS(Plane));
CACHE_CLASS_AND_CHECK(NodePath, GODOT_API_CLASS(NodePath));
CACHE_CLASS_AND_CHECK(RID, GODOT_API_CLASS(RID));
CACHE_CLASS_AND_CHECK(GodotObject, GODOT_API_CLASS(Object));
CACHE_CLASS_AND_CHECK(GodotResource, GODOT_API_CLASS(Resource));
CACHE_CLASS_AND_CHECK(Node, GODOT_API_CLASS(Node));
CACHE_CLASS_AND_CHECK(Control, GODOT_API_CLASS(Control));
CACHE_CLASS_AND_CHECK(Spatial, GODOT_API_CLASS(Spatial));
CACHE_CLASS_AND_CHECK(WeakRef, GODOT_API_CLASS(WeakRef));
CACHE_CLASS_AND_CHECK(Array, GODOT_API_NS_CLAS(BINDINGS_NAMESPACE_COLLECTIONS, Array));
CACHE_CLASS_AND_CHECK(Dictionary, GODOT_API_NS_CLAS(BINDINGS_NAMESPACE_COLLECTIONS, Dictionary));
CACHE_CLASS_AND_CHECK(MarshalUtils, GODOT_API_CLASS(MarshalUtils));
CACHE_CLASS_AND_CHECK(ISerializationListener, GODOT_API_CLASS(ISerializationListener));
#ifdef DEBUG_ENABLED
CACHE_CLASS_AND_CHECK(DebuggingUtils, GODOT_API_CLASS(DebuggingUtils));
#endif
// Attributes
CACHE_CLASS_AND_CHECK(ExportAttribute, GODOT_API_CLASS(ExportAttribute));
CACHE_FIELD_AND_CHECK(ExportAttribute, hint, CACHED_CLASS(ExportAttribute)->get_field("hint"));
CACHE_FIELD_AND_CHECK(ExportAttribute, hintString, CACHED_CLASS(ExportAttribute)->get_field("hintString"));
CACHE_CLASS_AND_CHECK(SignalAttribute, GODOT_API_CLASS(SignalAttribute));
CACHE_CLASS_AND_CHECK(ToolAttribute, GODOT_API_CLASS(ToolAttribute));
CACHE_CLASS_AND_CHECK(RemoteAttribute, GODOT_API_CLASS(RemoteAttribute));
CACHE_CLASS_AND_CHECK(SyncAttribute, GODOT_API_CLASS(SyncAttribute));
CACHE_CLASS_AND_CHECK(MasterAttribute, GODOT_API_CLASS(MasterAttribute));
CACHE_CLASS_AND_CHECK(PuppetAttribute, GODOT_API_CLASS(PuppetAttribute));
CACHE_CLASS_AND_CHECK(SlaveAttribute, GODOT_API_CLASS(SlaveAttribute));
CACHE_CLASS_AND_CHECK(RemoteSyncAttribute, GODOT_API_CLASS(RemoteSyncAttribute));
CACHE_CLASS_AND_CHECK(MasterSyncAttribute, GODOT_API_CLASS(MasterSyncAttribute));
CACHE_CLASS_AND_CHECK(PuppetSyncAttribute, GODOT_API_CLASS(PuppetSyncAttribute));
CACHE_CLASS_AND_CHECK(GodotMethodAttribute, GODOT_API_CLASS(GodotMethodAttribute));
CACHE_FIELD_AND_CHECK(GodotMethodAttribute, methodName, CACHED_CLASS(GodotMethodAttribute)->get_field("methodName"));
CACHE_FIELD_AND_CHECK(GodotObject, ptr, CACHED_CLASS(GodotObject)->get_field(BINDINGS_PTR_FIELD));
CACHE_FIELD_AND_CHECK(NodePath, ptr, CACHED_CLASS(NodePath)->get_field(BINDINGS_PTR_FIELD));
CACHE_FIELD_AND_CHECK(RID, ptr, CACHED_CLASS(RID)->get_field(BINDINGS_PTR_FIELD));
CACHE_METHOD_THUNK_AND_CHECK(GodotObject, Dispose, (GodotObject_Dispose)CACHED_CLASS(GodotObject)->get_method_thunk("Dispose", 0));
CACHE_METHOD_THUNK_AND_CHECK(Array, GetPtr, (Array_GetPtr)GODOT_API_NS_CLAS(BINDINGS_NAMESPACE_COLLECTIONS, Array)->get_method_thunk("GetPtr", 0));
CACHE_METHOD_THUNK_AND_CHECK(Dictionary, GetPtr, (Dictionary_GetPtr)GODOT_API_NS_CLAS(BINDINGS_NAMESPACE_COLLECTIONS, Dictionary)->get_method_thunk("GetPtr", 0));
CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, SignalCallback, (SignalAwaiter_SignalCallback)GODOT_API_CLASS(SignalAwaiter)->get_method_thunk("SignalCallback", 1));
CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, FailureCallback, (SignalAwaiter_FailureCallback)GODOT_API_CLASS(SignalAwaiter)->get_method_thunk("FailureCallback", 0));
CACHE_METHOD_THUNK_AND_CHECK(GodotTaskScheduler, Activate, (GodotTaskScheduler_Activate)GODOT_API_CLASS(GodotTaskScheduler)->get_method_thunk("Activate", 0));
// Start of MarshalUtils methods
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericArray, (TypeIsGenericArray)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("TypeIsGenericArray", 1));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericDictionary, (TypeIsGenericDictionary)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("TypeIsGenericDictionary", 1));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, ArrayGetElementType, (ArrayGetElementType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("ArrayGetElementType", 2));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, DictionaryGetKeyValueTypes, (DictionaryGetKeyValueTypes)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("DictionaryGetKeyValueTypes", 3));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIEnumerableIsAssignableFromType, (GenericIEnumerableIsAssignableFromType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIEnumerableIsAssignableFromType", 1));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIDictionaryIsAssignableFromType, (GenericIDictionaryIsAssignableFromType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIDictionaryIsAssignableFromType", 1));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIEnumerableIsAssignableFromType_with_info, (GenericIEnumerableIsAssignableFromType_with_info)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIEnumerableIsAssignableFromType", 2));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIDictionaryIsAssignableFromType_with_info, (GenericIDictionaryIsAssignableFromType_with_info)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIDictionaryIsAssignableFromType", 3));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, MakeGenericArrayType, (MakeGenericArrayType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("MakeGenericArrayType", 1));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, MakeGenericDictionaryType, (MakeGenericDictionaryType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("MakeGenericDictionaryType", 2));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, EnumerableToArray, (EnumerableToArray)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("EnumerableToArray", 2));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, IDictionaryToDictionary, (IDictionaryToDictionary)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("IDictionaryToDictionary", 2));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIDictionaryToDictionary, (GenericIDictionaryToDictionary)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIDictionaryToDictionary", 2));
// End of MarshalUtils methods
#ifdef DEBUG_ENABLED
CACHE_METHOD_THUNK_AND_CHECK(DebuggingUtils, GetStackFrameInfo, (DebugUtils_StackFrameInfo)GODOT_API_CLASS(DebuggingUtils)->get_method_thunk("GetStackFrameInfo", 4));
#endif
// TODO Move to CSharpLanguage::init() and do handle disposal
MonoObject *task_scheduler = mono_object_new(mono_domain_get(), GODOT_API_CLASS(GodotTaskScheduler)->get_mono_ptr());
GDMonoUtils::runtime_object_init(task_scheduler, GODOT_API_CLASS(GodotTaskScheduler));
mono_cache.task_scheduler_handle = MonoGCHandle::create_strong(task_scheduler);
mono_cache.godot_api_cache_updated = true;
}
MonoObject *unmanaged_get_managed(Object *unmanaged) {
if (!unmanaged)
@ -386,7 +126,7 @@ void set_main_thread(MonoThread *p_thread) {
void attach_current_thread() {
ERR_FAIL_COND(!GDMono::get_singleton()->is_runtime_initialized());
MonoThread *mono_thread = mono_thread_attach(mono_domain_get());
MonoThread *mono_thread = mono_thread_attach(mono_get_root_domain());
ERR_FAIL_NULL(mono_thread);
}
@ -421,7 +161,7 @@ GDMonoClass *type_get_proxy_class(const StringName &p_type) {
if (klass && klass->is_static()) {
// A static class means this is a Godot singleton class. If an instance is needed we use Godot.Object.
return mono_cache.class_GodotObject;
return GDMonoCache::cached_data.class_GodotObject;
}
#ifdef TOOLS_ENABLED
@ -751,16 +491,16 @@ uint64_t unbox_enum_value(MonoObject *p_boxed, MonoType *p_enum_basetype, bool &
}
void dispose(MonoObject *p_mono_object, MonoException **r_exc) {
invoke_method_thunk(CACHED_METHOD_THUNK(GodotObject, Dispose), p_mono_object, r_exc);
CACHED_METHOD_THUNK(GodotObject, Dispose).invoke(p_mono_object, r_exc);
}
namespace Marshal {
#ifdef MONO_GLUE_ENABLED
#ifdef TOOLS_ENABLED
#define NO_GLUE_RET(m_ret) \
{ \
if (!mono_cache.godot_api_cache_updated) return m_ret; \
#define NO_GLUE_RET(m_ret) \
{ \
if (!GDMonoCache::cached_data.godot_api_cache_updated) return m_ret; \
}
#else
#define NO_GLUE_RET(m_ret) \
@ -773,68 +513,60 @@ namespace Marshal {
bool type_is_generic_array(MonoReflectionType *p_reftype) {
NO_GLUE_RET(false);
TypeIsGenericArray thunk = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray);
MonoException *exc = NULL;
MonoBoolean res = invoke_method_thunk(thunk, p_reftype, &exc);
MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray).invoke(p_reftype, &exc);
UNHANDLED_EXCEPTION(exc);
return (bool)res;
}
bool type_is_generic_dictionary(MonoReflectionType *p_reftype) {
NO_GLUE_RET(false);
TypeIsGenericDictionary thunk = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary);
MonoException *exc = NULL;
MonoBoolean res = invoke_method_thunk(thunk, p_reftype, &exc);
MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary).invoke(p_reftype, &exc);
UNHANDLED_EXCEPTION(exc);
return (bool)res;
}
void array_get_element_type(MonoReflectionType *p_array_reftype, MonoReflectionType **r_elem_reftype) {
ArrayGetElementType thunk = CACHED_METHOD_THUNK(MarshalUtils, ArrayGetElementType);
MonoException *exc = NULL;
invoke_method_thunk(thunk, p_array_reftype, r_elem_reftype, &exc);
CACHED_METHOD_THUNK(MarshalUtils, ArrayGetElementType).invoke(p_array_reftype, r_elem_reftype, &exc);
UNHANDLED_EXCEPTION(exc);
}
void dictionary_get_key_value_types(MonoReflectionType *p_dict_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype) {
DictionaryGetKeyValueTypes thunk = CACHED_METHOD_THUNK(MarshalUtils, DictionaryGetKeyValueTypes);
MonoException *exc = NULL;
invoke_method_thunk(thunk, p_dict_reftype, r_key_reftype, r_value_reftype, &exc);
CACHED_METHOD_THUNK(MarshalUtils, DictionaryGetKeyValueTypes).invoke(p_dict_reftype, r_key_reftype, r_value_reftype, &exc);
UNHANDLED_EXCEPTION(exc);
}
bool generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype) {
NO_GLUE_RET(false);
GenericIEnumerableIsAssignableFromType thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIEnumerableIsAssignableFromType);
MonoException *exc = NULL;
MonoBoolean res = invoke_method_thunk(thunk, p_reftype, &exc);
MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, GenericIEnumerableIsAssignableFromType).invoke(p_reftype, &exc);
UNHANDLED_EXCEPTION(exc);
return (bool)res;
}
bool generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype) {
NO_GLUE_RET(false);
GenericIDictionaryIsAssignableFromType thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryIsAssignableFromType);
MonoException *exc = NULL;
MonoBoolean res = invoke_method_thunk(thunk, p_reftype, &exc);
MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryIsAssignableFromType).invoke(p_reftype, &exc);
UNHANDLED_EXCEPTION(exc);
return (bool)res;
}
bool generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_elem_reftype) {
NO_GLUE_RET(false);
GenericIEnumerableIsAssignableFromType_with_info thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIEnumerableIsAssignableFromType_with_info);
MonoException *exc = NULL;
MonoBoolean res = invoke_method_thunk(thunk, p_reftype, r_elem_reftype, &exc);
MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, GenericIEnumerableIsAssignableFromType_with_info).invoke(p_reftype, r_elem_reftype, &exc);
UNHANDLED_EXCEPTION(exc);
return (bool)res;
}
bool generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype) {
NO_GLUE_RET(false);
GenericIDictionaryIsAssignableFromType_with_info thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryIsAssignableFromType_with_info);
MonoException *exc = NULL;
MonoBoolean res = invoke_method_thunk(thunk, p_reftype, r_key_reftype, r_value_reftype, &exc);
MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryIsAssignableFromType_with_info).invoke(p_reftype, r_key_reftype, r_value_reftype, &exc);
UNHANDLED_EXCEPTION(exc);
return (bool)res;
}
@ -842,9 +574,8 @@ bool generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype, MonoR
Array enumerable_to_array(MonoObject *p_enumerable) {
NO_GLUE_RET(Array());
Array result;
EnumerableToArray thunk = CACHED_METHOD_THUNK(MarshalUtils, EnumerableToArray);
MonoException *exc = NULL;
invoke_method_thunk(thunk, p_enumerable, &result, &exc);
CACHED_METHOD_THUNK(MarshalUtils, EnumerableToArray).invoke(p_enumerable, &result, &exc);
UNHANDLED_EXCEPTION(exc);
return result;
}
@ -852,9 +583,8 @@ Array enumerable_to_array(MonoObject *p_enumerable) {
Dictionary idictionary_to_dictionary(MonoObject *p_idictionary) {
NO_GLUE_RET(Dictionary());
Dictionary result;
IDictionaryToDictionary thunk = CACHED_METHOD_THUNK(MarshalUtils, IDictionaryToDictionary);
MonoException *exc = NULL;
invoke_method_thunk(thunk, p_idictionary, &result, &exc);
CACHED_METHOD_THUNK(MarshalUtils, IDictionaryToDictionary).invoke(p_idictionary, &result, &exc);
UNHANDLED_EXCEPTION(exc);
return result;
}
@ -862,27 +592,24 @@ Dictionary idictionary_to_dictionary(MonoObject *p_idictionary) {
Dictionary generic_idictionary_to_dictionary(MonoObject *p_generic_idictionary) {
NO_GLUE_RET(Dictionary());
Dictionary result;
GenericIDictionaryToDictionary thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryToDictionary);
MonoException *exc = NULL;
invoke_method_thunk(thunk, p_generic_idictionary, &result, &exc);
CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryToDictionary).invoke(p_generic_idictionary, &result, &exc);
UNHANDLED_EXCEPTION(exc);
return result;
}
GDMonoClass *make_generic_array_type(MonoReflectionType *p_elem_reftype) {
NO_GLUE_RET(NULL);
MakeGenericArrayType thunk = CACHED_METHOD_THUNK(MarshalUtils, MakeGenericArrayType);
MonoException *exc = NULL;
MonoReflectionType *reftype = invoke_method_thunk(thunk, p_elem_reftype, &exc);
MonoReflectionType *reftype = CACHED_METHOD_THUNK(MarshalUtils, MakeGenericArrayType).invoke(p_elem_reftype, &exc);
UNHANDLED_EXCEPTION(exc);
return GDMono::get_singleton()->get_class(mono_class_from_mono_type(mono_reflection_type_get_type(reftype)));
}
GDMonoClass *make_generic_dictionary_type(MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype) {
NO_GLUE_RET(NULL);
MakeGenericDictionaryType thunk = CACHED_METHOD_THUNK(MarshalUtils, MakeGenericDictionaryType);
MonoException *exc = NULL;
MonoReflectionType *reftype = invoke_method_thunk(thunk, p_key_reftype, p_value_reftype, &exc);
MonoReflectionType *reftype = CACHED_METHOD_THUNK(MarshalUtils, MakeGenericDictionaryType).invoke(p_key_reftype, p_value_reftype, &exc);
UNHANDLED_EXCEPTION(exc);
return GDMono::get_singleton()->get_class(mono_class_from_mono_type(mono_reflection_type_get_type(reftype)));
}

View file

@ -49,33 +49,6 @@
namespace GDMonoUtils {
typedef void (*GodotObject_Dispose)(MonoObject *, MonoException **);
typedef Array *(*Array_GetPtr)(MonoObject *, MonoException **);
typedef Dictionary *(*Dictionary_GetPtr)(MonoObject *, MonoException **);
typedef MonoObject *(*SignalAwaiter_SignalCallback)(MonoObject *, MonoArray *, MonoException **);
typedef MonoObject *(*SignalAwaiter_FailureCallback)(MonoObject *, MonoException **);
typedef MonoObject *(*GodotTaskScheduler_Activate)(MonoObject *, MonoException **);
typedef MonoArray *(*StackTrace_GetFrames)(MonoObject *, MonoException **);
typedef void (*DebugUtils_StackFrameInfo)(MonoObject *, MonoString **, int *, MonoString **, MonoException **);
typedef MonoBoolean (*TypeIsGenericArray)(MonoReflectionType *, MonoException **);
typedef MonoBoolean (*TypeIsGenericDictionary)(MonoReflectionType *, MonoException **);
typedef void (*ArrayGetElementType)(MonoReflectionType *, MonoReflectionType **, MonoException **);
typedef void (*DictionaryGetKeyValueTypes)(MonoReflectionType *, MonoReflectionType **, MonoReflectionType **, MonoException **);
typedef MonoBoolean (*GenericIEnumerableIsAssignableFromType)(MonoReflectionType *, MonoException **);
typedef MonoBoolean (*GenericIDictionaryIsAssignableFromType)(MonoReflectionType *, MonoException **);
typedef MonoBoolean (*GenericIEnumerableIsAssignableFromType_with_info)(MonoReflectionType *, MonoReflectionType **, MonoException **);
typedef MonoBoolean (*GenericIDictionaryIsAssignableFromType_with_info)(MonoReflectionType *, MonoReflectionType **, MonoReflectionType **, MonoException **);
typedef MonoReflectionType *(*MakeGenericArrayType)(MonoReflectionType *, MonoException **);
typedef MonoReflectionType *(*MakeGenericDictionaryType)(MonoReflectionType *, MonoReflectionType *, MonoException **);
typedef void (*EnumerableToArray)(MonoObject *, Array *, MonoException **);
typedef void (*IDictionaryToDictionary)(MonoObject *, Dictionary *, MonoException **);
typedef void (*GenericIDictionaryToDictionary)(MonoObject *, Dictionary *, MonoException **);
namespace Marshal {
bool type_is_generic_array(MonoReflectionType *p_reftype);
@ -98,157 +71,6 @@ Dictionary generic_idictionary_to_dictionary(MonoObject *p_generic_idictionary);
} // namespace Marshal
// End of MarshalUtils methods
struct MonoCache {
// -----------------------------------------------
// corlib classes
// Let's use the no-namespace format for these too
GDMonoClass *class_MonoObject;
GDMonoClass *class_bool;
GDMonoClass *class_int8_t;
GDMonoClass *class_int16_t;
GDMonoClass *class_int32_t;
GDMonoClass *class_int64_t;
GDMonoClass *class_uint8_t;
GDMonoClass *class_uint16_t;
GDMonoClass *class_uint32_t;
GDMonoClass *class_uint64_t;
GDMonoClass *class_float;
GDMonoClass *class_double;
GDMonoClass *class_String;
GDMonoClass *class_IntPtr;
GDMonoClass *class_System_Collections_IEnumerable;
GDMonoClass *class_System_Collections_IDictionary;
#ifdef DEBUG_ENABLED
GDMonoClass *class_System_Diagnostics_StackTrace;
StackTrace_GetFrames methodthunk_System_Diagnostics_StackTrace_GetFrames;
GDMonoMethod *method_System_Diagnostics_StackTrace_ctor_bool;
GDMonoMethod *method_System_Diagnostics_StackTrace_ctor_Exception_bool;
#endif
GDMonoClass *class_KeyNotFoundException;
MonoClass *rawclass_Dictionary;
// -----------------------------------------------
GDMonoClass *class_Vector2;
GDMonoClass *class_Rect2;
GDMonoClass *class_Transform2D;
GDMonoClass *class_Vector3;
GDMonoClass *class_Basis;
GDMonoClass *class_Quat;
GDMonoClass *class_Transform;
GDMonoClass *class_AABB;
GDMonoClass *class_Color;
GDMonoClass *class_Plane;
GDMonoClass *class_NodePath;
GDMonoClass *class_RID;
GDMonoClass *class_GodotObject;
GDMonoClass *class_GodotResource;
GDMonoClass *class_Node;
GDMonoClass *class_Control;
GDMonoClass *class_Spatial;
GDMonoClass *class_WeakRef;
GDMonoClass *class_Array;
GDMonoClass *class_Dictionary;
GDMonoClass *class_MarshalUtils;
GDMonoClass *class_ISerializationListener;
#ifdef DEBUG_ENABLED
GDMonoClass *class_DebuggingUtils;
DebugUtils_StackFrameInfo methodthunk_DebuggingUtils_GetStackFrameInfo;
#endif
GDMonoClass *class_ExportAttribute;
GDMonoField *field_ExportAttribute_hint;
GDMonoField *field_ExportAttribute_hintString;
GDMonoClass *class_SignalAttribute;
GDMonoClass *class_ToolAttribute;
GDMonoClass *class_RemoteAttribute;
GDMonoClass *class_SyncAttribute;
GDMonoClass *class_RemoteSyncAttribute;
GDMonoClass *class_MasterSyncAttribute;
GDMonoClass *class_PuppetSyncAttribute;
GDMonoClass *class_MasterAttribute;
GDMonoClass *class_PuppetAttribute;
GDMonoClass *class_SlaveAttribute;
GDMonoClass *class_GodotMethodAttribute;
GDMonoField *field_GodotMethodAttribute_methodName;
GDMonoField *field_GodotObject_ptr;
GDMonoField *field_NodePath_ptr;
GDMonoField *field_Image_ptr;
GDMonoField *field_RID_ptr;
GodotObject_Dispose methodthunk_GodotObject_Dispose;
Array_GetPtr methodthunk_Array_GetPtr;
Dictionary_GetPtr methodthunk_Dictionary_GetPtr;
SignalAwaiter_SignalCallback methodthunk_SignalAwaiter_SignalCallback;
SignalAwaiter_FailureCallback methodthunk_SignalAwaiter_FailureCallback;
GodotTaskScheduler_Activate methodthunk_GodotTaskScheduler_Activate;
// Start of MarshalUtils methods
TypeIsGenericArray methodthunk_MarshalUtils_TypeIsGenericArray;
TypeIsGenericDictionary methodthunk_MarshalUtils_TypeIsGenericDictionary;
ArrayGetElementType methodthunk_MarshalUtils_ArrayGetElementType;
DictionaryGetKeyValueTypes methodthunk_MarshalUtils_DictionaryGetKeyValueTypes;
GenericIEnumerableIsAssignableFromType methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType;
GenericIDictionaryIsAssignableFromType methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType;
GenericIEnumerableIsAssignableFromType_with_info methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType_with_info;
GenericIDictionaryIsAssignableFromType_with_info methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType_with_info;
MakeGenericArrayType methodthunk_MarshalUtils_MakeGenericArrayType;
MakeGenericDictionaryType methodthunk_MarshalUtils_MakeGenericDictionaryType;
EnumerableToArray methodthunk_MarshalUtils_EnumerableToArray;
IDictionaryToDictionary methodthunk_MarshalUtils_IDictionaryToDictionary;
GenericIDictionaryToDictionary methodthunk_MarshalUtils_GenericIDictionaryToDictionary;
// End of MarshalUtils methods
Ref<MonoGCHandle> task_scheduler_handle;
bool corlib_cache_updated;
bool godot_api_cache_updated;
void clear_corlib_cache();
void clear_godot_api_cache();
MonoCache() {
clear_corlib_cache();
clear_godot_api_cache();
}
};
extern MonoCache mono_cache;
void update_corlib_cache();
void update_godot_api_cache();
inline void clear_corlib_cache() {
mono_cache.clear_corlib_cache();
}
inline void clear_godot_api_cache() {
mono_cache.clear_godot_api_cache();
}
_FORCE_INLINE_ bool tools_godot_api_check() {
#ifdef TOOLS_ENABLED
return mono_cache.godot_api_cache_updated;
#else
return true; // Assume it's updated if this was called, otherwise it's a bug
#endif
}
_FORCE_INLINE_ void hash_combine(uint32_t &p_hash, const uint32_t &p_with_hash) {
p_hash ^= p_with_hash + 0x9e3779b9 + (p_hash << 6) + (p_hash >> 2);
}
@ -324,20 +146,6 @@ void dispose(MonoObject *p_mono_object, MonoException **r_exc);
#define NATIVE_GDMONOCLASS_NAME(m_class) (GDMonoMarshal::mono_string_to_godot((MonoString *)m_class->get_field(BINDINGS_NATIVE_NAME_FIELD)->get_value(NULL)))
#define CACHED_CLASS(m_class) (GDMonoUtils::mono_cache.class_##m_class)
#define CACHED_CLASS_RAW(m_class) (GDMonoUtils::mono_cache.class_##m_class->get_mono_ptr())
#define CACHED_RAW_MONO_CLASS(m_class) (GDMonoUtils::mono_cache.rawclass_##m_class)
#define CACHED_FIELD(m_class, m_field) (GDMonoUtils::mono_cache.field_##m_class##_##m_field)
#define CACHED_METHOD(m_class, m_method) (GDMonoUtils::mono_cache.method_##m_class##_##m_method)
#define CACHED_METHOD_THUNK(m_class, m_method) (GDMonoUtils::mono_cache.methodthunk_##m_class##_##m_method)
#define CACHED_PROPERTY(m_class, m_property) (GDMonoUtils::mono_cache.property_##m_class##_##m_property)
#ifdef REAL_T_IS_DOUBLE
#define REAL_T_MONOCLASS CACHED_CLASS_RAW(double)
#else
#define REAL_T_MONOCLASS CACHED_CLASS_RAW(float)
#endif
#define GD_MONO_BEGIN_RUNTIME_INVOKE \
int &_runtime_invoke_count_ref = GDMonoUtils::get_runtime_invoke_count_ref(); \
_runtime_invoke_count_ref += 1;
@ -345,93 +153,4 @@ void dispose(MonoObject *p_mono_object, MonoException **r_exc);
#define GD_MONO_END_RUNTIME_INVOKE \
_runtime_invoke_count_ref -= 1;
inline void invoke_method_thunk(void (*p_method_thunk)()) {
GD_MONO_BEGIN_RUNTIME_INVOKE;
p_method_thunk();
GD_MONO_END_RUNTIME_INVOKE;
}
template <class R>
R invoke_method_thunk(R (*p_method_thunk)()) {
GD_MONO_BEGIN_RUNTIME_INVOKE;
R r = p_method_thunk();
GD_MONO_END_RUNTIME_INVOKE;
return r;
}
template <class P1>
void invoke_method_thunk(void (*p_method_thunk)(P1), P1 p_arg1) {
GD_MONO_BEGIN_RUNTIME_INVOKE;
p_method_thunk(p_arg1);
GD_MONO_END_RUNTIME_INVOKE;
}
template <class R, class P1>
R invoke_method_thunk(R (*p_method_thunk)(P1), P1 p_arg1) {
GD_MONO_BEGIN_RUNTIME_INVOKE;
R r = p_method_thunk(p_arg1);
GD_MONO_END_RUNTIME_INVOKE;
return r;
}
template <class P1, class P2>
void invoke_method_thunk(void (*p_method_thunk)(P1, P2), P1 p_arg1, P2 p_arg2) {
GD_MONO_BEGIN_RUNTIME_INVOKE;
p_method_thunk(p_arg1, p_arg2);
GD_MONO_END_RUNTIME_INVOKE;
}
template <class R, class P1, class P2>
R invoke_method_thunk(R (*p_method_thunk)(P1, P2), P1 p_arg1, P2 p_arg2) {
GD_MONO_BEGIN_RUNTIME_INVOKE;
R r = p_method_thunk(p_arg1, p_arg2);
GD_MONO_END_RUNTIME_INVOKE;
return r;
}
template <class P1, class P2, class P3>
void invoke_method_thunk(void (*p_method_thunk)(P1, P2, P3), P1 p_arg1, P2 p_arg2, P3 p_arg3) {
GD_MONO_BEGIN_RUNTIME_INVOKE;
p_method_thunk(p_arg1, p_arg2, p_arg3);
GD_MONO_END_RUNTIME_INVOKE;
}
template <class R, class P1, class P2, class P3>
R invoke_method_thunk(R (*p_method_thunk)(P1, P2, P3), P1 p_arg1, P2 p_arg2, P3 p_arg3) {
GD_MONO_BEGIN_RUNTIME_INVOKE;
R r = p_method_thunk(p_arg1, p_arg2, p_arg3);
GD_MONO_END_RUNTIME_INVOKE;
return r;
}
template <class P1, class P2, class P3, class P4>
void invoke_method_thunk(void (*p_method_thunk)(P1, P2, P3, P4), P1 p_arg1, P2 p_arg2, P3 p_arg3, P4 p_arg4) {
GD_MONO_BEGIN_RUNTIME_INVOKE;
p_method_thunk(p_arg1, p_arg2, p_arg3, p_arg4);
GD_MONO_END_RUNTIME_INVOKE;
}
template <class R, class P1, class P2, class P3, class P4>
R invoke_method_thunk(R (*p_method_thunk)(P1, P2, P3, P4), P1 p_arg1, P2 p_arg2, P3 p_arg3, P4 p_arg4) {
GD_MONO_BEGIN_RUNTIME_INVOKE;
R r = p_method_thunk(p_arg1, p_arg2, p_arg3, p_arg4);
GD_MONO_END_RUNTIME_INVOKE;
return r;
}
template <class P1, class P2, class P3, class P4, class P5>
void invoke_method_thunk(void (*p_method_thunk)(P1, P2, P3, P4, P5), P1 p_arg1, P2 p_arg2, P3 p_arg3, P4 p_arg4, P5 p_arg5) {
GD_MONO_BEGIN_RUNTIME_INVOKE;
p_method_thunk(p_arg1, p_arg2, p_arg3, p_arg4, p_arg5);
GD_MONO_END_RUNTIME_INVOKE;
}
template <class R, class P1, class P2, class P3, class P4, class P5>
R invoke_method_thunk(R (*p_method_thunk)(P1, P2, P3, P4, P5), P1 p_arg1, P2 p_arg2, P3 p_arg3, P4 p_arg4, P5 p_arg5) {
GD_MONO_BEGIN_RUNTIME_INVOKE;
R r = p_method_thunk(p_arg1, p_arg2, p_arg3, p_arg4, p_arg5);
GD_MONO_END_RUNTIME_INVOKE;
return r;
}
#endif // GD_MONOUTILS_H

View file

@ -31,6 +31,7 @@
#include "signal_awaiter_utils.h"
#include "csharp_script.h"
#include "mono_gd/gd_mono_cache.h"
#include "mono_gd/gd_mono_class.h"
#include "mono_gd/gd_mono_marshal.h"
#include "mono_gd/gd_mono_utils.h"
@ -98,7 +99,7 @@ Variant SignalAwaiterHandle::_signal_callback(const Variant **p_args, int p_argc
MonoException *exc = NULL;
GD_MONO_BEGIN_RUNTIME_INVOKE;
invoke_method_thunk(CACHED_METHOD_THUNK(SignalAwaiter, SignalCallback), get_target(), signal_args, &exc);
CACHED_METHOD_THUNK(SignalAwaiter, SignalCallback).invoke(get_target(), signal_args, &exc);
GD_MONO_END_RUNTIME_INVOKE;
if (exc) {
@ -130,7 +131,7 @@ SignalAwaiterHandle::~SignalAwaiterHandle() {
if (awaiter) {
MonoException *exc = NULL;
GD_MONO_BEGIN_RUNTIME_INVOKE;
invoke_method_thunk(CACHED_METHOD_THUNK(SignalAwaiter, FailureCallback), awaiter, &exc);
CACHED_METHOD_THUNK(SignalAwaiter, FailureCallback).invoke(awaiter, &exc);
GD_MONO_END_RUNTIME_INVOKE;
if (exc) {