Merge pull request #33603 from neikeq/ばか
Mono/C#: WebAssembly and initial AOT support
This commit is contained in:
commit
6cde380cee
48 changed files with 2054 additions and 1182 deletions
|
@ -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'
|
||||
|
|
|
@ -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
|
|
@ -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 [
|
||||
|
|
|
@ -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"
|
||||
|
@ -130,8 +132,6 @@ void CSharpLanguage::init() {
|
|||
|
||||
#ifdef TOOLS_ENABLED
|
||||
EditorNode::add_init_callback(&_editor_init_callback);
|
||||
|
||||
GLOBAL_DEF("mono/export/include_scripts_content", false);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -545,7 +545,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 +571,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 +583,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 +593,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 +616,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 +1077,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 +1086,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
|
||||
|
|
|
@ -44,7 +44,7 @@ namespace GodotTools.Build
|
|||
{
|
||||
get
|
||||
{
|
||||
if (OS.IsWindows())
|
||||
if (OS.IsWindows)
|
||||
{
|
||||
return (BuildManager.BuildTool) EditorSettings.GetSetting("mono/builds/build_tool")
|
||||
== BuildManager.BuildTool.MsBuildMono;
|
||||
|
|
|
@ -21,7 +21,7 @@ namespace GodotTools.Build
|
|||
var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
|
||||
var buildTool = (BuildManager.BuildTool) editorSettings.GetSetting("mono/builds/build_tool");
|
||||
|
||||
if (OS.IsWindows())
|
||||
if (OS.IsWindows)
|
||||
{
|
||||
switch (buildTool)
|
||||
{
|
||||
|
@ -59,7 +59,7 @@ namespace GodotTools.Build
|
|||
}
|
||||
}
|
||||
|
||||
if (OS.IsUnix())
|
||||
if (OS.IsUnixLike())
|
||||
{
|
||||
if (buildTool == BuildManager.BuildTool.MsBuildMono)
|
||||
{
|
||||
|
@ -91,7 +91,7 @@ namespace GodotTools.Build
|
|||
{
|
||||
var result = new List<string>();
|
||||
|
||||
if (OS.IsOSX())
|
||||
if (OS.IsOSX)
|
||||
{
|
||||
result.Add("/Library/Frameworks/Mono.framework/Versions/Current/bin/");
|
||||
result.Add("/usr/local/var/homebrew/linked/mono/bin/");
|
||||
|
@ -128,7 +128,7 @@ namespace GodotTools.Build
|
|||
|
||||
private static string FindMsBuildToolsPathOnWindows()
|
||||
{
|
||||
if (!OS.IsWindows())
|
||||
if (!OS.IsWindows)
|
||||
throw new PlatformNotSupportedException();
|
||||
|
||||
// Try to find 15.0 with vswhere
|
||||
|
|
|
@ -246,7 +246,7 @@ namespace GodotTools
|
|||
{
|
||||
// Build tool settings
|
||||
|
||||
EditorDef("mono/builds/build_tool", OS.IsWindows() ? BuildTool.MsBuildVs : BuildTool.MsBuildMono);
|
||||
EditorDef("mono/builds/build_tool", OS.IsWindows ? BuildTool.MsBuildVs : BuildTool.MsBuildMono);
|
||||
|
||||
var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
|
||||
|
||||
|
@ -255,7 +255,7 @@ namespace GodotTools
|
|||
["type"] = Godot.Variant.Type.Int,
|
||||
["name"] = "mono/builds/build_tool",
|
||||
["hint"] = Godot.PropertyHint.Enum,
|
||||
["hint_string"] = OS.IsWindows() ?
|
||||
["hint_string"] = OS.IsWindows ?
|
||||
$"{PropNameMsbuildMono},{PropNameMsbuildVs}" :
|
||||
$"{PropNameMsbuildMono}"
|
||||
});
|
||||
|
|
615
modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
Normal file
615
modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
Normal file
|
@ -0,0 +1,615 @@
|
|||
using Godot;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using GodotTools.Core;
|
||||
using GodotTools.Internals;
|
||||
using static GodotTools.Internals.Globals;
|
||||
using Directory = GodotTools.Utils.Directory;
|
||||
using File = GodotTools.Utils.File;
|
||||
using OS = GodotTools.Utils.OS;
|
||||
using Path = System.IO.Path;
|
||||
|
||||
namespace GodotTools.Export
|
||||
{
|
||||
public class ExportPlugin : EditorExportPlugin
|
||||
{
|
||||
public void RegisterExportSettings()
|
||||
{
|
||||
// TODO: These would be better as export preset options, but that doesn't seem to be supported yet
|
||||
|
||||
GlobalDef("mono/export/include_scripts_content", false);
|
||||
|
||||
GlobalDef("mono/export/aot/enabled", false);
|
||||
GlobalDef("mono/export/aot/full_aot", false);
|
||||
|
||||
// --aot or --aot=opt1,opt2 (use 'mono --aot=help AuxAssembly.dll' to list AOT options)
|
||||
GlobalDef("mono/export/aot/extra_aot_options", new string[] { });
|
||||
// --optimize/-O=opt1,opt2 (use 'mono --list-opt'' to list optimize options)
|
||||
GlobalDef("mono/export/aot/extra_optimizer_options", new string[] { });
|
||||
|
||||
GlobalDef("mono/export/aot/android_toolchain_path", "");
|
||||
}
|
||||
|
||||
private string maybeLastExportError;
|
||||
|
||||
private void AddFile(string srcPath, string dstPath, bool remap = false)
|
||||
{
|
||||
AddFile(dstPath, File.ReadAllBytes(srcPath), remap);
|
||||
}
|
||||
|
||||
public override void _ExportFile(string path, string type, string[] features)
|
||||
{
|
||||
base._ExportFile(path, type, features);
|
||||
|
||||
if (type != Internal.CSharpLanguageType)
|
||||
return;
|
||||
|
||||
if (Path.GetExtension(path) != $".{Internal.CSharpLanguageExtension}")
|
||||
throw new ArgumentException($"Resource of type {Internal.CSharpLanguageType} has an invalid file extension: {path}", nameof(path));
|
||||
|
||||
// TODO What if the source file is not part of the game's C# project
|
||||
|
||||
bool includeScriptsContent = (bool) ProjectSettings.GetSetting("mono/export/include_scripts_content");
|
||||
|
||||
if (!includeScriptsContent)
|
||||
{
|
||||
// We don't want to include the source code on exported games.
|
||||
|
||||
// Sadly, Godot prints errors when adding an empty file (nothing goes wrong, it's just noise).
|
||||
// Because of this, we add a file which contains a line break.
|
||||
AddFile(path, System.Text.Encoding.UTF8.GetBytes("\n"), remap: false);
|
||||
Skip();
|
||||
}
|
||||
}
|
||||
|
||||
public override void _ExportBegin(string[] features, bool isDebug, string path, int flags)
|
||||
{
|
||||
base._ExportBegin(features, isDebug, path, flags);
|
||||
|
||||
try
|
||||
{
|
||||
_ExportBeginImpl(features, isDebug, path, flags);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
maybeLastExportError = e.Message;
|
||||
GD.PushError($"Failed to export project: {e.Message}");
|
||||
Console.Error.WriteLine(e);
|
||||
// TODO: Do something on error once _ExportBegin supports failing.
|
||||
}
|
||||
}
|
||||
|
||||
private void _ExportBeginImpl(string[] features, bool isDebug, string path, int flags)
|
||||
{
|
||||
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
|
||||
return;
|
||||
|
||||
string platform = DeterminePlatformFromFeatures(features);
|
||||
|
||||
if (platform == null)
|
||||
throw new NotSupportedException("Target platform not supported");
|
||||
|
||||
string outputDir = new FileInfo(path).Directory?.FullName ??
|
||||
throw new FileNotFoundException("Base directory not found");
|
||||
|
||||
string buildConfig = isDebug ? "Debug" : "Release";
|
||||
|
||||
string scriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, $"scripts_metadata.{(isDebug ? "debug" : "release")}");
|
||||
CsProjOperations.GenerateScriptsMetadata(GodotSharpDirs.ProjectCsProjPath, scriptsMetadataPath);
|
||||
|
||||
AddFile(scriptsMetadataPath, scriptsMetadataPath);
|
||||
|
||||
// Turn export features into defines
|
||||
var godotDefines = features;
|
||||
|
||||
if (!BuildManager.BuildProjectBlocking(buildConfig, godotDefines))
|
||||
throw new Exception("Failed to build project");
|
||||
|
||||
// Add dependency assemblies
|
||||
|
||||
var dependencies = new Godot.Collections.Dictionary<string, string>();
|
||||
|
||||
var projectDllName = (string) ProjectSettings.GetSetting("application/config/name");
|
||||
if (projectDllName.Empty())
|
||||
{
|
||||
projectDllName = "UnnamedProject";
|
||||
}
|
||||
|
||||
string projectDllSrcDir = Path.Combine(GodotSharpDirs.ResTempAssembliesBaseDir, buildConfig);
|
||||
string projectDllSrcPath = Path.Combine(projectDllSrcDir, $"{projectDllName}.dll");
|
||||
|
||||
dependencies[projectDllName] = projectDllSrcPath;
|
||||
|
||||
{
|
||||
string platformBclDir = DeterminePlatformBclDir(platform);
|
||||
|
||||
internal_GetExportedAssemblyDependencies(projectDllName, projectDllSrcPath, buildConfig, platformBclDir, dependencies);
|
||||
}
|
||||
|
||||
string 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)
|
||||
string outputDataDir = null;
|
||||
|
||||
if (PlatformHasTemplateDir(platform))
|
||||
outputDataDir = ExportDataDirectory(features, platform, isDebug, outputDir);
|
||||
|
||||
// AOT
|
||||
|
||||
if ((bool) ProjectSettings.GetSetting("mono/export/aot/enabled"))
|
||||
{
|
||||
AotCompileDependencies(features, platform, isDebug, outputDir, outputDataDir, dependencies);
|
||||
}
|
||||
}
|
||||
|
||||
public override void _ExportEnd()
|
||||
{
|
||||
base._ExportEnd();
|
||||
|
||||
string aotTempDir = Path.Combine(Path.GetTempPath(), $"godot-aot-{Process.GetCurrentProcess().Id}");
|
||||
|
||||
if (Directory.Exists(aotTempDir))
|
||||
Directory.Delete(aotTempDir, recursive: true);
|
||||
|
||||
// TODO: Just a workaround until the export plugins can be made to abort with errors
|
||||
if (string.IsNullOrEmpty(maybeLastExportError)) // Check empty as well, because it's set to empty after hot-reloading
|
||||
{
|
||||
string lastExportError = maybeLastExportError;
|
||||
maybeLastExportError = null;
|
||||
|
||||
GodotSharpEditor.Instance.ShowErrorDialog(lastExportError, "C# export failed");
|
||||
}
|
||||
}
|
||||
|
||||
private static string ExportDataDirectory(string[] features, string platform, bool isDebug, string outputDir)
|
||||
{
|
||||
string target = isDebug ? "release_debug" : "release";
|
||||
|
||||
// NOTE: Bits is ok for now as all platforms with a data directory have it, but that may change in the future.
|
||||
string bits = features.Contains("64") ? "64" : "32";
|
||||
|
||||
string TemplateDirName() => $"data.mono.{platform}.{bits}.{target}";
|
||||
|
||||
string templateDirPath = Path.Combine(Internal.FullTemplatesDir, TemplateDirName());
|
||||
|
||||
if (!Directory.Exists(templateDirPath))
|
||||
{
|
||||
templateDirPath = null;
|
||||
|
||||
if (isDebug)
|
||||
{
|
||||
target = "debug"; // Support both 'release_debug' and 'debug' for the template data directory name
|
||||
templateDirPath = Path.Combine(Internal.FullTemplatesDir, TemplateDirName());
|
||||
|
||||
if (!Directory.Exists(templateDirPath))
|
||||
templateDirPath = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (templateDirPath == null)
|
||||
throw new FileNotFoundException("Data template directory not found");
|
||||
|
||||
string outputDataDir = Path.Combine(outputDir, DataDirName);
|
||||
|
||||
if (Directory.Exists(outputDataDir))
|
||||
Directory.Delete(outputDataDir, recursive: true); // Clean first
|
||||
|
||||
Directory.CreateDirectory(outputDataDir);
|
||||
|
||||
foreach (string dir in Directory.GetDirectories(templateDirPath, "*", SearchOption.AllDirectories))
|
||||
{
|
||||
Directory.CreateDirectory(Path.Combine(outputDataDir, dir.Substring(templateDirPath.Length + 1)));
|
||||
}
|
||||
|
||||
foreach (string file in Directory.GetFiles(templateDirPath, "*", SearchOption.AllDirectories))
|
||||
{
|
||||
File.Copy(file, Path.Combine(outputDataDir, file.Substring(templateDirPath.Length + 1)));
|
||||
}
|
||||
|
||||
return outputDataDir;
|
||||
}
|
||||
|
||||
private void AotCompileDependencies(string[] features, string platform, bool isDebug, string outputDir, string outputDataDir, IDictionary<string, string> dependencies)
|
||||
{
|
||||
// TODO: WASM
|
||||
|
||||
string bclDir = DeterminePlatformBclDir(platform) ?? typeof(object).Assembly.Location.GetBaseDir();
|
||||
|
||||
string aotTempDir = Path.Combine(Path.GetTempPath(), $"godot-aot-{Process.GetCurrentProcess().Id}");
|
||||
|
||||
if (!Directory.Exists(aotTempDir))
|
||||
Directory.CreateDirectory(aotTempDir);
|
||||
|
||||
var assemblies = new Dictionary<string, string>();
|
||||
|
||||
foreach (var dependency in dependencies)
|
||||
{
|
||||
string assemblyName = dependency.Key;
|
||||
string assemblyPath = dependency.Value;
|
||||
|
||||
string assemblyPathInBcl = Path.Combine(bclDir, assemblyName + ".dll");
|
||||
|
||||
if (File.Exists(assemblyPathInBcl))
|
||||
{
|
||||
// Don't create teporaries for assemblies from the BCL
|
||||
assemblies.Add(assemblyName, assemblyPathInBcl);
|
||||
}
|
||||
else
|
||||
{
|
||||
string tempAssemblyPath = Path.Combine(aotTempDir, assemblyName + ".dll");
|
||||
File.Copy(assemblyPath, tempAssemblyPath);
|
||||
assemblies.Add(assemblyName, tempAssemblyPath);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var assembly in assemblies)
|
||||
{
|
||||
string assemblyName = assembly.Key;
|
||||
string assemblyPath = assembly.Value;
|
||||
|
||||
string sharedLibExtension = platform == OS.Platforms.Windows ? ".dll" :
|
||||
platform == OS.Platforms.OSX ? ".dylib" :
|
||||
platform == OS.Platforms.HTML5 ? ".wasm" :
|
||||
".so";
|
||||
|
||||
string outputFileName = assemblyName + ".dll" + sharedLibExtension;
|
||||
|
||||
if (platform == OS.Platforms.Android)
|
||||
{
|
||||
// Not sure if the 'lib' prefix is an Android thing or just Godot being picky,
|
||||
// but we use '-aot-' as well just in case to avoid conflicts with other libs.
|
||||
outputFileName = "lib-aot-" + outputFileName;
|
||||
}
|
||||
|
||||
string outputFilePath = null;
|
||||
string tempOutputFilePath;
|
||||
|
||||
switch (platform)
|
||||
{
|
||||
case OS.Platforms.OSX:
|
||||
tempOutputFilePath = Path.Combine(aotTempDir, outputFileName);
|
||||
break;
|
||||
case OS.Platforms.Android:
|
||||
tempOutputFilePath = Path.Combine(aotTempDir, "%%ANDROID_ABI%%", outputFileName);
|
||||
break;
|
||||
case OS.Platforms.HTML5:
|
||||
tempOutputFilePath = Path.Combine(aotTempDir, outputFileName);
|
||||
outputFilePath = Path.Combine(outputDir, outputFileName);
|
||||
break;
|
||||
default:
|
||||
tempOutputFilePath = Path.Combine(aotTempDir, outputFileName);
|
||||
outputFilePath = Path.Combine(outputDataDir, "Mono", platform == OS.Platforms.Windows ? "bin" : "lib", outputFileName);
|
||||
break;
|
||||
}
|
||||
|
||||
var data = new Dictionary<string, string>();
|
||||
var enabledAndroidAbis = platform == OS.Platforms.Android ? GetEnabledAndroidAbis(features).ToArray() : null;
|
||||
|
||||
if (platform == OS.Platforms.Android)
|
||||
{
|
||||
Debug.Assert(enabledAndroidAbis != null);
|
||||
|
||||
foreach (var abi in enabledAndroidAbis)
|
||||
{
|
||||
data["abi"] = abi;
|
||||
var outputFilePathForThisAbi = tempOutputFilePath.Replace("%%ANDROID_ABI%%", abi);
|
||||
|
||||
AotCompileAssembly(platform, isDebug, data, assemblyPath, outputFilePathForThisAbi);
|
||||
|
||||
AddSharedObject(outputFilePathForThisAbi, tags: new[] {abi});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string bits = features.Contains("64") ? "64" : features.Contains("64") ? "32" : null;
|
||||
|
||||
if (bits != null)
|
||||
data["bits"] = bits;
|
||||
|
||||
AotCompileAssembly(platform, isDebug, data, assemblyPath, tempOutputFilePath);
|
||||
|
||||
if (platform == OS.Platforms.OSX)
|
||||
{
|
||||
AddSharedObject(tempOutputFilePath, tags: null);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert(outputFilePath != null);
|
||||
File.Copy(tempOutputFilePath, outputFilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void AotCompileAssembly(string platform, bool isDebug, Dictionary<string, string> data, string assemblyPath, string outputFilePath)
|
||||
{
|
||||
// Make sure the output directory exists
|
||||
Directory.CreateDirectory(outputFilePath.GetBaseDir());
|
||||
|
||||
string exeExt = OS.IsWindows ? ".exe" : string.Empty;
|
||||
|
||||
string monoCrossDirName = DetermineMonoCrossDirName(platform, data);
|
||||
string monoCrossRoot = Path.Combine(GodotSharpDirs.DataEditorToolsDir, "aot-compilers", monoCrossDirName);
|
||||
string monoCrossBin = Path.Combine(monoCrossRoot, "bin");
|
||||
|
||||
string toolPrefix = DetermineToolPrefix(monoCrossBin);
|
||||
string monoExeName = System.IO.File.Exists(Path.Combine(monoCrossBin, $"{toolPrefix}mono{exeExt}")) ? "mono" : "mono-sgen";
|
||||
|
||||
string compilerCommand = Path.Combine(monoCrossBin, $"{toolPrefix}{monoExeName}{exeExt}");
|
||||
|
||||
bool fullAot = (bool) ProjectSettings.GetSetting("mono/export/aot/full_aot");
|
||||
|
||||
string EscapeOption(string option) => option.Contains(',') ? $"\"{option}\"" : option;
|
||||
string OptionsToString(IEnumerable<string> options) => string.Join(",", options.Select(EscapeOption));
|
||||
|
||||
var aotOptions = new List<string>();
|
||||
var optimizerOptions = new List<string>();
|
||||
|
||||
if (fullAot)
|
||||
aotOptions.Add("full");
|
||||
|
||||
aotOptions.Add(isDebug ? "soft-debug" : "nodebug");
|
||||
|
||||
if (platform == OS.Platforms.Android)
|
||||
{
|
||||
string abi = data["abi"];
|
||||
|
||||
string androidToolchain = (string) ProjectSettings.GetSetting("mono/export/aot/android_toolchain_path");
|
||||
|
||||
if (string.IsNullOrEmpty(androidToolchain))
|
||||
{
|
||||
androidToolchain = Path.Combine(GodotSharpDirs.DataEditorToolsDir, "android-toolchains", $"{abi}"); // TODO: $"{abi}-{apiLevel}{(clang?"clang":"")}"
|
||||
|
||||
if (!Directory.Exists(androidToolchain))
|
||||
throw new FileNotFoundException("Missing android toolchain. Specify one in the AOT export settings.");
|
||||
}
|
||||
else if (!Directory.Exists(androidToolchain))
|
||||
{
|
||||
throw new FileNotFoundException("Android toolchain not found: " + androidToolchain);
|
||||
}
|
||||
|
||||
var androidToolPrefixes = new Dictionary<string, string>
|
||||
{
|
||||
["armeabi-v7a"] = "arm-linux-androideabi-",
|
||||
["arm64-v8a"] = "aarch64-linux-android-",
|
||||
["x86"] = "i686-linux-android-",
|
||||
["x86_64"] = "x86_64-linux-android-"
|
||||
};
|
||||
|
||||
aotOptions.Add("tool-prefix=" + Path.Combine(androidToolchain, "bin", androidToolPrefixes[abi]));
|
||||
|
||||
string triple = GetAndroidTriple(abi);
|
||||
aotOptions.Add ($"mtriple={triple}");
|
||||
}
|
||||
|
||||
aotOptions.Add($"outfile={outputFilePath}");
|
||||
|
||||
var extraAotOptions = (string[]) ProjectSettings.GetSetting("mono/export/aot/extra_aot_options");
|
||||
var extraOptimizerOptions = (string[]) ProjectSettings.GetSetting("mono/export/aot/extra_optimizer_options");
|
||||
|
||||
if (extraAotOptions.Length > 0)
|
||||
aotOptions.AddRange(extraAotOptions);
|
||||
|
||||
if (extraOptimizerOptions.Length > 0)
|
||||
optimizerOptions.AddRange(extraOptimizerOptions);
|
||||
|
||||
var compilerArgs = new List<string>();
|
||||
|
||||
if (isDebug)
|
||||
compilerArgs.Add("--debug"); // Required for --aot=soft-debug
|
||||
|
||||
compilerArgs.Add(aotOptions.Count > 0 ? $"--aot={OptionsToString(aotOptions)}" : "--aot");
|
||||
|
||||
if (optimizerOptions.Count > 0)
|
||||
compilerArgs.Add($"-O={OptionsToString(optimizerOptions)}");
|
||||
|
||||
compilerArgs.Add(ProjectSettings.GlobalizePath(assemblyPath));
|
||||
|
||||
// TODO: Once we move to .NET Standard 2.1 we can use ProcessStartInfo.ArgumentList instead
|
||||
string CmdLineArgsToString(IEnumerable<string> args)
|
||||
{
|
||||
// Not perfect, but as long as we are careful...
|
||||
return string.Join(" ", args.Select(arg => arg.Contains(" ") ? $@"""{arg}""" : arg));
|
||||
}
|
||||
|
||||
using (var process = new Process())
|
||||
{
|
||||
process.StartInfo = new ProcessStartInfo(compilerCommand, CmdLineArgsToString(compilerArgs))
|
||||
{
|
||||
UseShellExecute = false
|
||||
};
|
||||
|
||||
string platformBclDir = DeterminePlatformBclDir(platform);
|
||||
process.StartInfo.EnvironmentVariables.Add("MONO_PATH", string.IsNullOrEmpty(platformBclDir) ?
|
||||
typeof(object).Assembly.Location.GetBaseDir() :
|
||||
platformBclDir);
|
||||
|
||||
Console.WriteLine($"Running: \"{process.StartInfo.FileName}\" {process.StartInfo.Arguments}");
|
||||
|
||||
if (!process.Start())
|
||||
throw new Exception("Failed to start process for Mono AOT compiler");
|
||||
|
||||
process.WaitForExit();
|
||||
|
||||
if (process.ExitCode != 0)
|
||||
throw new Exception($"Mono AOT compiler exited with error code: {process.ExitCode}");
|
||||
|
||||
if (!System.IO.File.Exists(outputFilePath))
|
||||
throw new Exception("Mono AOT compiler finished successfully but the output file is missing");
|
||||
}
|
||||
}
|
||||
|
||||
private static string DetermineMonoCrossDirName(string platform, IReadOnlyDictionary<string, string> data)
|
||||
{
|
||||
switch (platform)
|
||||
{
|
||||
case OS.Platforms.Windows:
|
||||
case OS.Platforms.UWP:
|
||||
{
|
||||
string arch = data["bits"] == "64" ? "x86_64" : "i686";
|
||||
return $"windows-{arch}";
|
||||
}
|
||||
case OS.Platforms.OSX:
|
||||
{
|
||||
string arch = "x86_64";
|
||||
return $"{platform}-{arch}";
|
||||
}
|
||||
case OS.Platforms.X11:
|
||||
case OS.Platforms.Server:
|
||||
{
|
||||
string arch = data["bits"] == "64" ? "x86_64" : "i686";
|
||||
return $"linux-{arch}";
|
||||
}
|
||||
case OS.Platforms.Haiku:
|
||||
{
|
||||
string arch = data["bits"] == "64" ? "x86_64" : "i686";
|
||||
return $"{platform}-{arch}";
|
||||
}
|
||||
case OS.Platforms.Android:
|
||||
{
|
||||
string abi = data["abi"];
|
||||
return $"{platform}-{abi}";
|
||||
}
|
||||
case OS.Platforms.HTML5:
|
||||
return "wasm-wasm32";
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
private static string DetermineToolPrefix(string monoCrossBin)
|
||||
{
|
||||
string exeExt = OS.IsWindows ? ".exe" : string.Empty;
|
||||
|
||||
if (System.IO.File.Exists(Path.Combine(monoCrossBin, $"mono{exeExt}")))
|
||||
return string.Empty;
|
||||
|
||||
if (System.IO.File.Exists(Path.Combine(monoCrossBin, $"mono-sgen{exeExt}" + exeExt)))
|
||||
return string.Empty;
|
||||
|
||||
var files = new DirectoryInfo(monoCrossBin).GetFiles($"*mono{exeExt}" + exeExt, SearchOption.TopDirectoryOnly);
|
||||
if (files.Length > 0)
|
||||
{
|
||||
string fileName = files[0].Name;
|
||||
return fileName.Substring(0, fileName.Length - $"mono{exeExt}".Length);
|
||||
}
|
||||
|
||||
files = new DirectoryInfo(monoCrossBin).GetFiles($"*mono-sgen{exeExt}" + exeExt, SearchOption.TopDirectoryOnly);
|
||||
if (files.Length > 0)
|
||||
{
|
||||
string fileName = files[0].Name;
|
||||
return fileName.Substring(0, fileName.Length - $"mono-sgen{exeExt}".Length);
|
||||
}
|
||||
|
||||
throw new FileNotFoundException($"Cannot find the mono runtime executable in {monoCrossBin}");
|
||||
}
|
||||
|
||||
private static IEnumerable<string> GetEnabledAndroidAbis(string[] features)
|
||||
{
|
||||
var androidAbis = new[]
|
||||
{
|
||||
"armeabi-v7a",
|
||||
"arm64-v8a",
|
||||
"x86",
|
||||
"x86_64"
|
||||
};
|
||||
|
||||
return androidAbis.Where(features.Contains);
|
||||
}
|
||||
|
||||
private static string GetAndroidTriple(string abi)
|
||||
{
|
||||
var abiArchs = new Dictionary<string, string>
|
||||
{
|
||||
["armeabi-v7a"] = "armv7",
|
||||
["arm64-v8a"] = "aarch64-v8a",
|
||||
["x86"] = "i686",
|
||||
["x86_64"] = "x86_64"
|
||||
};
|
||||
|
||||
string arch = abiArchs[abi];
|
||||
|
||||
return $"{arch}-linux-android";
|
||||
}
|
||||
|
||||
private static bool PlatformHasTemplateDir(string platform)
|
||||
{
|
||||
// OSX export templates are contained in a zip, so we place our custom template inside it and let Godot do the rest.
|
||||
return !new[] {OS.Platforms.OSX, OS.Platforms.Android, OS.Platforms.HTML5}.Contains(platform);
|
||||
}
|
||||
|
||||
private static string DeterminePlatformFromFeatures(IEnumerable<string> features)
|
||||
{
|
||||
foreach (var feature in features)
|
||||
{
|
||||
if (OS.PlatformNameMap.TryGetValue(feature, out string platform))
|
||||
return platform;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string DeterminePlatformBclDir(string platform)
|
||||
{
|
||||
string templatesDir = Internal.FullTemplatesDir;
|
||||
string platformBclDir = Path.Combine(templatesDir, "bcl", platform);
|
||||
|
||||
if (!File.Exists(Path.Combine(platformBclDir, "mscorlib.dll")))
|
||||
{
|
||||
string profile = DeterminePlatformBclProfile(platform);
|
||||
platformBclDir = Path.Combine(templatesDir, "bcl", profile);
|
||||
|
||||
if (!File.Exists(Path.Combine(platformBclDir, "mscorlib.dll")))
|
||||
platformBclDir = null; // Use the one we're running on
|
||||
}
|
||||
|
||||
return platformBclDir;
|
||||
}
|
||||
|
||||
private static string DeterminePlatformBclProfile(string platform)
|
||||
{
|
||||
switch (platform)
|
||||
{
|
||||
case OS.Platforms.Windows:
|
||||
case OS.Platforms.UWP:
|
||||
case OS.Platforms.OSX:
|
||||
case OS.Platforms.X11:
|
||||
case OS.Platforms.Server:
|
||||
case OS.Platforms.Haiku:
|
||||
return "net_4_x";
|
||||
case OS.Platforms.Android:
|
||||
return "monodroid";
|
||||
case OS.Platforms.HTML5:
|
||||
return "wasm";
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
private static string DataDirName
|
||||
{
|
||||
get
|
||||
{
|
||||
var appName = (string) ProjectSettings.GetSetting("application/config/name");
|
||||
string appNameSafe = appName.ToSafeDirName(allowDirSeparator: false);
|
||||
return $"data_{appNameSafe}";
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern void internal_GetExportedAssemblyDependencies(string projectDllName, string projectDllSrcPath,
|
||||
string buildConfig, string customBclDir, Godot.Collections.Dictionary<string, string> dependencies);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using Godot;
|
||||
using GodotTools.Export;
|
||||
using GodotTools.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
@ -225,7 +226,7 @@ namespace GodotTools
|
|||
|
||||
bool osxAppBundleInstalled = false;
|
||||
|
||||
if (OS.IsOSX())
|
||||
if (OS.IsOSX)
|
||||
{
|
||||
// The package path is '/Applications/Visual Studio Code.app'
|
||||
const string vscodeBundleId = "com.microsoft.VSCode";
|
||||
|
@ -265,7 +266,7 @@ namespace GodotTools
|
|||
|
||||
string command;
|
||||
|
||||
if (OS.IsOSX())
|
||||
if (OS.IsOSX)
|
||||
{
|
||||
if (!osxAppBundleInstalled && _vsCodePath.Empty())
|
||||
{
|
||||
|
@ -415,18 +416,18 @@ namespace GodotTools
|
|||
|
||||
string settingsHintStr = "Disabled";
|
||||
|
||||
if (OS.IsWindows())
|
||||
if (OS.IsWindows)
|
||||
{
|
||||
settingsHintStr += $",MonoDevelop:{(int) ExternalEditorId.MonoDevelop}" +
|
||||
$",Visual Studio Code:{(int) ExternalEditorId.VsCode}";
|
||||
}
|
||||
else if (OS.IsOSX())
|
||||
else if (OS.IsOSX)
|
||||
{
|
||||
settingsHintStr += $",Visual Studio:{(int) ExternalEditorId.VisualStudioForMac}" +
|
||||
$",MonoDevelop:{(int) ExternalEditorId.MonoDevelop}" +
|
||||
$",Visual Studio Code:{(int) ExternalEditorId.VsCode}";
|
||||
}
|
||||
else if (OS.IsUnix())
|
||||
else if (OS.IsUnixLike())
|
||||
{
|
||||
settingsHintStr += $",MonoDevelop:{(int) ExternalEditorId.MonoDevelop}" +
|
||||
$",Visual Studio Code:{(int) ExternalEditorId.VsCode}";
|
||||
|
@ -441,8 +442,9 @@ namespace GodotTools
|
|||
});
|
||||
|
||||
// Export plugin
|
||||
var exportPlugin = new GodotSharpExport();
|
||||
var exportPlugin = new ExportPlugin();
|
||||
AddExportPlugin(exportPlugin);
|
||||
exportPlugin.RegisterExportSettings();
|
||||
exportPluginWeak = WeakRef(exportPlugin);
|
||||
|
||||
BuildManager.Initialize();
|
||||
|
@ -461,7 +463,7 @@ namespace GodotTools
|
|||
// Otherwise, if the GC disposes it at a later time, EditorExportPlatformAndroid
|
||||
// will be freed after EditorSettings already was, and its device polling thread
|
||||
// will try to access the EditorSettings singleton, resulting in null dereferencing.
|
||||
(exportPluginWeak.GetRef() as GodotSharpExport)?.Dispose();
|
||||
(exportPluginWeak.GetRef() as ExportPlugin)?.Dispose();
|
||||
|
||||
exportPluginWeak.Dispose();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace GodotTools.Ides.MonoDevelop
|
|||
|
||||
string command;
|
||||
|
||||
if (OS.IsOSX())
|
||||
if (OS.IsOSX)
|
||||
{
|
||||
string bundleId = BundleIds[editorId];
|
||||
|
||||
|
@ -81,7 +81,7 @@ namespace GodotTools.Ides.MonoDevelop
|
|||
|
||||
public Instance(string solutionFile, EditorId editorId)
|
||||
{
|
||||
if (editorId == EditorId.VisualStudioForMac && !OS.IsOSX())
|
||||
if (editorId == EditorId.VisualStudioForMac && !OS.IsOSX)
|
||||
throw new InvalidOperationException($"{nameof(EditorId.VisualStudioForMac)} not supported on this platform");
|
||||
|
||||
this.solutionFile = solutionFile;
|
||||
|
@ -93,7 +93,7 @@ namespace GodotTools.Ides.MonoDevelop
|
|||
|
||||
static Instance()
|
||||
{
|
||||
if (OS.IsOSX())
|
||||
if (OS.IsOSX)
|
||||
{
|
||||
ExecutableNames = new Dictionary<EditorId, string>
|
||||
{
|
||||
|
@ -107,7 +107,7 @@ namespace GodotTools.Ides.MonoDevelop
|
|||
{EditorId.VisualStudioForMac, "com.microsoft.visual-studio"}
|
||||
};
|
||||
}
|
||||
else if (OS.IsWindows())
|
||||
else if (OS.IsWindows)
|
||||
{
|
||||
ExecutableNames = new Dictionary<EditorId, string>
|
||||
{
|
||||
|
@ -118,7 +118,7 @@ namespace GodotTools.Ides.MonoDevelop
|
|||
{EditorId.MonoDevelop, "MonoDevelop.exe"}
|
||||
};
|
||||
}
|
||||
else if (OS.IsUnix())
|
||||
else if (OS.IsUnixLike())
|
||||
{
|
||||
ExecutableNames = new Dictionary<EditorId, string>
|
||||
{
|
||||
|
|
|
@ -52,7 +52,7 @@ namespace GodotTools.Internals
|
|||
|
||||
public static void ScriptEditorDebugger_ReloadScripts() => internal_ScriptEditorDebugger_ReloadScripts();
|
||||
|
||||
// Internal Calls
|
||||
#region Internal
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern string internal_UpdateApiAssembliesFromPrebuilt(string config);
|
||||
|
@ -110,5 +110,7 @@ namespace GodotTools.Internals
|
|||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern void internal_ScriptEditorDebugger_ReloadScripts();
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,72 +1,94 @@
|
|||
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[] UnixLikePlatforms = {Names.OSX, Names.X11, Names.Server, Names.Haiku, Names.Android};
|
||||
|
||||
public static bool IsUnix()
|
||||
public static bool IsUnixLike()
|
||||
{
|
||||
if (_isUnixCache.HasValue)
|
||||
return _isUnixCache.Value;
|
||||
|
||||
string osName = GetPlatformName();
|
||||
_isUnixCache = UnixPlatforms.Any(p => p.Equals(osName, StringComparison.OrdinalIgnoreCase));
|
||||
_isUnixCache = UnixLikePlatforms.Any(p => p.Equals(osName, StringComparison.OrdinalIgnoreCase));
|
||||
return _isUnixCache.Value;
|
||||
}
|
||||
|
||||
public static char PathSep => IsWindows() ? ';' : ':';
|
||||
public static char PathSep => IsWindows ? ';' : ':';
|
||||
|
||||
public static string PathWhich(string name)
|
||||
{
|
||||
string[] windowsExts = IsWindows() ? Environment.GetEnvironmentVariable("PATHEXT")?.Split(PathSep) : null;
|
||||
string[] windowsExts = IsWindows ? Environment.GetEnvironmentVariable("PATHEXT")?.Split(PathSep) : null;
|
||||
string[] pathDirs = Environment.GetEnvironmentVariable("PATH")?.Split(PathSep);
|
||||
|
||||
var searchDirs = new List<string>();
|
||||
|
@ -80,7 +102,7 @@ namespace GodotTools.Utils
|
|||
{
|
||||
string path = Path.Combine(dir, name);
|
||||
|
||||
if (IsWindows() && windowsExts != null)
|
||||
if (IsWindows && windowsExts != null)
|
||||
{
|
||||
foreach (var extension in windowsExts)
|
||||
{
|
||||
|
@ -102,12 +124,14 @@ namespace GodotTools.Utils
|
|||
|
||||
public static void RunProcess(string command, IEnumerable<string> arguments)
|
||||
{
|
||||
// TODO: Once we move to .NET Standard 2.1 we can use ProcessStartInfo.ArgumentList instead
|
||||
string CmdLineArgsToString(IEnumerable<string> args)
|
||||
{
|
||||
// Not perfect, but as long as we are careful...
|
||||
return string.Join(" ", args.Select(arg => arg.Contains(" ") ? $@"""{arg}""" : arg));
|
||||
}
|
||||
|
||||
ProcessStartInfo startInfo = new ProcessStartInfo(command, CmdLineArgsToString(arguments))
|
||||
var startInfo = new ProcessStartInfo(command, CmdLineArgsToString(arguments))
|
||||
{
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
|
|
|
@ -97,7 +97,7 @@
|
|||
#define C_METHOD_MONOARRAY_TO(m_type) C_NS_MONOMARSHAL "::mono_array_to_" #m_type
|
||||
#define C_METHOD_MONOARRAY_FROM(m_type) C_NS_MONOMARSHAL "::" #m_type "_to_mono_array"
|
||||
|
||||
#define BINDINGS_GENERATOR_VERSION UINT32_C(9)
|
||||
#define BINDINGS_GENERATOR_VERSION UINT32_C(11)
|
||||
|
||||
const char *BindingsGenerator::TypeInterface::DEFAULT_VARARG_C_IN("\t%0 %1_in = %1;\n");
|
||||
|
||||
|
@ -731,13 +731,26 @@ void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) {
|
|||
i++;
|
||||
}
|
||||
|
||||
String im_type_out = return_type->im_type_out;
|
||||
|
||||
if (return_type->ret_as_byref_arg) {
|
||||
// Doesn't affect the unique signature
|
||||
im_type_out = "void";
|
||||
|
||||
im_sig += ", ";
|
||||
im_sig += return_type->im_type_out;
|
||||
im_sig += " argRet";
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
// godot_icall_{argc}_{icallcount}
|
||||
String icall_method = ICALL_PREFIX;
|
||||
icall_method += itos(imethod.arguments.size());
|
||||
icall_method += "_";
|
||||
icall_method += itos(method_icalls.size());
|
||||
|
||||
InternalCall im_icall = InternalCall(p_itype.api_type, icall_method, return_type->im_type_out, im_sig, im_unique_sig);
|
||||
InternalCall im_icall = InternalCall(p_itype.api_type, icall_method, im_type_out, im_sig, im_unique_sig);
|
||||
|
||||
List<InternalCall>::Element *match = method_icalls.find(im_icall);
|
||||
|
||||
|
@ -1685,17 +1698,18 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
|
|||
const InternalCall *im_icall = match->value();
|
||||
|
||||
String im_call = im_icall->editor_only ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS;
|
||||
im_call += "." + im_icall->name + "(" + icall_params + ")";
|
||||
im_call += ".";
|
||||
im_call += im_icall->name;
|
||||
|
||||
if (p_imethod.arguments.size())
|
||||
p_output.append(cs_in_statements);
|
||||
|
||||
if (return_type->cname == name_cache.type_void) {
|
||||
p_output.append(im_call + ";\n");
|
||||
p_output.append(im_call + "(" + icall_params + ");\n");
|
||||
} else if (return_type->cs_out.empty()) {
|
||||
p_output.append("return " + im_call + ";\n");
|
||||
p_output.append("return " + im_call + "(" + icall_params + ");\n");
|
||||
} else {
|
||||
p_output.append(sformat(return_type->cs_out, im_call, return_type->cs_type, return_type->im_type_out));
|
||||
p_output.append(sformat(return_type->cs_out, im_call, icall_params, return_type->cs_type, return_type->im_type_out));
|
||||
p_output.append("\n");
|
||||
}
|
||||
|
||||
|
@ -1937,6 +1951,15 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte
|
|||
i++;
|
||||
}
|
||||
|
||||
if (return_type->ret_as_byref_arg) {
|
||||
c_func_sig += ", ";
|
||||
c_func_sig += return_type->c_type_in;
|
||||
c_func_sig += " ";
|
||||
c_func_sig += "arg_ret";
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
const Map<const MethodInterface *, const InternalCall *>::Element *match = method_icalls_map.find(&p_imethod);
|
||||
ERR_FAIL_NULL_V(match, ERR_BUG);
|
||||
|
||||
|
@ -1951,14 +1974,12 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte
|
|||
|
||||
// Generate icall function
|
||||
|
||||
p_output.append(ret_void ? "void " : return_type->c_type_out + " ");
|
||||
p_output.append((ret_void || return_type->ret_as_byref_arg) ? "void " : return_type->c_type_out + " ");
|
||||
p_output.append(icall_method);
|
||||
p_output.append("(");
|
||||
p_output.append(c_func_sig);
|
||||
p_output.append(") " OPEN_BLOCK);
|
||||
|
||||
String fail_ret = ret_void ? "" : ", " + (return_type->c_type_out.ends_with("*") ? "NULL" : return_type->c_type_out + "()");
|
||||
|
||||
if (!ret_void) {
|
||||
String ptrcall_return_type;
|
||||
String initialization;
|
||||
|
@ -1982,9 +2003,18 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte
|
|||
p_output.append("\t" + ptrcall_return_type);
|
||||
p_output.append(" " C_LOCAL_RET);
|
||||
p_output.append(initialization + ";\n");
|
||||
p_output.append("\tERR_FAIL_NULL_V(" CS_PARAM_INSTANCE);
|
||||
p_output.append(fail_ret);
|
||||
p_output.append(");\n");
|
||||
|
||||
String fail_ret = return_type->c_type_out.ends_with("*") && !return_type->ret_as_byref_arg ? "NULL" : return_type->c_type_out + "()";
|
||||
|
||||
if (return_type->ret_as_byref_arg) {
|
||||
p_output.append("\tif (" CS_PARAM_INSTANCE " == NULL) { *arg_ret = ");
|
||||
p_output.append(fail_ret);
|
||||
p_output.append("; ERR_FAIL_MSG(\"Parameter ' arg_ret ' is null.\"); }\n");
|
||||
} else {
|
||||
p_output.append("\tERR_FAIL_NULL_V(" CS_PARAM_INSTANCE ", ");
|
||||
p_output.append(fail_ret);
|
||||
p_output.append(");\n");
|
||||
}
|
||||
} else {
|
||||
p_output.append("\tERR_FAIL_NULL(" CS_PARAM_INSTANCE ");\n");
|
||||
}
|
||||
|
@ -2045,10 +2075,13 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte
|
|||
}
|
||||
|
||||
if (!ret_void) {
|
||||
if (return_type->c_out.empty())
|
||||
if (return_type->c_out.empty()) {
|
||||
p_output.append("\treturn " C_LOCAL_RET ";\n");
|
||||
else
|
||||
} else if (return_type->ret_as_byref_arg) {
|
||||
p_output.append(sformat(return_type->c_out, return_type->c_type_out, C_LOCAL_RET, return_type->name, "arg_ret"));
|
||||
} else {
|
||||
p_output.append(sformat(return_type->c_out, return_type->c_type_out, C_LOCAL_RET, return_type->name));
|
||||
}
|
||||
}
|
||||
|
||||
p_output.append(CLOSE_BLOCK "\n");
|
||||
|
@ -2620,30 +2653,32 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
|
|||
|
||||
TypeInterface itype;
|
||||
|
||||
#define INSERT_STRUCT_TYPE(m_type, m_type_in) \
|
||||
#define INSERT_STRUCT_TYPE(m_type) \
|
||||
{ \
|
||||
itype = TypeInterface::create_value_type(String(#m_type)); \
|
||||
itype.c_in = "\t%0 %1_in = MARSHALLED_IN(" #m_type ", %1);\n"; \
|
||||
itype.c_out = "\treturn MARSHALLED_OUT(" #m_type ", %1);\n"; \
|
||||
itype.c_out = "\t*%3 = MARSHALLED_OUT(" #m_type ", %1);\n"; \
|
||||
itype.c_arg_in = "&%s_in"; \
|
||||
itype.c_type_in = "GDMonoMarshal::M_" #m_type "*"; \
|
||||
itype.c_type_out = "GDMonoMarshal::M_" #m_type; \
|
||||
itype.cs_in = "ref %s"; \
|
||||
itype.cs_out = "return (%1)%0;"; \
|
||||
itype.im_type_out = itype.cs_type; \
|
||||
/* in cs_out, im_type_out (%3) includes the 'out ' part */ \
|
||||
itype.cs_out = "%0(%1, %3 argRet); return (%2)argRet;"; \
|
||||
itype.im_type_out = "out " + itype.cs_type; \
|
||||
itype.ret_as_byref_arg = true; \
|
||||
builtin_types.insert(itype.cname, itype); \
|
||||
}
|
||||
|
||||
INSERT_STRUCT_TYPE(Vector2, "real_t*")
|
||||
INSERT_STRUCT_TYPE(Rect2, "real_t*")
|
||||
INSERT_STRUCT_TYPE(Transform2D, "real_t*")
|
||||
INSERT_STRUCT_TYPE(Vector3, "real_t*")
|
||||
INSERT_STRUCT_TYPE(Basis, "real_t*")
|
||||
INSERT_STRUCT_TYPE(Quat, "real_t*")
|
||||
INSERT_STRUCT_TYPE(Transform, "real_t*")
|
||||
INSERT_STRUCT_TYPE(AABB, "real_t*")
|
||||
INSERT_STRUCT_TYPE(Color, "real_t*")
|
||||
INSERT_STRUCT_TYPE(Plane, "real_t*")
|
||||
INSERT_STRUCT_TYPE(Vector2)
|
||||
INSERT_STRUCT_TYPE(Rect2)
|
||||
INSERT_STRUCT_TYPE(Transform2D)
|
||||
INSERT_STRUCT_TYPE(Vector3)
|
||||
INSERT_STRUCT_TYPE(Basis)
|
||||
INSERT_STRUCT_TYPE(Quat)
|
||||
INSERT_STRUCT_TYPE(Transform)
|
||||
INSERT_STRUCT_TYPE(AABB)
|
||||
INSERT_STRUCT_TYPE(Color)
|
||||
INSERT_STRUCT_TYPE(Plane)
|
||||
|
||||
#undef INSERT_STRUCT_TYPE
|
||||
|
||||
|
@ -2687,11 +2722,44 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
|
|||
INSERT_INT_TYPE("sbyte", int8_t, int64_t);
|
||||
INSERT_INT_TYPE("short", int16_t, int64_t);
|
||||
INSERT_INT_TYPE("int", int32_t, int64_t);
|
||||
INSERT_INT_TYPE("long", int64_t, int64_t);
|
||||
INSERT_INT_TYPE("byte", uint8_t, int64_t);
|
||||
INSERT_INT_TYPE("ushort", uint16_t, int64_t);
|
||||
INSERT_INT_TYPE("uint", uint32_t, int64_t);
|
||||
INSERT_INT_TYPE("ulong", uint64_t, int64_t);
|
||||
|
||||
itype = TypeInterface::create_value_type(String("long"));
|
||||
{
|
||||
itype.c_out = "\treturn (%0)%1;\n";
|
||||
itype.c_in = "\t%0 %1_in = (%0)*%1;\n";
|
||||
itype.c_out = "\t*%3 = (%0)%1;\n";
|
||||
itype.c_type = "int64_t";
|
||||
itype.c_arg_in = "&%s_in";
|
||||
}
|
||||
itype.c_type_in = "int64_t*";
|
||||
itype.c_type_out = "int64_t";
|
||||
itype.im_type_in = "ref " + itype.name;
|
||||
itype.im_type_out = "out " + itype.name;
|
||||
itype.cs_in = "ref %0";
|
||||
/* in cs_out, im_type_out (%3) includes the 'out ' part */
|
||||
itype.cs_out = "%0(%1, %3 argRet); return (%2)argRet;";
|
||||
itype.ret_as_byref_arg = true;
|
||||
builtin_types.insert(itype.cname, itype);
|
||||
|
||||
itype = TypeInterface::create_value_type(String("ulong"));
|
||||
{
|
||||
itype.c_in = "\t%0 %1_in = (%0)*%1;\n";
|
||||
itype.c_out = "\t*%3 = (%0)%1;\n";
|
||||
itype.c_type = "int64_t";
|
||||
itype.c_arg_in = "&%s_in";
|
||||
}
|
||||
itype.c_type_in = "uint64_t*";
|
||||
itype.c_type_out = "uint64_t";
|
||||
itype.im_type_in = "ref " + itype.name;
|
||||
itype.im_type_out = "out " + itype.name;
|
||||
itype.cs_in = "ref %0";
|
||||
/* in cs_out, im_type_out (%3) includes the 'out ' part */
|
||||
itype.cs_out = "%0(%1, %3 argRet); return (%2)argRet;";
|
||||
itype.ret_as_byref_arg = true;
|
||||
builtin_types.insert(itype.cname, itype);
|
||||
}
|
||||
|
||||
// Floating point types
|
||||
|
@ -2703,16 +2771,20 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
|
|||
itype.proxy_name = "float";
|
||||
{
|
||||
// The expected type for 'float' in ptrcall is 'double'
|
||||
itype.c_in = "\t%0 %1_in = (%0)%1;\n";
|
||||
itype.c_out = "\treturn (%0)%1;\n";
|
||||
itype.c_in = "\t%0 %1_in = (%0)*%1;\n";
|
||||
itype.c_out = "\t*%3 = (%0)%1;\n";
|
||||
itype.c_type = "double";
|
||||
itype.c_type_in = "float";
|
||||
itype.c_type_in = "float*";
|
||||
itype.c_type_out = "float";
|
||||
itype.c_arg_in = "&%s_in";
|
||||
}
|
||||
itype.cs_type = itype.proxy_name;
|
||||
itype.im_type_in = itype.proxy_name;
|
||||
itype.im_type_out = itype.proxy_name;
|
||||
itype.im_type_in = "ref " + itype.proxy_name;
|
||||
itype.im_type_out = "out " + itype.proxy_name;
|
||||
itype.cs_in = "ref %0";
|
||||
/* in cs_out, im_type_out (%3) includes the 'out ' part */
|
||||
itype.cs_out = "%0(%1, %3 argRet); return (%2)argRet;";
|
||||
itype.ret_as_byref_arg = true;
|
||||
builtin_types.insert(itype.cname, itype);
|
||||
|
||||
// double
|
||||
|
@ -2720,13 +2792,21 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
|
|||
itype.name = "double";
|
||||
itype.cname = itype.name;
|
||||
itype.proxy_name = "double";
|
||||
itype.c_type = "double";
|
||||
itype.c_type_in = "double";
|
||||
itype.c_type_out = "double";
|
||||
itype.c_arg_in = "&%s";
|
||||
{
|
||||
itype.c_in = "\t%0 %1_in = (%0)*%1;\n";
|
||||
itype.c_out = "\t*%3 = (%0)%1;\n";
|
||||
itype.c_type = "double";
|
||||
itype.c_type_in = "double*";
|
||||
itype.c_type_out = "double";
|
||||
itype.c_arg_in = "&%s_in";
|
||||
}
|
||||
itype.cs_type = itype.proxy_name;
|
||||
itype.im_type_in = itype.proxy_name;
|
||||
itype.im_type_out = itype.proxy_name;
|
||||
itype.im_type_in = "ref " + itype.proxy_name;
|
||||
itype.im_type_out = "out " + itype.proxy_name;
|
||||
itype.cs_in = "ref %0";
|
||||
/* in cs_out, im_type_out (%3) includes the 'out ' part */
|
||||
itype.cs_out = "%0(%1, %3 argRet); return (%2)argRet;";
|
||||
itype.ret_as_byref_arg = true;
|
||||
builtin_types.insert(itype.cname, itype);
|
||||
}
|
||||
|
||||
|
@ -2757,7 +2837,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
|
|||
itype.c_type_out = itype.c_type + "*";
|
||||
itype.cs_type = itype.proxy_name;
|
||||
itype.cs_in = "NodePath." CS_SMETHOD_GETINSTANCE "(%0)";
|
||||
itype.cs_out = "return new %1(%0);";
|
||||
itype.cs_out = "return new %2(%0(%1));";
|
||||
itype.im_type_in = "IntPtr";
|
||||
itype.im_type_out = "IntPtr";
|
||||
builtin_types.insert(itype.cname, itype);
|
||||
|
@ -2773,7 +2853,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
|
|||
itype.c_type_out = itype.c_type + "*";
|
||||
itype.cs_type = itype.proxy_name;
|
||||
itype.cs_in = "RID." CS_SMETHOD_GETINSTANCE "(%0)";
|
||||
itype.cs_out = "return new %1(%0);";
|
||||
itype.cs_out = "return new %2(%0(%1));";
|
||||
itype.im_type_in = "IntPtr";
|
||||
itype.im_type_out = "IntPtr";
|
||||
builtin_types.insert(itype.cname, itype);
|
||||
|
@ -2855,7 +2935,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
|
|||
itype.c_type_out = itype.c_type + "*";
|
||||
itype.cs_type = BINDINGS_NAMESPACE_COLLECTIONS "." + itype.proxy_name;
|
||||
itype.cs_in = "%0." CS_SMETHOD_GETINSTANCE "()";
|
||||
itype.cs_out = "return new " + itype.cs_type + "(%0);";
|
||||
itype.cs_out = "return new " + itype.cs_type + "(%0(%1));";
|
||||
itype.im_type_in = "IntPtr";
|
||||
itype.im_type_out = "IntPtr";
|
||||
builtin_types.insert(itype.cname, itype);
|
||||
|
@ -2871,7 +2951,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
|
|||
itype.c_type_out = itype.c_type + "*";
|
||||
itype.cs_type = BINDINGS_NAMESPACE_COLLECTIONS "." + itype.proxy_name;
|
||||
itype.cs_in = "%0." CS_SMETHOD_GETINSTANCE "()";
|
||||
itype.cs_out = "return new " + itype.cs_type + "(%0);";
|
||||
itype.cs_out = "return new " + itype.cs_type + "(%0(%1));";
|
||||
itype.im_type_in = "IntPtr";
|
||||
itype.im_type_out = "IntPtr";
|
||||
builtin_types.insert(itype.cname, itype);
|
||||
|
|
|
@ -214,6 +214,14 @@ class BindingsGenerator {
|
|||
*/
|
||||
bool memory_own;
|
||||
|
||||
/**
|
||||
* This must be set to true for any struct bigger than 32-bits. Those cannot be passed/returned by value
|
||||
* with internal calls, so we must use pointers instead. Returns must be replace with out parameters.
|
||||
* In this case, [c_out] and [cs_out] must have a different format, explained below.
|
||||
* The Mono IL interpreter icall trampolines don't support passing structs bigger than 32-bits by value (at least not on WASM).
|
||||
*/
|
||||
bool ret_as_byref_arg;
|
||||
|
||||
// !! The comments of the following fields make reference to other fields via square brackets, e.g.: [field_name]
|
||||
// !! When renaming those fields, make sure to rename their references in the comments
|
||||
|
||||
|
@ -248,6 +256,14 @@ class BindingsGenerator {
|
|||
* %0: [c_type_out] of the return type
|
||||
* %1: name of the variable to be returned
|
||||
* %2: [name] of the return type
|
||||
* ---------------------------------------
|
||||
* If [ret_as_byref_arg] is true, the format is different. Instead of using a return statement,
|
||||
* the value must be assigned to a parameter. This type of this parameter is a pointer to [c_type_out].
|
||||
* Formatting elements:
|
||||
* %0: [c_type_out] of the return type
|
||||
* %1: name of the variable to be returned
|
||||
* %2: [name] of the return type
|
||||
* %3: name of the parameter that must be assigned the return value
|
||||
*/
|
||||
String c_out;
|
||||
|
||||
|
@ -291,9 +307,10 @@ class BindingsGenerator {
|
|||
* One or more statements that determine how a variable of this type is returned from a method.
|
||||
* It must contain the return statement(s).
|
||||
* Formatting elements:
|
||||
* %0: internal method call statement
|
||||
* %1: [cs_type] of the return type
|
||||
* %2: [im_type_out] of the return type
|
||||
* %0: internal method name
|
||||
* %1: internal method call arguments without surrounding parenthesis
|
||||
* %2: [cs_type] of the return type
|
||||
* %3: [im_type_out] of the return type
|
||||
*/
|
||||
String cs_out;
|
||||
|
||||
|
@ -417,7 +434,7 @@ class BindingsGenerator {
|
|||
|
||||
r_enum_itype.cs_type = r_enum_itype.proxy_name;
|
||||
r_enum_itype.cs_in = "(int)%s";
|
||||
r_enum_itype.cs_out = "return (%1)%0;";
|
||||
r_enum_itype.cs_out = "return (%2)%0(%1);";
|
||||
r_enum_itype.im_type_in = "int";
|
||||
r_enum_itype.im_type_out = "int";
|
||||
r_enum_itype.class_doc = &EditorHelp::get_doc_data()->class_list[r_enum_itype.proxy_name];
|
||||
|
@ -435,6 +452,8 @@ class BindingsGenerator {
|
|||
|
||||
memory_own = false;
|
||||
|
||||
ret_as_byref_arg = false;
|
||||
|
||||
c_arg_in = "%s";
|
||||
|
||||
class_doc = NULL;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -39,8 +39,8 @@
|
|||
#include "editor/editor_settings.h"
|
||||
#endif
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include "utils/android_utils.h"
|
||||
#ifdef ANDROID_ENABLED
|
||||
#include "mono_gd/gd_mono_android.h"
|
||||
#endif
|
||||
|
||||
#include "mono_gd/gd_mono.h"
|
||||
|
@ -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
|
||||
|
@ -160,8 +164,8 @@ private:
|
|||
String data_mono_root_dir = data_dir_root.plus_file("Mono");
|
||||
data_mono_etc_dir = data_mono_root_dir.plus_file("etc");
|
||||
|
||||
#if __ANDROID__
|
||||
data_mono_lib_dir = GDMonoUtils::Android::get_app_native_lib_dir();
|
||||
#ifdef ANDROID_ENABLED
|
||||
data_mono_lib_dir = GDMonoAndroid::get_app_native_lib_dir();
|
||||
#else
|
||||
data_mono_lib_dir = data_mono_root_dir.plus_file("lib");
|
||||
#endif
|
||||
|
@ -197,8 +201,8 @@ private:
|
|||
String data_mono_root_dir = data_dir_root.plus_file("Mono");
|
||||
data_mono_etc_dir = data_mono_root_dir.plus_file("etc");
|
||||
|
||||
#if __ANDROID__
|
||||
data_mono_lib_dir = GDMonoUtils::Android::get_app_native_lib_dir();
|
||||
#ifdef ANDROID_ENABLED
|
||||
data_mono_lib_dir = GDMonoAndroid::get_app_native_lib_dir();
|
||||
#else
|
||||
data_mono_lib_dir = data_mono_root_dir.plus_file("lib");
|
||||
#endif
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
116
modules/mono/mono_gd/gd_mono_android.cpp
Normal file
116
modules/mono/mono_gd/gd_mono_android.cpp
Normal file
|
@ -0,0 +1,116 @@
|
|||
#include "gd_mono_android.h"
|
||||
|
||||
#if defined(ANDROID_ENABLED)
|
||||
|
||||
#include <dlfcn.h> // dlopen, dlsym
|
||||
#include <mono/utils/mono-dl-fallback.h>
|
||||
|
||||
#include "core/os/os.h"
|
||||
#include "core/ustring.h"
|
||||
#include "platform/android/thread_jandroid.h"
|
||||
|
||||
#include "../utils/path_utils.h"
|
||||
#include "../utils/string_utils.h"
|
||||
|
||||
namespace GDMonoAndroid {
|
||||
|
||||
String app_native_lib_dir_cache;
|
||||
|
||||
String determine_app_native_lib_dir() {
|
||||
JNIEnv *env = ThreadAndroid::get_env();
|
||||
|
||||
jclass activityThreadClass = env->FindClass("android/app/ActivityThread");
|
||||
jmethodID currentActivityThread = env->GetStaticMethodID(activityThreadClass, "currentActivityThread", "()Landroid/app/ActivityThread;");
|
||||
jobject activityThread = env->CallStaticObjectMethod(activityThreadClass, currentActivityThread);
|
||||
jmethodID getApplication = env->GetMethodID(activityThreadClass, "getApplication", "()Landroid/app/Application;");
|
||||
jobject ctx = env->CallObjectMethod(activityThread, getApplication);
|
||||
|
||||
jmethodID getApplicationInfo = env->GetMethodID(env->GetObjectClass(ctx), "getApplicationInfo", "()Landroid/content/pm/ApplicationInfo;");
|
||||
jobject applicationInfo = env->CallObjectMethod(ctx, getApplicationInfo);
|
||||
jfieldID nativeLibraryDirField = env->GetFieldID(env->GetObjectClass(applicationInfo), "nativeLibraryDir", "Ljava/lang/String;");
|
||||
jstring nativeLibraryDir = (jstring)env->GetObjectField(applicationInfo, nativeLibraryDirField);
|
||||
|
||||
String result;
|
||||
|
||||
const char *const nativeLibraryDir_utf8 = env->GetStringUTFChars(nativeLibraryDir, NULL);
|
||||
if (nativeLibraryDir_utf8) {
|
||||
result.parse_utf8(nativeLibraryDir_utf8);
|
||||
env->ReleaseStringUTFChars(nativeLibraryDir, nativeLibraryDir_utf8);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
String get_app_native_lib_dir() {
|
||||
if (app_native_lib_dir_cache.empty())
|
||||
app_native_lib_dir_cache = determine_app_native_lib_dir();
|
||||
return app_native_lib_dir_cache;
|
||||
}
|
||||
|
||||
int gd_mono_convert_dl_flags(int flags) {
|
||||
// from mono's runtime-bootstrap.c
|
||||
|
||||
int lflags = flags & MONO_DL_LOCAL ? 0 : RTLD_GLOBAL;
|
||||
|
||||
if (flags & MONO_DL_LAZY)
|
||||
lflags |= RTLD_LAZY;
|
||||
else
|
||||
lflags |= RTLD_NOW;
|
||||
|
||||
return lflags;
|
||||
}
|
||||
|
||||
void *gd_mono_android_dlopen(const char *p_name, int p_flags, char **r_err, void *p_user_data) {
|
||||
String name = String::utf8(p_name);
|
||||
|
||||
if (name.ends_with(".dll.so") || name.ends_with(".exe.so")) {
|
||||
String app_native_lib_dir = get_app_native_lib_dir();
|
||||
|
||||
String orig_so_name = name.get_file();
|
||||
String so_name = "lib-aot-" + orig_so_name;
|
||||
String so_path = path::join(app_native_lib_dir, so_name);
|
||||
|
||||
if (!FileAccess::exists(so_path)) {
|
||||
if (OS::get_singleton()->is_stdout_verbose())
|
||||
OS::get_singleton()->print("Cannot find shared library: '%s'\n", so_path.utf8().get_data());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int lflags = gd_mono_convert_dl_flags(p_flags);
|
||||
|
||||
void *handle = dlopen(so_path.utf8().get_data(), lflags);
|
||||
|
||||
if (!handle) {
|
||||
if (OS::get_singleton()->is_stdout_verbose())
|
||||
OS::get_singleton()->print("Failed to open shared library: '%s'. Error: '%s'\n", so_path.utf8().get_data(), dlerror());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (OS::get_singleton()->is_stdout_verbose())
|
||||
OS::get_singleton()->print("Successfully loaded AOT shared library: '%s'\n", so_path.utf8().get_data());
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *gd_mono_android_dlsym(void *p_handle, const char *p_name, char **r_err, void *p_user_data) {
|
||||
void *sym_addr = dlsym(p_handle, p_name);
|
||||
|
||||
if (sym_addr)
|
||||
return sym_addr;
|
||||
|
||||
if (r_err)
|
||||
*r_err = str_format_new("%s\n", dlerror());
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void register_android_dl_fallback() {
|
||||
mono_dl_fallback_register(gd_mono_android_dlopen, gd_mono_android_dlsym, NULL, NULL);
|
||||
}
|
||||
|
||||
} // namespace GDMonoAndroid
|
||||
|
||||
#endif
|
18
modules/mono/mono_gd/gd_mono_android.h
Normal file
18
modules/mono/mono_gd/gd_mono_android.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
#ifndef GD_MONO_ANDROID_H
|
||||
#define GD_MONO_ANDROID_H
|
||||
|
||||
#if defined(ANDROID_ENABLED)
|
||||
|
||||
#include "core/ustring.h"
|
||||
|
||||
namespace GDMonoAndroid {
|
||||
|
||||
String get_app_native_lib_dir();
|
||||
|
||||
void register_android_dl_fallback();
|
||||
|
||||
} // namespace GDMonoAndroid
|
||||
|
||||
#endif // ANDROID_ENABLED
|
||||
|
||||
#endif // GD_MONO_ANDROID_H
|
|
@ -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;
|
||||
|
|
282
modules/mono/mono_gd/gd_mono_cache.cpp
Normal file
282
modules/mono/mono_gd/gd_mono_cache.cpp
Normal 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
|
174
modules/mono/mono_gd/gd_mono_cache.h
Normal file
174
modules/mono/mono_gd/gd_mono_cache.h
Normal 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
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
|
|
302
modules/mono/mono_gd/gd_mono_method_thunk.h
Normal file
302
modules/mono/mono_gd/gd_mono_method_thunk.h
Normal 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
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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)));
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
/*************************************************************************/
|
||||
/* android_utils.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "android_utils.h"
|
||||
|
||||
#ifdef __ANDROID__
|
||||
|
||||
#include "platform/android/thread_jandroid.h"
|
||||
|
||||
namespace GDMonoUtils {
|
||||
namespace Android {
|
||||
|
||||
String get_app_native_lib_dir() {
|
||||
JNIEnv *env = ThreadAndroid::get_env();
|
||||
|
||||
jclass activityThreadClass = env->FindClass("android/app/ActivityThread");
|
||||
jmethodID currentActivityThread = env->GetStaticMethodID(activityThreadClass, "currentActivityThread", "()Landroid/app/ActivityThread;");
|
||||
jobject activityThread = env->CallStaticObjectMethod(activityThreadClass, currentActivityThread);
|
||||
jmethodID getApplication = env->GetMethodID(activityThreadClass, "getApplication", "()Landroid/app/Application;");
|
||||
jobject ctx = env->CallObjectMethod(activityThread, getApplication);
|
||||
|
||||
jmethodID getApplicationInfo = env->GetMethodID(env->GetObjectClass(ctx), "getApplicationInfo", "()Landroid/content/pm/ApplicationInfo;");
|
||||
jobject applicationInfo = env->CallObjectMethod(ctx, getApplicationInfo);
|
||||
jfieldID nativeLibraryDirField = env->GetFieldID(env->GetObjectClass(applicationInfo), "nativeLibraryDir", "Ljava/lang/String;");
|
||||
jstring nativeLibraryDir = (jstring)env->GetObjectField(applicationInfo, nativeLibraryDirField);
|
||||
|
||||
String result;
|
||||
|
||||
const char *const nativeLibraryDir_utf8 = env->GetStringUTFChars(nativeLibraryDir, NULL);
|
||||
if (nativeLibraryDir_utf8) {
|
||||
result.parse_utf8(nativeLibraryDir_utf8);
|
||||
env->ReleaseStringUTFChars(nativeLibraryDir, nativeLibraryDir_utf8);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace Android
|
||||
} // namespace GDMonoUtils
|
||||
|
||||
#endif // __ANDROID__
|
|
@ -1,48 +0,0 @@
|
|||
/*************************************************************************/
|
||||
/* android_utils.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef ANDROID_UTILS_H
|
||||
#define ANDROID_UTILS_H
|
||||
|
||||
#ifdef __ANDROID__
|
||||
|
||||
#include "core/ustring.h"
|
||||
|
||||
namespace GDMonoUtils {
|
||||
namespace Android {
|
||||
|
||||
String get_app_native_lib_dir();
|
||||
|
||||
} // namespace Android
|
||||
} // namespace GDMonoUtils
|
||||
|
||||
#endif // __ANDROID__
|
||||
|
||||
#endif // ANDROID_UTILS_H
|
|
@ -216,6 +216,25 @@ String str_format(const char *p_format, ...) {
|
|||
#endif
|
||||
|
||||
String str_format(const char *p_format, va_list p_list) {
|
||||
char *buffer = str_format_new(p_format, p_list);
|
||||
|
||||
String res(buffer);
|
||||
memdelete_arr(buffer);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
char *str_format_new(const char *p_format, ...) {
|
||||
va_list list;
|
||||
|
||||
va_start(list, p_format);
|
||||
char *res = str_format_new(p_format, list);
|
||||
va_end(list);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
char *str_format_new(const char *p_format, va_list p_list) {
|
||||
va_list list;
|
||||
|
||||
va_copy(list, p_list);
|
||||
|
@ -230,8 +249,5 @@ String str_format(const char *p_format, va_list p_list) {
|
|||
gd_vsnprintf(buffer, len, p_format, list);
|
||||
va_end(list);
|
||||
|
||||
String res(buffer);
|
||||
memdelete_arr(buffer);
|
||||
|
||||
return res;
|
||||
return buffer;
|
||||
}
|
||||
|
|
|
@ -56,5 +56,7 @@ Error read_all_file_utf8(const String &p_path, String &r_content);
|
|||
|
||||
String str_format(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_1_2;
|
||||
String str_format(const char *p_format, va_list p_list) _PRINTF_FORMAT_ATTRIBUTE_1_0;
|
||||
char *str_format_new(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_1_2;
|
||||
char *str_format_new(const char *p_format, va_list p_list) _PRINTF_FORMAT_ATTRIBUTE_1_0;
|
||||
|
||||
#endif // STRING_FORMAT_H
|
||||
|
|
Loading…
Reference in a new issue