summaryrefslogtreecommitdiff
path: root/modules/mono
diff options
context:
space:
mode:
Diffstat (limited to 'modules/mono')
-rw-r--r--modules/mono/SCsub4
-rw-r--r--modules/mono/build_scripts/api_solution_build.py12
-rw-r--r--modules/mono/build_scripts/godot_tools_build.py11
-rw-r--r--modules/mono/build_scripts/mono_android_config.xml28
-rw-r--r--modules/mono/build_scripts/mono_configure.py131
-rw-r--r--modules/mono/build_scripts/patches/fix-mono-android-tkill.diff70
-rw-r--r--modules/mono/config.py12
-rw-r--r--modules/mono/csharp_script.cpp23
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs8
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/BuildManager.cs4
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs676
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/ExternalEditorId.cs3
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs36
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotSharpExport.cs197
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj20
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs3
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs10
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/Rider/.editorconfig6
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs416
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs117
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs4
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs146
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Utils/User32Dll.cs10
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/packages.config5
-rw-r--r--modules/mono/editor/bindings_generator.cpp170
-rw-r--r--modules/mono/editor/bindings_generator.h27
-rw-r--r--modules/mono/editor/editor_internal_calls.cpp15
-rw-r--r--modules/mono/editor/godotsharp_export.cpp26
-rw-r--r--modules/mono/editor/godotsharp_export.h5
-rw-r--r--modules/mono/glue/Managed/Files/AABB.cs5
-rw-r--r--modules/mono/glue/Managed/Files/Array.cs13
-rw-r--r--modules/mono/glue/Managed/Files/Basis.cs5
-rw-r--r--modules/mono/glue/Managed/Files/Color.cs5
-rw-r--r--modules/mono/glue/Managed/Files/Dictionary.cs13
-rw-r--r--modules/mono/glue/Managed/Files/GD.cs8
-rw-r--r--modules/mono/glue/Managed/Files/Mathf.cs69
-rw-r--r--modules/mono/glue/Managed/Files/MathfEx.cs3
-rw-r--r--modules/mono/glue/Managed/Files/NodePath.cs2
-rw-r--r--modules/mono/glue/Managed/Files/Plane.cs30
-rw-r--r--modules/mono/glue/Managed/Files/Quat.cs5
-rw-r--r--modules/mono/glue/Managed/Files/RID.cs2
-rw-r--r--modules/mono/glue/Managed/Files/Rect2.cs5
-rw-r--r--modules/mono/glue/Managed/Files/Transform.cs5
-rw-r--r--modules/mono/glue/Managed/Files/Transform2D.cs5
-rw-r--r--modules/mono/glue/Managed/Files/Vector2.cs5
-rw-r--r--modules/mono/glue/Managed/Files/Vector3.cs5
-rw-r--r--modules/mono/glue/base_object_glue.cpp1
-rw-r--r--modules/mono/glue/collections_glue.cpp11
-rw-r--r--modules/mono/glue/collections_glue.h4
-rw-r--r--modules/mono/glue/gd_glue.cpp3
-rw-r--r--modules/mono/godotsharp_dirs.cpp29
-rw-r--r--modules/mono/godotsharp_dirs.h2
-rw-r--r--modules/mono/mono_gd/gd_mono.cpp200
-rw-r--r--modules/mono/mono_gd/gd_mono.h4
-rw-r--r--modules/mono/mono_gd/gd_mono_android.cpp685
-rw-r--r--modules/mono/mono_gd/gd_mono_android.h (renamed from modules/mono/utils/android_utils.h)24
-rw-r--r--modules/mono/mono_gd/gd_mono_assembly.cpp41
-rw-r--r--modules/mono/mono_gd/gd_mono_assembly.h2
-rw-r--r--modules/mono/mono_gd/gd_mono_cache.cpp312
-rw-r--r--modules/mono/mono_gd/gd_mono_cache.h204
-rw-r--r--modules/mono/mono_gd/gd_mono_class.cpp7
-rw-r--r--modules/mono/mono_gd/gd_mono_class.h2
-rw-r--r--modules/mono/mono_gd/gd_mono_field.cpp6
-rw-r--r--modules/mono/mono_gd/gd_mono_header.h6
-rw-r--r--modules/mono/mono_gd/gd_mono_log.cpp63
-rw-r--r--modules/mono/mono_gd/gd_mono_log.h14
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.cpp11
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.h5
-rw-r--r--modules/mono/mono_gd/gd_mono_method.cpp6
-rw-r--r--modules/mono/mono_gd/gd_mono_method.h4
-rw-r--r--modules/mono/mono_gd/gd_mono_method_thunk.h332
-rw-r--r--modules/mono/mono_gd/gd_mono_property.cpp2
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.cpp315
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.h281
-rw-r--r--modules/mono/signal_awaiter_utils.cpp5
-rw-r--r--modules/mono/utils/android_utils.cpp68
-rw-r--r--modules/mono/utils/string_utils.cpp24
-rw-r--r--modules/mono/utils/string_utils.h2
79 files changed, 3750 insertions, 1307 deletions
diff --git a/modules/mono/SCsub b/modules/mono/SCsub
index a9afa7ccf6..457edfaeed 100644
--- a/modules/mono/SCsub
+++ b/modules/mono/SCsub
@@ -42,14 +42,14 @@ mono_configure.configure(env, env_mono)
if env_mono['tools'] and env_mono['mono_glue']:
import build_scripts.api_solution_build as api_solution_build
- api_solution_build.build(env_mono)
+ api_sln_cmd = api_solution_build.build(env_mono)
# Build GodotTools
if env_mono['tools']:
import build_scripts.godot_tools_build as godot_tools_build
if env_mono['mono_glue']:
- godot_tools_build.build(env_mono)
+ godot_tools_build.build(env_mono, api_sln_cmd)
else:
# Building without the glue sources so the Godot API solution may be missing.
# GodotTools depends on the Godot API solution. As such, we will only build
diff --git a/modules/mono/build_scripts/api_solution_build.py b/modules/mono/build_scripts/api_solution_build.py
index 1fe00a3028..be54d0a679 100644
--- a/modules/mono/build_scripts/api_solution_build.py
+++ b/modules/mono/build_scripts/api_solution_build.py
@@ -55,12 +55,22 @@ def build(env_mono):
'GodotSharpEditor.dll', 'GodotSharpEditor.pdb', 'GodotSharpEditor.xml'
]
+ depend_cmd = []
+
for build_config in ['Debug', 'Release']:
output_dir = Dir('#bin').abspath
editor_api_dir = os.path.join(output_dir, 'GodotSharp', 'Api', build_config)
targets = [os.path.join(editor_api_dir, filename) for filename in target_filenames]
- cmd = env_mono.CommandNoCache(targets, [], build_api_solution,
+ cmd = env_mono.CommandNoCache(targets, depend_cmd, build_api_solution,
module_dir=os.getcwd(), solution_build_config=build_config)
env_mono.AlwaysBuild(cmd)
+
+ # Make the Release build of the API solution depend on the Debug build.
+ # We do this in order to prevent SCons from building them in parallel,
+ # which can freak out MSBuild. In many cases, one of the builds would
+ # hang indefinitely requiring a key to be pressed for it to continue.
+ depend_cmd = cmd
+
+ return depend_cmd
diff --git a/modules/mono/build_scripts/godot_tools_build.py b/modules/mono/build_scripts/godot_tools_build.py
index 35daa6d307..6e5273f5e0 100644
--- a/modules/mono/build_scripts/godot_tools_build.py
+++ b/modules/mono/build_scripts/godot_tools_build.py
@@ -74,19 +74,16 @@ def build_godot_tools_project_editor(source, target, env):
copy_target(str(scons_target))
-def build(env_mono):
+def build(env_mono, api_sln_cmd):
assert env_mono['tools']
output_dir = Dir('#bin').abspath
editor_tools_dir = os.path.join(output_dir, 'GodotSharp', 'Tools')
- editor_api_dir = os.path.join(output_dir, 'GodotSharp', 'Api', 'Debug')
-
- source_filenames = ['GodotSharp.dll', 'GodotSharpEditor.dll']
- sources = [os.path.join(editor_api_dir, filename) for filename in source_filenames]
target_filenames = [
'GodotTools.dll', 'GodotTools.IdeConnection.dll', 'GodotTools.BuildLogger.dll',
- 'GodotTools.ProjectEditor.dll', 'DotNet.Glob.dll', 'GodotTools.Core.dll'
+ 'GodotTools.ProjectEditor.dll', 'DotNet.Glob.dll', 'GodotTools.Core.dll',
+ 'JetBrains.Annotations.dll', 'Newtonsoft.Json.dll'
]
if env_mono['target'] == 'debug':
@@ -97,7 +94,7 @@ def build(env_mono):
targets = [os.path.join(editor_tools_dir, filename) for filename in target_filenames]
- cmd = env_mono.CommandNoCache(targets, sources, build_godot_tools, module_dir=os.getcwd())
+ cmd = env_mono.CommandNoCache(targets, api_sln_cmd, build_godot_tools, module_dir=os.getcwd())
env_mono.AlwaysBuild(cmd)
diff --git a/modules/mono/build_scripts/mono_android_config.xml b/modules/mono/build_scripts/mono_android_config.xml
new file mode 100644
index 0000000000..e79670afd2
--- /dev/null
+++ b/modules/mono/build_scripts/mono_android_config.xml
@@ -0,0 +1,28 @@
+<configuration>
+ <dllmap wordsize="32" dll="i:cygwin1.dll" target="/system/lib/libc.so" />
+ <dllmap wordsize="64" dll="i:cygwin1.dll" target="/system/lib64/libc.so" />
+ <dllmap wordsize="32" dll="libc" target="/system/lib/libc.so" />
+ <dllmap wordsize="64" dll="libc" target="/system/lib64/libc.so" />
+ <dllmap wordsize="32" dll="intl" target="/system/lib/libc.so" />
+ <dllmap wordsize="64" dll="intl" target="/system/lib64/libc.so" />
+ <dllmap wordsize="32" dll="libintl" target="/system/lib/libc.so" />
+ <dllmap wordsize="64" dll="libintl" target="/system/lib64/libc.so" />
+ <dllmap dll="MonoPosixHelper" target="libMonoPosixHelper.so" />
+ <dllmap dll="System.Native" target="libmono-native.so" />
+ <dllmap wordsize="32" dll="i:msvcrt" target="/system/lib/libc.so" />
+ <dllmap wordsize="64" dll="i:msvcrt" target="/system/lib64/libc.so" />
+ <dllmap wordsize="32" dll="i:msvcrt.dll" target="/system/lib/libc.so" />
+ <dllmap wordsize="64" dll="i:msvcrt.dll" target="/system/lib64/libc.so" />
+ <dllmap wordsize="32" dll="sqlite" target="/system/lib/libsqlite.so" />
+ <dllmap wordsize="64" dll="sqlite" target="/system/lib64/libsqlite.so" />
+ <dllmap wordsize="32" dll="sqlite3" target="/system/lib/libsqlite.so" />
+ <dllmap wordsize="64" dll="sqlite3" target="/system/lib64/libsqlite.so" />
+ <dllmap wordsize="32" dll="liblog" target="/system/lib/liblog.so" />
+ <dllmap wordsize="64" dll="liblog" target="/system/lib64/liblog.so" />
+ <dllmap dll="i:kernel32.dll">
+ <dllentry dll="__Internal" name="CopyMemory" target="mono_win32_compat_CopyMemory"/>
+ <dllentry dll="__Internal" name="FillMemory" target="mono_win32_compat_FillMemory"/>
+ <dllentry dll="__Internal" name="MoveMemory" target="mono_win32_compat_MoveMemory"/>
+ <dllentry dll="__Internal" name="ZeroMemory" target="mono_win32_compat_ZeroMemory"/>
+ </dllmap>
+</configuration>
diff --git a/modules/mono/build_scripts/mono_configure.py b/modules/mono/build_scripts/mono_configure.py
index 4c1ebd8d74..89d56def7d 100644
--- a/modules/mono/build_scripts/mono_configure.py
+++ b/modules/mono/build_scripts/mono_configure.py
@@ -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 is_javascript:
+ mono_static = True
- if (os.getenv('MONO32_PREFIX') or os.getenv('MONO64_PREFIX')) and not mono_prefix:
+ 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':
@@ -92,9 +120,9 @@ def configure(env, env_mono):
env.Append(LIBPATH=mono_lib_path)
env_mono.Prepend(CPPPATH=os.path.join(mono_root, 'include', 'mono-2.0'))
- if mono_static:
- lib_suffix = Environment()['LIBSUFFIX']
+ lib_suffix = Environment()['LIBSUFFIX']
+ if mono_static:
if env.msvc:
mono_static_lib_name = 'libmono-static-sgen'
else:
@@ -116,13 +144,13 @@ def configure(env, env_mono):
env.Append(LIBS=['psapi'])
env.Append(LIBS=['version'])
else:
- mono_lib_name = find_file_in_dir(mono_lib_path, mono_lib_names, extension='.lib')
+ mono_lib_name = find_file_in_dir(mono_lib_path, mono_lib_names, extension=lib_suffix)
if not mono_lib_name:
raise RuntimeError('Could not find mono library in: ' + mono_lib_path)
if env.msvc:
- env.Append(LINKFLAGS=mono_lib_name + Environment()['LIBSUFFIX'])
+ env.Append(LINKFLAGS=mono_lib_name + lib_suffix)
else:
env.Append(LIBS=[mono_lib_name])
@@ -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')
@@ -178,12 +206,37 @@ def configure(env, env_mono):
env_mono.Append(CPPDEFINES=['_REENTRANT'])
if mono_static:
+ env.Append(LINKFLAGS=['-rdynamic'])
+
mono_lib_file = os.path.join(mono_lib_path, 'lib' + mono_lib + '.a')
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 +244,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'])
@@ -228,21 +283,23 @@ def configure(env, env_mono):
libs_output_dir = get_android_out_dir(env) if is_android else '#bin'
copy_file(mono_lib_path, libs_output_dir, 'lib' + mono_so_name + sharedlib_ext)
- 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
+ module_dir = os.getcwd()
+ config_file_path = os.path.join(module_dir, 'build_scripts', 'mono_android_config.xml')
+ make_android_mono_config.generate_compressed_config(config_file_path, 'mono_gd/')
- # Copy the required shared libraries
- copy_mono_shared_libs(env, mono_root, None)
+ # 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 +308,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 +319,9 @@ def make_template_dir(env, mono_root):
template_dir_name = ''
- if platform in ['windows', 'osx', 'x11', 'android']:
- 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 +334,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
@@ -371,12 +427,19 @@ def copy_mono_shared_libs(env, mono_root, target_mono_root_dir):
platform = env['platform']
if platform == 'windows':
+ src_mono_bin_dir = os.path.join(mono_root, 'bin')
target_mono_bin_dir = os.path.join(target_mono_root_dir, 'bin')
if not os.path.isdir(target_mono_bin_dir):
os.makedirs(target_mono_bin_dir)
- copy(os.path.join(mono_root, 'bin', 'MonoPosixHelper.dll'), target_mono_bin_dir)
+ mono_posix_helper_name = find_file_in_dir(src_mono_bin_dir, ['MonoPosixHelper', 'libMonoPosixHelper'], extension='.dll')
+ copy(os.path.join(src_mono_bin_dir, mono_posix_helper_name + '.dll'), os.path.join(target_mono_bin_dir, 'MonoPosixHelper.dll'))
+
+ # For newer versions
+ btls_dll_path = os.path.join(src_mono_bin_dir, 'libmono-btls-shared.dll')
+ if os.path.isfile(btls_dll_path):
+ copy(btls_dll_path, target_mono_bin_dir)
else:
target_mono_lib_dir = get_android_out_dir(env) if platform == 'android' else os.path.join(target_mono_root_dir, 'lib')
@@ -386,7 +449,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':
+ 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'
diff --git a/modules/mono/build_scripts/patches/fix-mono-android-tkill.diff b/modules/mono/build_scripts/patches/fix-mono-android-tkill.diff
deleted file mode 100644
index 05f8dcadcc..0000000000
--- a/modules/mono/build_scripts/patches/fix-mono-android-tkill.diff
+++ /dev/null
@@ -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
diff --git a/modules/mono/config.py b/modules/mono/config.py
index 9adf4ee6e5..70cb464c7a 100644
--- a/modules/mono/config.py
+++ b/modules/mono/config.py
@@ -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 [
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 83be10dee3..4536614379 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -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"
@@ -126,12 +128,11 @@ void CSharpLanguage::init() {
print_line("Run this binary with '--generate-mono-glue path/to/modules/mono/glue'");
#endif
- gdmono->initialize_load_assemblies();
+ if (gdmono->is_runtime_initialized())
+ gdmono->initialize_load_assemblies();
#ifdef TOOLS_ENABLED
EditorNode::add_init_callback(&_editor_init_callback);
-
- GLOBAL_DEF("mono/export/include_scripts_content", false);
#endif
}
@@ -545,7 +546,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 +572,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 +584,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 +594,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 +617,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 +1078,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 +1087,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
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
index 9a2b2e3a26..da90c960e5 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
@@ -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;
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs
index eb2c2dd77c..ad8a6516ab 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs
@@ -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
diff --git a/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs b/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs
index ab37d89955..217bf5c144 100644
--- a/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs
@@ -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}"
});
diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
new file mode 100644
index 0000000000..c7e8ea511a
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
@@ -0,0 +1,676 @@
+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/export_assemblies_inside_pck", true);
+
+ 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;
+
+ if (platform == OS.Platforms.Android)
+ {
+ string godotAndroidExtProfileDir = GetBclProfileDir("godot_android_ext");
+ string monoAndroidAssemblyPath = Path.Combine(godotAndroidExtProfileDir, "Mono.Android.dll");
+
+ if (!File.Exists(monoAndroidAssemblyPath))
+ throw new FileNotFoundException("Assembly not found: 'Mono.Android'", monoAndroidAssemblyPath);
+
+ dependencies["Mono.Android"] = monoAndroidAssemblyPath;
+ }
+
+ var initialDependencies = dependencies.Duplicate();
+ internal_GetExportedAssemblyDependencies(initialDependencies, buildConfig, DeterminePlatformBclDir(platform), dependencies);
+
+ string outputDataDir = null;
+
+ if (PlatformHasTemplateDir(platform))
+ outputDataDir = ExportDataDirectory(features, platform, isDebug, outputDir);
+
+ string apiConfig = isDebug ? "Debug" : "Release";
+ string resAssembliesDir = Path.Combine(GodotSharpDirs.ResAssembliesBaseDir, apiConfig);
+
+ bool assembliesInsidePck = (bool) ProjectSettings.GetSetting("mono/export/export_assemblies_inside_pck") || outputDataDir == null;
+
+ if (!assembliesInsidePck)
+ {
+ string outputDataGameAssembliesDir = Path.Combine(outputDataDir, "Assemblies");
+ if (!Directory.Exists(outputDataGameAssembliesDir))
+ Directory.CreateDirectory(outputDataGameAssembliesDir);
+ }
+
+ foreach (var dependency in dependencies)
+ {
+ string dependSrcPath = dependency.Value;
+
+ if (assembliesInsidePck)
+ {
+ string dependDstPath = Path.Combine(resAssembliesDir, dependSrcPath.GetFile());
+ AddFile(dependSrcPath, dependDstPath);
+ }
+ else
+ {
+ string dependDstPath = Path.Combine(outputDataDir, "Assemblies", dependSrcPath.GetFile());
+ File.Copy(dependSrcPath, dependDstPath);
+ }
+ }
+
+ // 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, "Failed to export C# project");
+ }
+ }
+
+ 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());
+ bool validTemplatePathFound = true;
+
+ if (!Directory.Exists(templateDirPath))
+ {
+ validTemplatePathFound = false;
+
+ if (isDebug)
+ {
+ target = "debug"; // Support both 'release_debug' and 'debug' for the template data directory name
+ templateDirPath = Path.Combine(Internal.FullTemplatesDir, TemplateDirName());
+ validTemplatePathFound = true;
+
+ if (!Directory.Exists(templateDirPath))
+ validTemplatePathFound = false;
+ }
+ }
+
+ if (!validTemplatePathFound)
+ throw new FileNotFoundException("Data template directory not found", templateDirPath);
+
+ 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 GetBclProfileDir(string profile)
+ {
+ string templatesDir = Internal.FullTemplatesDir;
+ return Path.Combine(templatesDir, "bcl", profile);
+ }
+
+ 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")))
+ {
+ if (PlatformRequiresCustomBcl(platform))
+ throw new FileNotFoundException($"Missing BCL (Base Class Library) for platform: {platform}");
+
+ platformBclDir = null; // Use the one we're running on
+ }
+ }
+
+ return platformBclDir;
+ }
+
+ /// <summary>
+ /// Determines whether the BCL bundled with the Godot editor can be used for the target platform,
+ /// or if it requires a custom BCL that must be distributed with the export templates.
+ /// </summary>
+ private static bool PlatformRequiresCustomBcl(string platform)
+ {
+ if (new[] {OS.Platforms.Android, OS.Platforms.HTML5}.Contains(platform))
+ return true;
+
+ // The 'net_4_x' BCL is not compatible between Windows and the other platforms.
+ // We use the names 'net_4_x_win' and 'net_4_x' to differentiate between the two.
+
+ bool isWinOrUwp = new[]
+ {
+ OS.Platforms.Windows,
+ OS.Platforms.UWP
+ }.Contains(platform);
+
+ return OS.IsWindows ? !isWinOrUwp : isWinOrUwp;
+ }
+
+ private static string DeterminePlatformBclProfile(string platform)
+ {
+ switch (platform)
+ {
+ case OS.Platforms.Windows:
+ case OS.Platforms.UWP:
+ return "net_4_x_win";
+ 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(Godot.Collections.Dictionary<string, string> initialDependencies,
+ string buildConfig, string customBclDir, Godot.Collections.Dictionary<string, string> dependencies);
+ }
+}
diff --git a/modules/mono/editor/GodotTools/GodotTools/ExternalEditorId.cs b/modules/mono/editor/GodotTools/GodotTools/ExternalEditorId.cs
index 4312ca0230..bb218c2f19 100644
--- a/modules/mono/editor/GodotTools/GodotTools/ExternalEditorId.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/ExternalEditorId.cs
@@ -6,6 +6,7 @@ namespace GodotTools
VisualStudio, // TODO (Windows-only)
VisualStudioForMac, // Mac-only
MonoDevelop,
- VsCode
+ VsCode,
+ Rider
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
index 12edd651df..660971d912 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
@@ -1,12 +1,15 @@
using Godot;
+using GodotTools.Export;
using GodotTools.Utils;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using GodotTools.Ides;
+using GodotTools.Ides.Rider;
using GodotTools.Internals;
using GodotTools.ProjectEditor;
+using JetBrains.Annotations;
using static GodotTools.Internals.Globals;
using File = GodotTools.Utils.File;
using OS = GodotTools.Utils.OS;
@@ -188,6 +191,7 @@ namespace GodotTools
"code", "code-oss", "vscode", "vscode-oss", "visual-studio-code", "visual-studio-code-oss"
};
+ [UsedImplicitly]
public Error OpenInExternalEditor(Script script, int line, int col)
{
var editor = (ExternalEditorId) editorSettings.GetSetting("mono/editor/external_editor");
@@ -201,6 +205,12 @@ namespace GodotTools
throw new NotSupportedException();
case ExternalEditorId.VisualStudioForMac:
goto case ExternalEditorId.MonoDevelop;
+ case ExternalEditorId.Rider:
+ {
+ string scriptPath = ProjectSettings.GlobalizePath(script.ResourcePath);
+ RiderPathManager.OpenFile(GodotSharpDirs.ProjectSlnPath, scriptPath, line);
+ return Error.Ok;
+ }
case ExternalEditorId.MonoDevelop:
{
string scriptPath = ProjectSettings.GlobalizePath(script.ResourcePath);
@@ -225,7 +235,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 +275,7 @@ namespace GodotTools
string command;
- if (OS.IsOSX())
+ if (OS.IsOSX)
{
if (!osxAppBundleInstalled && _vsCodePath.Empty())
{
@@ -305,6 +315,7 @@ namespace GodotTools
return Error.Ok;
}
+ [UsedImplicitly]
public bool OverridesExternalEditor()
{
return (ExternalEditorId) editorSettings.GetSetting("mono/editor/external_editor") != ExternalEditorId.None;
@@ -415,21 +426,24 @@ namespace GodotTools
string settingsHintStr = "Disabled";
- if (OS.IsWindows())
+ if (OS.IsWindows)
{
settingsHintStr += $",MonoDevelop:{(int) ExternalEditorId.MonoDevelop}" +
- $",Visual Studio Code:{(int) ExternalEditorId.VsCode}";
+ $",Visual Studio Code:{(int) ExternalEditorId.VsCode}" +
+ $",JetBrains Rider:{(int) ExternalEditorId.Rider}";
}
- else if (OS.IsOSX())
+ else if (OS.IsOSX)
{
settingsHintStr += $",Visual Studio:{(int) ExternalEditorId.VisualStudioForMac}" +
$",MonoDevelop:{(int) ExternalEditorId.MonoDevelop}" +
- $",Visual Studio Code:{(int) ExternalEditorId.VsCode}";
+ $",Visual Studio Code:{(int) ExternalEditorId.VsCode}" +
+ $",JetBrains Rider:{(int) ExternalEditorId.Rider}";
}
- else if (OS.IsUnix())
+ else if (OS.IsUnixLike())
{
settingsHintStr += $",MonoDevelop:{(int) ExternalEditorId.MonoDevelop}" +
- $",Visual Studio Code:{(int) ExternalEditorId.VsCode}";
+ $",Visual Studio Code:{(int) ExternalEditorId.VsCode}" +
+ $",JetBrains Rider:{(int) ExternalEditorId.Rider}";
}
editorSettings.AddPropertyInfo(new Godot.Collections.Dictionary
@@ -441,11 +455,13 @@ namespace GodotTools
});
// Export plugin
- var exportPlugin = new GodotSharpExport();
+ var exportPlugin = new ExportPlugin();
AddExportPlugin(exportPlugin);
+ exportPlugin.RegisterExportSettings();
exportPluginWeak = WeakRef(exportPlugin);
BuildManager.Initialize();
+ RiderPathManager.Initialize();
GodotIdeManager = new GodotIdeManager();
AddChild(GodotIdeManager);
@@ -461,7 +477,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();
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpExport.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpExport.cs
deleted file mode 100644
index 4f93ef8530..0000000000
--- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpExport.cs
+++ /dev/null
@@ -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);
- }
-}
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
index 3c57900873..be2b70529e 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
@@ -30,6 +30,15 @@
<ConsolePause>false</ConsolePause>
</PropertyGroup>
<ItemGroup>
+ <Reference Include="JetBrains.Annotations, Version=2019.1.3.0, Culture=neutral, PublicKeyToken=1010a0d8d6380325">
+ <HintPath>..\packages\JetBrains.Annotations.2019.1.3\lib\net20\JetBrains.Annotations.dll</HintPath>
+ <Private>True</Private>
+ </Reference>
+ <Reference Include="Mono.Posix" />
+ <Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed">
+ <HintPath>..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
+ <Private>True</Private>
+ </Reference>
<Reference Include="System" />
<Reference Include="GodotSharp">
<HintPath>$(GodotSourceRootPath)/bin/GodotSharp/Api/$(GodotApiConfiguration)/GodotSharp.dll</HintPath>
@@ -40,11 +49,14 @@
</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" />
<Compile Include="Ides\MonoDevelop\EditorId.cs" />
<Compile Include="Ides\MonoDevelop\Instance.cs" />
+ <Compile Include="Ides\Rider\RiderPathLocator.cs" />
+ <Compile Include="Ides\Rider\RiderPathManager.cs" />
<Compile Include="Internals\BindingsGenerator.cs" />
<Compile Include="Internals\EditorProgress.cs" />
<Compile Include="Internals\GodotSharpDirs.cs" />
@@ -63,9 +75,9 @@
<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" />
+ <Compile Include="Utils\User32Dll.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\GodotTools.BuildLogger\GodotTools.BuildLogger.csproj">
@@ -85,5 +97,11 @@
<Name>GodotTools.Core</Name>
</ProjectReference>
</ItemGroup>
+ <ItemGroup>
+ <None Include="packages.config" />
+ </ItemGroup>
+ <ItemGroup>
+ <Content Include="Ides\Rider\.editorconfig" />
+ </ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project> \ No newline at end of file
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs
index 01aa0d0ab1..3213de0127 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs
@@ -72,6 +72,7 @@ namespace GodotTools.Ides
case ExternalEditorId.None:
case ExternalEditorId.VisualStudio:
case ExternalEditorId.VsCode:
+ case ExternalEditorId.Rider:
throw new NotSupportedException();
case ExternalEditorId.VisualStudioForMac:
goto case ExternalEditorId.MonoDevelop;
@@ -79,7 +80,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);
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs
index 1fdccf5bbd..6026c109ad 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs
@@ -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>
{
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/.editorconfig b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/.editorconfig
new file mode 100644
index 0000000000..aca19790ca
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/.editorconfig
@@ -0,0 +1,6 @@
+root = true
+
+[*]
+indent_style = space
+indent_size = 2
+end_of_line = lf \ No newline at end of file
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs
new file mode 100644
index 0000000000..901ade71e3
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs
@@ -0,0 +1,416 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Godot;
+using JetBrains.Annotations;
+using Microsoft.Win32;
+using Newtonsoft.Json;
+using Directory = System.IO.Directory;
+using Environment = System.Environment;
+using File = System.IO.File;
+using Path = System.IO.Path;
+using OS = GodotTools.Utils.OS;
+
+namespace GodotTools.Ides.Rider
+{
+ /// <summary>
+ /// This code is a modified version of the JetBrains resharper-unity plugin listed under Apache License 2.0 license:
+ /// https://github.com/JetBrains/resharper-unity/blob/master/unity/JetBrains.Rider.Unity.Editor/EditorPlugin/RiderPathLocator.cs
+ /// </summary>
+ public static class RiderPathLocator
+ {
+ public static RiderInfo[] GetAllRiderPaths()
+ {
+ try
+ {
+ if (OS.IsWindows)
+ {
+ return CollectRiderInfosWindows();
+ }
+ if (OS.IsOSX)
+ {
+ return CollectRiderInfosMac();
+ }
+ if (OS.IsUnixLike())
+ {
+ return CollectAllRiderPathsLinux();
+ }
+ throw new Exception("Unexpected OS.");
+ }
+ catch (Exception e)
+ {
+ GD.PushWarning(e.Message);
+ }
+
+ return new RiderInfo[0];
+ }
+
+ private static RiderInfo[] CollectAllRiderPathsLinux()
+ {
+ var installInfos = new List<RiderInfo>();
+ var home = Environment.GetEnvironmentVariable("HOME");
+ if (!string.IsNullOrEmpty(home))
+ {
+ var toolboxRiderRootPath = GetToolboxBaseDir();
+ installInfos.AddRange(CollectPathsFromToolbox(toolboxRiderRootPath, "bin", "rider.sh", false)
+ .Select(a => new RiderInfo(a, true)).ToList());
+
+ //$Home/.local/share/applications/jetbrains-rider.desktop
+ var shortcut = new FileInfo(Path.Combine(home, @".local/share/applications/jetbrains-rider.desktop"));
+
+ if (shortcut.Exists)
+ {
+ var lines = File.ReadAllLines(shortcut.FullName);
+ foreach (var line in lines)
+ {
+ if (!line.StartsWith("Exec=\""))
+ continue;
+ var path = line.Split('"').Where((item, index) => index == 1).SingleOrDefault();
+ if (string.IsNullOrEmpty(path))
+ continue;
+
+ if (installInfos.Any(a => a.Path == path)) // avoid adding similar build as from toolbox
+ continue;
+ installInfos.Add(new RiderInfo(path, false));
+ }
+ }
+ }
+
+ // snap install
+ var snapInstallPath = "/snap/rider/current/bin/rider.sh";
+ if (new FileInfo(snapInstallPath).Exists)
+ installInfos.Add(new RiderInfo(snapInstallPath, false));
+
+ return installInfos.ToArray();
+ }
+
+ private static RiderInfo[] CollectRiderInfosMac()
+ {
+ var installInfos = new List<RiderInfo>();
+ // "/Applications/*Rider*.app"
+ var folder = new DirectoryInfo("/Applications");
+ if (folder.Exists)
+ {
+ installInfos.AddRange(folder.GetDirectories("*Rider*.app")
+ .Select(a => new RiderInfo(a.FullName, false))
+ .ToList());
+ }
+
+ // /Users/user/Library/Application Support/JetBrains/Toolbox/apps/Rider/ch-1/181.3870.267/Rider EAP.app
+ var toolboxRiderRootPath = GetToolboxBaseDir();
+ var paths = CollectPathsFromToolbox(toolboxRiderRootPath, "", "Rider*.app", true)
+ .Select(a => new RiderInfo(a, true));
+ installInfos.AddRange(paths);
+
+ return installInfos.ToArray();
+ }
+
+ private static RiderInfo[] CollectRiderInfosWindows()
+ {
+ var installInfos = new List<RiderInfo>();
+ var toolboxRiderRootPath = GetToolboxBaseDir();
+ var installPathsToolbox = CollectPathsFromToolbox(toolboxRiderRootPath, "bin", "rider64.exe", false).ToList();
+ installInfos.AddRange(installPathsToolbox.Select(a => new RiderInfo(a, true)).ToList());
+
+ var installPaths = new List<string>();
+ const string registryKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
+ CollectPathsFromRegistry(registryKey, installPaths);
+ const string wowRegistryKey = @"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall";
+ CollectPathsFromRegistry(wowRegistryKey, installPaths);
+
+ installInfos.AddRange(installPaths.Select(a => new RiderInfo(a, false)).ToList());
+
+ return installInfos.ToArray();
+ }
+
+ private static string GetToolboxBaseDir()
+ {
+ if (OS.IsWindows)
+ {
+ var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
+ return Path.Combine(localAppData, @"JetBrains\Toolbox\apps\Rider");
+ }
+
+ if (OS.IsOSX)
+ {
+ var home = Environment.GetEnvironmentVariable("HOME");
+ if (!string.IsNullOrEmpty(home))
+ {
+ return Path.Combine(home, @"Library/Application Support/JetBrains/Toolbox/apps/Rider");
+ }
+ }
+
+ if (OS.IsUnixLike())
+ {
+ var home = Environment.GetEnvironmentVariable("HOME");
+ if (!string.IsNullOrEmpty(home))
+ {
+ return Path.Combine(home, @".local/share/JetBrains/Toolbox/apps/Rider");
+ }
+ }
+
+ throw new Exception("Unexpected OS.");
+ }
+
+ internal static ProductInfo GetBuildVersion(string path)
+ {
+ var buildTxtFileInfo = new FileInfo(Path.Combine(path, GetRelativePathToBuildTxt()));
+ var dir = buildTxtFileInfo.DirectoryName;
+ if (!Directory.Exists(dir))
+ return null;
+ var buildVersionFile = new FileInfo(Path.Combine(dir, "product-info.json"));
+ if (!buildVersionFile.Exists)
+ return null;
+ var json = File.ReadAllText(buildVersionFile.FullName);
+ return ProductInfo.GetProductInfo(json);
+ }
+
+ internal static Version GetBuildNumber(string path)
+ {
+ var file = new FileInfo(Path.Combine(path, GetRelativePathToBuildTxt()));
+ if (!file.Exists)
+ return null;
+ var text = File.ReadAllText(file.FullName);
+ if (text.Length <= 3)
+ return null;
+
+ var versionText = text.Substring(3);
+ return Version.TryParse(versionText, out var v) ? v : null;
+ }
+
+ internal static bool IsToolbox(string path)
+ {
+ return path.StartsWith(GetToolboxBaseDir());
+ }
+
+ private static string GetRelativePathToBuildTxt()
+ {
+ if (OS.IsWindows || OS.IsUnixLike())
+ return "../../build.txt";
+ if (OS.IsOSX)
+ return "Contents/Resources/build.txt";
+ throw new Exception("Unknown OS.");
+ }
+
+ private static void CollectPathsFromRegistry(string registryKey, List<string> installPaths)
+ {
+ using (var key = Registry.LocalMachine.OpenSubKey(registryKey))
+ {
+ if (key == null) return;
+ foreach (var subkeyName in key.GetSubKeyNames().Where(a => a.Contains("Rider")))
+ {
+ using (var subkey = key.OpenSubKey(subkeyName))
+ {
+ var folderObject = subkey?.GetValue("InstallLocation");
+ if (folderObject == null) continue;
+ var folder = folderObject.ToString();
+ var possiblePath = Path.Combine(folder, @"bin\rider64.exe");
+ if (File.Exists(possiblePath))
+ installPaths.Add(possiblePath);
+ }
+ }
+ }
+ }
+
+ private static string[] CollectPathsFromToolbox(string toolboxRiderRootPath, string dirName, string searchPattern,
+ bool isMac)
+ {
+ if (!Directory.Exists(toolboxRiderRootPath))
+ return new string[0];
+
+ var channelDirs = Directory.GetDirectories(toolboxRiderRootPath);
+ var paths = channelDirs.SelectMany(channelDir =>
+ {
+ try
+ {
+ // use history.json - last entry stands for the active build https://jetbrains.slack.com/archives/C07KNP99D/p1547807024066500?thread_ts=1547731708.057700&cid=C07KNP99D
+ var historyFile = Path.Combine(channelDir, ".history.json");
+ if (File.Exists(historyFile))
+ {
+ var json = File.ReadAllText(historyFile);
+ var build = ToolboxHistory.GetLatestBuildFromJson(json);
+ if (build != null)
+ {
+ var buildDir = Path.Combine(channelDir, build);
+ var executablePaths = GetExecutablePaths(dirName, searchPattern, isMac, buildDir);
+ if (executablePaths.Any())
+ return executablePaths;
+ }
+ }
+
+ var channelFile = Path.Combine(channelDir, ".channel.settings.json");
+ if (File.Exists(channelFile))
+ {
+ var json = File.ReadAllText(channelFile).Replace("active-application", "active_application");
+ var build = ToolboxInstallData.GetLatestBuildFromJson(json);
+ if (build != null)
+ {
+ var buildDir = Path.Combine(channelDir, build);
+ var executablePaths = GetExecutablePaths(dirName, searchPattern, isMac, buildDir);
+ if (executablePaths.Any())
+ return executablePaths;
+ }
+ }
+
+ // changes in toolbox json files format may brake the logic above, so return all found Rider installations
+ return Directory.GetDirectories(channelDir)
+ .SelectMany(buildDir => GetExecutablePaths(dirName, searchPattern, isMac, buildDir));
+ }
+ catch (Exception e)
+ {
+ // do not write to Debug.Log, just log it.
+ Logger.Warn($"Failed to get RiderPath from {channelDir}", e);
+ }
+
+ return new string[0];
+ })
+ .Where(c => !string.IsNullOrEmpty(c))
+ .ToArray();
+ return paths;
+ }
+
+ private static string[] GetExecutablePaths(string dirName, string searchPattern, bool isMac, string buildDir)
+ {
+ var folder = new DirectoryInfo(Path.Combine(buildDir, dirName));
+ if (!folder.Exists)
+ return new string[0];
+
+ if (!isMac)
+ return new[] {Path.Combine(folder.FullName, searchPattern)}.Where(File.Exists).ToArray();
+ return folder.GetDirectories(searchPattern).Select(f => f.FullName)
+ .Where(Directory.Exists).ToArray();
+ }
+
+ // Disable the "field is never assigned" compiler warning. We never assign it, but Unity does.
+ // Note that Unity disable this warning in the generated C# projects
+#pragma warning disable 0649
+
+ [Serializable]
+ class ToolboxHistory
+ {
+ public List<ItemNode> history;
+
+ public static string GetLatestBuildFromJson(string json)
+ {
+ try
+ {
+ return JsonConvert.DeserializeObject<ToolboxHistory>(json).history.LastOrDefault()?.item.build;
+ }
+ catch (Exception)
+ {
+ Logger.Warn($"Failed to get latest build from json {json}");
+ }
+
+ return null;
+ }
+ }
+
+ [Serializable]
+ class ItemNode
+ {
+ public BuildNode item;
+ }
+
+ [Serializable]
+ class BuildNode
+ {
+ public string build;
+ }
+
+ [Serializable]
+ public class ProductInfo
+ {
+ public string version;
+ public string versionSuffix;
+
+ [CanBeNull]
+ internal static ProductInfo GetProductInfo(string json)
+ {
+ try
+ {
+ var productInfo = JsonConvert.DeserializeObject<ProductInfo>(json);
+ return productInfo;
+ }
+ catch (Exception)
+ {
+ Logger.Warn($"Failed to get version from json {json}");
+ }
+
+ return null;
+ }
+ }
+
+ // ReSharper disable once ClassNeverInstantiated.Global
+ [Serializable]
+ class ToolboxInstallData
+ {
+ // ReSharper disable once InconsistentNaming
+ public ActiveApplication active_application;
+
+ [CanBeNull]
+ public static string GetLatestBuildFromJson(string json)
+ {
+ try
+ {
+ var toolbox = JsonConvert.DeserializeObject<ToolboxInstallData>(json);
+ var builds = toolbox.active_application.builds;
+ if (builds != null && builds.Any())
+ return builds.First();
+ }
+ catch (Exception)
+ {
+ Logger.Warn($"Failed to get latest build from json {json}");
+ }
+
+ return null;
+ }
+ }
+
+ [Serializable]
+ class ActiveApplication
+ {
+ // ReSharper disable once InconsistentNaming
+ public List<string> builds;
+ }
+
+#pragma warning restore 0649
+
+ public struct RiderInfo
+ {
+ public bool IsToolbox;
+ public string Presentation;
+ public Version BuildNumber;
+ public ProductInfo ProductInfo;
+ public string Path;
+
+ public RiderInfo(string path, bool isToolbox)
+ {
+ BuildNumber = GetBuildNumber(path);
+ ProductInfo = GetBuildVersion(path);
+ Path = new FileInfo(path).FullName; // normalize separators
+ var presentation = $"Rider {BuildNumber}";
+
+ if (ProductInfo != null && !string.IsNullOrEmpty(ProductInfo.version))
+ {
+ var suffix = string.IsNullOrEmpty(ProductInfo.versionSuffix) ? "" : $" {ProductInfo.versionSuffix}";
+ presentation = $"Rider {ProductInfo.version}{suffix}";
+ }
+
+ if (isToolbox)
+ presentation += " (JetBrains Toolbox)";
+
+ Presentation = presentation;
+ IsToolbox = isToolbox;
+ }
+ }
+
+ private static class Logger
+ {
+ internal static void Warn(string message, Exception e = null)
+ {
+ throw new Exception(message, e);
+ }
+ }
+ }
+}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs
new file mode 100644
index 0000000000..b7dba13bbe
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs
@@ -0,0 +1,117 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Godot;
+using GodotTools.Internals;
+
+namespace GodotTools.Ides.Rider
+{
+ public static class RiderPathManager
+ {
+ private static readonly string editorPathSettingName= "mono/editor/editor_path_optional";
+
+ private static string GetRiderPathFromSettings()
+ {
+ var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
+ if (editorSettings.HasSetting(editorPathSettingName))
+ return (string) editorSettings.GetSetting(editorPathSettingName);
+ return null;
+ }
+
+ public static void Initialize()
+ {
+ var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
+ var editor = (ExternalEditorId) editorSettings.GetSetting("mono/editor/external_editor");
+ if (editor == ExternalEditorId.Rider)
+ {
+ if (!editorSettings.HasSetting(editorPathSettingName))
+ {
+ Globals.EditorDef(editorPathSettingName, "Optional");
+ editorSettings.AddPropertyInfo(new Godot.Collections.Dictionary
+ {
+ ["type"] = Variant.Type.String,
+ ["name"] = editorPathSettingName,
+ ["hint"] = PropertyHint.File,
+ ["hint_string"] = ""
+ });
+ }
+
+ var riderPath = (string) editorSettings.GetSetting(editorPathSettingName);
+ if (IsRiderAndExists(riderPath))
+ {
+ Globals.EditorDef(editorPathSettingName, riderPath);
+ return;
+ }
+
+ var paths = RiderPathLocator.GetAllRiderPaths();
+
+ if (!paths.Any())
+ return;
+
+ var newPath = paths.Last().Path;
+ Globals.EditorDef(editorPathSettingName, newPath);
+ editorSettings.SetSetting(editorPathSettingName, newPath);
+ }
+ }
+
+ private static bool IsRider(string path)
+ {
+ if (string.IsNullOrEmpty(path))
+ {
+ return false;
+ }
+
+ var fileInfo = new FileInfo(path);
+ var filename = fileInfo.Name.ToLowerInvariant();
+ return filename.StartsWith("rider", StringComparison.Ordinal);
+ }
+
+ private static string CheckAndUpdatePath(string riderPath)
+ {
+ if (IsRiderAndExists(riderPath))
+ {
+ return riderPath;
+ }
+
+ var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
+ var paths = RiderPathLocator.GetAllRiderPaths();
+
+ if (!paths.Any())
+ return null;
+
+ var newPath = paths.Last().Path;
+ editorSettings.SetSetting(editorPathSettingName, newPath);
+ Globals.EditorDef(editorPathSettingName, newPath);
+ return newPath;
+ }
+
+ private static bool IsRiderAndExists(string riderPath)
+ {
+ return !string.IsNullOrEmpty(riderPath) && IsRider(riderPath) && new FileInfo(riderPath).Exists;
+ }
+
+ public static void OpenFile(string slnPath, string scriptPath, int line)
+ {
+ var pathFromSettings = GetRiderPathFromSettings();
+ var path = CheckAndUpdatePath(pathFromSettings);
+
+ var args = new List<string>();
+ args.Add(slnPath);
+ if (line >= 0)
+ {
+ args.Add("--line");
+ args.Add(line.ToString());
+ }
+ args.Add(scriptPath);
+ try
+ {
+ Utils.OS.RunProcess(path, args);
+ }
+ catch (Exception e)
+ {
+ GD.PushError($"Error when trying to run code editor: JetBrains Rider. Exception message: '{e.Message}'");
+ }
+ }
+ }
+}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
index 836c9c11e4..de361ba844 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
@@ -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
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs b/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs
index e48b1115db..1fe07e0bb6 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs
@@ -1,72 +1,102 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
+using Mono.Unix.Native;
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);
- }
-
- public static bool IsUWP()
+ [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 UWPName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
+ return name.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
}
- public static bool IsWindows()
- {
- return WindowsName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
- }
-
- public static bool IsX11()
- {
- return X11Name.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
- }
+ private static readonly Lazy<bool> _isWindows = new Lazy<bool>(() => IsOS(Names.Windows));
+ private static readonly Lazy<bool> _isOSX = new Lazy<bool>(() => IsOS(Names.OSX));
+ private static readonly Lazy<bool> _isX11 = new Lazy<bool>(() => IsOS(Names.X11));
+ private static readonly Lazy<bool> _isServer = new Lazy<bool>(() => IsOS(Names.Server));
+ private static readonly Lazy<bool> _isUWP = new Lazy<bool>(() => IsOS(Names.UWP));
+ private static readonly Lazy<bool> _isHaiku = new Lazy<bool>(() => IsOS(Names.Haiku));
+ private static readonly Lazy<bool> _isAndroid = new Lazy<bool>(() => IsOS(Names.Android));
+ private static readonly Lazy<bool> _isHTML5 = new Lazy<bool>(() => IsOS(Names.HTML5));
+
+ public static bool IsWindows => _isWindows.Value || IsUWP;
+ public static bool IsOSX => _isOSX.Value;
+ public static bool IsX11 => _isX11.Value;
+ public static bool IsServer => _isServer.Value;
+ public static bool IsUWP => _isUWP.Value;
+ public static bool IsHaiku => _isHaiku.Value;
+ public static bool IsAndroid => _isAndroid.Value;
+ public static bool IsHTML5 => _isHTML5.Value;
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;
+ return IsWindows ? PathWhichWindows(name) : PathWhichUnix(name);
+ }
+
+ private static string PathWhichWindows(string name)
+ {
+ string[] windowsExts = Environment.GetEnvironmentVariable("PATHEXT")?.Split(PathSep) ?? new string[] { };
string[] pathDirs = Environment.GetEnvironmentVariable("PATH")?.Split(PathSep);
var searchDirs = new List<string>();
@@ -74,40 +104,46 @@ namespace GodotTools.Utils
if (pathDirs != null)
searchDirs.AddRange(pathDirs);
+ string nameExt = Path.GetExtension(name);
+ bool hasPathExt = string.IsNullOrEmpty(nameExt) || windowsExts.Contains(nameExt, StringComparer.OrdinalIgnoreCase);
+
searchDirs.Add(System.IO.Directory.GetCurrentDirectory()); // last in the list
- foreach (var dir in searchDirs)
- {
- string path = Path.Combine(dir, name);
-
- if (IsWindows() && windowsExts != null)
- {
- foreach (var extension in windowsExts)
- {
- string pathWithExtension = path + extension;
-
- if (File.Exists(pathWithExtension))
- return pathWithExtension;
- }
- }
- else
- {
- if (File.Exists(path))
- return path;
- }
- }
+ if (hasPathExt)
+ return searchDirs.Select(dir => Path.Combine(dir, name)).FirstOrDefault(File.Exists);
+
+ return (from dir in searchDirs
+ select Path.Combine(dir, name)
+ into path
+ from ext in windowsExts
+ select path + ext).FirstOrDefault(File.Exists);
+ }
+
+ private static string PathWhichUnix(string name)
+ {
+ string[] pathDirs = Environment.GetEnvironmentVariable("PATH")?.Split(PathSep);
+
+ var searchDirs = new List<string>();
+
+ if (pathDirs != null)
+ searchDirs.AddRange(pathDirs);
+
+ searchDirs.Add(System.IO.Directory.GetCurrentDirectory()); // last in the list
- return null;
+ return searchDirs.Select(dir => Path.Combine(dir, name))
+ .FirstOrDefault(path => File.Exists(path) && Syscall.access(path, AccessModes.X_OK) == 0);
}
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,
@@ -121,6 +157,8 @@ namespace GodotTools.Utils
process.BeginOutputReadLine();
process.BeginErrorReadLine();
+ if (IsWindows && process.Id>0)
+ User32Dll.AllowSetForegroundWindow(process.Id); // allows application to focus itself
}
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Utils/User32Dll.cs b/modules/mono/editor/GodotTools/GodotTools/Utils/User32Dll.cs
new file mode 100644
index 0000000000..6810a991b3
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools/Utils/User32Dll.cs
@@ -0,0 +1,10 @@
+using System.Runtime.InteropServices;
+
+namespace GodotTools.Utils
+{
+ public static class User32Dll
+ {
+ [DllImport("user32.dll")]
+ public static extern bool AllowSetForegroundWindow(int dwProcessId);
+ }
+}
diff --git a/modules/mono/editor/GodotTools/GodotTools/packages.config b/modules/mono/editor/GodotTools/GodotTools/packages.config
new file mode 100644
index 0000000000..2db4b4acc6
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools/packages.config
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+ <package id="JetBrains.Annotations" version="2019.1.3" targetFramework="net45" />
+ <package id="Newtonsoft.Json" version="12.0.3" targetFramework="net45" />
+</packages> \ No newline at end of file
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 28cab2ab61..2252f7676d 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -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);
diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h
index 8f3676940b..07918a2d03 100644
--- a/modules/mono/editor/bindings_generator.h
+++ b/modules/mono/editor/bindings_generator.h
@@ -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;
diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp
index 1564d73c2a..443b4ba841 100644
--- a/modules/mono/editor/editor_internal_calls.cpp
+++ b/modules/mono/editor/editor_internal_calls.cpp
@@ -219,15 +219,14 @@ 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) {
- 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);
+uint32_t godot_icall_ExportPlugin_GetExportedAssemblyDependencies(MonoObject *p_initial_dependencies,
+ MonoString *p_build_config, MonoString *p_custom_bcl_dir, MonoObject *r_dependencies) {
+ Dictionary initial_dependencies = GDMonoMarshal::mono_object_to_variant(p_initial_dependencies);
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(initial_dependencies, build_config, custom_bcl_dir, dependencies);
}
MonoString *godot_icall_Internal_UpdateApiAssembliesFromPrebuilt(MonoString *p_config) {
@@ -411,8 +410,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);
diff --git a/modules/mono/editor/godotsharp_export.cpp b/modules/mono/editor/godotsharp_export.cpp
index e83152d668..e02bd3be58 100644
--- a/modules/mono/editor/godotsharp_export.cpp
+++ b/modules/mono/editor/godotsharp_export.cpp
@@ -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 {
@@ -100,23 +101,32 @@ Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String>
return OK;
}
-Error get_exported_assembly_dependencies(const String &p_project_dll_name, const String &p_project_dll_src_path, const String &p_build_config, const String &p_custom_bcl_dir, Dictionary &r_dependencies) {
+Error get_exported_assembly_dependencies(const Dictionary &p_initial_dependencies,
+ const String &p_build_config, const String &p_custom_bcl_dir, Dictionary &r_dependencies) {
MonoDomain *export_domain = GDMonoUtils::create_domain("GodotEngine.Domain.ProjectExport");
ERR_FAIL_NULL_V(export_domain, FAILED);
_GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(export_domain);
_GDMONO_SCOPE_DOMAIN_(export_domain);
- GDMonoAssembly *scripts_assembly = NULL;
- bool load_success = GDMono::get_singleton()->load_assembly_from(p_project_dll_name,
- p_project_dll_src_path, &scripts_assembly, /* refonly: */ true);
-
- ERR_FAIL_COND_V_MSG(!load_success, ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + p_project_dll_name + "'.");
-
Vector<String> search_dirs;
GDMonoAssembly::fill_search_dirs(search_dirs, p_build_config, p_custom_bcl_dir);
- return get_assembly_dependencies(scripts_assembly, search_dirs, r_dependencies);
+ for (const Variant *key = p_initial_dependencies.next(); key; key = p_initial_dependencies.next(key)) {
+ String assembly_name = *key;
+ String assembly_path = p_initial_dependencies[*key];
+
+ GDMonoAssembly *assembly = NULL;
+ bool load_success = GDMono::get_singleton()->load_assembly_from(assembly_name, assembly_path, &assembly, /* refonly: */ true);
+
+ ERR_FAIL_COND_V_MSG(!load_success, ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + assembly_name + "'.");
+
+ Error err = get_assembly_dependencies(assembly, search_dirs, r_dependencies);
+ if (err != OK)
+ return err;
+ }
+
+ return OK;
}
} // namespace GodotSharpExport
diff --git a/modules/mono/editor/godotsharp_export.h b/modules/mono/editor/godotsharp_export.h
index 58e46e2f2d..8eb5a4d9dc 100644
--- a/modules/mono/editor/godotsharp_export.h
+++ b/modules/mono/editor/godotsharp_export.h
@@ -41,9 +41,8 @@ namespace GodotSharpExport {
Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_dependencies);
-Error get_exported_assembly_dependencies(const String &p_project_dll_name,
- const String &p_project_dll_src_path, const String &p_build_config,
- const String &p_custom_lib_dir, Dictionary &r_dependencies);
+Error get_exported_assembly_dependencies(const Dictionary &p_initial_dependencies,
+ const String &p_build_config, const String &p_custom_lib_dir, Dictionary &r_dependencies);
} // namespace GodotSharpExport
diff --git a/modules/mono/glue/Managed/Files/AABB.cs b/modules/mono/glue/Managed/Files/AABB.cs
index 98a73382f4..6a4f785551 100644
--- a/modules/mono/glue/Managed/Files/AABB.cs
+++ b/modules/mono/glue/Managed/Files/AABB.cs
@@ -458,6 +458,11 @@ namespace Godot
return _position == other._position && _size == other._size;
}
+ public bool IsEqualApprox(AABB other)
+ {
+ return _position.IsEqualApprox(other._position) && _size.IsEqualApprox(other._size);
+ }
+
public override int GetHashCode()
{
return _position.GetHashCode() ^ _size.GetHashCode();
diff --git a/modules/mono/glue/Managed/Files/Array.cs b/modules/mono/glue/Managed/Files/Array.cs
index 0e7b0362e0..aba1065498 100644
--- a/modules/mono/glue/Managed/Files/Array.cs
+++ b/modules/mono/glue/Managed/Files/Array.cs
@@ -64,6 +64,11 @@ namespace Godot.Collections
return safeHandle.DangerousGetHandle();
}
+ public Array Duplicate(bool deep = false)
+ {
+ return new Array(godot_icall_Array_Duplicate(GetPtr(), deep));
+ }
+
public Error Resize(int newSize)
{
return godot_icall_Array_Resize(GetPtr(), newSize);
@@ -179,6 +184,9 @@ namespace Godot.Collections
internal extern static void godot_icall_Array_CopyTo(IntPtr ptr, System.Array array, int arrayIndex);
[MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static IntPtr godot_icall_Array_Duplicate(IntPtr ptr, bool deep);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
internal extern static int godot_icall_Array_IndexOf(IntPtr ptr, object item);
[MethodImpl(MethodImplOptions.InternalCall)]
@@ -250,6 +258,11 @@ namespace Godot.Collections
return from.objectArray;
}
+ public Array<T> Duplicate(bool deep = false)
+ {
+ return new Array<T>(objectArray.Duplicate(deep));
+ }
+
public Error Resize(int newSize)
{
return objectArray.Resize(newSize);
diff --git a/modules/mono/glue/Managed/Files/Basis.cs b/modules/mono/glue/Managed/Files/Basis.cs
index 0eb76e9c63..c5e62b77c8 100644
--- a/modules/mono/glue/Managed/Files/Basis.cs
+++ b/modules/mono/glue/Managed/Files/Basis.cs
@@ -654,6 +654,11 @@ namespace Godot
return Row0.Equals(other.Row0) && Row1.Equals(other.Row1) && Row2.Equals(other.Row2);
}
+ public bool IsEqualApprox(Basis other)
+ {
+ return Row0.IsEqualApprox(other.Row0) && Row1.IsEqualApprox(other.Row1) && Row2.IsEqualApprox(other.Row2);
+ }
+
public override int GetHashCode()
{
return Row0.GetHashCode() ^ Row1.GetHashCode() ^ Row2.GetHashCode();
diff --git a/modules/mono/glue/Managed/Files/Color.cs b/modules/mono/glue/Managed/Files/Color.cs
index 3a52a1a13b..df817e47e9 100644
--- a/modules/mono/glue/Managed/Files/Color.cs
+++ b/modules/mono/glue/Managed/Files/Color.cs
@@ -661,6 +661,11 @@ namespace Godot
public bool Equals(Color other)
{
+ return r == other.r && g == other.g && b == other.b && a == other.a;
+ }
+
+ public bool IsEqualApprox(Color other)
+ {
return Mathf.IsEqualApprox(r, other.r) && Mathf.IsEqualApprox(g, other.g) && Mathf.IsEqualApprox(b, other.b) && Mathf.IsEqualApprox(a, other.a);
}
diff --git a/modules/mono/glue/Managed/Files/Dictionary.cs b/modules/mono/glue/Managed/Files/Dictionary.cs
index 6ab8549a01..d72109de92 100644
--- a/modules/mono/glue/Managed/Files/Dictionary.cs
+++ b/modules/mono/glue/Managed/Files/Dictionary.cs
@@ -80,6 +80,11 @@ namespace Godot.Collections
disposed = true;
}
+ public Dictionary Duplicate(bool deep = false)
+ {
+ return new Dictionary(godot_icall_Dictionary_Duplicate(GetPtr(), deep));
+ }
+
// IDictionary
public ICollection Keys
@@ -235,6 +240,9 @@ namespace Godot.Collections
internal extern static bool godot_icall_Dictionary_ContainsKey(IntPtr ptr, object key);
[MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static IntPtr godot_icall_Dictionary_Duplicate(IntPtr ptr, bool deep);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
internal extern static bool godot_icall_Dictionary_RemoveKey(IntPtr ptr, object key);
[MethodImpl(MethodImplOptions.InternalCall)]
@@ -313,6 +321,11 @@ namespace Godot.Collections
return objectDict.GetPtr();
}
+ public Dictionary<TKey, TValue> Duplicate(bool deep = false)
+ {
+ return new Dictionary<TKey, TValue>(objectDict.Duplicate(deep));
+ }
+
// IDictionary<TKey, TValue>
public TValue this[TKey key]
diff --git a/modules/mono/glue/Managed/Files/GD.cs b/modules/mono/glue/Managed/Files/GD.cs
index 2068099ac6..19962d418a 100644
--- a/modules/mono/glue/Managed/Files/GD.cs
+++ b/modules/mono/glue/Managed/Files/GD.cs
@@ -93,22 +93,22 @@ namespace Godot
public static void PrintErr(params object[] what)
{
- godot_icall_GD_printerr(Array.ConvertAll(what, x => x.ToString()));
+ godot_icall_GD_printerr(Array.ConvertAll(what, x => x?.ToString()));
}
public static void PrintRaw(params object[] what)
{
- godot_icall_GD_printraw(Array.ConvertAll(what, x => x.ToString()));
+ godot_icall_GD_printraw(Array.ConvertAll(what, x => x?.ToString()));
}
public static void PrintS(params object[] what)
{
- godot_icall_GD_prints(Array.ConvertAll(what, x => x.ToString()));
+ godot_icall_GD_prints(Array.ConvertAll(what, x => x?.ToString()));
}
public static void PrintT(params object[] what)
{
- godot_icall_GD_printt(Array.ConvertAll(what, x => x.ToString()));
+ godot_icall_GD_printt(Array.ConvertAll(what, x => x?.ToString()));
}
public static float Randf()
diff --git a/modules/mono/glue/Managed/Files/Mathf.cs b/modules/mono/glue/Managed/Files/Mathf.cs
index ce34cd6a99..54821fe790 100644
--- a/modules/mono/glue/Managed/Files/Mathf.cs
+++ b/modules/mono/glue/Managed/Files/Mathf.cs
@@ -19,12 +19,12 @@ namespace Godot
private const real_t Deg2RadConst = (real_t) 0.0174532925199432957692369077M; // 0.0174532924f and 0.0174532925199433
private const real_t Rad2DegConst = (real_t) 57.295779513082320876798154814M; // 57.29578f and 57.2957795130823
- public static real_t Abs(real_t s)
+ public static int Abs(int s)
{
return Math.Abs(s);
}
- public static int Abs(int s)
+ public static real_t Abs(real_t s)
{
return Math.Abs(s);
}
@@ -79,29 +79,6 @@ namespace Godot
return (real_t)Math.Cosh(s);
}
- public static int StepDecimals(real_t step)
- {
- double[] sd = new double[] {
- 0.9999,
- 0.09999,
- 0.009999,
- 0.0009999,
- 0.00009999,
- 0.000009999,
- 0.0000009999,
- 0.00000009999,
- 0.000000009999,
- };
- double abs = Mathf.Abs(step);
- double decs = abs - (int)abs; // Strip away integer part
- for (int i = 0; i < sd.Length; i++) {
- if (decs >= sd[i]) {
- return i;
- }
- }
- return 0;
- }
-
public static real_t Deg2Rad(real_t deg)
{
return deg * Deg2RadConst;
@@ -159,12 +136,14 @@ namespace Godot
public static bool IsEqualApprox(real_t a, real_t b)
{
// Check for exact equality first, required to handle "infinity" values.
- if (a == b) {
+ if (a == b)
+ {
return true;
}
// Then check for approximate equality.
real_t tolerance = Epsilon * Abs(a);
- if (tolerance < Epsilon) {
+ if (tolerance < Epsilon)
+ {
tolerance = Epsilon;
}
return Abs(a - b) < tolerance;
@@ -190,7 +169,8 @@ namespace Godot
return from + (to - from) * weight;
}
- public static real_t LerpAngle(real_t from, real_t to, real_t weight) {
+ public static real_t LerpAngle(real_t from, real_t to, real_t weight)
+ {
real_t difference = (to - from) % Mathf.Tau;
real_t distance = ((2 * difference) % Mathf.Tau) - difference;
return from + distance * weight;
@@ -246,9 +226,9 @@ namespace Godot
/// <summary>
/// Performs a canonical Modulus operation, where the output is on the range [0, b).
/// </summary>
- public static real_t PosMod(real_t a, real_t b)
+ public static int PosMod(int a, int b)
{
- real_t c = a % b;
+ int c = a % b;
if ((c < 0 && b > 0) || (c > 0 && b < 0))
{
c += b;
@@ -259,9 +239,9 @@ namespace Godot
/// <summary>
/// Performs a canonical Modulus operation, where the output is on the range [0, b).
/// </summary>
- public static int PosMod(int a, int b)
+ public static real_t PosMod(real_t a, real_t b)
{
- int c = a % b;
+ real_t c = a % b;
if ((c < 0 && b > 0) || (c > 0 && b < 0))
{
c += b;
@@ -319,6 +299,31 @@ namespace Godot
return (real_t)Math.Sqrt(s);
}
+ public static int StepDecimals(real_t step)
+ {
+ double[] sd = new double[] {
+ 0.9999,
+ 0.09999,
+ 0.009999,
+ 0.0009999,
+ 0.00009999,
+ 0.000009999,
+ 0.0000009999,
+ 0.00000009999,
+ 0.000000009999,
+ };
+ double abs = Mathf.Abs(step);
+ double decs = abs - (int)abs; // Strip away integer part
+ for (int i = 0; i < sd.Length; i++)
+ {
+ if (decs >= sd[i])
+ {
+ return i;
+ }
+ }
+ return 0;
+ }
+
public static real_t Stepify(real_t s, real_t step)
{
if (step != 0f)
diff --git a/modules/mono/glue/Managed/Files/MathfEx.cs b/modules/mono/glue/Managed/Files/MathfEx.cs
index 6cffc7f01d..1b7fd4906f 100644
--- a/modules/mono/glue/Managed/Files/MathfEx.cs
+++ b/modules/mono/glue/Managed/Files/MathfEx.cs
@@ -49,7 +49,8 @@ namespace Godot
public static bool IsEqualApprox(real_t a, real_t b, real_t tolerance)
{
// Check for exact equality first, required to handle "infinity" values.
- if (a == b) {
+ if (a == b)
+ {
return true;
}
// Then check for approximate equality.
diff --git a/modules/mono/glue/Managed/Files/NodePath.cs b/modules/mono/glue/Managed/Files/NodePath.cs
index 4de4e1e6a9..8c5872ba5a 100644
--- a/modules/mono/glue/Managed/Files/NodePath.cs
+++ b/modules/mono/glue/Managed/Files/NodePath.cs
@@ -12,7 +12,7 @@ namespace Godot
internal static IntPtr GetPtr(NodePath instance)
{
if (instance == null)
- return IntPtr.Zero;
+ throw new NullReferenceException($"The instance of type {nameof(NodePath)} is null.");
if (instance.disposed)
throw new ObjectDisposedException(instance.GetType().FullName);
diff --git a/modules/mono/glue/Managed/Files/Plane.cs b/modules/mono/glue/Managed/Files/Plane.cs
index a13161d2e6..885845e3a4 100644
--- a/modules/mono/glue/Managed/Files/Plane.cs
+++ b/modules/mono/glue/Managed/Files/Plane.cs
@@ -82,12 +82,12 @@ namespace Godot
return Mathf.Abs(dist) <= epsilon;
}
- public Vector3 Intersect3(Plane b, Plane c)
+ public Vector3? Intersect3(Plane b, Plane c)
{
real_t denom = _normal.Cross(b._normal).Dot(c._normal);
- if (Mathf.Abs(denom) <= Mathf.Epsilon)
- return new Vector3();
+ if (Mathf.IsZeroApprox(denom))
+ return null;
Vector3 result = b._normal.Cross(c._normal) * D +
c._normal.Cross(_normal) * b.D +
@@ -96,34 +96,35 @@ namespace Godot
return result / denom;
}
- public Vector3 IntersectRay(Vector3 from, Vector3 dir)
+ public Vector3? IntersectRay(Vector3 from, Vector3 dir)
{
real_t den = _normal.Dot(dir);
- if (Mathf.Abs(den) <= Mathf.Epsilon)
- return new Vector3();
+ if (Mathf.IsZeroApprox(den))
+ return null;
real_t dist = (_normal.Dot(from) - D) / den;
// This is a ray, before the emitting pos (from) does not exist
if (dist > Mathf.Epsilon)
- return new Vector3();
+ return null;
return from + dir * -dist;
}
- public Vector3 IntersectSegment(Vector3 begin, Vector3 end)
+ public Vector3? IntersectSegment(Vector3 begin, Vector3 end)
{
Vector3 segment = begin - end;
real_t den = _normal.Dot(segment);
- if (Mathf.Abs(den) <= Mathf.Epsilon)
- return new Vector3();
+ if (Mathf.IsZeroApprox(den))
+ return null;
real_t dist = (_normal.Dot(begin) - D) / den;
+ // Only allow dist to be in the range of 0 to 1, with tolerance.
if (dist < -Mathf.Epsilon || dist > 1.0f + Mathf.Epsilon)
- return new Vector3();
+ return null;
return begin + segment * -dist;
}
@@ -203,7 +204,12 @@ namespace Godot
public bool Equals(Plane other)
{
- return _normal == other._normal && Mathf.IsEqualApprox(D, other.D);
+ return _normal == other._normal && D == other.D;
+ }
+
+ public bool IsEqualApprox(Plane other)
+ {
+ return _normal.IsEqualApprox(other._normal) && Mathf.IsEqualApprox(D, other.D);
}
public override int GetHashCode()
diff --git a/modules/mono/glue/Managed/Files/Quat.cs b/modules/mono/glue/Managed/Files/Quat.cs
index 845c7c730e..8f60867ac3 100644
--- a/modules/mono/glue/Managed/Files/Quat.cs
+++ b/modules/mono/glue/Managed/Files/Quat.cs
@@ -363,6 +363,11 @@ namespace Godot
public bool Equals(Quat other)
{
+ return x == other.x && y == other.y && z == other.z && w == other.w;
+ }
+
+ public bool IsEqualApprox(Quat other)
+ {
return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y) && Mathf.IsEqualApprox(z, other.z) && Mathf.IsEqualApprox(w, other.w);
}
diff --git a/modules/mono/glue/Managed/Files/RID.cs b/modules/mono/glue/Managed/Files/RID.cs
index 12064beca2..94761531b1 100644
--- a/modules/mono/glue/Managed/Files/RID.cs
+++ b/modules/mono/glue/Managed/Files/RID.cs
@@ -12,7 +12,7 @@ namespace Godot
internal static IntPtr GetPtr(RID instance)
{
if (instance == null)
- return IntPtr.Zero;
+ throw new NullReferenceException($"The instance of type {nameof(RID)} is null.");
if (instance.disposed)
throw new ObjectDisposedException(instance.GetType().FullName);
diff --git a/modules/mono/glue/Managed/Files/Rect2.cs b/modules/mono/glue/Managed/Files/Rect2.cs
index 99542d0c0a..91e614dc7b 100644
--- a/modules/mono/glue/Managed/Files/Rect2.cs
+++ b/modules/mono/glue/Managed/Files/Rect2.cs
@@ -231,6 +231,11 @@ namespace Godot
return _position.Equals(other._position) && _size.Equals(other._size);
}
+ public bool IsEqualApprox(Rect2 other)
+ {
+ return _position.IsEqualApprox(other._position) && _size.IsEqualApprox(other.Size);
+ }
+
public override int GetHashCode()
{
return _position.GetHashCode() ^ _size.GetHashCode();
diff --git a/modules/mono/glue/Managed/Files/Transform.cs b/modules/mono/glue/Managed/Files/Transform.cs
index cc4d26158d..0b84050f07 100644
--- a/modules/mono/glue/Managed/Files/Transform.cs
+++ b/modules/mono/glue/Managed/Files/Transform.cs
@@ -185,6 +185,11 @@ namespace Godot
return basis.Equals(other.basis) && origin.Equals(other.origin);
}
+ public bool IsEqualApprox(Transform other)
+ {
+ return basis.IsEqualApprox(other.basis) && origin.IsEqualApprox(other.origin);
+ }
+
public override int GetHashCode()
{
return basis.GetHashCode() ^ origin.GetHashCode();
diff --git a/modules/mono/glue/Managed/Files/Transform2D.cs b/modules/mono/glue/Managed/Files/Transform2D.cs
index 814332dc07..77ea3e5830 100644
--- a/modules/mono/glue/Managed/Files/Transform2D.cs
+++ b/modules/mono/glue/Managed/Files/Transform2D.cs
@@ -357,6 +357,11 @@ namespace Godot
return x.Equals(other.x) && y.Equals(other.y) && origin.Equals(other.origin);
}
+ public bool IsEqualApprox(Transform2D other)
+ {
+ return x.IsEqualApprox(other.x) && y.IsEqualApprox(other.y) && origin.IsEqualApprox(other.origin);
+ }
+
public override int GetHashCode()
{
return x.GetHashCode() ^ y.GetHashCode() ^ origin.GetHashCode();
diff --git a/modules/mono/glue/Managed/Files/Vector2.cs b/modules/mono/glue/Managed/Files/Vector2.cs
index 0daa94057e..f92453f546 100644
--- a/modules/mono/glue/Managed/Files/Vector2.cs
+++ b/modules/mono/glue/Managed/Files/Vector2.cs
@@ -455,6 +455,11 @@ namespace Godot
public bool Equals(Vector2 other)
{
+ return x == other.x && y == other.y;
+ }
+
+ public bool IsEqualApprox(Vector2 other)
+ {
return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y);
}
diff --git a/modules/mono/glue/Managed/Files/Vector3.cs b/modules/mono/glue/Managed/Files/Vector3.cs
index 9076dbd3b0..025b09199f 100644
--- a/modules/mono/glue/Managed/Files/Vector3.cs
+++ b/modules/mono/glue/Managed/Files/Vector3.cs
@@ -513,6 +513,11 @@ namespace Godot
public bool Equals(Vector3 other)
{
+ return x == other.x && y == other.y && z == other.z;
+ }
+
+ public bool IsEqualApprox(Vector3 other)
+ {
return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y) && Mathf.IsEqualApprox(z, other.z);
}
diff --git a/modules/mono/glue/base_object_glue.cpp b/modules/mono/glue/base_object_glue.cpp
index 6d85f55b97..2488f78350 100644
--- a/modules/mono/glue/base_object_glue.cpp
+++ b/modules/mono/glue/base_object_glue.cpp
@@ -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"
diff --git a/modules/mono/glue/collections_glue.cpp b/modules/mono/glue/collections_glue.cpp
index e67c8b9ad9..e84a5becd6 100644
--- a/modules/mono/glue/collections_glue.cpp
+++ b/modules/mono/glue/collections_glue.cpp
@@ -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"
@@ -102,6 +103,10 @@ void godot_icall_Array_CopyTo(Array *ptr, MonoArray *array, int array_index) {
}
}
+Array *godot_icall_Array_Duplicate(Array *ptr, MonoBoolean deep) {
+ return memnew(Array(ptr->duplicate(deep)));
+}
+
int godot_icall_Array_IndexOf(Array *ptr, MonoObject *item) {
return ptr->find(GDMonoMarshal::mono_object_to_variant(item));
}
@@ -223,6 +228,10 @@ MonoBoolean godot_icall_Dictionary_ContainsKey(Dictionary *ptr, MonoObject *key)
return ptr->has(GDMonoMarshal::mono_object_to_variant(key));
}
+Dictionary *godot_icall_Dictionary_Duplicate(Dictionary *ptr, MonoBoolean deep) {
+ return memnew(Dictionary(ptr->duplicate(deep)));
+}
+
MonoBoolean godot_icall_Dictionary_RemoveKey(Dictionary *ptr, MonoObject *key) {
return ptr->erase(GDMonoMarshal::mono_object_to_variant(key));
}
@@ -283,6 +292,7 @@ void godot_register_collections_icalls() {
mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Clear", (void *)godot_icall_Array_Clear);
mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Contains", (void *)godot_icall_Array_Contains);
mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_CopyTo", (void *)godot_icall_Array_CopyTo);
+ mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Duplicate", (void *)godot_icall_Array_Duplicate);
mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_IndexOf", (void *)godot_icall_Array_IndexOf);
mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Insert", (void *)godot_icall_Array_Insert);
mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Remove", (void *)godot_icall_Array_Remove);
@@ -303,6 +313,7 @@ void godot_register_collections_icalls() {
mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Clear", (void *)godot_icall_Dictionary_Clear);
mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Contains", (void *)godot_icall_Dictionary_Contains);
mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_ContainsKey", (void *)godot_icall_Dictionary_ContainsKey);
+ mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Duplicate", (void *)godot_icall_Dictionary_Duplicate);
mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_RemoveKey", (void *)godot_icall_Dictionary_RemoveKey);
mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Remove", (void *)godot_icall_Dictionary_Remove);
mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_TryGetValue", (void *)godot_icall_Dictionary_TryGetValue);
diff --git a/modules/mono/glue/collections_glue.h b/modules/mono/glue/collections_glue.h
index 54ed42c3b6..a79a651fcd 100644
--- a/modules/mono/glue/collections_glue.h
+++ b/modules/mono/glue/collections_glue.h
@@ -59,6 +59,8 @@ MonoBoolean godot_icall_Array_Contains(Array *ptr, MonoObject *item);
void godot_icall_Array_CopyTo(Array *ptr, MonoArray *array, int array_index);
+Array *godot_icall_Array_Duplicate(Array *ptr, MonoBoolean deep);
+
int godot_icall_Array_IndexOf(Array *ptr, MonoObject *item);
void godot_icall_Array_Insert(Array *ptr, int index, MonoObject *item);
@@ -99,6 +101,8 @@ MonoBoolean godot_icall_Dictionary_Contains(Dictionary *ptr, MonoObject *key, Mo
MonoBoolean godot_icall_Dictionary_ContainsKey(Dictionary *ptr, MonoObject *key);
+Dictionary *godot_icall_Dictionary_Duplicate(Dictionary *ptr, MonoBoolean deep);
+
MonoBoolean godot_icall_Dictionary_RemoveKey(Dictionary *ptr, MonoObject *key);
MonoBoolean godot_icall_Dictionary_Remove(Dictionary *ptr, MonoObject *key, MonoObject *value);
diff --git a/modules/mono/glue/gd_glue.cpp b/modules/mono/glue/gd_glue.cpp
index 8b9a1380d8..1381d79e2e 100644
--- a/modules/mono/glue/gd_glue.cpp
+++ b/modules/mono/glue/gd_glue.cpp
@@ -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() {
diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp
index 5fa8aed5a9..ef30a52b72 100644
--- a/modules/mono/godotsharp_dirs.cpp
+++ b/modules/mono/godotsharp_dirs.cpp
@@ -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"
@@ -108,6 +108,10 @@ public:
String data_editor_tools_dir;
String data_editor_prebuilt_api_dir;
+#else
+ // Equivalent of res_assemblies_dir, but in the data directory rather than in 'res://'.
+ // Only defined on export templates. Used when exporting assemblies outside of PCKs.
+ String data_game_assemblies_dir;
#endif
String data_mono_etc_dir;
@@ -130,7 +134,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 +168,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,10 +205,11 @@ 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");
+ data_game_assemblies_dir = data_dir_root.plus_file("Assemblies");
#endif
#ifdef WINDOWS_ENABLED
@@ -212,6 +221,10 @@ private:
data_mono_etc_dir = exe_dir.plus_file("../Resources/GodotSharp/Mono/etc");
data_mono_lib_dir = exe_dir.plus_file("../Frameworks/GodotSharp/Mono/lib");
}
+
+ if (!DirAccess::exists(data_game_assemblies_dir)) {
+ data_game_assemblies_dir = exe_dir.plus_file("../Frameworks/GodotSharp/Assemblies");
+ }
#endif
#endif
@@ -291,6 +304,10 @@ String get_data_editor_tools_dir() {
String get_data_editor_prebuilt_api_dir() {
return _GodotSharpDirs::get_singleton().data_editor_prebuilt_api_dir;
}
+#else
+String get_data_game_assemblies_dir() {
+ return _GodotSharpDirs::get_singleton().data_game_assemblies_dir;
+}
#endif
String get_data_mono_etc_dir() {
diff --git a/modules/mono/godotsharp_dirs.h b/modules/mono/godotsharp_dirs.h
index ff51888d1c..43da44b0f5 100644
--- a/modules/mono/godotsharp_dirs.h
+++ b/modules/mono/godotsharp_dirs.h
@@ -56,6 +56,8 @@ String get_project_csproj_path();
String get_data_editor_tools_dir();
String get_data_editor_prebuilt_api_dir();
+#else
+String get_data_game_assemblies_dir();
#endif
String get_data_mono_etc_dir();
diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp
index 504b8d41d0..33ba877352 100644
--- a/modules/mono/mono_gd/gd_mono.cpp
+++ b/modules/mono/mono_gd/gd_mono.cpp
@@ -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)
@@ -199,35 +239,22 @@ void GDMono::add_mono_shared_libs_dir_to_path() {
#endif // WINDOWS_ENABLED || UNIX_ENABLED
}
-void GDMono::initialize() {
+void GDMono::determine_mono_dirs(String &r_assembly_rootdir, String &r_config_dir) {
- ERR_FAIL_NULL(Engine::get_singleton());
-
- print_verbose("Mono: Initializing module...");
-
- char *runtime_build_info = mono_get_runtime_build_info();
- print_verbose("Mono JIT compiler version " + String(runtime_build_info));
- mono_free(runtime_build_info);
-
-#ifdef DEBUG_METHODS_ENABLED
- _initialize_and_check_api_hashes();
-#endif
-
- GDMonoLog::get_singleton()->initialize();
-
- String assembly_rootdir;
- String config_dir;
+ String bundled_assembly_rootdir = GodotSharpDirs::get_data_mono_lib_dir();
+ String bundled_config_dir = GodotSharpDirs::get_data_mono_etc_dir();
#ifdef TOOLS_ENABLED
+
#if defined(WINDOWS_ENABLED)
mono_reg_info = MonoRegUtils::find_mono();
if (mono_reg_info.assembly_dir.length() && DirAccess::exists(mono_reg_info.assembly_dir)) {
- assembly_rootdir = mono_reg_info.assembly_dir;
+ r_assembly_rootdir = mono_reg_info.assembly_dir;
}
if (mono_reg_info.config_dir.length() && DirAccess::exists(mono_reg_info.config_dir)) {
- config_dir = mono_reg_info.config_dir;
+ r_config_dir = mono_reg_info.config_dir;
}
#elif defined(OSX_ENABLED)
const char *c_assembly_rootdir = mono_assembly_getrootdir();
@@ -244,29 +271,24 @@ void GDMono::initialize() {
String hint_config_dir = path::join(locations[i], "etc");
if (FileAccess::exists(hint_mscorlib_path) && DirAccess::exists(hint_config_dir)) {
- assembly_rootdir = hint_assembly_rootdir;
- config_dir = hint_config_dir;
+ r_assembly_rootdir = hint_assembly_rootdir;
+ r_config_dir = hint_config_dir;
break;
}
}
}
#endif
-#endif // TOOLS_ENABLED
-
- String bundled_assembly_rootdir = GodotSharpDirs::get_data_mono_lib_dir();
- String bundled_config_dir = GodotSharpDirs::get_data_mono_etc_dir();
-#ifdef TOOLS_ENABLED
if (DirAccess::exists(bundled_assembly_rootdir)) {
- assembly_rootdir = bundled_assembly_rootdir;
+ r_assembly_rootdir = bundled_assembly_rootdir;
}
if (DirAccess::exists(bundled_config_dir)) {
- config_dir = bundled_config_dir;
+ r_config_dir = bundled_config_dir;
}
#ifdef WINDOWS_ENABLED
- if (assembly_rootdir.empty() || config_dir.empty()) {
+ if (r_assembly_rootdir.empty() || r_config_dir.empty()) {
ERR_PRINT("Cannot find Mono in the registry.");
// Assertion: if they are not set, then they weren't found in the registry
CRASH_COND(mono_reg_info.assembly_dir.length() > 0 || mono_reg_info.config_dir.length() > 0);
@@ -274,35 +296,47 @@ void GDMono::initialize() {
#endif // WINDOWS_ENABLED
#else
- // These are always the directories in export templates
- assembly_rootdir = bundled_assembly_rootdir;
- config_dir = bundled_config_dir;
-#endif // TOOLS_ENABLED
+ // Export templates always use the bundled directories
+ r_assembly_rootdir = bundled_assembly_rootdir;
+ r_config_dir = bundled_config_dir;
+#endif
+}
+
+void GDMono::initialize() {
+
+ ERR_FAIL_NULL(Engine::get_singleton());
+
+ print_verbose("Mono: Initializing module...");
+
+ char *runtime_build_info = mono_get_runtime_build_info();
+ print_verbose("Mono JIT compiler version " + String(runtime_build_info));
+ mono_free(runtime_build_info);
+
+ _init_godot_api_hashes();
+ _init_exception_policy();
+
+ GDMonoLog::get_singleton()->initialize();
+
+#if !defined(JAVASCRIPT_ENABLED)
+ String assembly_rootdir;
+ String config_dir;
+ determine_mono_dirs(assembly_rootdir, config_dir);
// Leak if we call mono_set_dirs more than once
mono_set_dirs(assembly_rootdir.length() ? assembly_rootdir.utf8().get_data() : NULL,
config_dir.length() ? config_dir.utf8().get_data() : NULL);
add_mono_shared_libs_dir_to_path();
+#endif
- {
- PropertyInfo exc_policy_prop = PropertyInfo(Variant::INT, "mono/unhandled_exception_policy", PROPERTY_HINT_ENUM,
- vformat("Terminate Application:%s,Log Error:%s", (int)POLICY_TERMINATE_APP, (int)POLICY_LOG_ERROR));
- unhandled_exception_policy = (UnhandledExceptionPolicy)(int)GLOBAL_DEF(exc_policy_prop.name, (int)POLICY_TERMINATE_APP);
- ProjectSettings::get_singleton()->set_custom_property_info(exc_policy_prop.name, exc_policy_prop);
-
- if (Engine::get_singleton()->is_editor_hint()) {
- // Unhandled exceptions should not terminate the editor
- unhandled_exception_policy = POLICY_LOG_ERROR;
- }
- }
+#if defined(ANDROID_ENABLED)
+ GDMonoAndroid::initialize();
+#endif
GDMonoAssembly::initialize();
- gdmono_profiler_init();
-
-#ifdef DEBUG_ENABLED
- gdmono_debug_init();
+#if !defined(JAVASCRIPT_ENABLED)
+ gd_mono_profiler_init();
#endif
#ifdef ANDROID_ENABLED
@@ -314,9 +348,12 @@ void GDMono::initialize() {
mono_install_unhandled_exception_hook(&unhandled_exception_hook, NULL);
#ifndef TOOLS_ENABLED
- // Export templates only load the Mono runtime if the project uses it
- if (!DirAccess::exists("res://.mono"))
+ // Exported games that don't use C# must still work. They likely don't ship with mscorlib.
+ // We only initialize the Mono runtime if we can find mscorlib. Otherwise it would crash.
+ if (GDMonoAssembly::find_assembly("mscorlib.dll").empty()) {
+ print_verbose("Mono: Skipping runtime initialization because 'mscorlib.dll' could not be found");
return;
+ }
#endif
#if !defined(WINDOWS_ENABLED) && !defined(NO_MONO_THREADS_SUSPEND_WORKAROUND)
@@ -326,17 +363,26 @@ void GDMono::initialize() {
}
#endif
- root_domain = mono_jit_init_version("GodotEngine.RootDomain", "v4.0.30319");
+ // NOTE: Internal calls must be registered after the Mono runtime initialization.
+ // Otherwise registration fails with the error: 'assertion 'hash != NULL' failed'.
+
+ root_domain = gd_initialize_mono_runtime();
ERR_FAIL_NULL_MSG(root_domain, "Mono: Failed to initialize runtime.");
GDMonoUtils::set_main_thread(GDMonoUtils::get_current_thread());
- 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;
print_verbose("Mono: Runtime initialized");
+#if defined(ANDROID_ENABLED)
+ GDMonoAndroid::register_internal_calls();
+#endif
+
// mscorlib assembly MUST be present at initialization
bool corlib_loaded = _load_corlib_assembly();
ERR_FAIL_COND_MSG(!corlib_loaded, "Mono: Failed to load mscorlib assembly.");
@@ -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;
@@ -429,9 +475,8 @@ void GDMono::_register_internal_calls() {
GodotSharpBindings::register_generated_icalls();
}
-void GDMono::_initialize_and_check_api_hashes() {
-#ifdef MONO_GLUE_ENABLED
-#ifdef DEBUG_METHODS_ENABLED
+void GDMono::_init_godot_api_hashes() {
+#if defined(MONO_GLUE_ENABLED) && defined(DEBUG_METHODS_ENABLED)
if (get_api_core_hash() != GodotSharpBindings::get_core_api_hash()) {
ERR_PRINT("Mono: Core API hash mismatch.");
}
@@ -441,8 +486,19 @@ void GDMono::_initialize_and_check_api_hashes() {
ERR_PRINT("Mono: Editor API hash mismatch.");
}
#endif // TOOLS_ENABLED
-#endif // DEBUG_METHODS_ENABLED
-#endif // MONO_GLUE_ENABLED
+#endif // MONO_GLUE_ENABLED && DEBUG_METHODS_ENABLED
+}
+
+void GDMono::_init_exception_policy() {
+ PropertyInfo exc_policy_prop = PropertyInfo(Variant::INT, "mono/unhandled_exception_policy", PROPERTY_HINT_ENUM,
+ vformat("Terminate Application:%s,Log Error:%s", (int)POLICY_TERMINATE_APP, (int)POLICY_LOG_ERROR));
+ unhandled_exception_policy = (UnhandledExceptionPolicy)(int)GLOBAL_DEF(exc_policy_prop.name, (int)POLICY_TERMINATE_APP);
+ ProjectSettings::get_singleton()->set_custom_property_info(exc_policy_prop.name, exc_policy_prop);
+
+ if (Engine::get_singleton()->is_editor_hint()) {
+ // Unhandled exceptions should not terminate the editor
+ unhandled_exception_policy = POLICY_LOG_ERROR;
+ }
}
void GDMono::add_assembly(uint32_t p_domain_id, GDMonoAssembly *p_assembly) {
@@ -561,7 +617,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 +890,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 +940,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 +1040,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));
@@ -1204,6 +1260,10 @@ GDMono::~GDMono() {
mono_jit_cleanup(root_domain);
+#if defined(ANDROID_ENABLED)
+ GDMonoAndroid::cleanup();
+#endif
+
print_verbose("Mono: Finalized");
runtime_initialized = false;
diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h
index e14a0d8409..7fb03b82ad 100644
--- a/modules/mono/mono_gd/gd_mono.h
+++ b/modules/mono/mono_gd/gd_mono.h
@@ -153,7 +153,8 @@ private:
#ifdef TOOLS_ENABLED
uint64_t api_editor_hash;
#endif
- void _initialize_and_check_api_hashes();
+ void _init_godot_api_hashes();
+ void _init_exception_policy();
GDMonoLog *gdmono_log;
@@ -162,6 +163,7 @@ private:
#endif
void add_mono_shared_libs_dir_to_path();
+ void determine_mono_dirs(String &r_assembly_rootdir, String &r_config_dir);
protected:
static GDMono *singleton;
diff --git a/modules/mono/mono_gd/gd_mono_android.cpp b/modules/mono/mono_gd/gd_mono_android.cpp
new file mode 100644
index 0000000000..86af8d1812
--- /dev/null
+++ b/modules/mono/mono_gd/gd_mono_android.cpp
@@ -0,0 +1,685 @@
+/*************************************************************************/
+/* gd_mono_android.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 "gd_mono_android.h"
+
+#if defined(ANDROID_ENABLED)
+
+#include <dlfcn.h> // dlopen, dlsym
+#include <mono/utils/mono-dl-fallback.h>
+#include <sys/system_properties.h>
+#include <cstddef>
+
+#if __ANDROID_API__ < 24
+#include "thirdparty/misc/ifaddrs-android.h"
+#else
+#include <ifaddrs.h>
+#endif
+
+#include "core/os/os.h"
+#include "core/ustring.h"
+#include "platform/android/java_godot_wrapper.h"
+#include "platform/android/os_android.h"
+#include "platform/android/thread_jandroid.h"
+
+#include "../utils/path_utils.h"
+#include "../utils/string_utils.h"
+#include "gd_mono_cache.h"
+#include "gd_mono_marshal.h"
+
+// Warning: JNI boilerplate ahead... continue at your own risk
+
+namespace GDMonoAndroid {
+
+template <typename T>
+struct ScopedLocalRef {
+ JNIEnv *env;
+ T local_ref;
+
+ _FORCE_INLINE_ T get() const { return local_ref; }
+ _FORCE_INLINE_ operator T() const { return local_ref; }
+ _FORCE_INLINE_ operator jvalue() const { return (jvalue)local_ref; }
+
+ _FORCE_INLINE_ operator bool() const { return local_ref != NULL; }
+
+ _FORCE_INLINE_ bool operator==(std::nullptr_t) const {
+ return local_ref == nullptr;
+ }
+
+ _FORCE_INLINE_ bool operator!=(std::nullptr_t) const {
+ return local_ref != nullptr;
+ }
+
+ ScopedLocalRef(const ScopedLocalRef &) = delete;
+ ScopedLocalRef &operator=(const ScopedLocalRef &) = delete;
+
+ ScopedLocalRef(JNIEnv *p_env, T p_local_ref) :
+ env(p_env), local_ref(p_local_ref) {
+ }
+
+ ~ScopedLocalRef() {
+ if (local_ref) {
+ env->DeleteLocalRef(local_ref);
+ }
+ }
+};
+
+bool jni_exception_check(JNIEnv *p_env) {
+ if (p_env->ExceptionCheck()) {
+ // Print the exception to logcat
+ p_env->ExceptionDescribe();
+
+ p_env->ExceptionClear();
+ return true;
+ }
+
+ return false;
+}
+
+String app_native_lib_dir_cache;
+
+String determine_app_native_lib_dir() {
+ JNIEnv *env = ThreadAndroid::get_env();
+
+ ScopedLocalRef<jclass> activityThreadClass(env, env->FindClass("android/app/ActivityThread"));
+ jmethodID currentActivityThread = env->GetStaticMethodID(activityThreadClass, "currentActivityThread", "()Landroid/app/ActivityThread;");
+ ScopedLocalRef<jobject> activityThread(env, env->CallStaticObjectMethod(activityThreadClass, currentActivityThread));
+ jmethodID getApplication = env->GetMethodID(activityThreadClass, "getApplication", "()Landroid/app/Application;");
+ ScopedLocalRef<jobject> ctx(env, env->CallObjectMethod(activityThread, getApplication));
+
+ jmethodID getApplicationInfo = env->GetMethodID(env->GetObjectClass(ctx), "getApplicationInfo", "()Landroid/content/pm/ApplicationInfo;");
+ ScopedLocalRef<jobject> applicationInfo(env, env->CallObjectMethod(ctx, getApplicationInfo));
+ jfieldID nativeLibraryDirField = env->GetFieldID(env->GetObjectClass(applicationInfo), "nativeLibraryDir", "Ljava/lang/String;");
+ ScopedLocalRef<jstring> nativeLibraryDir(env, (jstring)env->GetObjectField(applicationInfo, nativeLibraryDirField));
+
+ String result;
+
+ const char *const nativeLibraryDirUtf8 = env->GetStringUTFChars(nativeLibraryDir, NULL);
+ if (nativeLibraryDirUtf8) {
+ result.parse_utf8(nativeLibraryDirUtf8);
+ env->ReleaseStringUTFChars(nativeLibraryDir, nativeLibraryDirUtf8);
+ }
+
+ 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;
+}
+
+#ifndef GD_MONO_ANDROID_SO_NAME
+#define GD_MONO_ANDROID_SO_NAME "libmonosgen-2.0.so"
+#endif
+
+const char *mono_so_name = GD_MONO_ANDROID_SO_NAME;
+const char *godot_so_name = "libgodot_android.so";
+
+void *mono_dl_handle = NULL;
+void *godot_dl_handle = NULL;
+
+void *try_dlopen(const String &p_so_path, int p_flags) {
+ if (!FileAccess::exists(p_so_path)) {
+ if (OS::get_singleton()->is_stdout_verbose())
+ OS::get_singleton()->print("Cannot find shared library: '%s'\n", p_so_path.utf8().get_data());
+ return NULL;
+ }
+
+ int lflags = gd_mono_convert_dl_flags(p_flags);
+
+ void *handle = dlopen(p_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", p_so_path.utf8().get_data(), dlerror());
+ return NULL;
+ }
+
+ if (OS::get_singleton()->is_stdout_verbose())
+ OS::get_singleton()->print("Successfully loaded shared library: '%s'\n", p_so_path.utf8().get_data());
+
+ return handle;
+}
+
+void *gd_mono_android_dlopen(const char *p_name, int p_flags, char **r_err, void *p_user_data) {
+ if (p_name == NULL) {
+ // __Internal
+
+ if (!mono_dl_handle) {
+ String app_native_lib_dir = get_app_native_lib_dir();
+ String so_path = path::join(app_native_lib_dir, mono_so_name);
+
+ mono_dl_handle = try_dlopen(so_path, p_flags);
+ }
+
+ return mono_dl_handle;
+ }
+
+ 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);
+
+ return try_dlopen(so_path, p_flags);
+ }
+
+ 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 (p_handle == mono_dl_handle && godot_dl_handle) {
+ // Looking up for '__Internal' P/Invoke. We want to search in both the Mono and Godot shared libraries.
+ // This is needed to resolve the monodroid P/Invoke functions that are defined at the bottom of the file.
+ sym_addr = dlsym(godot_dl_handle, p_name);
+
+ if (sym_addr)
+ return sym_addr;
+ }
+
+ if (r_err)
+ *r_err = str_format_new("%s\n", dlerror());
+
+ return NULL;
+}
+
+void *gd_mono_android_dlclose(void *p_handle, void *p_user_data) {
+ dlclose(p_handle);
+
+ // Not sure if this ever happens. Does Mono close the handle for the main module?
+ if (p_handle == mono_dl_handle)
+ mono_dl_handle = NULL;
+
+ return NULL;
+}
+
+int32_t build_version_sdk_int = 0;
+
+int32_t get_build_version_sdk_int() {
+ // The JNI code is the equivalent of:
+ //
+ // android.os.Build.VERSION.SDK_INT
+
+ if (build_version_sdk_int == 0) {
+ JNIEnv *env = ThreadAndroid::get_env();
+
+ jclass versionClass = env->FindClass("android/os/Build$VERSION");
+ ERR_FAIL_NULL_V(versionClass, 0);
+
+ jfieldID sdkIntField = env->GetStaticFieldID(versionClass, "SDK_INT", "I");
+ ERR_FAIL_NULL_V(sdkIntField, 0);
+
+ build_version_sdk_int = (int32_t)env->GetStaticIntField(versionClass, sdkIntField);
+ }
+
+ return build_version_sdk_int;
+}
+
+jobject certStore = NULL; // KeyStore
+
+MonoBoolean _gd_mono_init_cert_store() {
+ // The JNI code is the equivalent of:
+ //
+ // try {
+ // certStoreLocal = KeyStore.getInstance("AndroidCAStore");
+ // certStoreLocal.load(null);
+ // certStore = certStoreLocal;
+ // return true;
+ // } catch (Exception e) {
+ // return false;
+ // }
+
+ JNIEnv *env = ThreadAndroid::get_env();
+
+ ScopedLocalRef<jclass> keyStoreClass(env, env->FindClass("java/security/KeyStore"));
+
+ jmethodID getInstance = env->GetStaticMethodID(keyStoreClass, "getInstance", "(Ljava/lang/String;)Ljava/security/KeyStore;");
+ jmethodID load = env->GetMethodID(keyStoreClass, "load", "(Ljava/security/KeyStore$LoadStoreParameter;)V");
+
+ ScopedLocalRef<jstring> androidCAStoreString(env, env->NewStringUTF("AndroidCAStore"));
+
+ ScopedLocalRef<jobject> certStoreLocal(env, env->CallStaticObjectMethod(keyStoreClass, getInstance, androidCAStoreString.get()));
+
+ if (jni_exception_check(env))
+ return 0;
+
+ env->CallVoidMethod(certStoreLocal, load, NULL);
+
+ if (jni_exception_check(env))
+ return 0;
+
+ certStore = env->NewGlobalRef(certStoreLocal);
+
+ return 1;
+}
+
+MonoArray *_gd_mono_android_cert_store_lookup(MonoString *p_alias) {
+ // The JNI code is the equivalent of:
+ //
+ // Certificate certificate = certStore.getCertificate(alias);
+ // if (certificate == null)
+ // return null;
+ // return certificate.getEncoded();
+
+ MonoError mono_error;
+ char *alias_utf8 = mono_string_to_utf8_checked(p_alias, &mono_error);
+
+ if (!mono_error_ok(&mono_error)) {
+ ERR_PRINTS(String() + "Failed to convert MonoString* to UTF-8: '" + mono_error_get_message(&mono_error) + "'.");
+ mono_error_cleanup(&mono_error);
+ return NULL;
+ }
+
+ JNIEnv *env = ThreadAndroid::get_env();
+
+ ScopedLocalRef<jstring> js_alias(env, env->NewStringUTF(alias_utf8));
+ mono_free(alias_utf8);
+
+ ScopedLocalRef<jclass> keyStoreClass(env, env->FindClass("java/security/KeyStore"));
+ ERR_FAIL_NULL_V(keyStoreClass, NULL);
+ ScopedLocalRef<jclass> certificateClass(env, env->FindClass("java/security/cert/Certificate"));
+ ERR_FAIL_NULL_V(certificateClass, NULL);
+
+ jmethodID getCertificate = env->GetMethodID(keyStoreClass, "getCertificate", "(Ljava/lang/String;)Ljava/security/cert/Certificate;");
+ ERR_FAIL_NULL_V(getCertificate, NULL);
+
+ jmethodID getEncoded = env->GetMethodID(certificateClass, "getEncoded", "()[B");
+ ERR_FAIL_NULL_V(getEncoded, NULL);
+
+ ScopedLocalRef<jobject> certificate(env, env->CallObjectMethod(certStore, getCertificate, js_alias.get()));
+
+ if (!certificate)
+ return NULL;
+
+ ScopedLocalRef<jbyteArray> encoded(env, (jbyteArray)env->CallObjectMethod(certificate, getEncoded));
+ jsize encodedLength = env->GetArrayLength(encoded);
+
+ MonoArray *encoded_ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(uint8_t), encodedLength);
+ uint8_t *dest = (uint8_t *)mono_array_addr(encoded_ret, uint8_t, 0);
+
+ env->GetByteArrayRegion(encoded, 0, encodedLength, reinterpret_cast<jbyte *>(dest));
+
+ return encoded_ret;
+}
+
+void initialize() {
+ // We need to set this environment variable to make the monodroid BCL use btls instead of legacy as the default provider
+ OS::get_singleton()->set_environment("XA_TLS_PROVIDER", "btls");
+
+ mono_dl_fallback_register(gd_mono_android_dlopen, gd_mono_android_dlsym, gd_mono_android_dlclose, NULL);
+
+ String app_native_lib_dir = get_app_native_lib_dir();
+ String so_path = path::join(app_native_lib_dir, godot_so_name);
+
+ godot_dl_handle = try_dlopen(so_path, gd_mono_convert_dl_flags(MONO_DL_LAZY));
+}
+
+void register_internal_calls() {
+ mono_add_internal_call("Android.Runtime.AndroidEnvironment::_gd_mono_init_cert_store", (void *)_gd_mono_init_cert_store);
+ mono_add_internal_call("Android.Runtime.AndroidEnvironment::_gd_mono_android_cert_store_lookup", (void *)_gd_mono_android_cert_store_lookup);
+}
+
+void cleanup() {
+ // This is called after shutting down the Mono runtime
+
+ if (mono_dl_handle)
+ gd_mono_android_dlclose(mono_dl_handle, NULL);
+
+ if (godot_dl_handle)
+ gd_mono_android_dlclose(godot_dl_handle, NULL);
+
+ JNIEnv *env = ThreadAndroid::get_env();
+
+ if (certStore) {
+ env->DeleteGlobalRef(certStore);
+ certStore = NULL;
+ }
+}
+
+} // namespace GDMonoAndroid
+
+using namespace GDMonoAndroid;
+
+// The following are P/Invoke functions required by the monodroid profile of the BCL.
+// These are P/Invoke functions and not internal calls, hence why they use
+// 'mono_bool' and 'const char*' instead of 'MonoBoolean' and 'MonoString*'.
+
+#define GD_PINVOKE_EXPORT extern "C" __attribute__((visibility("default")))
+
+GD_PINVOKE_EXPORT int32_t _monodroid_get_android_api_level() {
+ return get_build_version_sdk_int();
+}
+
+GD_PINVOKE_EXPORT void monodroid_free(void *ptr) {
+ free(ptr);
+}
+
+GD_PINVOKE_EXPORT int32_t monodroid_get_system_property(const char *p_name, char **r_value) {
+ char prop_value_str[PROP_VALUE_MAX + 1] = { 0 };
+
+ int len = __system_property_get(p_name, prop_value_str);
+
+ if (r_value) {
+ if (len >= 0) {
+ *r_value = (char *)malloc(len + 1);
+ if (!*r_value)
+ return -1;
+ memcpy(*r_value, prop_value_str, len);
+ (*r_value)[len] = '\0';
+ } else {
+ *r_value = NULL;
+ }
+ }
+
+ return len;
+}
+
+GD_PINVOKE_EXPORT mono_bool _monodroid_get_network_interface_up_state(const char *p_ifname, mono_bool *r_is_up) {
+ // The JNI code is the equivalent of:
+ //
+ // NetworkInterface.getByName(p_ifname).isUp()
+
+ if (!r_is_up || !p_ifname || strlen(p_ifname) == 0)
+ return 0;
+
+ *r_is_up = 0;
+
+ JNIEnv *env = ThreadAndroid::get_env();
+
+ jclass networkInterfaceClass = env->FindClass("java/net/NetworkInterface");
+ ERR_FAIL_NULL_V(networkInterfaceClass, 0);
+
+ jmethodID getByName = env->GetStaticMethodID(networkInterfaceClass, "getByName", "(Ljava/lang/String;)Ljava/net/NetworkInterface;");
+ ERR_FAIL_NULL_V(getByName, 0);
+
+ jmethodID isUp = env->GetMethodID(networkInterfaceClass, "isUp", "()Z");
+ ERR_FAIL_NULL_V(isUp, 0);
+
+ ScopedLocalRef<jstring> js_ifname(env, env->NewStringUTF(p_ifname));
+ ScopedLocalRef<jobject> networkInterface(env, env->CallStaticObjectMethod(networkInterfaceClass, getByName, js_ifname.get()));
+
+ if (!networkInterface)
+ return 0;
+
+ *r_is_up = (mono_bool)env->CallBooleanMethod(networkInterface, isUp);
+
+ return 1;
+}
+
+GD_PINVOKE_EXPORT mono_bool _monodroid_get_network_interface_supports_multicast(const char *p_ifname, mono_bool *r_supports_multicast) {
+ // The JNI code is the equivalent of:
+ //
+ // NetworkInterface.getByName(p_ifname).supportsMulticast()
+
+ if (!r_supports_multicast || !p_ifname || strlen(p_ifname) == 0)
+ return 0;
+
+ *r_supports_multicast = 0;
+
+ JNIEnv *env = ThreadAndroid::get_env();
+
+ jclass networkInterfaceClass = env->FindClass("java/net/NetworkInterface");
+ ERR_FAIL_NULL_V(networkInterfaceClass, 0);
+
+ jmethodID getByName = env->GetStaticMethodID(networkInterfaceClass, "getByName", "(Ljava/lang/String;)Ljava/net/NetworkInterface;");
+ ERR_FAIL_NULL_V(getByName, 0);
+
+ jmethodID supportsMulticast = env->GetMethodID(networkInterfaceClass, "supportsMulticast", "()Z");
+ ERR_FAIL_NULL_V(supportsMulticast, 0);
+
+ ScopedLocalRef<jstring> js_ifname(env, env->NewStringUTF(p_ifname));
+ ScopedLocalRef<jobject> networkInterface(env, env->CallStaticObjectMethod(networkInterfaceClass, getByName, js_ifname.get()));
+
+ if (!networkInterface)
+ return 0;
+
+ *r_supports_multicast = (mono_bool)env->CallBooleanMethod(networkInterface, supportsMulticast);
+
+ return 1;
+}
+
+static const int dns_servers_len = 8;
+
+static void interop_get_active_network_dns_servers(char **r_dns_servers, int *dns_servers_count) {
+ // The JNI code is the equivalent of:
+ //
+ // ConnectivityManager connectivityManager = (ConnectivityManager)getApplicationContext()
+ // .getSystemService(Context.CONNECTIVITY_SERVICE);
+ // Network activeNerwork = connectivityManager.getActiveNetwork();
+ // LinkProperties linkProperties = connectivityManager.getLinkProperties(activeNerwork);
+ // List<String> dnsServers = linkProperties.getDnsServers().stream()
+ // .map(inetAddress -> inetAddress.getHostAddress()).collect(Collectors.toList());
+
+#ifdef DEBUG_ENABLED
+ CRASH_COND(get_build_version_sdk_int() < 23);
+#endif
+
+ JNIEnv *env = ThreadAndroid::get_env();
+
+ GodotJavaWrapper *godot_java = ((OS_Android *)OS::get_singleton())->get_godot_java();
+ jobject activity = godot_java->get_activity();
+
+ ScopedLocalRef<jclass> activityClass(env, env->GetObjectClass(activity));
+ ERR_FAIL_NULL(activityClass);
+
+ jmethodID getApplicationContext = env->GetMethodID(activityClass, "getApplicationContext", "()Landroid/content/Context;");
+
+ ScopedLocalRef<jobject> applicationContext(env, env->CallObjectMethod(activity, getApplicationContext));
+
+ ScopedLocalRef<jclass> contextClass(env, env->FindClass("android/content/Context"));
+ ERR_FAIL_NULL(contextClass);
+
+ jfieldID connectivityServiceField = env->GetStaticFieldID(contextClass, "CONNECTIVITY_SERVICE", "Ljava/lang/String;");
+ ScopedLocalRef<jstring> connectivityServiceString(env, (jstring)env->GetStaticObjectField(contextClass, connectivityServiceField));
+
+ jmethodID getSystemService = env->GetMethodID(contextClass, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
+
+ ScopedLocalRef<jobject> connectivityManager(env, env->CallObjectMethod(applicationContext, getSystemService, connectivityServiceString.get()));
+
+ if (!connectivityManager)
+ return;
+
+ ScopedLocalRef<jclass> connectivityManagerClass(env, env->FindClass("android/net/ConnectivityManager"));
+ ERR_FAIL_NULL(connectivityManagerClass);
+
+ jmethodID getActiveNetwork = env->GetMethodID(connectivityManagerClass, "getActiveNetwork", "()Landroid/net/Network;");
+ ERR_FAIL_NULL(getActiveNetwork);
+
+ ScopedLocalRef<jobject> activeNetwork(env, env->CallObjectMethod(connectivityManager, getActiveNetwork));
+
+ if (!activeNetwork)
+ return;
+
+ jmethodID getLinkProperties = env->GetMethodID(connectivityManagerClass,
+ "getLinkProperties", "(Landroid/net/Network;)Landroid/net/LinkProperties;");
+ ERR_FAIL_NULL(getLinkProperties);
+
+ ScopedLocalRef<jobject> linkProperties(env, env->CallObjectMethod(connectivityManager, getLinkProperties, activeNetwork.get()));
+
+ if (!linkProperties)
+ return;
+
+ ScopedLocalRef<jclass> linkPropertiesClass(env, env->FindClass("android/net/LinkProperties"));
+ ERR_FAIL_NULL(linkPropertiesClass);
+
+ jmethodID getDnsServers = env->GetMethodID(linkPropertiesClass, "getDnsServers", "()Ljava/util/List;");
+ ERR_FAIL_NULL(getDnsServers);
+
+ ScopedLocalRef<jobject> dnsServers(env, env->CallObjectMethod(linkProperties, getDnsServers));
+
+ if (!dnsServers)
+ return;
+
+ ScopedLocalRef<jclass> listClass(env, env->FindClass("java/util/List"));
+ ERR_FAIL_NULL(listClass);
+
+ jmethodID listSize = env->GetMethodID(listClass, "size", "()I");
+ ERR_FAIL_NULL(listSize);
+
+ int dnsServersCount = env->CallIntMethod(dnsServers, listSize);
+
+ if (dnsServersCount > dns_servers_len)
+ dnsServersCount = dns_servers_len;
+
+ if (dnsServersCount <= 0)
+ return;
+
+ jmethodID listGet = env->GetMethodID(listClass, "get", "(I)Ljava/lang/Object;");
+ ERR_FAIL_NULL(listGet);
+
+ ScopedLocalRef<jclass> inetAddressClass(env, env->FindClass("java/net/InetAddress"));
+ ERR_FAIL_NULL(inetAddressClass);
+
+ jmethodID getHostAddress = env->GetMethodID(inetAddressClass, "getHostAddress", "()Ljava/lang/String;");
+ ERR_FAIL_NULL(getHostAddress);
+
+ for (int i = 0; i < dnsServersCount; i++) {
+ ScopedLocalRef<jobject> dnsServer(env, env->CallObjectMethod(dnsServers, listGet, (jint)i));
+ if (!dnsServer)
+ continue;
+
+ ScopedLocalRef<jstring> hostAddress(env, (jstring)env->CallObjectMethod(dnsServer, getHostAddress));
+ const char *host_address = env->GetStringUTFChars(hostAddress, 0);
+
+ r_dns_servers[i] = strdup(host_address); // freed by the BCL
+ (*dns_servers_count)++;
+
+ env->ReleaseStringUTFChars(hostAddress, host_address);
+ }
+
+ // jesus...
+}
+
+GD_PINVOKE_EXPORT int32_t _monodroid_get_dns_servers(void **r_dns_servers_array) {
+ if (!r_dns_servers_array)
+ return -1;
+
+ *r_dns_servers_array = NULL;
+
+ char *dns_servers[dns_servers_len];
+ int dns_servers_count = 0;
+
+ if (_monodroid_get_android_api_level() < 26) {
+ // The 'net.dns*' system properties are no longer available in Android 8.0 (API level 26) and greater:
+ // https://developer.android.com/about/versions/oreo/android-8.0-changes.html#o-pri
+
+ char prop_name[] = "net.dns*";
+
+ for (int i = 0; i < dns_servers_len; i++) {
+ prop_name[7] = (char)(i + 0x31);
+ char *prop_value;
+ int32_t len = monodroid_get_system_property(prop_name, &prop_value);
+
+ if (len > 0) {
+ dns_servers[dns_servers_count] = strndup(prop_value, (size_t)len); // freed by the BCL
+ dns_servers_count++;
+ free(prop_value);
+ }
+ }
+ } else {
+ // Alternative for Oreo and greater
+ interop_get_active_network_dns_servers(dns_servers, &dns_servers_count);
+ }
+
+ if (dns_servers_count > 0) {
+ size_t ret_size = sizeof(char *) * (size_t)dns_servers_count;
+ *r_dns_servers_array = malloc(ret_size); // freed by the BCL
+ memcpy(*r_dns_servers_array, dns_servers, ret_size);
+ }
+
+ return dns_servers_count;
+}
+
+GD_PINVOKE_EXPORT const char *_monodroid_timezone_get_default_id() {
+ // The JNI code is the equivalent of:
+ //
+ // TimeZone.getDefault().getID()
+
+ JNIEnv *env = ThreadAndroid::get_env();
+
+ ScopedLocalRef<jclass> timeZoneClass(env, env->FindClass("java/util/TimeZone"));
+ ERR_FAIL_NULL_V(timeZoneClass, NULL);
+
+ jmethodID getDefault = env->GetStaticMethodID(timeZoneClass, "getDefault", "()Ljava/util/TimeZone;");
+ ERR_FAIL_NULL_V(getDefault, NULL);
+
+ jmethodID getID = env->GetMethodID(timeZoneClass, "getID", "()Ljava/lang/String;");
+ ERR_FAIL_NULL_V(getID, NULL);
+
+ ScopedLocalRef<jobject> defaultTimeZone(env, env->CallStaticObjectMethod(timeZoneClass, getDefault));
+
+ if (!defaultTimeZone)
+ return NULL;
+
+ ScopedLocalRef<jstring> defaultTimeZoneID(env, (jstring)env->CallObjectMethod(defaultTimeZone, getID));
+
+ if (!defaultTimeZoneID)
+ return NULL;
+
+ const char *default_time_zone_id = env->GetStringUTFChars(defaultTimeZoneID, 0);
+
+ char *result = strdup(default_time_zone_id); // freed by the BCL
+
+ env->ReleaseStringUTFChars(defaultTimeZoneID, default_time_zone_id);
+
+ return result;
+}
+
+GD_PINVOKE_EXPORT int32_t _monodroid_getifaddrs(struct ifaddrs **p_ifap) {
+ return getifaddrs(p_ifap);
+}
+
+GD_PINVOKE_EXPORT void _monodroid_freeifaddrs(struct ifaddrs *p_ifap) {
+ freeifaddrs(p_ifap);
+}
+
+#endif
diff --git a/modules/mono/utils/android_utils.h b/modules/mono/mono_gd/gd_mono_android.h
index f911c3fdfe..20ca266428 100644
--- a/modules/mono/utils/android_utils.h
+++ b/modules/mono/mono_gd/gd_mono_android.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* android_utils.h */
+/* gd_mono_android.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,21 +28,25 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef ANDROID_UTILS_H
-#define ANDROID_UTILS_H
+#ifndef GD_MONO_ANDROID_H
+#define GD_MONO_ANDROID_H
-#ifdef __ANDROID__
+#if defined(ANDROID_ENABLED)
#include "core/ustring.h"
-namespace GDMonoUtils {
-namespace Android {
+namespace GDMonoAndroid {
String get_app_native_lib_dir();
-} // namespace Android
-} // namespace GDMonoUtils
+void initialize();
-#endif // __ANDROID__
+void register_internal_calls();
-#endif // ANDROID_UTILS_H
+void cleanup();
+
+} // namespace GDMonoAndroid
+
+#endif // ANDROID_ENABLED
+
+#endif // GD_MONO_ANDROID_H
diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp
index a82bb42731..105560fe9a 100644
--- a/modules/mono/mono_gd/gd_mono_assembly.cpp
+++ b/modules/mono/mono_gd/gd_mono_assembly.cpp
@@ -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;
@@ -61,6 +62,13 @@ void GDMonoAssembly::fill_search_dirs(Vector<String> &r_search_dirs, const Strin
r_search_dirs.push_back(framework_dir.plus_file("Facades"));
}
+#if !defined(TOOLS_ENABLED)
+ String data_game_assemblies_dir = GodotSharpDirs::get_data_game_assemblies_dir();
+ if (!data_game_assemblies_dir.empty()) {
+ r_search_dirs.push_back(data_game_assemblies_dir);
+ }
+#endif
+
if (p_custom_config.length()) {
r_search_dirs.push_back(GodotSharpDirs::get_res_temp_assemblies_base_dir().plus_file(p_custom_config));
} else {
@@ -146,10 +154,6 @@ MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **, vo
(void)user_data; // UNUSED
- if (search_dirs.empty()) {
- fill_search_dirs(search_dirs);
- }
-
{
// If we find the assembly here, we load it with 'mono_assembly_load_from_full',
// which in turn invokes load hooks before returning the MonoAssembly to us.
@@ -227,6 +231,33 @@ GDMonoAssembly *GDMonoAssembly::_load_assembly_search(const String &p_name, cons
return NULL;
}
+String GDMonoAssembly::find_assembly(const String &p_name) {
+
+ String path;
+
+ bool has_extension = p_name.ends_with(".dll") || p_name.ends_with(".exe");
+
+ for (int i = 0; i < search_dirs.size(); i++) {
+ const String &search_dir = search_dirs[i];
+
+ if (has_extension) {
+ path = search_dir.plus_file(p_name);
+ if (FileAccess::exists(path))
+ return path;
+ } else {
+ path = search_dir.plus_file(p_name + ".dll");
+ if (FileAccess::exists(path))
+ return path;
+
+ path = search_dir.plus_file(p_name + ".exe");
+ if (FileAccess::exists(path))
+ return path;
+ }
+ }
+
+ return String();
+}
+
GDMonoAssembly *GDMonoAssembly::_load_assembly_from(const String &p_name, const String &p_path, bool p_refonly) {
GDMonoAssembly *assembly = memnew(GDMonoAssembly(p_name, p_path));
@@ -263,6 +294,8 @@ void GDMonoAssembly::_wrap_mono_assembly(MonoAssembly *assembly) {
void GDMonoAssembly::initialize() {
+ fill_search_dirs(search_dirs);
+
mono_install_assembly_search_hook(&assembly_search_hook, NULL);
mono_install_assembly_refonly_search_hook(&assembly_refonly_search_hook, NULL);
mono_install_assembly_preload_hook(&assembly_preload_hook, NULL);
diff --git a/modules/mono/mono_gd/gd_mono_assembly.h b/modules/mono/mono_gd/gd_mono_assembly.h
index 39749dfc1d..04a219f742 100644
--- a/modules/mono/mono_gd/gd_mono_assembly.h
+++ b/modules/mono/mono_gd/gd_mono_assembly.h
@@ -122,6 +122,8 @@ public:
GDMonoClass *get_object_derived_class(const StringName &p_class);
+ static String find_assembly(const String &p_name);
+
static void fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config = String(), const String &p_custom_bcl_dir = String());
static GDMonoAssembly *load_from(const String &p_name, const String &p_path, bool p_refonly);
diff --git a/modules/mono/mono_gd/gd_mono_cache.cpp b/modules/mono/mono_gd/gd_mono_cache.cpp
new file mode 100644
index 0000000000..caa1ca9203
--- /dev/null
+++ b/modules/mono/mono_gd/gd_mono_cache.cpp
@@ -0,0 +1,312 @@
+/*************************************************************************/
+/* gd_mono_cache.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 "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
diff --git a/modules/mono/mono_gd/gd_mono_cache.h b/modules/mono/mono_gd/gd_mono_cache.h
new file mode 100644
index 0000000000..a6d6da4f2b
--- /dev/null
+++ b/modules/mono/mono_gd/gd_mono_cache.h
@@ -0,0 +1,204 @@
+/*************************************************************************/
+/* gd_mono_cache.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 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; // object
+ GDMonoClass *class_bool; // bool
+ GDMonoClass *class_int8_t; // sbyte
+ GDMonoClass *class_int16_t; // short
+ GDMonoClass *class_int32_t; // int
+ GDMonoClass *class_int64_t; // long
+ GDMonoClass *class_uint8_t; // byte
+ GDMonoClass *class_uint16_t; // ushort
+ GDMonoClass *class_uint32_t; // uint
+ GDMonoClass *class_uint64_t; // ulong
+ GDMonoClass *class_float; // float
+ GDMonoClass *class_double; // double
+ GDMonoClass *class_String; // string
+ GDMonoClass *class_IntPtr; // System.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
diff --git a/modules/mono/mono_gd/gd_mono_class.cpp b/modules/mono/mono_gd/gd_mono_class.cpp
index 89a88fcfb2..fb9b6be3d4 100644
--- a/modules/mono/mono_gd/gd_mono_class.cpp
+++ b/modules/mono/mono_gd/gd_mono_class.cpp
@@ -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);
diff --git a/modules/mono/mono_gd/gd_mono_class.h b/modules/mono/mono_gd/gd_mono_class.h
index 40e1574927..562c337822 100644
--- a/modules/mono/mono_gd/gd_mono_class.h
+++ b/modules/mono/mono_gd/gd_mono_class.h
@@ -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();
diff --git a/modules/mono/mono_gd/gd_mono_field.cpp b/modules/mono/mono_gd/gd_mono_field.cpp
index 7b8e6f89e9..d84359b1ab 100644
--- a/modules/mono/mono_gd/gd_mono_field.cpp
+++ b/modules/mono/mono_gd/gd_mono_field.cpp
@@ -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;
diff --git a/modules/mono/mono_gd/gd_mono_header.h b/modules/mono/mono_gd/gd_mono_header.h
index d7962eac8b..173e1613bf 100644
--- a/modules/mono/mono_gd/gd_mono_header.h
+++ b/modules/mono/mono_gd/gd_mono_header.h
@@ -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;
diff --git a/modules/mono/mono_gd/gd_mono_log.cpp b/modules/mono/mono_gd/gd_mono_log.cpp
index 7b3421fdb3..261b800619 100644
--- a/modules/mono/mono_gd/gd_mono_log.cpp
+++ b/modules/mono/mono_gd/gd_mono_log.cpp
@@ -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)
diff --git a/modules/mono/mono_gd/gd_mono_log.h b/modules/mono/mono_gd/gd_mono_log.h
index 91d0557aa3..4cd5a662fb 100644
--- a/modules/mono/mono_gd/gd_mono_log.h
+++ b/modules/mono/mono_gd/gd_mono_log.h
@@ -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();
};
diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp
index 7aac691102..f74fe5715c 100644
--- a/modules/mono/mono_gd/gd_mono_marshal.cpp
+++ b/modules/mono/mono_gd/gd_mono_marshal.cpp
@@ -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();
}
@@ -934,6 +935,8 @@ Array mono_array_to_Array(MonoArray *p_array) {
return ret;
}
+// TODO: Use memcpy where possible
+
MonoArray *PoolIntArray_to_mono_array(const PoolIntArray &p_array) {
PoolIntArray::Read r = p_array.read();
diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h
index 3fa958ac32..53eae45320 100644
--- a/modules/mono/mono_gd/gd_mono_marshal.h
+++ b/modules/mono/mono_gd/gd_mono_marshal.h
@@ -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)
diff --git a/modules/mono/mono_gd/gd_mono_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp
index 968b316a3e..080b3a676a 100644
--- a/modules/mono/mono_gd/gd_mono_method.cpp
+++ b/modules/mono/mono_gd/gd_mono_method.cpp
@@ -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());
diff --git a/modules/mono/mono_gd/gd_mono_method.h b/modules/mono/mono_gd/gd_mono_method.h
index 2fc8628f27..abbae92e29 100644
--- a/modules/mono/mono_gd/gd_mono_method.h
+++ b/modules/mono/mono_gd/gd_mono_method.h
@@ -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);
diff --git a/modules/mono/mono_gd/gd_mono_method_thunk.h b/modules/mono/mono_gd/gd_mono_method_thunk.h
new file mode 100644
index 0000000000..f8cc736ec3
--- /dev/null
+++ b/modules/mono/mono_gd/gd_mono_method_thunk.h
@@ -0,0 +1,332 @@
+/*************************************************************************/
+/* gd_mono_method_thunk.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 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
diff --git a/modules/mono/mono_gd/gd_mono_property.cpp b/modules/mono/mono_gd/gd_mono_property.cpp
index f1da00638f..277fe10087 100644
--- a/modules/mono/mono_gd/gd_mono_property.cpp
+++ b/modules/mono/mono_gd/gd_mono_property.cpp
@@ -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>
diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp
index 6504fbe423..8d7aaa97f2 100644
--- a/modules/mono/mono_gd/gd_mono_utils.cpp
+++ b/modules/mono/mono_gd/gd_mono_utils.cpp
@@ -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)));
}
diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h
index d73743bf0b..848df843fe 100644
--- a/modules/mono/mono_gd/gd_mono_utils.h
+++ b/modules/mono/mono_gd/gd_mono_utils.h
@@ -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
diff --git a/modules/mono/signal_awaiter_utils.cpp b/modules/mono/signal_awaiter_utils.cpp
index 189ceaab1b..ee16327856 100644
--- a/modules/mono/signal_awaiter_utils.cpp
+++ b/modules/mono/signal_awaiter_utils.cpp
@@ -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) {
diff --git a/modules/mono/utils/android_utils.cpp b/modules/mono/utils/android_utils.cpp
deleted file mode 100644
index 7dd67e3b8e..0000000000
--- a/modules/mono/utils/android_utils.cpp
+++ /dev/null
@@ -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__
diff --git a/modules/mono/utils/string_utils.cpp b/modules/mono/utils/string_utils.cpp
index e9efc7626d..88366a6a03 100644
--- a/modules/mono/utils/string_utils.cpp
+++ b/modules/mono/utils/string_utils.cpp
@@ -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;
}
diff --git a/modules/mono/utils/string_utils.h b/modules/mono/utils/string_utils.h
index 565b9bb644..e7f02955bd 100644
--- a/modules/mono/utils/string_utils.h
+++ b/modules/mono/utils/string_utils.h
@@ -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