summaryrefslogtreecommitdiff
path: root/modules/mono
diff options
context:
space:
mode:
Diffstat (limited to 'modules/mono')
-rw-r--r--modules/mono/SCsub49
-rw-r--r--modules/mono/build_scripts/api_solution_build.py35
-rw-r--r--modules/mono/build_scripts/gen_cs_glue_version.py20
-rw-r--r--modules/mono/build_scripts/godot_tools_build.py23
-rw-r--r--modules/mono/build_scripts/make_android_mono_config.py20
-rw-r--r--modules/mono/build_scripts/mono_configure.py509
-rw-r--r--modules/mono/build_scripts/mono_reg_utils.py59
-rw-r--r--modules/mono/build_scripts/solution_builder.py132
-rw-r--r--modules/mono/build_scripts/tls_configure.py36
-rw-r--r--modules/mono/class_db_api_json.cpp10
-rw-r--r--modules/mono/config.py62
-rw-r--r--modules/mono/csharp_script.cpp731
-rw-r--r--modules/mono/csharp_script.h121
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.BuildLogger/Properties/AssemblyInfo.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeConnection/Properties/AssemblyInfo.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs37
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectExtensions.cs78
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs64
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs192
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/Properties/AssemblyInfo.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs26
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/BuildManager.cs8
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/BuildTab.cs12
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/CsProjOperations.cs12
-rwxr-xr-xmodules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs618
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs404
-rwxr-xr-xmodules/mono/editor/GodotTools/GodotTools/Export/XcodeHelper.cs93
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs109
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs4
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs4
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs5
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs44
-rw-r--r--modules/mono/editor/bindings_generator.cpp385
-rw-r--r--modules/mono/editor/bindings_generator.h67
-rw-r--r--modules/mono/editor/csharp_project.cpp4
-rw-r--r--modules/mono/editor/editor_internal_calls.cpp18
-rw-r--r--modules/mono/editor/godotsharp_export.cpp13
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp.sln2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs31
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs395
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/DynamicObject.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ResourceLoaderExtensions.cs4
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs37
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs59
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs29
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs262
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs12
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs17
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs82
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs26
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs380
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs18
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs402
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj7
-rw-r--r--modules/mono/glue/base_object_glue.cpp51
-rw-r--r--modules/mono/glue/base_object_glue.h8
-rw-r--r--modules/mono/glue/collections_glue.cpp26
-rw-r--r--modules/mono/glue/gd_glue.cpp28
-rw-r--r--modules/mono/glue/gd_glue.h2
-rw-r--r--modules/mono/glue/glue_header.h4
-rw-r--r--modules/mono/glue/string_name_glue.cpp61
-rw-r--r--modules/mono/glue/string_name_glue.h54
-rw-r--r--modules/mono/godotsharp_dirs.cpp12
-rw-r--r--modules/mono/icons/CSharpScript.svg (renamed from modules/mono/icons/icon_c_sharp_script.svg)0
-rw-r--r--modules/mono/managed_callable.cpp145
-rw-r--r--modules/mono/managed_callable.h77
-rw-r--r--modules/mono/mono_gc_handle.cpp57
-rw-r--r--modules/mono/mono_gc_handle.h87
-rw-r--r--modules/mono/mono_gd/gd_mono.cpp203
-rw-r--r--modules/mono/mono_gd/gd_mono.h14
-rw-r--r--modules/mono/mono_gd/gd_mono_assembly.cpp288
-rw-r--r--modules/mono/mono_gd/gd_mono_assembly.h35
-rw-r--r--modules/mono/mono_gd/gd_mono_cache.cpp182
-rw-r--r--modules/mono/mono_gd/gd_mono_cache.h23
-rw-r--r--modules/mono/mono_gd/gd_mono_class.cpp78
-rw-r--r--modules/mono/mono_gd/gd_mono_field.cpp89
-rw-r--r--modules/mono/mono_gd/gd_mono_field.h15
-rw-r--r--modules/mono/mono_gd/gd_mono_internals.cpp22
-rw-r--r--modules/mono/mono_gd/gd_mono_log.cpp10
-rw-r--r--modules/mono/mono_gd/gd_mono_log.h9
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.cpp441
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.h102
-rw-r--r--modules/mono/mono_gd/gd_mono_method.cpp88
-rw-r--r--modules/mono/mono_gd/gd_mono_method.h26
-rw-r--r--modules/mono/mono_gd/gd_mono_method_thunk.h52
-rw-r--r--modules/mono/mono_gd/gd_mono_property.cpp32
-rw-r--r--modules/mono/mono_gd/gd_mono_property.h20
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.cpp168
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.h23
-rw-r--r--modules/mono/mono_gd/managed_type.h2
-rwxr-xr-x[-rw-r--r--]modules/mono/mono_gd/support/android_support.cpp (renamed from modules/mono/mono_gd/gd_mono_android.cpp)98
-rwxr-xr-x[-rw-r--r--]modules/mono/mono_gd/support/android_support.h (renamed from modules/mono/mono_gd/gd_mono_android.h)19
-rwxr-xr-x[-rw-r--r--]modules/mono/mono_gd/support/ios_support.h (renamed from modules/mono/utils/thread_local.cpp)86
-rwxr-xr-xmodules/mono/mono_gd/support/ios_support.mm151
-rw-r--r--modules/mono/register_types.cpp4
-rw-r--r--modules/mono/signal_awaiter_utils.cpp215
-rw-r--r--modules/mono/signal_awaiter_utils.h68
-rw-r--r--modules/mono/utils/macros.h53
-rw-r--r--modules/mono/utils/mono_reg_utils.cpp6
-rw-r--r--modules/mono/utils/osx_utils.cpp6
-rw-r--r--modules/mono/utils/path_utils.cpp10
-rw-r--r--modules/mono/utils/string_utils.cpp2
-rw-r--r--modules/mono/utils/thread_local.h177
107 files changed, 6571 insertions, 2641 deletions
diff --git a/modules/mono/SCsub b/modules/mono/SCsub
index 41be367f2f..c723b210cb 100644
--- a/modules/mono/SCsub
+++ b/modules/mono/SCsub
@@ -1,54 +1,57 @@
#!/usr/bin/env python
-import build_scripts.tls_configure as tls_configure
import build_scripts.mono_configure as mono_configure
-Import('env')
-Import('env_modules')
+Import("env")
+Import("env_modules")
env_mono = env_modules.Clone()
-if env_mono['tools']:
+if env_mono["tools"]:
# NOTE: It is safe to generate this file here, since this is still executed serially
import build_scripts.gen_cs_glue_version as gen_cs_glue_version
- gen_cs_glue_version.generate_header('glue/GodotSharp', 'glue/cs_glue_version.gen.h')
+
+ gen_cs_glue_version.generate_header("glue/GodotSharp", "glue/cs_glue_version.gen.h")
# Glue sources
-if env_mono['mono_glue']:
- env_mono.Append(CPPDEFINES=['MONO_GLUE_ENABLED'])
+if env_mono["mono_glue"]:
+ env_mono.Append(CPPDEFINES=["MONO_GLUE_ENABLED"])
import os.path
- if not os.path.isfile('glue/mono_glue.gen.cpp'):
- raise RuntimeError("Mono glue sources not found. Did you forget to run '--generate-mono-glue'?")
-if env_mono['tools'] or env_mono['target'] != 'release':
- env_mono.Append(CPPDEFINES=['GD_MONO_HOT_RELOAD'])
-
-# Configure Thread Local Storage
+ if not os.path.isfile("glue/mono_glue.gen.cpp"):
+ raise RuntimeError("Mono glue sources not found. Did you forget to run '--generate-mono-glue'?")
-conf = Configure(env_mono)
-tls_configure.configure(conf)
-env_mono = conf.Finish()
+if env_mono["tools"] or env_mono["target"] != "release":
+ env_mono.Append(CPPDEFINES=["GD_MONO_HOT_RELOAD"])
# Configure Mono
mono_configure.configure(env, env_mono)
-if env_mono['tools'] and env_mono['mono_glue']:
+if env_mono["tools"] and env_mono["mono_glue"]:
# Build Godot API solution
import build_scripts.api_solution_build as api_solution_build
+
api_sln_cmd = api_solution_build.build(env_mono)
# Build GodotTools
import build_scripts.godot_tools_build as godot_tools_build
+
godot_tools_build.build(env_mono, api_sln_cmd)
# Add sources
-env_mono.add_source_files(env.modules_sources, '*.cpp')
-env_mono.add_source_files(env.modules_sources, 'glue/*.cpp')
-env_mono.add_source_files(env.modules_sources, 'mono_gd/*.cpp')
-env_mono.add_source_files(env.modules_sources, 'utils/*.cpp')
+env_mono.add_source_files(env.modules_sources, "*.cpp")
+env_mono.add_source_files(env.modules_sources, "glue/*.cpp")
+env_mono.add_source_files(env.modules_sources, "mono_gd/*.cpp")
+env_mono.add_source_files(env.modules_sources, "utils/*.cpp")
+
+env_mono.add_source_files(env.modules_sources, "mono_gd/support/*.cpp")
+
+if env["platform"] in ["osx", "iphone"]:
+ env_mono.add_source_files(env.modules_sources, "mono_gd/support/*.mm")
+ env_mono.add_source_files(env.modules_sources, "mono_gd/support/*.m")
-if env['tools']:
- env_mono.add_source_files(env.modules_sources, 'editor/*.cpp')
+if env["tools"]:
+ env_mono.add_source_files(env.modules_sources, "editor/*.cpp")
diff --git a/modules/mono/build_scripts/api_solution_build.py b/modules/mono/build_scripts/api_solution_build.py
index 639197c285..9abac22df6 100644
--- a/modules/mono/build_scripts/api_solution_build.py
+++ b/modules/mono/build_scripts/api_solution_build.py
@@ -8,21 +8,22 @@ from SCons.Script import Dir
def build_api_solution(source, target, env):
# source and target elements are of type SCons.Node.FS.File, hence why we convert them to str
- module_dir = env['module_dir']
+ module_dir = env["module_dir"]
- solution_path = os.path.join(module_dir, 'glue/GodotSharp/GodotSharp.sln')
+ solution_path = os.path.join(module_dir, "glue/GodotSharp/GodotSharp.sln")
- build_config = env['solution_build_config']
+ build_config = env["solution_build_config"]
- extra_msbuild_args = ['/p:NoWarn=1591'] # Ignore missing documentation warnings
+ extra_msbuild_args = ["/p:NoWarn=1591"] # Ignore missing documentation warnings
from .solution_builder import build_solution
+
build_solution(env, solution_path, build_config, extra_msbuild_args=extra_msbuild_args)
# Copy targets
- core_src_dir = os.path.abspath(os.path.join(solution_path, os.pardir, 'GodotSharp', 'bin', build_config))
- editor_src_dir = os.path.abspath(os.path.join(solution_path, os.pardir, 'GodotSharpEditor', 'bin', build_config))
+ core_src_dir = os.path.abspath(os.path.join(solution_path, os.pardir, "GodotSharp", "bin", build_config))
+ editor_src_dir = os.path.abspath(os.path.join(solution_path, os.pardir, "GodotSharpEditor", "bin", build_config))
dst_dir = os.path.abspath(os.path.join(str(target[0]), os.pardir))
@@ -32,6 +33,7 @@ def build_api_solution(source, target, env):
def copy_target(target_path):
from shutil import copy
+
filename = os.path.basename(target_path)
src_path = os.path.join(core_src_dir, filename)
@@ -45,23 +47,28 @@ def build_api_solution(source, target, env):
def build(env_mono):
- assert env_mono['tools']
+ assert env_mono["tools"]
target_filenames = [
- 'GodotSharp.dll', 'GodotSharp.pdb', 'GodotSharp.xml',
- 'GodotSharpEditor.dll', 'GodotSharpEditor.pdb', 'GodotSharpEditor.xml'
+ "GodotSharp.dll",
+ "GodotSharp.pdb",
+ "GodotSharp.xml",
+ "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)
+ 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, depend_cmd, build_api_solution,
- module_dir=os.getcwd(), solution_build_config=build_config)
+ 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.
diff --git a/modules/mono/build_scripts/gen_cs_glue_version.py b/modules/mono/build_scripts/gen_cs_glue_version.py
index 5d1056c2fc..98bbb4d9be 100644
--- a/modules/mono/build_scripts/gen_cs_glue_version.py
+++ b/modules/mono/build_scripts/gen_cs_glue_version.py
@@ -1,20 +1,20 @@
-
def generate_header(solution_dir, version_header_dst):
import os
+
latest_mtime = 0
for root, dirs, files in os.walk(solution_dir, topdown=True):
- dirs[:] = [d for d in dirs if d not in ['Generated']] # Ignored generated files
- files = [f for f in files if f.endswith('.cs')]
+ dirs[:] = [d for d in dirs if d not in ["Generated"]] # Ignored generated files
+ files = [f for f in files if f.endswith(".cs")]
for file in files:
filepath = os.path.join(root, file)
mtime = os.path.getmtime(filepath)
latest_mtime = mtime if mtime > latest_mtime else latest_mtime
- glue_version = int(latest_mtime) # The latest modified time will do for now
+ glue_version = int(latest_mtime) # The latest modified time will do for now
- with open(version_header_dst, 'w') as version_header:
- version_header.write('/* THIS FILE IS GENERATED DO NOT EDIT */\n')
- version_header.write('#ifndef CS_GLUE_VERSION_H\n')
- version_header.write('#define CS_GLUE_VERSION_H\n\n')
- version_header.write('#define CS_GLUE_VERSION UINT32_C(' + str(glue_version) + ')\n')
- version_header.write('\n#endif // CS_GLUE_VERSION_H\n')
+ with open(version_header_dst, "w") as version_header:
+ version_header.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n")
+ version_header.write("#ifndef CS_GLUE_VERSION_H\n")
+ version_header.write("#define CS_GLUE_VERSION_H\n\n")
+ version_header.write("#define CS_GLUE_VERSION UINT32_C(" + str(glue_version) + ")\n")
+ version_header.write("\n#endif // CS_GLUE_VERSION_H\n")
diff --git a/modules/mono/build_scripts/godot_tools_build.py b/modules/mono/build_scripts/godot_tools_build.py
index 99341c631e..cffacf2577 100644
--- a/modules/mono/build_scripts/godot_tools_build.py
+++ b/modules/mono/build_scripts/godot_tools_build.py
@@ -8,30 +8,31 @@ from SCons.Script import Dir
def build_godot_tools(source, target, env):
# source and target elements are of type SCons.Node.FS.File, hence why we convert them to str
- module_dir = env['module_dir']
+ module_dir = env["module_dir"]
- solution_path = os.path.join(module_dir, 'editor/GodotTools/GodotTools.sln')
- build_config = 'Debug' if env['target'] == 'debug' else 'Release'
+ solution_path = os.path.join(module_dir, "editor/GodotTools/GodotTools.sln")
+ build_config = "Debug" if env["target"] == "debug" else "Release"
# Custom build target to make sure output is always copied to the data dir.
- extra_build_args = ['/Target:Build;GodotTools:BuildAlwaysCopyToDataDir']
+ extra_build_args = ["/Target:Build;GodotTools:BuildAlwaysCopyToDataDir"]
+
+ from .solution_builder import build_solution, nuget_restore
- from . solution_builder import build_solution, nuget_restore
nuget_restore(env, solution_path)
build_solution(env, solution_path, build_config, extra_build_args)
# No need to copy targets. The GodotTools csproj takes care of copying them.
def build(env_mono, api_sln_cmd):
- assert env_mono['tools']
+ assert env_mono["tools"]
- output_dir = Dir('#bin').abspath
- editor_tools_dir = os.path.join(output_dir, 'GodotSharp', 'Tools')
+ output_dir = Dir("#bin").abspath
+ editor_tools_dir = os.path.join(output_dir, "GodotSharp", "Tools")
- target_filenames = ['GodotTools.dll']
+ target_filenames = ["GodotTools.dll"]
- if env_mono['target'] == 'debug':
- target_filenames += ['GodotTools.pdb']
+ if env_mono["target"] == "debug":
+ target_filenames += ["GodotTools.pdb"]
targets = [os.path.join(editor_tools_dir, filename) for filename in target_filenames]
diff --git a/modules/mono/build_scripts/make_android_mono_config.py b/modules/mono/build_scripts/make_android_mono_config.py
index 0afd939c57..d276d7d886 100644
--- a/modules/mono/build_scripts/make_android_mono_config.py
+++ b/modules/mono/build_scripts/make_android_mono_config.py
@@ -1,24 +1,24 @@
-
def generate_compressed_config(config_src, output_dir):
import os.path
- from compat import byte_to_str
# Source file
- with open(os.path.join(output_dir, 'android_mono_config.gen.cpp'), 'w') as cpp:
- with open(config_src, 'rb') as f:
+ with open(os.path.join(output_dir, "android_mono_config.gen.cpp"), "w") as cpp:
+ with open(config_src, "rb") as f:
buf = f.read()
decompr_size = len(buf)
import zlib
+
buf = zlib.compress(buf)
compr_size = len(buf)
- bytes_seq_str = ''
+ bytes_seq_str = ""
for i, buf_idx in enumerate(range(compr_size)):
if i > 0:
- bytes_seq_str += ', '
- bytes_seq_str += byte_to_str(buf[buf_idx])
+ bytes_seq_str += ", "
+ bytes_seq_str += str(buf[buf_idx])
- cpp.write('''/* THIS FILE IS GENERATED DO NOT EDIT */
+ cpp.write(
+ """/* THIS FILE IS GENERATED DO NOT EDIT */
#include "android_mono_config.h"
#ifdef ANDROID_ENABLED
@@ -49,4 +49,6 @@ String get_godot_android_mono_config() {
}
#endif // ANDROID_ENABLED
-''' % (compr_size, decompr_size, bytes_seq_str))
+"""
+ % (compr_size, decompr_size, bytes_seq_str)
+ )
diff --git a/modules/mono/build_scripts/mono_configure.py b/modules/mono/build_scripts/mono_configure.py
index 9a6198f13a..23f01b3cca 100644
--- a/modules/mono/build_scripts/mono_configure.py
+++ b/modules/mono/build_scripts/mono_configure.py
@@ -5,178 +5,230 @@ import subprocess
from SCons.Script import Dir, Environment
-if os.name == 'nt':
+if os.name == "nt":
from . import mono_reg_utils as monoreg
android_arch_dirs = {
- 'armv7': 'armeabi-v7a',
- 'arm64v8': 'arm64-v8a',
- 'x86': 'x86',
- 'x86_64': 'x86_64'
+ "armv7": "armeabi-v7a",
+ "arm64v8": "arm64-v8a",
+ "x86": "x86",
+ "x86_64": "x86_64",
}
def get_android_out_dir(env):
- return os.path.join(Dir('#platform/android/java/lib/libs').abspath,
- 'release' if env['target'] == 'release' else 'debug',
- android_arch_dirs[env['android_arch']])
-
-
-def find_file_in_dir(directory, files, prefix='', extension=''):
- if not extension.startswith('.'):
- extension = '.' + extension
- for curfile in files:
- if os.path.isfile(os.path.join(directory, prefix + curfile + extension)):
- return curfile
- return ''
-
-
-def copy_file(src_dir, dst_dir, name):
+ return os.path.join(
+ Dir("#platform/android/java/lib/libs").abspath,
+ "release" if env["target"] == "release" else "debug",
+ android_arch_dirs[env["android_arch"]],
+ )
+
+
+def find_name_in_dir_files(directory, names, prefixes=[""], extensions=[""]):
+ for extension in extensions:
+ if extension and not extension.startswith("."):
+ extension = "." + extension
+ for prefix in prefixes:
+ for curname in names:
+ if os.path.isfile(os.path.join(directory, prefix + curname + extension)):
+ return curname
+ return ""
+
+
+def find_file_in_dir(directory, names, prefixes=[""], extensions=[""]):
+ for extension in extensions:
+ if extension and not extension.startswith("."):
+ extension = "." + extension
+ for prefix in prefixes:
+ for curname in names:
+ filename = prefix + curname + extension
+ if os.path.isfile(os.path.join(directory, filename)):
+ return filename
+ return ""
+
+
+def copy_file(src_dir, dst_dir, src_name, dst_name=""):
from shutil import copy
- src_path = os.path.join(Dir(src_dir).abspath, name)
+ src_path = os.path.join(Dir(src_dir).abspath, src_name)
dst_dir = Dir(dst_dir).abspath
if not os.path.isdir(dst_dir):
os.makedirs(dst_dir)
- copy(src_path, dst_dir)
+ if dst_name:
+ copy(src_path, os.path.join(dst_dir, dst_name))
+ else:
+ copy(src_path, dst_dir)
def is_desktop(platform):
- return platform in ['windows', 'osx', 'x11', 'server', 'uwp', 'haiku']
+ return platform in ["windows", "osx", "linuxbsd", "server", "uwp", "haiku"]
def is_unix_like(platform):
- return platform in ['osx', 'x11', 'server', 'android', 'haiku']
+ return platform in ["osx", "linuxbsd", "server", "android", "haiku", "iphone"]
def module_supports_tools_on(platform):
- return platform not in ['android', 'javascript']
+ return platform not in ["android", "javascript", "iphone"]
def find_wasm_src_dir(mono_root):
hint_dirs = [
- os.path.join(mono_root, 'src'),
- os.path.join(mono_root, '../src'),
+ 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')):
+ if os.path.isfile(os.path.join(hint_dir, "driver.c")):
return hint_dir
- return ''
+ return ""
def configure(env, env_mono):
- bits = env['bits']
- is_android = env['platform'] == 'android'
- is_javascript = env['platform'] == 'javascript'
+ bits = env["bits"]
+ is_android = env["platform"] == "android"
+ is_javascript = env["platform"] == "javascript"
+ is_ios = env["platform"] == "iphone"
+ is_ios_sim = is_ios and env["arch"] in ["x86", "x86_64"]
- tools_enabled = env['tools']
- mono_static = env['mono_static']
- copy_mono_root = env['copy_mono_root']
+ tools_enabled = env["tools"]
+ mono_static = env["mono_static"]
+ copy_mono_root = env["copy_mono_root"]
- mono_prefix = env['mono_prefix']
+ mono_prefix = env["mono_prefix"]
- mono_lib_names = ['mono-2.0-sgen', 'monosgen-2.0']
+ mono_lib_names = ["mono-2.0-sgen", "monosgen-2.0"]
- is_travis = os.environ.get('TRAVIS') == 'true'
+ is_travis = os.environ.get("TRAVIS") == "true"
if is_travis:
# Travis CI may have a Mono version lower than 5.12
- env_mono.Append(CPPDEFINES=['NO_PENDING_EXCEPTIONS'])
+ 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 the specified \'android_arch\': ' + env['android_arch'])
+ if is_android and not env["android_arch"] in android_arch_dirs:
+ raise RuntimeError("This module does not support the specified 'android_arch': " + env["android_arch"])
- if tools_enabled and not module_supports_tools_on(env['platform']):
+ 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')
+ raise RuntimeError("This module does not currently support building for this platform with tools enabled")
if is_android and mono_static:
- # 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')
+ # FIXME: When static linking and doing something that requires libmono-native, we get a dlopen error as 'libmono-native'
+ # seems to depend on 'libmonosgen-2.0'. Could be fixed by re-directing to '__Internal' with a dllmap or in the dlopen hook.
+ raise RuntimeError("Statically linking Mono is not currently supported for this platform")
+
+ if not mono_static and (is_javascript or is_ios):
+ raise RuntimeError("Dynamically linking Mono is not currently supported for this platform")
+
+ if not mono_prefix and (os.getenv("MONO32_PREFIX") or os.getenv("MONO64_PREFIX")):
+ print(
+ "WARNING: The environment variables 'MONO32_PREFIX' and 'MONO64_PREFIX' are deprecated; use the 'mono_prefix' SCons parameter instead"
+ )
+
+ # Although we don't support building with tools for any platform where we currently use static AOT,
+ # if these are supported in the future, we won't be using static AOT for them as that would be
+ # too restrictive for the editor. These builds would probably be made to only use the interpreter.
+ mono_aot_static = (is_ios and not is_ios_sim) and not env["tools"]
- if is_javascript:
- mono_static = True
+ # Static AOT is only supported on the root domain
+ mono_single_appdomain = mono_aot_static
- 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 mono_single_appdomain:
+ env_mono.Append(CPPDEFINES=["GD_MONO_SINGLE_APPDOMAIN"])
- if env['platform'] == 'windows':
+ if (env["tools"] or env["target"] != "release") and not mono_single_appdomain:
+ env_mono.Append(CPPDEFINES=["GD_MONO_HOT_RELOAD"])
+
+ if env["platform"] == "windows":
mono_root = mono_prefix
- if not mono_root and os.name == 'nt':
+ if not mono_root and os.name == "nt":
mono_root = monoreg.find_mono_root_dir(bits)
if not mono_root:
- raise RuntimeError("Mono installation directory not found; specify one manually with the 'mono_prefix' SCons parameter")
+ raise RuntimeError(
+ "Mono installation directory not found; specify one manually with the 'mono_prefix' SCons parameter"
+ )
- print('Found Mono root directory: ' + mono_root)
+ print("Found Mono root directory: " + mono_root)
- mono_lib_path = os.path.join(mono_root, 'lib')
+ mono_lib_path = os.path.join(mono_root, "lib")
env.Append(LIBPATH=mono_lib_path)
- env_mono.Prepend(CPPPATH=os.path.join(mono_root, 'include', 'mono-2.0'))
+ env_mono.Prepend(CPPPATH=os.path.join(mono_root, "include", "mono-2.0"))
+
+ lib_suffixes = [".lib"]
- lib_suffix = Environment()['LIBSUFFIX']
+ if not env.msvc:
+ # MingW supports both '.a' and '.lib'
+ lib_suffixes.insert(0, ".a")
if mono_static:
if env.msvc:
- mono_static_lib_name = 'libmono-static-sgen'
+ mono_static_lib_name = "libmono-static-sgen"
else:
- mono_static_lib_name = 'libmonosgen-2.0'
+ mono_static_lib_name = "libmonosgen-2.0"
- if not os.path.isfile(os.path.join(mono_lib_path, mono_static_lib_name + lib_suffix)):
- raise RuntimeError('Could not find static mono library in: ' + mono_lib_path)
+ mono_static_lib_file = find_file_in_dir(mono_lib_path, [mono_static_lib_name], extensions=lib_suffixes)
+
+ if not mono_static_lib_file:
+ raise RuntimeError("Could not find static mono library in: " + mono_lib_path)
if env.msvc:
- env.Append(LINKFLAGS=mono_static_lib_name + lib_suffix)
+ env.Append(LINKFLAGS=mono_static_lib_file)
- env.Append(LINKFLAGS='Mincore' + lib_suffix)
- env.Append(LINKFLAGS='msvcrt' + lib_suffix)
- env.Append(LINKFLAGS='LIBCMT' + lib_suffix)
- env.Append(LINKFLAGS='Psapi' + lib_suffix)
+ env.Append(LINKFLAGS="Mincore.lib")
+ env.Append(LINKFLAGS="msvcrt.lib")
+ env.Append(LINKFLAGS="LIBCMT.lib")
+ env.Append(LINKFLAGS="Psapi.lib")
else:
- env.Append(LINKFLAGS=os.path.join(mono_lib_path, mono_static_lib_name + lib_suffix))
+ mono_static_lib_file_path = os.path.join(mono_lib_path, mono_static_lib_file)
+ env.Append(LINKFLAGS=["-Wl,-whole-archive", mono_static_lib_file_path, "-Wl,-no-whole-archive"])
- env.Append(LIBS=['psapi'])
- env.Append(LIBS=['version'])
+ env.Append(LIBS=["psapi"])
+ env.Append(LIBS=["version"])
else:
- mono_lib_name = find_file_in_dir(mono_lib_path, mono_lib_names, extension=lib_suffix)
+ mono_lib_name = find_name_in_dir_files(
+ mono_lib_path, mono_lib_names, prefixes=["", "lib"], extensions=lib_suffixes
+ )
if not mono_lib_name:
- raise RuntimeError('Could not find mono library in: ' + mono_lib_path)
+ raise RuntimeError("Could not find mono library in: " + mono_lib_path)
if env.msvc:
- env.Append(LINKFLAGS=mono_lib_name + lib_suffix)
+ env.Append(LINKFLAGS=mono_lib_name + ".lib")
else:
env.Append(LIBS=[mono_lib_name])
- mono_bin_path = os.path.join(mono_root, 'bin')
+ mono_bin_path = os.path.join(mono_root, "bin")
- mono_dll_name = find_file_in_dir(mono_bin_path, mono_lib_names, extension='.dll')
+ mono_dll_file = find_file_in_dir(mono_bin_path, mono_lib_names, prefixes=["", "lib"], extensions=[".dll"])
- if not mono_dll_name:
- raise RuntimeError('Could not find mono shared library in: ' + mono_bin_path)
+ if not mono_dll_file:
+ raise RuntimeError("Could not find mono shared library in: " + mono_bin_path)
- copy_file(mono_bin_path, '#bin', mono_dll_name + '.dll')
+ copy_file(mono_bin_path, "#bin", mono_dll_file)
else:
- is_apple = env['platform'] in ['osx', 'iphone']
+ is_apple = env["platform"] in ["osx", "iphone"]
+ is_macos = is_apple and not is_ios
- sharedlib_ext = '.dylib' if is_apple else '.so'
+ sharedlib_ext = ".dylib" if is_apple else ".so"
mono_root = mono_prefix
- mono_lib_path = ''
- mono_so_name = ''
+ mono_lib_path = ""
+ mono_so_file = ""
- 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_android or is_javascript or is_ios):
+ raise RuntimeError(
+ "Mono installation directory not found; specify one manually with the 'mono_prefix' SCons parameter"
+ )
- if not mono_root and is_apple:
+ if not mono_root and is_macos:
# Try with some known directories under OSX
- hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current', '/usr/local/var/homebrew/linked/mono']
+ hint_dirs = ["/Library/Frameworks/Mono.framework/Versions/Current", "/usr/local/var/homebrew/linked/mono"]
for hint_dir in hint_dirs:
if os.path.isdir(hint_dir):
mono_root = hint_dir
@@ -187,126 +239,165 @@ def configure(env, env_mono):
if not mono_root and mono_static:
mono_root = pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext)
if not mono_root:
- raise RuntimeError("Building with mono_static=yes, but failed to find the mono prefix with pkg-config; " + \
- "specify one manually with the 'mono_prefix' SCons parameter")
+ raise RuntimeError(
+ "Building with mono_static=yes, but failed to find the mono prefix with pkg-config; "
+ + "specify one manually with the 'mono_prefix' SCons parameter"
+ )
+
+ if is_ios and not is_ios_sim:
+ env_mono.Append(CPPDEFINES=["IOS_DEVICE"])
if mono_root:
- print('Found Mono root directory: ' + mono_root)
+ print("Found Mono root directory: " + mono_root)
- mono_lib_path = os.path.join(mono_root, 'lib')
+ mono_lib_path = os.path.join(mono_root, "lib")
env.Append(LIBPATH=[mono_lib_path])
- env_mono.Prepend(CPPPATH=os.path.join(mono_root, 'include', 'mono-2.0'))
+ 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')
+ mono_lib = find_name_in_dir_files(mono_lib_path, mono_lib_names, prefixes=["lib"], extensions=[".a"])
if not mono_lib:
- raise RuntimeError('Could not find mono library in: ' + mono_lib_path)
+ raise RuntimeError("Could not find mono library in: " + mono_lib_path)
- env_mono.Append(CPPDEFINES=['_REENTRANT'])
+ env_mono.Append(CPPDEFINES=["_REENTRANT"])
if mono_static:
- env.Append(LINKFLAGS=['-rdynamic'])
+ env.Append(LINKFLAGS=["-rdynamic"])
- mono_lib_file = os.path.join(mono_lib_path, 'lib' + mono_lib + '.a')
+ 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])
+ if is_macos:
+ env.Append(LINKFLAGS=["-Wl,-force_load," + mono_lib_file])
+ else:
+ arch = env["arch"]
+
+ def copy_mono_lib(libname_wo_ext):
+ copy_file(
+ mono_lib_path, "#bin", libname_wo_ext + ".a", "%s.iphone.%s.a" % (libname_wo_ext, arch)
+ )
+
+ # Copy Mono libraries to the output folder. These are meant to be bundled with
+ # the export templates and added to the Xcode project when exporting a game.
+ copy_mono_lib("lib" + mono_lib)
+ copy_mono_lib("libmono-native")
+ copy_mono_lib("libmono-profiler-log")
+
+ if not is_ios_sim:
+ copy_mono_lib("libmono-ee-interp")
+ copy_mono_lib("libmono-icall-table")
+ copy_mono_lib("libmono-ilgen")
else:
- assert is_desktop(env['platform']) or is_android or is_javascript
- env.Append(LINKFLAGS=['-Wl,-whole-archive', mono_lib_file, '-Wl,-no-whole-archive'])
+ 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'])
+ env.Append(LIBS=["mono-icall-table", "mono-native", "mono-ilgen", "mono-ee-interp"])
- wasm_src_dir = os.path.join(mono_root, 'src')
+ 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')
+ 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')
- ])
+ env_mono.Append(CPPDEFINES=["CORE_BINDINGS"])
+
+ env_mono.add_source_files(
+ env.modules_sources,
+ [
+ os.path.join(wasm_src_dir, "driver.c"),
+ os.path.join(wasm_src_dir, "zlib-helper.c"),
+ os.path.join(wasm_src_dir, "corebindings.c"),
+ ],
+ )
+
+ env.Append(
+ LINKFLAGS=[
+ "--js-library",
+ os.path.join(wasm_src_dir, "library_mono.js"),
+ "--js-library",
+ os.path.join(wasm_src_dir, "binding_support.js"),
+ "--js-library",
+ os.path.join(wasm_src_dir, "dotnet_support.js"),
+ ]
+ )
else:
env.Append(LIBS=[mono_lib])
- if is_apple:
- env.Append(LIBS=['iconv', 'pthread'])
+ if is_macos:
+ env.Append(LIBS=["iconv", "pthread"])
elif is_android:
- pass # Nothing
+ pass # Nothing
+ elif is_ios:
+ pass # Nothing, linking is delegated to the exported Xcode project
elif is_javascript:
- env.Append(LIBS=['m', 'rt', 'dl', 'pthread'])
+ env.Append(LIBS=["m", "rt", "dl", "pthread"])
else:
- env.Append(LIBS=['m', 'rt', 'dl', 'pthread'])
+ env.Append(LIBS=["m", "rt", "dl", "pthread"])
if not mono_static:
- mono_so_name = find_file_in_dir(mono_lib_path, mono_lib_names, prefix='lib', extension=sharedlib_ext)
+ mono_so_file = find_file_in_dir(
+ mono_lib_path, mono_lib_names, prefixes=["lib"], extensions=[sharedlib_ext]
+ )
- if not mono_so_name:
- raise RuntimeError('Could not find mono shared library in: ' + mono_lib_path)
-
- copy_file(mono_lib_path, '#bin', 'lib' + mono_so_name + sharedlib_ext)
+ if not mono_so_file:
+ raise RuntimeError("Could not find mono shared library in: " + mono_lib_path)
else:
assert not mono_static
# TODO: Add option to force using pkg-config
- print('Mono root directory not found. Using pkg-config instead')
+ print("Mono root directory not found. Using pkg-config instead")
- env.ParseConfig('pkg-config monosgen-2 --libs')
- env_mono.ParseConfig('pkg-config monosgen-2 --cflags')
+ env.ParseConfig("pkg-config monosgen-2 --libs")
+ env_mono.ParseConfig("pkg-config monosgen-2 --cflags")
tmpenv = Environment()
- tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH'))
- tmpenv.ParseConfig('pkg-config monosgen-2 --libs-only-L')
+ tmpenv.AppendENVPath("PKG_CONFIG_PATH", os.getenv("PKG_CONFIG_PATH"))
+ tmpenv.ParseConfig("pkg-config monosgen-2 --libs-only-L")
- for hint_dir in tmpenv['LIBPATH']:
- name_found = find_file_in_dir(hint_dir, mono_lib_names, prefix='lib', extension=sharedlib_ext)
- if name_found:
+ for hint_dir in tmpenv["LIBPATH"]:
+ file_found = find_file_in_dir(hint_dir, mono_lib_names, prefixes=["lib"], extensions=[sharedlib_ext])
+ if file_found:
mono_lib_path = hint_dir
- mono_so_name = name_found
+ mono_so_file = file_found
break
- if not mono_so_name:
- raise RuntimeError('Could not find mono shared library in: ' + str(tmpenv['LIBPATH']))
+ if not mono_so_file:
+ raise RuntimeError("Could not find mono shared library in: " + str(tmpenv["LIBPATH"]))
if not mono_static:
- libs_output_dir = get_android_out_dir(env) if is_android else '#bin'
- copy_file(mono_lib_path, libs_output_dir, 'lib' + mono_so_name + sharedlib_ext)
+ libs_output_dir = get_android_out_dir(env) if is_android else "#bin"
+ copy_file(mono_lib_path, libs_output_dir, mono_so_file)
if not tools_enabled:
- if is_desktop(env['platform']):
+ if is_desktop(env["platform"]):
if not mono_root:
- mono_root = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip()
+ mono_root = (
+ subprocess.check_output(["pkg-config", "mono-2", "--variable=prefix"]).decode("utf8").strip()
+ )
make_template_dir(env, mono_root)
elif is_android:
# Compress Android Mono Config
from . import make_android_mono_config
+
module_dir = os.getcwd()
- config_file_path = os.path.join(module_dir, 'build_scripts', 'mono_android_config.xml')
- make_android_mono_config.generate_compressed_config(config_file_path, 'mono_gd/')
+ config_file_path = os.path.join(module_dir, "build_scripts", "mono_android_config.xml")
+ make_android_mono_config.generate_compressed_config(config_file_path, "mono_gd/")
# Copy the required shared libraries
copy_mono_shared_libs(env, mono_root, None)
elif is_javascript:
- pass # No data directory for this platform
+ pass # No data directory for this platform
+ elif is_ios:
+ pass # No data directory for this platform
if copy_mono_root:
if not mono_root:
- mono_root = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip()
+ mono_root = subprocess.check_output(["pkg-config", "mono-2", "--variable=prefix"]).decode("utf8").strip()
if tools_enabled:
- copy_mono_root_files(env, mono_root)
+ copy_mono_root_files(env, mono_root)
else:
print("Ignoring option: 'copy_mono_root'; only available for builds with 'tools' enabled.")
@@ -314,26 +405,26 @@ def configure(env, env_mono):
def make_template_dir(env, mono_root):
from shutil import rmtree
- platform = env['platform']
- target = env['target']
+ platform = env["platform"]
+ target = env["target"]
- template_dir_name = ''
+ template_dir_name = ""
assert is_desktop(platform)
- template_dir_name = 'data.mono.%s.%s.%s' % (platform, env['bits'], target)
+ template_dir_name = "data.mono.%s.%s.%s" % (platform, env["bits"], target)
- output_dir = Dir('#bin').abspath
+ output_dir = Dir("#bin").abspath
template_dir = os.path.join(output_dir, template_dir_name)
- template_mono_root_dir = os.path.join(template_dir, 'Mono')
+ template_mono_root_dir = os.path.join(template_dir, "Mono")
if os.path.isdir(template_mono_root_dir):
- rmtree(template_mono_root_dir) # Clean first
+ rmtree(template_mono_root_dir) # Clean first
# Copy etc/mono/
- template_mono_config_dir = os.path.join(template_mono_root_dir, 'etc', 'mono')
+ template_mono_config_dir = os.path.join(template_mono_root_dir, "etc", "mono")
copy_mono_etc_dir(mono_root, template_mono_config_dir, platform)
# Copy the required shared libraries
@@ -347,18 +438,18 @@ def copy_mono_root_files(env, mono_root):
from shutil import rmtree
if not mono_root:
- raise RuntimeError('Mono installation directory not found')
+ raise RuntimeError("Mono installation directory not found")
- output_dir = Dir('#bin').abspath
- editor_mono_root_dir = os.path.join(output_dir, 'GodotSharp', 'Mono')
+ output_dir = Dir("#bin").abspath
+ editor_mono_root_dir = os.path.join(output_dir, "GodotSharp", "Mono")
if os.path.isdir(editor_mono_root_dir):
- rmtree(editor_mono_root_dir) # Clean first
+ rmtree(editor_mono_root_dir) # Clean first
# Copy etc/mono/
- editor_mono_config_dir = os.path.join(editor_mono_root_dir, 'etc', 'mono')
- copy_mono_etc_dir(mono_root, editor_mono_config_dir, env['platform'])
+ editor_mono_config_dir = os.path.join(editor_mono_root_dir, "etc", "mono")
+ copy_mono_etc_dir(mono_root, editor_mono_config_dir, env["platform"])
# Copy the required shared libraries
@@ -366,20 +457,20 @@ def copy_mono_root_files(env, mono_root):
# Copy framework assemblies
- mono_framework_dir = os.path.join(mono_root, 'lib', 'mono', '4.5')
- mono_framework_facades_dir = os.path.join(mono_framework_dir, 'Facades')
+ mono_framework_dir = os.path.join(mono_root, "lib", "mono", "4.5")
+ mono_framework_facades_dir = os.path.join(mono_framework_dir, "Facades")
- editor_mono_framework_dir = os.path.join(editor_mono_root_dir, 'lib', 'mono', '4.5')
- editor_mono_framework_facades_dir = os.path.join(editor_mono_framework_dir, 'Facades')
+ editor_mono_framework_dir = os.path.join(editor_mono_root_dir, "lib", "mono", "4.5")
+ editor_mono_framework_facades_dir = os.path.join(editor_mono_framework_dir, "Facades")
if not os.path.isdir(editor_mono_framework_dir):
os.makedirs(editor_mono_framework_dir)
if not os.path.isdir(editor_mono_framework_facades_dir):
os.makedirs(editor_mono_framework_facades_dir)
- for assembly in glob(os.path.join(mono_framework_dir, '*.dll')):
+ for assembly in glob(os.path.join(mono_framework_dir, "*.dll")):
copy(assembly, editor_mono_framework_dir)
- for assembly in glob(os.path.join(mono_framework_facades_dir, '*.dll')):
+ for assembly in glob(os.path.join(mono_framework_facades_dir, "*.dll")):
copy(assembly, editor_mono_framework_facades_dir)
@@ -391,28 +482,28 @@ def copy_mono_etc_dir(mono_root, target_mono_config_dir, platform):
if not os.path.isdir(target_mono_config_dir):
os.makedirs(target_mono_config_dir)
- mono_etc_dir = os.path.join(mono_root, 'etc', 'mono')
+ mono_etc_dir = os.path.join(mono_root, "etc", "mono")
if not os.path.isdir(mono_etc_dir):
- mono_etc_dir = ''
+ mono_etc_dir = ""
etc_hint_dirs = []
- if platform != 'windows':
- etc_hint_dirs += ['/etc/mono', '/usr/local/etc/mono']
- if 'MONO_CFG_DIR' in os.environ:
- etc_hint_dirs += [os.path.join(os.environ['MONO_CFG_DIR'], 'mono')]
+ if platform != "windows":
+ etc_hint_dirs += ["/etc/mono", "/usr/local/etc/mono"]
+ if "MONO_CFG_DIR" in os.environ:
+ etc_hint_dirs += [os.path.join(os.environ["MONO_CFG_DIR"], "mono")]
for etc_hint_dir in etc_hint_dirs:
if os.path.isdir(etc_hint_dir):
mono_etc_dir = etc_hint_dir
break
if not mono_etc_dir:
- raise RuntimeError('Mono installation etc directory not found')
+ raise RuntimeError("Mono installation etc directory not found")
- copy_tree(os.path.join(mono_etc_dir, '2.0'), os.path.join(target_mono_config_dir, '2.0'))
- copy_tree(os.path.join(mono_etc_dir, '4.0'), os.path.join(target_mono_config_dir, '4.0'))
- copy_tree(os.path.join(mono_etc_dir, '4.5'), os.path.join(target_mono_config_dir, '4.5'))
- if os.path.isdir(os.path.join(mono_etc_dir, 'mconfig')):
- copy_tree(os.path.join(mono_etc_dir, 'mconfig'), os.path.join(target_mono_config_dir, 'mconfig'))
+ copy_tree(os.path.join(mono_etc_dir, "2.0"), os.path.join(target_mono_config_dir, "2.0"))
+ copy_tree(os.path.join(mono_etc_dir, "4.0"), os.path.join(target_mono_config_dir, "4.0"))
+ copy_tree(os.path.join(mono_etc_dir, "4.5"), os.path.join(target_mono_config_dir, "4.5"))
+ if os.path.isdir(os.path.join(mono_etc_dir, "mconfig")):
+ copy_tree(os.path.join(mono_etc_dir, "mconfig"), os.path.join(target_mono_config_dir, "mconfig"))
- for file in glob(os.path.join(mono_etc_dir, '*')):
+ for file in glob(os.path.join(mono_etc_dir, "*")):
if os.path.isfile(file):
copy(file, target_mono_config_dir)
@@ -424,48 +515,66 @@ def copy_mono_shared_libs(env, mono_root, target_mono_root_dir):
if os.path.isfile(src):
copy(src, dst)
- platform = env['platform']
+ 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 platform == "windows":
+ src_mono_bin_dir = os.path.join(mono_root, "bin")
+ target_mono_bin_dir = os.path.join(target_mono_root_dir, "bin")
if not os.path.isdir(target_mono_bin_dir):
os.makedirs(target_mono_bin_dir)
- mono_posix_helper_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'))
+ mono_posix_helper_file = find_file_in_dir(
+ src_mono_bin_dir, ["MonoPosixHelper"], prefixes=["", "lib"], extensions=[".dll"]
+ )
+ copy(
+ os.path.join(src_mono_bin_dir, mono_posix_helper_file),
+ os.path.join(target_mono_bin_dir, "MonoPosixHelper.dll"),
+ )
# For newer versions
- btls_dll_path = os.path.join(src_mono_bin_dir, 'libmono-btls-shared.dll')
+ 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')
+ target_mono_lib_dir = (
+ get_android_out_dir(env) if platform == "android" else os.path.join(target_mono_root_dir, "lib")
+ )
if not os.path.isdir(target_mono_lib_dir):
os.makedirs(target_mono_lib_dir)
lib_file_names = []
- if platform == 'osx':
- lib_file_names = [lib_name + '.dylib' for lib_name in [
- 'libmono-btls-shared', 'libmono-native-compat', 'libMonoPosixHelper'
- ]]
+ if platform == "osx":
+ lib_file_names = [
+ lib_name + ".dylib"
+ for lib_name in ["libmono-btls-shared", "libmono-native-compat", "libMonoPosixHelper"]
+ ]
elif is_unix_like(platform):
- lib_file_names = [lib_name + '.so' for lib_name in [
- 'libmono-btls-shared', 'libmono-ee-interp', 'libmono-native', 'libMonoPosixHelper',
- 'libmono-profiler-aot', 'libmono-profiler-coverage', 'libmono-profiler-log', 'libMonoSupportW'
- ]]
+ lib_file_names = [
+ lib_name + ".so"
+ for lib_name in [
+ "libmono-btls-shared",
+ "libmono-ee-interp",
+ "libmono-native",
+ "libMonoPosixHelper",
+ "libmono-profiler-aot",
+ "libmono-profiler-coverage",
+ "libmono-profiler-log",
+ "libMonoSupportW",
+ ]
+ ]
for lib_file_name in lib_file_names:
- copy_if_exists(os.path.join(mono_root, 'lib', lib_file_name), target_mono_lib_dir)
+ copy_if_exists(os.path.join(mono_root, "lib", lib_file_name), target_mono_lib_dir)
+
def pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext):
tmpenv = Environment()
- tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH'))
- tmpenv.ParseConfig('pkg-config monosgen-2 --libs-only-L')
- for hint_dir in tmpenv['LIBPATH']:
- name_found = find_file_in_dir(hint_dir, mono_lib_names, prefix='lib', extension=sharedlib_ext)
- if name_found and os.path.isdir(os.path.join(hint_dir, '..', 'include', 'mono-2.0')):
- return os.path.join(hint_dir, '..')
- return ''
+ tmpenv.AppendENVPath("PKG_CONFIG_PATH", os.getenv("PKG_CONFIG_PATH"))
+ tmpenv.ParseConfig("pkg-config monosgen-2 --libs-only-L")
+ for hint_dir in tmpenv["LIBPATH"]:
+ name_found = find_name_in_dir_files(hint_dir, mono_lib_names, prefixes=["lib"], extensions=[sharedlib_ext])
+ if name_found and os.path.isdir(os.path.join(hint_dir, "..", "include", "mono-2.0")):
+ return os.path.join(hint_dir, "..")
+ return ""
diff --git a/modules/mono/build_scripts/mono_reg_utils.py b/modules/mono/build_scripts/mono_reg_utils.py
index b2c48f0a61..3090a4759a 100644
--- a/modules/mono/build_scripts/mono_reg_utils.py
+++ b/modules/mono/build_scripts/mono_reg_utils.py
@@ -1,21 +1,16 @@
import os
import platform
-from compat import decode_utf8
-
-if os.name == 'nt':
+if os.name == "nt":
import sys
- if sys.version_info < (3,):
- import _winreg as winreg
- else:
- import winreg
+ import winreg
def _reg_open_key(key, subkey):
try:
return winreg.OpenKey(key, subkey)
except (WindowsError, OSError):
- if platform.architecture()[0] == '32bit':
+ if platform.architecture()[0] == "32bit":
bitness_sam = winreg.KEY_WOW64_64KEY
else:
bitness_sam = winreg.KEY_WOW64_32KEY
@@ -25,12 +20,12 @@ def _reg_open_key(key, subkey):
def _reg_open_key_bits(key, subkey, bits):
sam = winreg.KEY_READ
- if platform.architecture()[0] == '32bit':
- if bits == '64':
+ if platform.architecture()[0] == "32bit":
+ if bits == "64":
# Force 32bit process to search in 64bit registry
sam |= winreg.KEY_WOW64_64KEY
else:
- if bits == '32':
+ if bits == "32":
# Force 64bit process to search in 32bit registry
sam |= winreg.KEY_WOW64_32KEY
@@ -40,7 +35,7 @@ def _reg_open_key_bits(key, subkey, bits):
def _find_mono_in_reg(subkey, bits):
try:
with _reg_open_key_bits(winreg.HKEY_LOCAL_MACHINE, subkey, bits) as hKey:
- value = winreg.QueryValueEx(hKey, 'SdkInstallRoot')[0]
+ value = winreg.QueryValueEx(hKey, "SdkInstallRoot")[0]
return value
except (WindowsError, OSError):
return None
@@ -49,70 +44,70 @@ def _find_mono_in_reg(subkey, bits):
def _find_mono_in_reg_old(subkey, bits):
try:
with _reg_open_key_bits(winreg.HKEY_LOCAL_MACHINE, subkey, bits) as hKey:
- default_clr = winreg.QueryValueEx(hKey, 'DefaultCLR')[0]
+ default_clr = winreg.QueryValueEx(hKey, "DefaultCLR")[0]
if default_clr:
- return _find_mono_in_reg(subkey + '\\' + default_clr, bits)
+ return _find_mono_in_reg(subkey + "\\" + default_clr, bits)
return None
except (WindowsError, EnvironmentError):
return None
def find_mono_root_dir(bits):
- root_dir = _find_mono_in_reg(r'SOFTWARE\Mono', bits)
+ root_dir = _find_mono_in_reg(r"SOFTWARE\Mono", bits)
if root_dir is not None:
return str(root_dir)
- root_dir = _find_mono_in_reg_old(r'SOFTWARE\Novell\Mono', bits)
+ root_dir = _find_mono_in_reg_old(r"SOFTWARE\Novell\Mono", bits)
if root_dir is not None:
return str(root_dir)
- return ''
+ return ""
def find_msbuild_tools_path_reg():
import subprocess
- vswhere = os.getenv('PROGRAMFILES(X86)')
+ vswhere = os.getenv("PROGRAMFILES(X86)")
if not vswhere:
- vswhere = os.getenv('PROGRAMFILES')
- vswhere += r'\Microsoft Visual Studio\Installer\vswhere.exe'
+ vswhere = os.getenv("PROGRAMFILES")
+ vswhere += r"\Microsoft Visual Studio\Installer\vswhere.exe"
- vswhere_args = ['-latest', '-products', '*', '-requires', 'Microsoft.Component.MSBuild']
+ vswhere_args = ["-latest", "-products", "*", "-requires", "Microsoft.Component.MSBuild"]
try:
lines = subprocess.check_output([vswhere] + vswhere_args).splitlines()
for line in lines:
- parts = decode_utf8(line).split(':', 1)
+ parts = line.decode("utf-8").split(":", 1)
- if len(parts) < 2 or parts[0] != 'installationPath':
+ if len(parts) < 2 or parts[0] != "installationPath":
continue
val = parts[1].strip()
if not val:
- raise ValueError('Value of `installationPath` entry is empty')
+ raise ValueError("Value of `installationPath` entry is empty")
# Since VS2019, the directory is simply named "Current"
- msbuild_dir = os.path.join(val, 'MSBuild\\Current\\Bin')
+ msbuild_dir = os.path.join(val, "MSBuild\\Current\\Bin")
if os.path.isdir(msbuild_dir):
return msbuild_dir
# Directory name "15.0" is used in VS 2017
- return os.path.join(val, 'MSBuild\\15.0\\Bin')
+ return os.path.join(val, "MSBuild\\15.0\\Bin")
- raise ValueError('Cannot find `installationPath` entry')
+ raise ValueError("Cannot find `installationPath` entry")
except ValueError as e:
- print('Error reading output from vswhere: ' + e.message)
+ print("Error reading output from vswhere: " + e.message)
except WindowsError:
- pass # Fine, vswhere not found
+ pass # Fine, vswhere not found
except (subprocess.CalledProcessError, OSError):
pass
# Try to find 14.0 in the Registry
try:
- subkey = r'SOFTWARE\Microsoft\MSBuild\ToolsVersions\14.0'
+ subkey = r"SOFTWARE\Microsoft\MSBuild\ToolsVersions\14.0"
with _reg_open_key(winreg.HKEY_LOCAL_MACHINE, subkey) as hKey:
- value = winreg.QueryValueEx(hKey, 'MSBuildToolsPath')[0]
+ value = winreg.QueryValueEx(hKey, "MSBuildToolsPath")[0]
return value
except (WindowsError, OSError):
- return ''
+ return ""
diff --git a/modules/mono/build_scripts/solution_builder.py b/modules/mono/build_scripts/solution_builder.py
index d1529a64d2..db6b4ff7aa 100644
--- a/modules/mono/build_scripts/solution_builder.py
+++ b/modules/mono/build_scripts/solution_builder.py
@@ -1,4 +1,3 @@
-
import os
@@ -8,35 +7,38 @@ verbose = False
def find_nuget_unix():
import os
- if 'NUGET_PATH' in os.environ:
- hint_path = os.environ['NUGET_PATH']
+ if "NUGET_PATH" in os.environ:
+ hint_path = os.environ["NUGET_PATH"]
if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
return hint_path
- hint_path = os.path.join(hint_path, 'nuget')
+ hint_path = os.path.join(hint_path, "nuget")
if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
return hint_path
import os.path
import sys
- hint_dirs = ['/opt/novell/mono/bin']
- if sys.platform == 'darwin':
- hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current/bin', '/usr/local/var/homebrew/linked/mono/bin'] + hint_dirs
+ hint_dirs = ["/opt/novell/mono/bin"]
+ if sys.platform == "darwin":
+ hint_dirs = [
+ "/Library/Frameworks/Mono.framework/Versions/Current/bin",
+ "/usr/local/var/homebrew/linked/mono/bin",
+ ] + hint_dirs
for hint_dir in hint_dirs:
- hint_path = os.path.join(hint_dir, 'nuget')
+ hint_path = os.path.join(hint_dir, "nuget")
if os.path.isfile(hint_path):
return hint_path
- elif os.path.isfile(hint_path + '.exe'):
- return hint_path + '.exe'
+ elif os.path.isfile(hint_path + ".exe"):
+ return hint_path + ".exe"
- for hint_dir in os.environ['PATH'].split(os.pathsep):
+ for hint_dir in os.environ["PATH"].split(os.pathsep):
hint_dir = hint_dir.strip('"')
- hint_path = os.path.join(hint_dir, 'nuget')
+ hint_path = os.path.join(hint_dir, "nuget")
if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
return hint_path
- if os.path.isfile(hint_path + '.exe') and os.access(hint_path + '.exe', os.X_OK):
- return hint_path + '.exe'
+ if os.path.isfile(hint_path + ".exe") and os.access(hint_path + ".exe", os.X_OK):
+ return hint_path + ".exe"
return None
@@ -44,30 +46,30 @@ def find_nuget_unix():
def find_nuget_windows(env):
import os
- if 'NUGET_PATH' in os.environ:
- hint_path = os.environ['NUGET_PATH']
+ if "NUGET_PATH" in os.environ:
+ hint_path = os.environ["NUGET_PATH"]
if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
return hint_path
- hint_path = os.path.join(hint_path, 'nuget.exe')
+ hint_path = os.path.join(hint_path, "nuget.exe")
if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
return hint_path
- from . mono_reg_utils import find_mono_root_dir
+ from .mono_reg_utils import find_mono_root_dir
- mono_root = env['mono_prefix'] or find_mono_root_dir(env['bits'])
+ mono_root = env["mono_prefix"] or find_mono_root_dir(env["bits"])
if mono_root:
- mono_bin_dir = os.path.join(mono_root, 'bin')
- nuget_mono = os.path.join(mono_bin_dir, 'nuget.bat')
+ mono_bin_dir = os.path.join(mono_root, "bin")
+ nuget_mono = os.path.join(mono_bin_dir, "nuget.bat")
if os.path.isfile(nuget_mono):
return nuget_mono
# Standalone NuGet
- for hint_dir in os.environ['PATH'].split(os.pathsep):
+ for hint_dir in os.environ["PATH"].split(os.pathsep):
hint_dir = hint_dir.strip('"')
- hint_path = os.path.join(hint_dir, 'nuget.exe')
+ hint_path = os.path.join(hint_dir, "nuget.exe")
if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
return hint_path
@@ -78,52 +80,55 @@ def find_msbuild_unix(filename):
import os.path
import sys
- hint_dirs = ['/opt/novell/mono/bin']
- if sys.platform == 'darwin':
- hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current/bin', '/usr/local/var/homebrew/linked/mono/bin'] + hint_dirs
+ hint_dirs = ["/opt/novell/mono/bin"]
+ if sys.platform == "darwin":
+ hint_dirs = [
+ "/Library/Frameworks/Mono.framework/Versions/Current/bin",
+ "/usr/local/var/homebrew/linked/mono/bin",
+ ] + hint_dirs
for hint_dir in hint_dirs:
hint_path = os.path.join(hint_dir, filename)
if os.path.isfile(hint_path):
return hint_path
- elif os.path.isfile(hint_path + '.exe'):
- return hint_path + '.exe'
+ elif os.path.isfile(hint_path + ".exe"):
+ return hint_path + ".exe"
- for hint_dir in os.environ['PATH'].split(os.pathsep):
+ for hint_dir in os.environ["PATH"].split(os.pathsep):
hint_dir = hint_dir.strip('"')
hint_path = os.path.join(hint_dir, filename)
if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
return hint_path
- if os.path.isfile(hint_path + '.exe') and os.access(hint_path + '.exe', os.X_OK):
- return hint_path + '.exe'
+ if os.path.isfile(hint_path + ".exe") and os.access(hint_path + ".exe", os.X_OK):
+ return hint_path + ".exe"
return None
def find_msbuild_windows(env):
- from . mono_reg_utils import find_mono_root_dir, find_msbuild_tools_path_reg
+ from .mono_reg_utils import find_mono_root_dir, find_msbuild_tools_path_reg
- mono_root = env['mono_prefix'] or find_mono_root_dir(env['bits'])
+ mono_root = env["mono_prefix"] or find_mono_root_dir(env["bits"])
if not mono_root:
- raise RuntimeError('Cannot find mono root directory')
+ raise RuntimeError("Cannot find mono root directory")
- mono_bin_dir = os.path.join(mono_root, 'bin')
- msbuild_mono = os.path.join(mono_bin_dir, 'msbuild.bat')
+ mono_bin_dir = os.path.join(mono_root, "bin")
+ msbuild_mono = os.path.join(mono_bin_dir, "msbuild.bat")
msbuild_tools_path = find_msbuild_tools_path_reg()
if msbuild_tools_path:
- return (os.path.join(msbuild_tools_path, 'MSBuild.exe'), {})
+ return (os.path.join(msbuild_tools_path, "MSBuild.exe"), {})
if os.path.isfile(msbuild_mono):
# The (Csc/Vbc/Fsc)ToolExe environment variables are required when
# building with Mono's MSBuild. They must point to the batch files
# in Mono's bin directory to make sure they are executed with Mono.
mono_msbuild_env = {
- 'CscToolExe': os.path.join(mono_bin_dir, 'csc.bat'),
- 'VbcToolExe': os.path.join(mono_bin_dir, 'vbc.bat'),
- 'FscToolExe': os.path.join(mono_bin_dir, 'fsharpc.bat')
+ "CscToolExe": os.path.join(mono_bin_dir, "csc.bat"),
+ "VbcToolExe": os.path.join(mono_bin_dir, "vbc.bat"),
+ "FscToolExe": os.path.join(mono_bin_dir, "fsharpc.bat"),
}
return (msbuild_mono, mono_msbuild_env)
@@ -132,7 +137,7 @@ def find_msbuild_windows(env):
def run_command(command, args, env_override=None, name=None):
def cmd_args_to_str(cmd_args):
- return ' '.join([arg if not ' ' in arg else '"%s"' % arg for arg in cmd_args])
+ return " ".join([arg if not " " in arg else '"%s"' % arg for arg in cmd_args])
args = [command] + args
@@ -143,6 +148,7 @@ def run_command(command, args, env_override=None, name=None):
print("Running '%s': %s" % (name, cmd_args_to_str(args)))
import subprocess
+
try:
if env_override is None:
subprocess.check_call(args)
@@ -154,61 +160,61 @@ def run_command(command, args, env_override=None, name=None):
def nuget_restore(env, *args):
global verbose
- verbose = env['verbose']
+ verbose = env["verbose"]
# Find NuGet
- nuget_path = find_nuget_windows(env) if os.name == 'nt' else find_nuget_unix()
+ nuget_path = find_nuget_windows(env) if os.name == "nt" else find_nuget_unix()
if nuget_path is None:
- raise RuntimeError('Cannot find NuGet executable')
+ raise RuntimeError("Cannot find NuGet executable")
- print('NuGet path: ' + nuget_path)
+ print("NuGet path: " + nuget_path)
# Do NuGet restore
- run_command(nuget_path, ['restore'] + list(args), name='nuget restore')
+ run_command(nuget_path, ["restore"] + list(args), name="nuget restore")
def build_solution(env, solution_path, build_config, extra_msbuild_args=[]):
global verbose
- verbose = env['verbose']
+ verbose = env["verbose"]
msbuild_env = os.environ.copy()
# Needed when running from Developer Command Prompt for VS
- if 'PLATFORM' in msbuild_env:
- del msbuild_env['PLATFORM']
+ if "PLATFORM" in msbuild_env:
+ del msbuild_env["PLATFORM"]
# Find MSBuild
- if os.name == 'nt':
+ if os.name == "nt":
msbuild_info = find_msbuild_windows(env)
if msbuild_info is None:
- raise RuntimeError('Cannot find MSBuild executable')
+ raise RuntimeError("Cannot find MSBuild executable")
msbuild_path = msbuild_info[0]
msbuild_env.update(msbuild_info[1])
else:
- msbuild_path = find_msbuild_unix('msbuild')
+ msbuild_path = find_msbuild_unix("msbuild")
if msbuild_path is None:
- xbuild_fallback = env['xbuild_fallback']
+ xbuild_fallback = env["xbuild_fallback"]
- if xbuild_fallback and os.name == 'nt':
- print('Option \'xbuild_fallback\' not supported on Windows')
+ if xbuild_fallback and os.name == "nt":
+ print("Option 'xbuild_fallback' not supported on Windows")
xbuild_fallback = False
if xbuild_fallback:
- print('Cannot find MSBuild executable, trying with xbuild')
- print('Warning: xbuild is deprecated')
+ print("Cannot find MSBuild executable, trying with xbuild")
+ print("Warning: xbuild is deprecated")
- msbuild_path = find_msbuild_unix('xbuild')
+ msbuild_path = find_msbuild_unix("xbuild")
if msbuild_path is None:
- raise RuntimeError('Cannot find xbuild executable')
+ raise RuntimeError("Cannot find xbuild executable")
else:
- raise RuntimeError('Cannot find MSBuild executable')
+ raise RuntimeError("Cannot find MSBuild executable")
- print('MSBuild path: ' + msbuild_path)
+ print("MSBuild path: " + msbuild_path)
# Build solution
- msbuild_args = [solution_path, '/p:Configuration=' + build_config]
+ msbuild_args = [solution_path, "/p:Configuration=" + build_config]
msbuild_args += extra_msbuild_args
- run_command(msbuild_path, msbuild_args, env_override=msbuild_env, name='msbuild')
+ run_command(msbuild_path, msbuild_args, env_override=msbuild_env, name="msbuild")
diff --git a/modules/mono/build_scripts/tls_configure.py b/modules/mono/build_scripts/tls_configure.py
deleted file mode 100644
index 622280b00b..0000000000
--- a/modules/mono/build_scripts/tls_configure.py
+++ /dev/null
@@ -1,36 +0,0 @@
-from __future__ import print_function
-
-def supported(result):
- return 'supported' if result else 'not supported'
-
-
-def check_cxx11_thread_local(conf):
- print('Checking for `thread_local` support...', end=" ")
- result = conf.TryCompile('thread_local int foo = 0; int main() { return foo; }', '.cpp')
- print(supported(result))
- return bool(result)
-
-
-def check_declspec_thread(conf):
- print('Checking for `__declspec(thread)` support...', end=" ")
- result = conf.TryCompile('__declspec(thread) int foo = 0; int main() { return foo; }', '.cpp')
- print(supported(result))
- return bool(result)
-
-
-def check_gcc___thread(conf):
- print('Checking for `__thread` support...', end=" ")
- result = conf.TryCompile('__thread int foo = 0; int main() { return foo; }', '.cpp')
- print(supported(result))
- return bool(result)
-
-
-def configure(conf):
- if check_cxx11_thread_local(conf):
- conf.env.Append(CPPDEFINES=['HAVE_CXX11_THREAD_LOCAL'])
- else:
- if conf.env.msvc:
- if check_declspec_thread(conf):
- conf.env.Append(CPPDEFINES=['HAVE_DECLSPEC_THREAD'])
- elif check_gcc___thread(conf):
- conf.env.Append(CPPDEFINES=['HAVE_GCC___THREAD'])
diff --git a/modules/mono/class_db_api_json.cpp b/modules/mono/class_db_api_json.cpp
index b04e53bd81..384685d04b 100644
--- a/modules/mono/class_db_api_json.cpp
+++ b/modules/mono/class_db_api_json.cpp
@@ -42,7 +42,7 @@ void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api) {
List<StringName> names;
- const StringName *k = NULL;
+ const StringName *k = nullptr;
while ((k = ClassDB::classes.next(k))) {
@@ -67,7 +67,7 @@ void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api) {
List<StringName> snames;
- k = NULL;
+ k = nullptr;
while ((k = t->method_map.next(k))) {
@@ -132,7 +132,7 @@ void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api) {
List<StringName> snames;
- k = NULL;
+ k = nullptr;
while ((k = t->constant_map.next(k))) {
@@ -160,7 +160,7 @@ void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api) {
List<StringName> snames;
- k = NULL;
+ k = nullptr;
while ((k = t->signal_map.next(k))) {
@@ -196,7 +196,7 @@ void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api) {
List<StringName> snames;
- k = NULL;
+ k = nullptr;
while ((k = t->property_setget.next(k))) {
diff --git a/modules/mono/config.py b/modules/mono/config.py
index 70cb464c7a..106ca6e028 100644
--- a/modules/mono/config.py
+++ b/modules/mono/config.py
@@ -1,42 +1,70 @@
+supported_platforms = ["windows", "osx", "linuxbsd", "server", "android", "haiku", "javascript", "iphone"]
+
+
def can_build(env, platform):
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')
+ platform = env["platform"]
+
+ if platform not in supported_platforms:
+ raise RuntimeError("This module does not currently support building for this platform")
env.use_ptrcall = True
- env.add_module_version_string('mono')
+ env.add_module_version_string("mono")
- from SCons.Script import BoolVariable, PathVariable, Variables
+ from SCons.Script import BoolVariable, PathVariable, Variables, Help
+
+ default_mono_static = platform in ["iphone", "javascript"]
+ default_mono_bundles_zlib = platform in ["javascript"]
envvars = Variables()
- envvars.Add(PathVariable('mono_prefix', 'Path to the mono installation directory for the target platform and architecture', '', PathVariable.PathAccept))
- envvars.Add(BoolVariable('mono_static', 'Statically link mono', False))
- envvars.Add(BoolVariable('mono_glue', 'Build with the mono glue sources', True))
- envvars.Add(BoolVariable('copy_mono_root', 'Make a copy of the mono installation directory to bundle with the editor', False))
- envvars.Add(BoolVariable('xbuild_fallback', 'If MSBuild is not found, fallback to xbuild', False))
+ envvars.Add(
+ PathVariable(
+ "mono_prefix",
+ "Path to the mono installation directory for the target platform and architecture",
+ "",
+ PathVariable.PathAccept,
+ )
+ )
+ envvars.Add(BoolVariable("mono_static", "Statically link mono", default_mono_static))
+ envvars.Add(BoolVariable("mono_glue", "Build with the mono glue sources", True))
+ envvars.Add(
+ BoolVariable(
+ "copy_mono_root", "Make a copy of the mono installation directory to bundle with the editor", False
+ )
+ )
+ envvars.Add(BoolVariable("xbuild_fallback", "If MSBuild is not found, fallback to xbuild", False))
+
+ # TODO: It would be great if this could be detected automatically instead
+ envvars.Add(
+ BoolVariable(
+ "mono_bundles_zlib", "Specify if the Mono runtime was built with bundled zlib", default_mono_bundles_zlib
+ )
+ )
+
envvars.Update(env)
+ Help(envvars.GenerateHelpText(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
+ if env["mono_bundles_zlib"]:
+ # Mono may come with zlib bundled for WASM or on newer version when built with MinGW.
+ print("This Mono runtime comes with zlib bundled. Disabling 'builtin_zlib'...")
+ env["builtin_zlib"] = False
thirdparty_zlib_dir = "#thirdparty/zlib/"
env.Prepend(CPPPATH=[thirdparty_zlib_dir])
def get_doc_classes():
return [
- '@C#',
- 'CSharpScript',
- 'GodotSharp',
+ "@C#",
+ "CSharpScript",
+ "GodotSharp",
]
def get_doc_path():
- return 'doc_classes'
+ return "doc_classes"
def is_enabled():
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 6809cbdff9..0b5d3c8dbc 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -33,6 +33,8 @@
#include <mono/metadata/threads.h>
#include <stdint.h>
+#include "core/debugger/engine_debugger.h"
+#include "core/debugger/script_debugger.h"
#include "core/io/json.h"
#include "core/os/file_access.h"
#include "core/os/mutex.h"
@@ -60,7 +62,6 @@
#include "signal_awaiter_utils.h"
#include "utils/macros.h"
#include "utils/string_utils.h"
-#include "utils/thread_local.h"
#define CACHED_STRING_NAME(m_var) (CSharpLanguage::get_singleton()->get_string_names().m_var)
@@ -73,7 +74,7 @@ static bool _create_project_solution_if_needed() {
if (!FileAccess::exists(sln_path) || !FileAccess::exists(csproj_path)) {
// A solution does not yet exist, create a new one
- CRASH_COND(CSharpLanguage::get_singleton()->get_godotsharp_editor() == NULL);
+ CRASH_COND(CSharpLanguage::get_singleton()->get_godotsharp_editor() == nullptr);
return CSharpLanguage::get_singleton()->get_godotsharp_editor()->call("CreateProjectSolution");
}
@@ -81,7 +82,7 @@ static bool _create_project_solution_if_needed() {
}
#endif
-CSharpLanguage *CSharpLanguage::singleton = NULL;
+CSharpLanguage *CSharpLanguage::singleton = nullptr;
String CSharpLanguage::get_name() const {
@@ -140,21 +141,24 @@ void CSharpLanguage::init() {
void CSharpLanguage::finish() {
+ if (finalized)
+ return;
+
finalizing = true;
// Make sure all script binding gchandles are released before finalizing GDMono
for (Map<Object *, CSharpScriptBinding>::Element *E = script_bindings.front(); E; E = E->next()) {
CSharpScriptBinding &script_binding = E->value();
- if (script_binding.gchandle.is_valid()) {
- script_binding.gchandle->release();
+ if (!script_binding.gchandle.is_released()) {
+ script_binding.gchandle.release();
script_binding.inited = false;
}
}
if (gdmono) {
memdelete(gdmono);
- gdmono = NULL;
+ gdmono = nullptr;
}
// Clear here, after finalizing all domains to make sure there is nothing else referencing the elements.
@@ -173,7 +177,10 @@ void CSharpLanguage::finish() {
}
#endif
+ memdelete(managed_callable_middleman);
+
finalizing = false;
+ finalized = true;
}
void CSharpLanguage::get_reserved_words(List<String> *p_words) const {
@@ -309,7 +316,8 @@ void CSharpLanguage::get_string_delimiters(List<String> *p_delimiters) const {
p_delimiters->push_back("' '"); // character literal
p_delimiters->push_back("\" \""); // regular string literal
- p_delimiters->push_back("@\" \""); // verbatim string literal
+ // Verbatim string literals (`@" "`) don't render correctly, so don't highlight them.
+ // Generic string highlighting suffices as a workaround for now.
}
static String get_base_class_name(const String &p_base_class_name, const String p_class_name) {
@@ -432,13 +440,12 @@ static String variant_type_to_managed_name(const String &p_var_type_name) {
return "byte[]";
if (p_var_type_name == Variant::get_type_name(Variant::PACKED_INT32_ARRAY))
return "int[]";
- if (p_var_type_name == Variant::get_type_name(Variant::PACKED_FLOAT32_ARRAY)) {
-#ifdef REAL_T_IS_DOUBLE
- return "double[]";
-#else
+ if (p_var_type_name == Variant::get_type_name(Variant::PACKED_INT64_ARRAY))
+ return "long[]";
+ if (p_var_type_name == Variant::get_type_name(Variant::PACKED_FLOAT32_ARRAY))
return "float[]";
-#endif
- }
+ if (p_var_type_name == Variant::get_type_name(Variant::PACKED_FLOAT64_ARRAY))
+ return "double[]";
if (p_var_type_name == Variant::get_type_name(Variant::PACKED_STRING_ARRAY))
return "string[]";
if (p_var_type_name == Variant::get_type_name(Variant::PACKED_VECTOR2_ARRAY))
@@ -448,12 +455,18 @@ static String variant_type_to_managed_name(const String &p_var_type_name) {
if (p_var_type_name == Variant::get_type_name(Variant::PACKED_COLOR_ARRAY))
return "Color[]";
+ if (p_var_type_name == Variant::get_type_name(Variant::SIGNAL))
+ return "SignalInfo";
+
Variant::Type var_types[] = {
Variant::BOOL,
Variant::INT,
Variant::VECTOR2,
+ Variant::VECTOR2I,
Variant::RECT2,
+ Variant::RECT2I,
Variant::VECTOR3,
+ Variant::VECTOR3I,
Variant::TRANSFORM2D,
Variant::PLANE,
Variant::QUAT,
@@ -461,8 +474,10 @@ static String variant_type_to_managed_name(const String &p_var_type_name) {
Variant::BASIS,
Variant::TRANSFORM,
Variant::COLOR,
+ Variant::STRING_NAME,
Variant::NODE_PATH,
- Variant::_RID
+ Variant::_RID,
+ Variant::CALLABLE
};
for (unsigned int i = 0; i < sizeof(var_types) / sizeof(Variant::Type); i++) {
@@ -559,7 +574,13 @@ String CSharpLanguage::debug_get_stack_level_source(int p_level) const {
Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info() {
#ifdef DEBUG_ENABLED
- _TLS_RECURSION_GUARD_V_(Vector<StackInfo>());
+ // Printing an error here will result in endless recursion, so we must be careful
+ static thread_local bool _recursion_flag_ = false;
+ if (_recursion_flag_)
+ return Vector<StackInfo>();
+ _recursion_flag_ = true;
+ SCOPE_EXIT { _recursion_flag_ = false; };
+
GD_MONO_SCOPE_THREAD_ATTACH;
if (!gdmono->is_runtime_initialized() || !GDMono::get_singleton()->get_core_api_assembly() || !GDMonoCache::cached_data.corlib_cache_updated)
@@ -584,10 +605,16 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info()
#ifdef DEBUG_ENABLED
Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObject *p_stack_trace) {
- _TLS_RECURSION_GUARD_V_(Vector<StackInfo>());
+ // Printing an error here will result in endless recursion, so we must be careful
+ static thread_local bool _recursion_flag_ = false;
+ if (_recursion_flag_)
+ return Vector<StackInfo>();
+ _recursion_flag_ = true;
+ SCOPE_EXIT { _recursion_flag_ = false; };
+
GD_MONO_SCOPE_THREAD_ATTACH;
- MonoException *exc = NULL;
+ MonoException *exc = nullptr;
MonoArray *frames = CACHED_METHOD_THUNK(System_Diagnostics_StackTrace, GetFrames).invoke(p_stack_trace, &exc);
@@ -652,14 +679,14 @@ void CSharpLanguage::pre_unsafe_unreference(Object *p_obj) {
void CSharpLanguage::frame() {
- if (gdmono && gdmono->is_runtime_initialized() && gdmono->get_core_api_assembly() != NULL) {
- const Ref<MonoGCHandle> &task_scheduler_handle = GDMonoCache::cached_data.task_scheduler_handle;
+ if (gdmono && gdmono->is_runtime_initialized() && gdmono->get_core_api_assembly() != nullptr) {
+ const Ref<MonoGCHandleRef> &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;
+ MonoException *exc = nullptr;
CACHED_METHOD_THUNK(GodotTaskScheduler, Activate).invoke(task_scheduler, &exc);
if (exc) {
@@ -737,7 +764,7 @@ bool CSharpLanguage::is_assembly_reloading_needed() {
if (proj_assembly) {
String proj_asm_path = proj_assembly->get_path();
- if (!FileAccess::exists(proj_assembly->get_path())) {
+ if (!FileAccess::exists(proj_asm_path)) {
// Maybe it wasn't loaded from the default path, so check this as well
proj_asm_path = GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(appname_safe);
if (!FileAccess::exists(proj_asm_path))
@@ -761,7 +788,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
// There is no soft reloading with Mono. It's always hard reloading.
- List<Ref<CSharpScript> > scripts;
+ List<Ref<CSharpScript>> scripts;
{
MutexLock lock(script_instances_mutex);
@@ -772,10 +799,40 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
}
}
- List<Ref<CSharpScript> > to_reload;
+ scripts.sort_custom<CSharpScriptDepSort>(); // Update in inheritance dependency order
+
+ // Serialize managed callables
+ {
+ MutexLock lock(ManagedCallable::instances_mutex);
+
+ for (SelfList<ManagedCallable> *elem = ManagedCallable::instances.first(); elem; elem = elem->next()) {
+ ManagedCallable *managed_callable = elem->self();
+
+ MonoDelegate *delegate = (MonoDelegate *)managed_callable->delegate_handle.get_target();
+
+ Array serialized_data;
+ MonoObject *managed_serialized_data = GDMonoMarshal::variant_to_mono_object(serialized_data);
+
+ MonoException *exc = nullptr;
+ bool success = (bool)CACHED_METHOD_THUNK(DelegateUtils, TrySerializeDelegate).invoke(delegate, managed_serialized_data, &exc);
+
+ if (exc) {
+ GDMonoUtils::debug_print_unhandled_exception(exc);
+ continue;
+ }
+
+ if (success) {
+ ManagedCallable::instances_pending_reload.insert(managed_callable, serialized_data);
+ } else if (OS::get_singleton()->is_stdout_verbose()) {
+ OS::get_singleton()->print("Failed to serialize delegate\n");
+ }
+ }
+ }
+
+ List<Ref<CSharpScript>> to_reload;
// We need to keep reference instances alive during reloading
- List<Ref<Reference> > ref_instances;
+ List<Ref<Reference>> ref_instances;
for (Map<Object *, CSharpScriptBinding>::Element *E = script_bindings.front(); E; E = E->next()) {
CSharpScriptBinding &script_binding = E->value();
@@ -787,9 +844,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
// As scripts are going to be reloaded, must proceed without locking here
- scripts.sort_custom<CSharpScriptDepSort>(); // Update in inheritance dependency order
-
- for (List<Ref<CSharpScript> >::Element *E = scripts.front(); E; E = E->next()) {
+ for (List<Ref<CSharpScript>>::Element *E = scripts.front(); E; E = E->next()) {
Ref<CSharpScript> &script = E->get();
to_reload.push_back(script);
@@ -843,13 +898,14 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
// TODO: Proper state backup (Not only variants, serialize managed state of scripts)
csi->get_properties_state_for_reloading(state.properties);
+ csi->get_event_signals_state_for_reloading(state.event_signals);
owners_map[obj->get_instance_id()] = state;
}
}
// After the state of all instances is saved, clear scripts and script instances
- for (List<Ref<CSharpScript> >::Element *E = scripts.front(); E; E = E->next()) {
+ for (List<Ref<CSharpScript>>::Element *E = scripts.front(); E; E = E->next()) {
Ref<CSharpScript> &script = E->get();
while (script->instances.front()) {
@@ -864,7 +920,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
if (gdmono->reload_scripts_domain() != OK) {
// Failed to reload the scripts domain
// Make sure to add the scripts back to their owners before returning
- for (List<Ref<CSharpScript> >::Element *E = to_reload.front(); E; E = E->next()) {
+ for (List<Ref<CSharpScript>>::Element *E = to_reload.front(); E; E = E->next()) {
Ref<CSharpScript> scr = E->get();
for (const Map<ObjectID, CSharpScript::StateBackup>::Element *F = scr->pending_reload_state.front(); F; F = F->next()) {
@@ -889,8 +945,8 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
#endif
// Restore Variant properties state, it will be kept by the placeholder until the next script reloading
- for (List<Pair<StringName, Variant> >::Element *G = scr->pending_reload_state[obj_id].properties.front(); G; G = G->next()) {
- placeholder->property_set_fallback(G->get().first, G->get().second, NULL);
+ for (List<Pair<StringName, Variant>>::Element *G = scr->pending_reload_state[obj_id].properties.front(); G; G = G->next()) {
+ placeholder->property_set_fallback(G->get().first, G->get().second, nullptr);
}
scr->pending_reload_state.erase(obj_id);
@@ -900,9 +956,9 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
return;
}
- List<Ref<CSharpScript> > to_reload_state;
+ List<Ref<CSharpScript>> to_reload_state;
- for (List<Ref<CSharpScript> >::Element *E = to_reload.front(); E; E = E->next()) {
+ for (List<Ref<CSharpScript>>::Element *E = to_reload.front(); E; E = E->next()) {
Ref<CSharpScript> script = E->get();
if (!script->get_path().empty()) {
@@ -924,12 +980,12 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
GDMonoAssembly *project_assembly = gdmono->get_project_assembly();
// Search in project and tools assemblies first as those are the most likely to have the class
- GDMonoClass *script_class = (project_assembly ? project_assembly->get_class(class_namespace, class_name) : NULL);
+ GDMonoClass *script_class = (project_assembly ? project_assembly->get_class(class_namespace, class_name) : nullptr);
#ifdef TOOLS_ENABLED
if (!script_class) {
GDMonoAssembly *tools_assembly = gdmono->get_tools_assembly();
- script_class = (tools_assembly ? tools_assembly->get_class(class_namespace, class_name) : NULL);
+ script_class = (tools_assembly ? tools_assembly->get_class(class_namespace, class_name) : nullptr);
}
#endif
@@ -955,7 +1011,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
CSharpScript::initialize_for_managed_type(script, script_class, native);
}
- String native_name = NATIVE_GDMONOCLASS_NAME(script->native);
+ StringName native_name = NATIVE_GDMONOCLASS_NAME(script->native);
{
for (Set<ObjectID>::Element *F = script->pending_reload_instances.front(); F; F = F->next()) {
@@ -1000,7 +1056,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
continue;
}
#else
- CRASH_COND(si != NULL);
+ CRASH_COND(si != nullptr);
#endif
// Re-create script instance
obj->set_script(script); // will create the script instance as well
@@ -1010,7 +1066,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
to_reload_state.push_back(script);
}
- for (List<Ref<CSharpScript> >::Element *E = to_reload_state.front(); E; E = E->next()) {
+ for (List<Ref<CSharpScript>>::Element *E = to_reload_state.front(); E; E = E->next()) {
Ref<CSharpScript> script = E->get();
for (Set<ObjectID>::Element *F = script->pending_reload_instances.front(); F; F = F->next()) {
@@ -1028,19 +1084,84 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
CSharpScript::StateBackup &state_backup = script->pending_reload_state[obj_id];
- for (List<Pair<StringName, Variant> >::Element *G = state_backup.properties.front(); G; G = G->next()) {
+ for (List<Pair<StringName, Variant>>::Element *G = state_backup.properties.front(); G; G = G->next()) {
obj->get_script_instance()->set(G->get().first, G->get().second);
}
- // Call OnAfterDeserialization
CSharpInstance *csi = CAST_CSHARP_INSTANCE(obj->get_script_instance());
- if (csi && csi->script->script_class->implements_interface(CACHED_CLASS(ISerializationListener)))
- obj->get_script_instance()->call_multilevel(string_names.on_after_deserialize);
+
+ if (csi) {
+ for (List<Pair<StringName, Array>>::Element *G = state_backup.event_signals.front(); G; G = G->next()) {
+ const StringName &name = G->get().first;
+ const Array &serialized_data = G->get().second;
+
+ Map<StringName, CSharpScript::EventSignal>::Element *match = script->event_signals.find(name);
+
+ if (!match) {
+ // The event or its signal attribute were removed
+ continue;
+ }
+
+ const CSharpScript::EventSignal &event_signal = match->value();
+
+ MonoObject *managed_serialized_data = GDMonoMarshal::variant_to_mono_object(serialized_data);
+ MonoDelegate *delegate = nullptr;
+
+ MonoException *exc = nullptr;
+ bool success = (bool)CACHED_METHOD_THUNK(DelegateUtils, TryDeserializeDelegate).invoke(managed_serialized_data, &delegate, &exc);
+
+ if (exc) {
+ GDMonoUtils::debug_print_unhandled_exception(exc);
+ continue;
+ }
+
+ if (success) {
+ ERR_CONTINUE(delegate == nullptr);
+ event_signal.field->set_value(csi->get_mono_object(), (MonoObject *)delegate);
+ } else if (OS::get_singleton()->is_stdout_verbose()) {
+ OS::get_singleton()->print("Failed to deserialize event signal delegate\n");
+ }
+ }
+
+ // Call OnAfterDeserialization
+ if (csi->script->script_class->implements_interface(CACHED_CLASS(ISerializationListener)))
+ obj->get_script_instance()->call_multilevel(string_names.on_after_deserialize);
+ }
}
script->pending_reload_instances.clear();
}
+ // Deserialize managed callables
+ {
+ MutexLock lock(ManagedCallable::instances_mutex);
+
+ for (Map<ManagedCallable *, Array>::Element *elem = ManagedCallable::instances_pending_reload.front(); elem; elem = elem->next()) {
+ ManagedCallable *managed_callable = elem->key();
+ const Array &serialized_data = elem->value();
+
+ MonoObject *managed_serialized_data = GDMonoMarshal::variant_to_mono_object(serialized_data);
+ MonoDelegate *delegate = nullptr;
+
+ MonoException *exc = nullptr;
+ bool success = (bool)CACHED_METHOD_THUNK(DelegateUtils, TryDeserializeDelegate).invoke(managed_serialized_data, &delegate, &exc);
+
+ if (exc) {
+ GDMonoUtils::debug_print_unhandled_exception(exc);
+ continue;
+ }
+
+ if (success) {
+ ERR_CONTINUE(delegate == nullptr);
+ managed_callable->set_delegate(delegate);
+ } else if (OS::get_singleton()->is_stdout_verbose()) {
+ OS::get_singleton()->print("Failed to deserialize delegate\n");
+ }
+ }
+
+ ManagedCallable::instances_pending_reload.clear();
+ }
+
#ifdef TOOLS_ENABLED
// FIXME: Hack to refresh editor in order to display new properties and signals. See if there is a better alternative.
if (Engine::get_singleton()->is_editor_hint()) {
@@ -1134,11 +1255,11 @@ void CSharpLanguage::thread_exit() {
bool CSharpLanguage::debug_break_parse(const String &p_file, int p_line, const String &p_error) {
// Not a parser error in our case, but it's still used for other type of errors
- if (ScriptDebugger::get_singleton() && Thread::get_caller_id() == Thread::get_main_id()) {
+ if (EngineDebugger::is_active() && Thread::get_caller_id() == Thread::get_main_id()) {
_debug_parse_err_line = p_line;
_debug_parse_err_file = p_file;
_debug_error = p_error;
- ScriptDebugger::get_singleton()->debug(this, false, true);
+ EngineDebugger::get_script_debugger()->debug(this, false, true);
return true;
} else {
return false;
@@ -1147,11 +1268,11 @@ bool CSharpLanguage::debug_break_parse(const String &p_file, int p_line, const S
bool CSharpLanguage::debug_break(const String &p_error, bool p_allow_continue) {
- if (ScriptDebugger::get_singleton() && Thread::get_caller_id() == Thread::get_main_id()) {
+ if (EngineDebugger::is_active() && Thread::get_caller_id() == Thread::get_main_id()) {
_debug_parse_err_line = -1;
_debug_parse_err_file = "";
_debug_error = p_error;
- ScriptDebugger::get_singleton()->debug(this, p_allow_continue);
+ EngineDebugger::get_script_debugger()->debug(this, p_allow_continue);
return true;
} else {
return false;
@@ -1161,9 +1282,20 @@ bool CSharpLanguage::debug_break(const String &p_error, bool p_allow_continue) {
void CSharpLanguage::_on_scripts_domain_unloaded() {
for (Map<Object *, CSharpScriptBinding>::Element *E = script_bindings.front(); E; E = E->next()) {
CSharpScriptBinding &script_binding = E->value();
+ script_binding.gchandle.release();
script_binding.inited = false;
}
+ {
+ MutexLock lock(ManagedCallable::instances_mutex);
+
+ for (SelfList<ManagedCallable> *elem = ManagedCallable::instances.first(); elem; elem = elem->next()) {
+ ManagedCallable *managed_callable = elem->self();
+ managed_callable->delegate_handle.release();
+ managed_callable->delegate_invoke = nullptr;
+ }
+ }
+
scripts_metadata_invalidated = true;
}
@@ -1175,17 +1307,17 @@ void CSharpLanguage::_editor_init_callback() {
// Initialize GodotSharpEditor
GDMonoClass *editor_klass = GDMono::get_singleton()->get_tools_assembly()->get_class("GodotTools", "GodotSharpEditor");
- CRASH_COND(editor_klass == NULL);
+ CRASH_COND(editor_klass == nullptr);
MonoObject *mono_object = mono_object_new(mono_domain_get(), editor_klass->get_mono_ptr());
- CRASH_COND(mono_object == NULL);
+ CRASH_COND(mono_object == nullptr);
- MonoException *exc = NULL;
+ MonoException *exc = nullptr;
GDMonoUtils::runtime_object_init(mono_object, editor_klass, &exc);
UNHANDLED_EXCEPTION(exc);
EditorPlugin *godotsharp_editor = Object::cast_to<EditorPlugin>(GDMonoMarshal::mono_object_to_variant(mono_object));
- CRASH_COND(godotsharp_editor == NULL);
+ CRASH_COND(godotsharp_editor == nullptr);
// Enable it as a plugin
EditorNode::add_editor_plugin(godotsharp_editor);
@@ -1201,57 +1333,45 @@ void CSharpLanguage::set_language_index(int p_idx) {
lang_idx = p_idx;
}
-void CSharpLanguage::release_script_gchandle(Ref<MonoGCHandle> &p_gchandle) {
+void CSharpLanguage::release_script_gchandle(MonoGCHandleData &p_gchandle) {
- if (!p_gchandle->is_released()) { // Do not lock unnecessarily
+ if (!p_gchandle.is_released()) { // Do not lock unnecessarily
MutexLock lock(get_singleton()->script_gchandle_release_mutex);
- p_gchandle->release();
+ p_gchandle.release();
}
}
-void CSharpLanguage::release_script_gchandle(MonoObject *p_expected_obj, Ref<MonoGCHandle> &p_gchandle) {
+void CSharpLanguage::release_script_gchandle(MonoObject *p_expected_obj, MonoGCHandleData &p_gchandle) {
- uint32_t pinned_gchandle = MonoGCHandle::new_strong_handle_pinned(p_expected_obj); // We might lock after this, so pin it
+ uint32_t pinned_gchandle = GDMonoUtils::new_strong_gchandle_pinned(p_expected_obj); // We might lock after this, so pin it
- if (!p_gchandle->is_released()) { // Do not lock unnecessarily
+ if (!p_gchandle.is_released()) { // Do not lock unnecessarily
MutexLock lock(get_singleton()->script_gchandle_release_mutex);
- MonoObject *target = p_gchandle->get_target();
+ MonoObject *target = p_gchandle.get_target();
// We release the gchandle if it points to the MonoObject* we expect (otherwise it was
// already released and could have been replaced) or if we can't get its target MonoObject*
// (which doesn't necessarily mean it was released, and we want it released in order to
// avoid locking other threads unnecessarily).
- if (target == p_expected_obj || target == NULL) {
- p_gchandle->release();
+ if (target == p_expected_obj || target == nullptr) {
+ p_gchandle.release();
}
}
- MonoGCHandle::free_handle(pinned_gchandle);
+ GDMonoUtils::free_gchandle(pinned_gchandle);
}
CSharpLanguage::CSharpLanguage() {
ERR_FAIL_COND_MSG(singleton, "C# singleton already exist.");
singleton = this;
-
- finalizing = false;
-
- gdmono = NULL;
-
- lang_idx = -1;
-
- scripts_metadata_invalidated = true;
-
-#ifdef TOOLS_ENABLED
- godotsharp_editor = NULL;
-#endif
}
CSharpLanguage::~CSharpLanguage() {
finish();
- singleton = NULL;
+ singleton = nullptr;
}
bool CSharpLanguage::setup_csharp_script_binding(CSharpScriptBinding &r_script_binding, Object *p_object) {
@@ -1260,7 +1380,7 @@ bool CSharpLanguage::setup_csharp_script_binding(CSharpScriptBinding &r_script_b
// I don't trust you
if (p_object->get_script_instance()) {
CSharpInstance *csharp_instance = CAST_CSHARP_INSTANCE(p_object->get_script_instance());
- CRASH_COND(csharp_instance != NULL && !csharp_instance->is_destructing_script_instance());
+ CRASH_COND(csharp_instance != nullptr && !csharp_instance->is_destructing_script_instance());
}
#endif
@@ -1284,7 +1404,7 @@ bool CSharpLanguage::setup_csharp_script_binding(CSharpScriptBinding &r_script_b
r_script_binding.inited = true;
r_script_binding.type_name = type_name;
r_script_binding.wrapper_class = type_class; // cache
- r_script_binding.gchandle = MonoGCHandle::create_strong(mono_object);
+ r_script_binding.gchandle = MonoGCHandleData::new_strong_handle(mono_object);
r_script_binding.owner = p_object;
// Tie managed to unmanaged
@@ -1314,7 +1434,7 @@ void *CSharpLanguage::alloc_instance_binding_data(Object *p_object) {
CSharpScriptBinding script_binding;
if (!setup_csharp_script_binding(script_binding, p_object))
- return NULL;
+ return nullptr;
return (void *)insert_script_binding(p_object, script_binding);
}
@@ -1326,7 +1446,7 @@ Map<Object *, CSharpScriptBinding>::Element *CSharpLanguage::insert_script_bindi
void CSharpLanguage::free_instance_binding_data(void *p_data) {
- if (GDMono::get_singleton() == NULL) {
+ if (GDMono::get_singleton() == nullptr) {
#ifdef DEBUG_ENABLED
CRASH_COND(!script_bindings.empty());
#endif
@@ -1349,10 +1469,11 @@ void CSharpLanguage::free_instance_binding_data(void *p_data) {
if (script_binding.inited) {
// Set the native instance field to IntPtr.Zero, if not yet garbage collected.
// This is done to avoid trying to dispose the native instance from Dispose(bool).
- MonoObject *mono_object = script_binding.gchandle->get_target();
+ MonoObject *mono_object = script_binding.gchandle.get_target();
if (mono_object) {
- CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, NULL);
+ CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, nullptr);
}
+ script_binding.gchandle.release();
}
script_bindings.erase(data);
@@ -1372,26 +1493,26 @@ void CSharpLanguage::refcount_incremented_instance_binding(Object *p_object) {
CRASH_COND(!data);
CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
- Ref<MonoGCHandle> &gchandle = script_binding.gchandle;
+ MonoGCHandleData &gchandle = script_binding.gchandle;
if (!script_binding.inited)
return;
- if (ref_owner->reference_get_count() > 1 && gchandle->is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
+ if (ref_owner->reference_get_count() > 1 && gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
GD_MONO_SCOPE_THREAD_ATTACH;
// The reference count was increased after the managed side was the only one referencing our owner.
// This means the owner is being referenced again by the unmanaged side,
// so the owner must hold the managed side alive again to avoid it from being GCed.
- MonoObject *target = gchandle->get_target();
+ MonoObject *target = gchandle.get_target();
if (!target)
return; // Called after the managed side was collected, so nothing to do here
// Release the current weak handle and replace it with a strong handle.
- uint32_t strong_gchandle = MonoGCHandle::new_strong_handle(target);
- gchandle->release();
- gchandle->set_handle(strong_gchandle, MonoGCHandle::STRONG_HANDLE);
+ MonoGCHandleData strong_gchandle = MonoGCHandleData::new_strong_handle(target);
+ gchandle.release();
+ gchandle = strong_gchandle;
}
}
@@ -1408,27 +1529,27 @@ bool CSharpLanguage::refcount_decremented_instance_binding(Object *p_object) {
CRASH_COND(!data);
CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
- Ref<MonoGCHandle> &gchandle = script_binding.gchandle;
+ MonoGCHandleData &gchandle = script_binding.gchandle;
int refcount = ref_owner->reference_get_count();
if (!script_binding.inited)
return refcount == 0;
- if (refcount == 1 && gchandle.is_valid() && !gchandle->is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
+ if (refcount == 1 && !gchandle.is_released() && !gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
GD_MONO_SCOPE_THREAD_ATTACH;
// If owner owner is no longer referenced by the unmanaged side,
// the managed instance takes responsibility of deleting the owner when GCed.
- MonoObject *target = gchandle->get_target();
+ MonoObject *target = gchandle.get_target();
if (!target)
return refcount == 0; // Called after the managed side was collected, so nothing to do here
// Release the current strong handle and replace it with a weak handle.
- uint32_t weak_gchandle = MonoGCHandle::new_weak_handle(target);
- gchandle->release();
- gchandle->set_handle(weak_gchandle, MonoGCHandle::WEAK_HANDLE);
+ MonoGCHandleData weak_gchandle = MonoGCHandleData::new_weak_handle(target);
+ gchandle.release();
+ gchandle = weak_gchandle;
return false;
}
@@ -1436,14 +1557,13 @@ bool CSharpLanguage::refcount_decremented_instance_binding(Object *p_object) {
return refcount == 0;
}
-CSharpInstance *CSharpInstance::create_for_managed_type(Object *p_owner, CSharpScript *p_script, const Ref<MonoGCHandle> &p_gchandle) {
+CSharpInstance *CSharpInstance::create_for_managed_type(Object *p_owner, CSharpScript *p_script, const MonoGCHandleData &p_gchandle) {
- CSharpInstance *instance = memnew(CSharpInstance);
+ CSharpInstance *instance = memnew(CSharpInstance(Ref<CSharpScript>(p_script)));
Reference *ref = Object::cast_to<Reference>(p_owner);
- instance->base_ref = ref != NULL;
- instance->script = Ref<CSharpScript>(p_script);
+ instance->base_ref = ref != nullptr;
instance->owner = p_owner;
instance->gchandle = p_gchandle;
@@ -1457,8 +1577,8 @@ CSharpInstance *CSharpInstance::create_for_managed_type(Object *p_owner, CSharpS
MonoObject *CSharpInstance::get_mono_object() const {
- ERR_FAIL_COND_V(gchandle.is_null(), NULL);
- return gchandle->get_target();
+ ERR_FAIL_COND_V(gchandle.is_released(), nullptr);
+ return gchandle.get_target();
}
Object *CSharpInstance::get_owner() {
@@ -1542,7 +1662,7 @@ bool CSharpInstance::get(const StringName &p_name, Variant &r_ret) const {
GDMonoProperty *property = top->get_property(p_name);
if (property) {
- MonoException *exc = NULL;
+ MonoException *exc = nullptr;
MonoObject *value = property->get_value(mono_object, &exc);
if (exc) {
r_ret = Variant();
@@ -1583,7 +1703,7 @@ bool CSharpInstance::get(const StringName &p_name, Variant &r_ret) const {
return false;
}
-void CSharpInstance::get_properties_state_for_reloading(List<Pair<StringName, Variant> > &r_state) {
+void CSharpInstance::get_properties_state_for_reloading(List<Pair<StringName, Variant>> &r_state) {
List<PropertyInfo> pinfo;
get_property_list(&pinfo);
@@ -1608,6 +1728,37 @@ void CSharpInstance::get_properties_state_for_reloading(List<Pair<StringName, Va
}
}
+void CSharpInstance::get_event_signals_state_for_reloading(List<Pair<StringName, Array>> &r_state) {
+
+ MonoObject *owner_managed = get_mono_object();
+ ERR_FAIL_NULL(owner_managed);
+
+ for (const Map<StringName, CSharpScript::EventSignal>::Element *E = script->event_signals.front(); E; E = E->next()) {
+ const CSharpScript::EventSignal &event_signal = E->value();
+
+ MonoDelegate *delegate_field_value = (MonoDelegate *)event_signal.field->get_value(owner_managed);
+ if (!delegate_field_value)
+ continue; // Empty
+
+ Array serialized_data;
+ MonoObject *managed_serialized_data = GDMonoMarshal::variant_to_mono_object(serialized_data);
+
+ MonoException *exc = nullptr;
+ bool success = (bool)CACHED_METHOD_THUNK(DelegateUtils, TrySerializeDelegate).invoke(delegate_field_value, managed_serialized_data, &exc);
+
+ if (exc) {
+ GDMonoUtils::debug_print_unhandled_exception(exc);
+ continue;
+ }
+
+ if (success) {
+ r_state.push_back(Pair<StringName, Array>(event_signal.field->get_name(), serialized_data));
+ } else if (OS::get_singleton()->is_stdout_verbose()) {
+ OS::get_singleton()->print("Failed to serialize event signal delegate\n");
+ }
+ }
+}
+
void CSharpInstance::get_property_list(List<PropertyInfo> *p_properties) const {
for (Map<StringName, PropertyInfo>::Element *E = script->member_info.front(); E; E = E->next()) {
@@ -1759,7 +1910,7 @@ bool CSharpInstance::_reference_owner_unsafe() {
#ifdef DEBUG_ENABLED
CRASH_COND(!base_ref);
- CRASH_COND(owner == NULL);
+ CRASH_COND(owner == nullptr);
CRASH_COND(unsafe_referenced); // already referenced
#endif
@@ -1781,7 +1932,7 @@ bool CSharpInstance::_unreference_owner_unsafe() {
#ifdef DEBUG_ENABLED
CRASH_COND(!base_ref);
- CRASH_COND(owner == NULL);
+ CRASH_COND(owner == nullptr);
#endif
if (!unsafe_referenced)
@@ -1800,19 +1951,15 @@ bool CSharpInstance::_unreference_owner_unsafe() {
}
MonoObject *CSharpInstance::_internal_new_managed() {
-#ifdef DEBUG_ENABLED
- CRASH_COND(!gchandle.is_valid());
-#endif
-
// Search the constructor first, to fail with an error if it's not found before allocating anything else.
GDMonoMethod *ctor = script->script_class->get_method(CACHED_STRING_NAME(dotctor), 0);
- ERR_FAIL_NULL_V_MSG(ctor, NULL,
+ ERR_FAIL_NULL_V_MSG(ctor, nullptr,
"Cannot create script instance because the class does not define a parameterless constructor: '" + script->get_path() + "'.");
CSharpLanguage::get_singleton()->release_script_gchandle(gchandle);
- ERR_FAIL_NULL_V(owner, NULL);
- ERR_FAIL_COND_V(script.is_null(), NULL);
+ ERR_FAIL_NULL_V(owner, nullptr);
+ ERR_FAIL_COND_V(script.is_null(), nullptr);
MonoObject *mono_object = mono_object_new(mono_domain_get(), script->script_class->get_mono_ptr());
@@ -1824,13 +1971,13 @@ MonoObject *CSharpInstance::_internal_new_managed() {
// Not ok for the owner to die here. If there is a situation where this can happen, it will be considered a bug.
CRASH_COND(die == true);
- owner = NULL;
+ owner = nullptr;
- ERR_FAIL_V_MSG(NULL, "Failed to allocate memory for the object.");
+ ERR_FAIL_V_MSG(nullptr, "Failed to allocate memory for the object.");
}
// Tie managed to unmanaged
- gchandle = MonoGCHandle::create_strong(mono_object);
+ gchandle = MonoGCHandleData::new_strong_handle(mono_object);
if (base_ref)
_reference_owner_unsafe(); // Here, after assigning the gchandle (for the refcount_incremented callback)
@@ -1838,16 +1985,18 @@ MonoObject *CSharpInstance::_internal_new_managed() {
CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, owner);
// Construct
- ctor->invoke_raw(mono_object, NULL);
+ ctor->invoke_raw(mono_object, nullptr);
return mono_object;
}
void CSharpInstance::mono_object_disposed(MonoObject *p_obj) {
+ disconnect_event_signals();
+
#ifdef DEBUG_ENABLED
CRASH_COND(base_ref);
- CRASH_COND(gchandle.is_null());
+ CRASH_COND(gchandle.is_released());
#endif
CSharpLanguage::get_singleton()->release_script_gchandle(p_obj, gchandle);
}
@@ -1856,7 +2005,7 @@ void CSharpInstance::mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_f
#ifdef DEBUG_ENABLED
CRASH_COND(!base_ref);
- CRASH_COND(gchandle.is_null());
+ CRASH_COND(gchandle.is_released());
#endif
r_remove_script_instance = false;
@@ -1886,16 +2035,43 @@ void CSharpInstance::mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_f
}
}
+void CSharpInstance::connect_event_signals() {
+ for (const Map<StringName, CSharpScript::EventSignal>::Element *E = script->event_signals.front(); E; E = E->next()) {
+ const CSharpScript::EventSignal &event_signal = E->value();
+
+ StringName signal_name = event_signal.field->get_name();
+
+ // TODO: Use pooling for ManagedCallable instances.
+ auto event_signal_callable = memnew(EventSignalCallable(owner, &event_signal));
+
+ owner->connect(signal_name, Callable(event_signal_callable));
+ }
+}
+
+void CSharpInstance::disconnect_event_signals() {
+ for (const Map<StringName, CSharpScript::EventSignal>::Element *E = script->event_signals.front(); E; E = E->next()) {
+ const CSharpScript::EventSignal &event_signal = E->value();
+
+ StringName signal_name = event_signal.field->get_name();
+
+ // TODO: It would be great if we could store this EventSignalCallable on the stack.
+ // The problem is that Callable memdeletes it when it's destructed...
+ auto event_signal_callable = memnew(EventSignalCallable(owner, &event_signal));
+
+ owner->disconnect(signal_name, Callable(event_signal_callable));
+ }
+}
+
void CSharpInstance::refcount_incremented() {
#ifdef DEBUG_ENABLED
CRASH_COND(!base_ref);
- CRASH_COND(owner == NULL);
+ CRASH_COND(owner == nullptr);
#endif
Reference *ref_owner = Object::cast_to<Reference>(owner);
- if (ref_owner->reference_get_count() > 1 && gchandle->is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
+ if (ref_owner->reference_get_count() > 1 && gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
GD_MONO_SCOPE_THREAD_ATTACH;
// The reference count was increased after the managed side was the only one referencing our owner.
@@ -1903,9 +2079,9 @@ void CSharpInstance::refcount_incremented() {
// so the owner must hold the managed side alive again to avoid it from being GCed.
// Release the current weak handle and replace it with a strong handle.
- uint32_t strong_gchandle = MonoGCHandle::new_strong_handle(gchandle->get_target());
- gchandle->release();
- gchandle->set_handle(strong_gchandle, MonoGCHandle::STRONG_HANDLE);
+ MonoGCHandleData strong_gchandle = MonoGCHandleData::new_strong_handle(gchandle.get_target());
+ gchandle.release();
+ gchandle = strong_gchandle;
}
}
@@ -1913,23 +2089,23 @@ bool CSharpInstance::refcount_decremented() {
#ifdef DEBUG_ENABLED
CRASH_COND(!base_ref);
- CRASH_COND(owner == NULL);
+ CRASH_COND(owner == nullptr);
#endif
Reference *ref_owner = Object::cast_to<Reference>(owner);
int refcount = ref_owner->reference_get_count();
- if (refcount == 1 && !gchandle->is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
+ if (refcount == 1 && !gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
GD_MONO_SCOPE_THREAD_ATTACH;
// If owner owner is no longer referenced by the unmanaged side,
// the managed instance takes responsibility of deleting the owner when GCed.
// Release the current strong handle and replace it with a weak handle.
- uint32_t weak_gchandle = MonoGCHandle::new_weak_handle(gchandle->get_target());
- gchandle->release();
- gchandle->set_handle(weak_gchandle, MonoGCHandle::WEAK_HANDLE);
+ MonoGCHandleData weak_gchandle = MonoGCHandleData::new_weak_handle(gchandle.get_target());
+ gchandle.release();
+ gchandle = weak_gchandle;
return false;
}
@@ -2005,7 +2181,7 @@ void CSharpInstance::notification(int p_notification) {
MonoObject *mono_object = get_mono_object();
ERR_FAIL_NULL(mono_object);
- MonoException *exc = NULL;
+ MonoException *exc = nullptr;
GDMonoUtils::dispose(mono_object, &exc);
if (exc) {
@@ -2050,13 +2226,13 @@ String CSharpInstance::to_string(bool *r_valid) {
MonoObject *mono_object = get_mono_object();
- if (mono_object == NULL) {
+ if (mono_object == nullptr) {
if (r_valid)
*r_valid = false;
return String();
}
- MonoException *exc = NULL;
+ MonoException *exc = nullptr;
MonoString *result = GDMonoUtils::object_to_string(mono_object, &exc);
if (exc) {
@@ -2066,7 +2242,7 @@ String CSharpInstance::to_string(bool *r_valid) {
return String();
}
- if (result == NULL) {
+ if (result == nullptr) {
if (r_valid)
*r_valid = false;
return String();
@@ -2085,13 +2261,8 @@ ScriptLanguage *CSharpInstance::get_language() {
return CSharpLanguage::get_singleton();
}
-CSharpInstance::CSharpInstance() :
- owner(NULL),
- base_ref(false),
- ref_dying(false),
- unsafe_referenced(false),
- predelete_notified(false),
- destructing_script_instance(false) {
+CSharpInstance::CSharpInstance(const Ref<CSharpScript> &p_script) :
+ script(p_script) {
}
CSharpInstance::~CSharpInstance() {
@@ -2100,18 +2271,18 @@ CSharpInstance::~CSharpInstance() {
destructing_script_instance = true;
- if (gchandle.is_valid()) {
+ if (!gchandle.is_released()) {
if (!predelete_notified && !ref_dying) {
// This destructor is not called from the owners destructor.
// This could be being called from the owner's set_script_instance method,
// meaning this script is being replaced with another one. If this is the case,
- // we must call Dispose here, because Dispose calls owner->set_script_instance(NULL)
+ // we must call Dispose here, because Dispose calls owner->set_script_instance(nullptr)
// and that would mess up with the new script instance if called later.
- MonoObject *mono_object = gchandle->get_target();
+ MonoObject *mono_object = gchandle.get_target();
if (mono_object) {
- MonoException *exc = NULL;
+ MonoException *exc = nullptr;
GDMonoUtils::dispose(mono_object, &exc);
if (exc) {
@@ -2120,7 +2291,7 @@ CSharpInstance::~CSharpInstance() {
}
}
- gchandle->release(); // Make sure the gchandle is released
+ gchandle.release(); // Make sure the gchandle is released
}
// If not being called from the owner's destructor, and we still hold a reference to the owner
@@ -2141,7 +2312,7 @@ CSharpInstance::~CSharpInstance() {
CRASH_COND(die == true); // `owner_keep_alive` holds a reference, so it can't die
void *data = owner->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index());
- CRASH_COND(data == NULL);
+ CRASH_COND(data == nullptr);
CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
@@ -2280,23 +2451,23 @@ bool CSharpScript::_update_exports() {
return false;
}
- uint32_t tmp_pinned_gchandle = MonoGCHandle::new_strong_handle_pinned(tmp_object); // pin it (not sure if needed)
+ uint32_t tmp_pinned_gchandle = GDMonoUtils::new_strong_gchandle_pinned(tmp_object); // pin it (not sure if needed)
GDMonoMethod *ctor = script_class->get_method(CACHED_STRING_NAME(dotctor), 0);
- ERR_FAIL_NULL_V_MSG(ctor, NULL,
+ ERR_FAIL_NULL_V_MSG(ctor, false,
"Cannot construct temporary MonoObject because the class does not define a parameterless constructor: '" + get_path() + "'.");
- MonoException *ctor_exc = NULL;
- ctor->invoke(tmp_object, NULL, &ctor_exc);
+ MonoException *ctor_exc = nullptr;
+ ctor->invoke(tmp_object, nullptr, &ctor_exc);
Object *tmp_native = GDMonoMarshal::unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(tmp_object));
if (ctor_exc) {
// TODO: Should we free 'tmp_native' if the exception was thrown after its creation?
- MonoGCHandle::free_handle(tmp_pinned_gchandle);
- tmp_object = NULL;
+ GDMonoUtils::free_gchandle(tmp_pinned_gchandle);
+ tmp_object = nullptr;
ERR_PRINT("Exception thrown from constructor of temporary MonoObject:");
GDMonoUtils::debug_print_unhandled_exception(ctor_exc);
@@ -2343,7 +2514,7 @@ bool CSharpScript::_update_exports() {
exported_members_cache.push_front(prop_info);
if (tmp_object) {
- MonoException *exc = NULL;
+ MonoException *exc = nullptr;
MonoObject *ret = property->get_value(tmp_object, &exc);
if (exc) {
exported_members_defval_cache[member_name] = Variant();
@@ -2362,11 +2533,11 @@ bool CSharpScript::_update_exports() {
}
// Need to check this here, before disposal
- bool base_ref = Object::cast_to<Reference>(tmp_native) != NULL;
+ bool base_ref = Object::cast_to<Reference>(tmp_native) != nullptr;
// Dispose the temporary managed instance
- MonoException *exc = NULL;
+ MonoException *exc = nullptr;
GDMonoUtils::dispose(tmp_object, &exc);
if (exc) {
@@ -2374,8 +2545,8 @@ bool CSharpScript::_update_exports() {
GDMonoUtils::debug_print_unhandled_exception(exc);
}
- MonoGCHandle::free_handle(tmp_pinned_gchandle);
- tmp_object = NULL;
+ GDMonoUtils::free_gchandle(tmp_pinned_gchandle);
+ tmp_object = nullptr;
if (tmp_native && !base_ref) {
Node *node = Object::cast_to<Node>(tmp_native);
@@ -2414,6 +2585,7 @@ void CSharpScript::load_script_signals(GDMonoClass *p_class, GDMonoClass *p_nati
// make sure this classes signals are empty when loading for the first time
_signals.clear();
+ event_signals.clear();
GD_MONO_SCOPE_THREAD_ATTACH;
@@ -2421,56 +2593,90 @@ void CSharpScript::load_script_signals(GDMonoClass *p_class, GDMonoClass *p_nati
while (top && top != p_native_class) {
const Vector<GDMonoClass *> &delegates = top->get_all_delegates();
for (int i = delegates.size() - 1; i >= 0; --i) {
- Vector<Argument> parameters;
-
GDMonoClass *delegate = delegates[i];
- if (_get_signal(top, delegate, parameters)) {
+ if (!delegate->has_attribute(CACHED_CLASS(SignalAttribute)))
+ continue;
+
+ // Arguments are accessibles as arguments of .Invoke method
+ GDMonoMethod *invoke_method = delegate->get_method(mono_get_delegate_invoke(delegate->get_mono_ptr()));
+
+ Vector<SignalParameter> parameters;
+ if (_get_signal(top, invoke_method, parameters)) {
_signals[delegate->get_name()] = parameters;
}
}
+ List<StringName> found_event_signals;
+
+ void *iter = nullptr;
+ MonoEvent *raw_event = nullptr;
+ while ((raw_event = mono_class_get_events(top->get_mono_ptr(), &iter)) != nullptr) {
+ MonoCustomAttrInfo *event_attrs = mono_custom_attrs_from_event(top->get_mono_ptr(), raw_event);
+ if (event_attrs) {
+ if (mono_custom_attrs_has_attr(event_attrs, CACHED_CLASS(SignalAttribute)->get_mono_ptr())) {
+ const char *event_name = mono_event_get_name(raw_event);
+ found_event_signals.push_back(StringName(event_name));
+ }
+
+ mono_custom_attrs_free(event_attrs);
+ }
+ }
+
+ const Vector<GDMonoField *> &fields = top->get_all_fields();
+ for (int i = 0; i < fields.size(); i++) {
+ GDMonoField *field = fields[i];
+
+ GDMonoClass *field_class = field->get_type().type_class;
+
+ if (!mono_class_is_delegate(field_class->get_mono_ptr()))
+ continue;
+
+ if (!found_event_signals.find(field->get_name()))
+ continue;
+
+ GDMonoMethod *invoke_method = field_class->get_method(mono_get_delegate_invoke(field_class->get_mono_ptr()));
+
+ Vector<SignalParameter> parameters;
+ if (_get_signal(top, invoke_method, parameters)) {
+ event_signals[field->get_name()] = { field, invoke_method, parameters };
+ }
+ }
+
top = top->get_parent_class();
}
signals_invalidated = false;
}
-bool CSharpScript::_get_signal(GDMonoClass *p_class, GDMonoClass *p_delegate, Vector<Argument> &params) {
+bool CSharpScript::_get_signal(GDMonoClass *p_class, GDMonoMethod *p_delegate_invoke, Vector<SignalParameter> &params) {
GD_MONO_ASSERT_THREAD_ATTACHED;
- if (p_delegate->has_attribute(CACHED_CLASS(SignalAttribute))) {
- MonoType *raw_type = p_delegate->get_mono_type();
+ Vector<StringName> names;
+ Vector<ManagedType> types;
+ p_delegate_invoke->get_parameter_names(names);
+ p_delegate_invoke->get_parameter_types(types);
- if (mono_type_get_type(raw_type) == MONO_TYPE_CLASS) {
- // Arguments are accessibles as arguments of .Invoke method
- GDMonoMethod *invoke = p_delegate->get_method("Invoke", -1);
-
- Vector<StringName> names;
- Vector<ManagedType> types;
- invoke->get_parameter_names(names);
- invoke->get_parameter_types(types);
-
- if (names.size() == types.size()) {
- for (int i = 0; i < names.size(); ++i) {
- Argument arg;
- arg.name = names[i];
- arg.type = GDMonoMarshal::managed_to_variant_type(types[i]);
-
- if (arg.type == Variant::NIL) {
- ERR_PRINT("Unknown type of signal parameter: '" + arg.name + "' in '" + p_class->get_full_name() + "'.");
- return false;
- }
+ for (int i = 0; i < names.size(); ++i) {
+ SignalParameter arg;
+ arg.name = names[i];
- params.push_back(arg);
- }
+ bool nil_is_variant = false;
+ arg.type = GDMonoMarshal::managed_to_variant_type(types[i], &nil_is_variant);
- return true;
+ if (arg.type == Variant::NIL) {
+ if (nil_is_variant) {
+ arg.nil_is_variant = true;
+ } else {
+ ERR_PRINT("Unknown type of signal parameter: '" + arg.name + "' in '" + p_class->get_full_name() + "'.");
+ return false;
}
}
+
+ params.push_back(arg);
}
- return false;
+ return true;
}
#ifdef TOOLS_ENABLED
@@ -2521,7 +2727,8 @@ bool CSharpScript::_get_member_export(IMonoClassMember *p_member, bool p_inspect
}
}
- Variant::Type variant_type = GDMonoMarshal::managed_to_variant_type(type);
+ bool nil_is_variant = false;
+ Variant::Type variant_type = GDMonoMarshal::managed_to_variant_type(type, &nil_is_variant);
if (!p_inspect_export || !exported) {
r_prop_info = PropertyInfo(variant_type, (String)p_member->get_name(), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_SCRIPT_VARIABLE);
@@ -2534,7 +2741,7 @@ bool CSharpScript::_get_member_export(IMonoClassMember *p_member, bool p_inspect
PropertyHint hint = PROPERTY_HINT_NONE;
String hint_string;
- if (variant_type == Variant::NIL) {
+ if (variant_type == Variant::NIL && !nil_is_variant) {
ERR_PRINT("Unknown exported member type: '" + MEMBER_FULL_QUALIFIED_NAME(p_member) + "'.");
return false;
}
@@ -2550,7 +2757,14 @@ bool CSharpScript::_get_member_export(IMonoClassMember *p_member, bool p_inspect
hint_string = CACHED_FIELD(ExportAttribute, hintString)->get_string_value(attr);
}
- r_prop_info = PropertyInfo(variant_type, (String)p_member->get_name(), hint, hint_string, PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE);
+ uint32_t prop_usage = PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE;
+
+ if (variant_type == Variant::NIL) {
+ // System.Object (Variant)
+ prop_usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
+ }
+
+ r_prop_info = PropertyInfo(variant_type, (String)p_member->get_name(), hint, hint_string, prop_usage);
r_exported = true;
return true;
@@ -2560,6 +2774,11 @@ bool CSharpScript::_get_member_export(IMonoClassMember *p_member, bool p_inspect
int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, ManagedType p_type, Variant::Type p_variant_type, bool p_allow_generics, PropertyHint &r_hint, String &r_hint_string) {
+ if (p_variant_type == Variant::NIL) {
+ // System.Object (Variant)
+ return 1;
+ }
+
GD_MONO_ASSERT_THREAD_ATTACHED;
if (p_variant_type == Variant::INT && p_type.type_encoding == MONO_TYPE_VALUETYPE && mono_class_is_enum(p_type.type_class->get_mono_ptr())) {
@@ -2592,7 +2811,7 @@ int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, Manage
// Instead of using mono_field_get_value_object, we can do this without boxing. Check the
// internal mono functions: ves_icall_System_Enum_GetEnumValuesAndNames and the get_enum_field.
- MonoObject *val_obj = mono_field_get_value_object(mono_domain_get(), field, NULL);
+ MonoObject *val_obj = mono_field_get_value_object(mono_domain_get(), field, nullptr);
ERR_FAIL_NULL_V_MSG(val_obj, -1, "Failed to get '" + enum_field_name + "' constant enum value.");
@@ -2616,10 +2835,10 @@ int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, Manage
}
} else if (p_variant_type == Variant::OBJECT && CACHED_CLASS(GodotResource)->is_assignable_from(p_type.type_class)) {
GDMonoClass *field_native_class = GDMonoUtils::get_class_native_base(p_type.type_class);
- CRASH_COND(field_native_class == NULL);
+ CRASH_COND(field_native_class == nullptr);
r_hint = PROPERTY_HINT_RESOURCE_TYPE;
- r_hint_string = NATIVE_GDMONOCLASS_NAME(field_native_class);
+ r_hint_string = String(NATIVE_GDMONOCLASS_NAME(field_native_class));
} else if (p_allow_generics && p_variant_type == Variant::ARRAY) {
// Nested arrays are not supported in the inspector
@@ -2658,14 +2877,14 @@ void CSharpScript::_clear() {
tool = false;
valid = false;
- base = NULL;
- native = NULL;
- script_class = NULL;
+ base = nullptr;
+ native = nullptr;
+ script_class = nullptr;
}
Variant CSharpScript::call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
- if (unlikely(GDMono::get_singleton() == NULL)) {
+ if (unlikely(GDMono::get_singleton() == nullptr)) {
// Probably not the best error but eh.
r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
return Variant();
@@ -2679,7 +2898,7 @@ Variant CSharpScript::call(const StringName &p_method, const Variant **p_args, i
GDMonoMethod *method = top->get_method(p_method, p_argcount);
if (method && method->is_static()) {
- MonoObject *result = method->invoke(NULL, p_args);
+ MonoObject *result = method->invoke(nullptr, p_args);
if (result) {
return GDMonoMarshal::mono_object_to_variant(result);
@@ -2741,7 +2960,7 @@ Ref<CSharpScript> CSharpScript::create_for_managed_type(GDMonoClass *p_class, GD
// This method should not fail, only assertions allowed
- CRASH_COND(p_class == NULL);
+ CRASH_COND(p_class == nullptr);
// TODO OPTIMIZE: Cache the 'CSharpScript' associated with this 'p_class' instead of allocating a new one every time
Ref<CSharpScript> script = memnew(CSharpScript);
@@ -2755,13 +2974,13 @@ void CSharpScript::initialize_for_managed_type(Ref<CSharpScript> p_script, GDMon
// This method should not fail, only assertions allowed
- CRASH_COND(p_class == NULL);
+ CRASH_COND(p_class == nullptr);
p_script->name = p_class->get_name();
p_script->script_class = p_class;
p_script->native = p_native;
- CRASH_COND(p_script->native == NULL);
+ CRASH_COND(p_script->native == nullptr);
GDMonoClass *base = p_script->script_class->get_parent_class();
@@ -2818,22 +3037,6 @@ void CSharpScript::initialize_for_managed_type(Ref<CSharpScript> p_script, GDMon
bool CSharpScript::can_instance() const {
#ifdef TOOLS_ENABLED
- if (Engine::get_singleton()->is_editor_hint()) {
-
- // Hack to lower the risk of attached scripts not being added to the C# project
- if (!get_path().empty() && get_path().find("::") == -1) { // Ignore if built-in script. Can happen if the file is deleted...
- if (_create_project_solution_if_needed()) {
- CSharpProject::add_item(GodotSharpDirs::get_project_csproj_path(),
- "Compile",
- ProjectSettings::get_singleton()->globalize_path(get_path()));
- } else {
- ERR_PRINT("C# project could not be created; cannot add file: '" + get_path() + "'.");
- }
- }
- }
-#endif
-
-#ifdef TOOLS_ENABLED
bool extra_cond = tool || ScriptServer::is_scripting_enabled();
#else
bool extra_cond = true;
@@ -2843,12 +3046,12 @@ bool CSharpScript::can_instance() const {
// For tool scripts, this will never fire if the class is not found. That's because we
// don't know if it's a tool script if we can't find the class to access the attributes.
if (extra_cond && !script_class) {
- if (GDMono::get_singleton()->get_project_assembly() == NULL) {
+ if (GDMono::get_singleton()->get_project_assembly() == nullptr) {
// The project assembly is not loaded
- ERR_FAIL_V_MSG(NULL, "Cannot instance script because the project assembly is not loaded. Script: '" + get_path() + "'.");
+ ERR_FAIL_V_MSG(false, "Cannot instance script because the project assembly is not loaded. Script: '" + get_path() + "'.");
} else {
// The project assembly is loaded, but the class could not found
- ERR_FAIL_V_MSG(NULL, "Cannot instance script because the class '" + name + "' could not be found. Script: '" + get_path() + "'.");
+ ERR_FAIL_V_MSG(false, "Cannot instance script because the class '" + name + "' could not be found. Script: '" + get_path() + "'.");
}
}
@@ -2871,13 +3074,13 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg
// Search the constructor first, to fail with an error if it's not found before allocating anything else.
GDMonoMethod *ctor = script_class->get_method(CACHED_STRING_NAME(dotctor), p_argcount);
- if (ctor == NULL) {
- ERR_FAIL_COND_V_MSG(p_argcount == 0, NULL,
+ if (ctor == nullptr) {
+ ERR_FAIL_COND_V_MSG(p_argcount == 0, nullptr,
"Cannot create script instance. The class '" + script_class->get_full_name() +
"' does not define a parameterless constructor." +
(get_path().empty() ? String() : " Path: '" + get_path() + "'."));
- ERR_FAIL_V_MSG(NULL, "Constructor not found.");
+ ERR_FAIL_V_MSG(nullptr, "Constructor not found.");
}
Ref<Reference> ref;
@@ -2889,13 +3092,13 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg
// If the object had a script instance binding, dispose it before adding the CSharpInstance
if (p_owner->has_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index())) {
void *data = p_owner->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index());
- CRASH_COND(data == NULL);
+ CRASH_COND(data == nullptr);
CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
- if (script_binding.inited && script_binding.gchandle.is_valid()) {
- MonoObject *mono_object = script_binding.gchandle->get_target();
+ if (script_binding.inited && !script_binding.gchandle.is_released()) {
+ MonoObject *mono_object = script_binding.gchandle.get_target();
if (mono_object) {
- MonoException *exc = NULL;
+ MonoException *exc = nullptr;
GDMonoUtils::dispose(mono_object, &exc);
if (exc) {
@@ -2903,13 +3106,13 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg
}
}
+ script_binding.gchandle.release(); // Just in case
script_binding.inited = false;
}
}
- CSharpInstance *instance = memnew(CSharpInstance);
+ CSharpInstance *instance = memnew(CSharpInstance(Ref<CSharpScript>(this)));
instance->base_ref = p_isref;
- instance->script = Ref<CSharpScript>(this);
instance->owner = p_owner;
instance->owner->set_script_instance(instance);
@@ -2920,19 +3123,19 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg
if (!mono_object) {
// Important to clear this before destroying the script instance here
instance->script = Ref<CSharpScript>();
- instance->owner = NULL;
+ instance->owner = nullptr;
bool die = instance->_unreference_owner_unsafe();
// Not ok for the owner to die here. If there is a situation where this can happen, it will be considered a bug.
CRASH_COND(die == true);
- p_owner->set_script_instance(NULL);
+ p_owner->set_script_instance(nullptr);
r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
- ERR_FAIL_V_MSG(NULL, "Failed to allocate memory for the object.");
+ ERR_FAIL_V_MSG(nullptr, "Failed to allocate memory for the object.");
}
// Tie managed to unmanaged
- instance->gchandle = MonoGCHandle::create_strong(mono_object);
+ instance->gchandle = MonoGCHandleData::new_strong_handle(mono_object);
if (instance->base_ref)
instance->_reference_owner_unsafe(); // Here, after assigning the gchandle (for the refcount_incremented callback)
@@ -2974,7 +3177,7 @@ Variant CSharpScript::_new(const Variant **p_args, int p_argcount, Callable::Cal
ref = REF(r);
}
- CSharpInstance *instance = _create_instance(p_args, p_argcount, owner, r != NULL, r_error);
+ CSharpInstance *instance = _create_instance(p_args, p_argcount, owner, r != nullptr, r_error);
if (!instance) {
if (ref.is_null()) {
memdelete(owner); //no owner, sorry
@@ -2996,20 +3199,22 @@ ScriptInstance *CSharpScript::instance_create(Object *p_this) {
#endif
if (native) {
- String native_name = NATIVE_GDMONOCLASS_NAME(native);
+ StringName native_name = NATIVE_GDMONOCLASS_NAME(native);
if (!ClassDB::is_parent_class(p_this->get_class_name(), native_name)) {
- if (ScriptDebugger::get_singleton()) {
- CSharpLanguage::get_singleton()->debug_break_parse(get_path(), 0, "Script inherits from native type '" + native_name + "', so it can't be instanced in object of type: '" + p_this->get_class() + "'");
+ if (EngineDebugger::is_active()) {
+ CSharpLanguage::get_singleton()->debug_break_parse(get_path(), 0,
+ "Script inherits from native type '" + String(native_name) +
+ "', so it can't be instanced in object of type: '" + p_this->get_class() + "'");
}
- ERR_FAIL_V_MSG(NULL, "Script inherits from native type '" + native_name +
- "', so it can't be instanced in object of type: '" + p_this->get_class() + "'.");
+ ERR_FAIL_V_MSG(nullptr, "Script inherits from native type '" + String(native_name) +
+ "', so it can't be instanced in object of type: '" + p_this->get_class() + "'.");
}
}
GD_MONO_SCOPE_THREAD_ATTACH;
Callable::CallError unchecked_error;
- return _create_instance(NULL, 0, p_this, Object::cast_to<Reference>(p_this) != NULL, unchecked_error);
+ return _create_instance(nullptr, 0, p_this, Object::cast_to<Reference>(p_this) != nullptr, unchecked_error);
}
PlaceHolderScriptInstance *CSharpScript::placeholder_instance_create(Object *p_this) {
@@ -3020,7 +3225,7 @@ PlaceHolderScriptInstance *CSharpScript::placeholder_instance_create(Object *p_t
_update_exports();
return si;
#else
- return NULL;
+ return nullptr;
#endif
}
@@ -3128,7 +3333,7 @@ Error CSharpScript::reload(bool p_keep_state) {
script_class = project_assembly->get_object_derived_class(name);
}
- valid = script_class != NULL;
+ valid = script_class != nullptr;
if (script_class) {
#ifdef DEBUG_ENABLED
@@ -3150,7 +3355,7 @@ Error CSharpScript::reload(bool p_keep_state) {
native = GDMonoUtils::get_class_native_base(script_class);
- CRASH_COND(native == NULL);
+ CRASH_COND(native == nullptr);
GDMonoClass *base_class = script_class->get_parent_class();
@@ -3288,19 +3493,45 @@ void CSharpScript::update_exports() {
}
bool CSharpScript::has_script_signal(const StringName &p_signal) const {
- return _signals.has(p_signal);
+ return event_signals.has(p_signal) || _signals.has(p_signal);
}
void CSharpScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
- for (const Map<StringName, Vector<Argument> >::Element *E = _signals.front(); E; E = E->next()) {
+
+ for (const Map<StringName, Vector<SignalParameter>>::Element *E = _signals.front(); E; E = E->next()) {
MethodInfo mi;
+ mi.name = E->key();
+
+ const Vector<SignalParameter> &params = E->value();
+ for (int i = 0; i < params.size(); i++) {
+ const SignalParameter &param = params[i];
+
+ PropertyInfo arg_info = PropertyInfo(param.type, param.name);
+ if (param.type == Variant::NIL && param.nil_is_variant)
+ arg_info.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
+
+ mi.arguments.push_back(arg_info);
+ }
+
+ r_signals->push_back(mi);
+ }
+ for (const Map<StringName, EventSignal>::Element *E = event_signals.front(); E; E = E->next()) {
+ MethodInfo mi;
mi.name = E->key();
- for (int i = 0; i < E->get().size(); i++) {
- PropertyInfo arg;
- arg.name = E->get()[i].name;
- mi.arguments.push_back(arg);
+
+ const EventSignal &event_signal = E->value();
+ const Vector<SignalParameter> &params = event_signal.parameters;
+ for (int i = 0; i < params.size(); i++) {
+ const SignalParameter &param = params[i];
+
+ PropertyInfo arg_info = PropertyInfo(param.type, param.name);
+ if (param.type == Variant::NIL && param.nil_is_variant)
+ arg_info.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
+
+ mi.arguments.push_back(arg_info);
}
+
r_signals->push_back(mi);
}
}
@@ -3418,19 +3649,10 @@ StringName CSharpScript::get_script_name() const {
return name;
}
-CSharpScript::CSharpScript() :
- script_list(this) {
+CSharpScript::CSharpScript() {
_clear();
-#ifdef TOOLS_ENABLED
- source_changed_cache = false;
- placeholder_fallback_enabled = false;
- exports_invalidated = true;
-#endif
-
- signals_invalidated = true;
-
_resource_path_changed();
#ifdef DEBUG_ENABLED
@@ -3545,7 +3767,7 @@ void ResourceFormatSaverCSharpScript::get_recognized_extensions(const RES &p_res
bool ResourceFormatSaverCSharpScript::recognize(const RES &p_resource) const {
- return Object::cast_to<CSharpScript>(p_resource.ptr()) != NULL;
+ return Object::cast_to<CSharpScript>(p_resource.ptr()) != nullptr;
}
CSharpLanguage::StringNameCache::StringNameCache() {
@@ -3559,4 +3781,5 @@ CSharpLanguage::StringNameCache::StringNameCache() {
on_before_serialize = StaticCString::create("OnBeforeSerialize");
on_after_deserialize = StaticCString::create("OnAfterDeserialize");
dotctor = StaticCString::create(".ctor");
+ delegate_invoke_method_name = StaticCString::create("Invoke");
}
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index 18c53aab52..29c33b50bb 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -53,8 +53,8 @@ class CSharpLanguage;
template <typename TScriptInstance, typename TScriptLanguage>
TScriptInstance *cast_script_instance(ScriptInstance *p_inst) {
if (!p_inst)
- return NULL;
- return p_inst->get_language() == TScriptLanguage::get_singleton() ? static_cast<TScriptInstance *>(p_inst) : NULL;
+ return nullptr;
+ return p_inst->get_language() == TScriptLanguage::get_singleton() ? static_cast<TScriptInstance *>(p_inst) : nullptr;
}
#else
template <typename TScriptInstance, typename TScriptLanguage>
@@ -69,18 +69,32 @@ class CSharpScript : public Script {
GDCLASS(CSharpScript, Script);
+public:
+ struct SignalParameter {
+ String name;
+ Variant::Type type;
+ bool nil_is_variant = false;
+ };
+
+ struct EventSignal {
+ GDMonoField *field = nullptr;
+ GDMonoMethod *invoke_method = nullptr;
+ Vector<SignalParameter> parameters;
+ };
+
+private:
friend class CSharpInstance;
friend class CSharpLanguage;
friend struct CSharpScriptDepSort;
- bool tool;
- bool valid;
+ bool tool = false;
+ bool valid = false;
bool builtin;
- GDMonoClass *base;
- GDMonoClass *native;
- GDMonoClass *script_class;
+ GDMonoClass *base = nullptr;
+ GDMonoClass *native = nullptr;
+ GDMonoClass *script_class = nullptr;
Ref<CSharpScript> base_cache; // TODO what's this for?
@@ -91,7 +105,8 @@ class CSharpScript : public Script {
// TODO
// Replace with buffer containing the serialized state of managed scripts.
// Keep variant state backup to use only with script instance placeholders.
- List<Pair<StringName, Variant> > properties;
+ List<Pair<StringName, Variant>> properties;
+ List<Pair<StringName, Array>> event_signals;
};
Set<ObjectID> pending_reload_instances;
@@ -103,15 +118,11 @@ class CSharpScript : public Script {
String source;
StringName name;
- SelfList<CSharpScript> script_list;
-
- struct Argument {
- String name;
- Variant::Type type;
- };
+ SelfList<CSharpScript> script_list = this;
- Map<StringName, Vector<Argument> > _signals;
- bool signals_invalidated;
+ Map<StringName, Vector<SignalParameter>> _signals;
+ Map<StringName, EventSignal> event_signals;
+ bool signals_invalidated = true;
Vector<ScriptNetData> rpc_functions;
Vector<ScriptNetData> rpc_variables;
@@ -120,9 +131,9 @@ class CSharpScript : public Script {
List<PropertyInfo> exported_members_cache; // members_cache
Map<StringName, Variant> exported_members_defval_cache; // member_default_values_cache
Set<PlaceHolderScriptInstance *> placeholders;
- bool source_changed_cache;
- bool placeholder_fallback_enabled;
- bool exports_invalidated;
+ bool source_changed_cache = false;
+ bool placeholder_fallback_enabled = false;
+ bool exports_invalidated = true;
void _update_exports_values(Map<StringName, Variant> &values, List<PropertyInfo> &propnames);
void _update_member_info_no_exports();
virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder);
@@ -133,7 +144,7 @@ class CSharpScript : public Script {
void _clear();
void load_script_signals(GDMonoClass *p_class, GDMonoClass *p_native_class);
- bool _get_signal(GDMonoClass *p_class, GDMonoClass *p_delegate, Vector<Argument> &params);
+ bool _get_signal(GDMonoClass *p_class, GDMonoMethod *p_delegate_invoke, Vector<SignalParameter> &params);
bool _update_exports();
#ifdef TOOLS_ENABLED
@@ -221,15 +232,17 @@ class CSharpInstance : public ScriptInstance {
friend class CSharpScript;
friend class CSharpLanguage;
- Object *owner;
- bool base_ref;
- bool ref_dying;
- bool unsafe_referenced;
- bool predelete_notified;
- bool destructing_script_instance;
+ Object *owner = nullptr;
+ bool base_ref = false;
+ bool ref_dying = false;
+ bool unsafe_referenced = false;
+ bool predelete_notified = false;
+ bool destructing_script_instance = false;
Ref<CSharpScript> script;
- Ref<MonoGCHandle> gchandle;
+ MonoGCHandleData gchandle;
+
+ Vector<Callable> event_signal_callables;
bool _reference_owner_unsafe();
@@ -239,17 +252,18 @@ class CSharpInstance : public ScriptInstance {
bool _unreference_owner_unsafe();
/*
- * If NULL is returned, the caller must destroy the script instance by removing it from its owner.
+ * If nullptr is returned, the caller must destroy the script instance by removing it from its owner.
*/
MonoObject *_internal_new_managed();
// Do not use unless you know what you are doing
friend void GDMonoInternals::tie_managed_to_unmanaged(MonoObject *, Object *);
- static CSharpInstance *create_for_managed_type(Object *p_owner, CSharpScript *p_script, const Ref<MonoGCHandle> &p_gchandle);
+ static CSharpInstance *create_for_managed_type(Object *p_owner, CSharpScript *p_script, const MonoGCHandleData &p_gchandle);
void _call_multilevel(MonoObject *p_mono_object, const StringName &p_method, const Variant **p_args, int p_argcount);
- void get_properties_state_for_reloading(List<Pair<StringName, Variant> > &r_state);
+ void get_properties_state_for_reloading(List<Pair<StringName, Variant>> &r_state);
+ void get_event_signals_state_for_reloading(List<Pair<StringName, Array>> &r_state);
public:
MonoObject *get_mono_object() const;
@@ -272,11 +286,14 @@ public:
void mono_object_disposed(MonoObject *p_obj);
/*
- * If 'r_delete_owner' is set to true, the caller must memdelete the script instance's owner. Otherwise, if
+ * If 'r_delete_owner' is set to true, the caller must memdelete the script instance's owner. Otherwise, ifevent_signal
* 'r_remove_script_instance' is set to true, the caller must destroy the script instance by removing it from its owner.
*/
void mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_finalizer, bool &r_delete_owner, bool &r_remove_script_instance);
+ void connect_event_signals();
+ void disconnect_event_signals();
+
virtual void refcount_incremented();
virtual bool refcount_decremented();
@@ -301,7 +318,7 @@ public:
virtual ScriptLanguage *get_language();
- CSharpInstance();
+ CSharpInstance(const Ref<CSharpScript> &p_script);
~CSharpInstance();
};
@@ -309,8 +326,18 @@ struct CSharpScriptBinding {
bool inited;
StringName type_name;
GDMonoClass *wrapper_class;
- Ref<MonoGCHandle> gchandle;
+ MonoGCHandleData gchandle;
Object *owner;
+
+ CSharpScriptBinding() :
+ inited(false),
+ wrapper_class(nullptr),
+ owner(nullptr) {
+ }
+};
+
+class ManagedCallableMiddleman : public Object {
+ GDCLASS(ManagedCallableMiddleman, Object);
};
class CSharpLanguage : public ScriptLanguage {
@@ -320,9 +347,10 @@ class CSharpLanguage : public ScriptLanguage {
static CSharpLanguage *singleton;
- bool finalizing;
+ bool finalizing = false;
+ bool finalized = false;
- GDMono *gdmono;
+ GDMono *gdmono = nullptr;
SelfList<CSharpScript>::List script_list;
Mutex script_instances_mutex;
@@ -337,6 +365,8 @@ class CSharpLanguage : public ScriptLanguage {
Mutex unsafe_object_references_lock;
#endif
+ ManagedCallableMiddleman *managed_callable_middleman = memnew(ManagedCallableMiddleman);
+
struct StringNameCache {
StringName _signal_callback;
@@ -348,17 +378,18 @@ class CSharpLanguage : public ScriptLanguage {
StringName dotctor; // .ctor
StringName on_before_serialize; // OnBeforeSerialize
StringName on_after_deserialize; // OnAfterDeserialize
+ StringName delegate_invoke_method_name;
StringNameCache();
};
- int lang_idx;
+ int lang_idx = -1;
Dictionary scripts_metadata;
- bool scripts_metadata_invalidated;
+ bool scripts_metadata_invalidated = true;
// For debug_break and debug_break_parse
- int _debug_parse_err_line;
+ int _debug_parse_err_line = -1;
String _debug_parse_err_file;
String _debug_error;
@@ -368,7 +399,7 @@ class CSharpLanguage : public ScriptLanguage {
void _on_scripts_domain_unloaded();
#ifdef TOOLS_ENABLED
- EditorPlugin *godotsharp_editor;
+ EditorPlugin *godotsharp_editor = nullptr;
static void _editor_init_callback();
#endif
@@ -389,8 +420,8 @@ public:
_FORCE_INLINE_ EditorPlugin *get_godotsharp_editor() const { return godotsharp_editor; }
#endif
- static void release_script_gchandle(Ref<MonoGCHandle> &p_gchandle);
- static void release_script_gchandle(MonoObject *p_expected_obj, Ref<MonoGCHandle> &p_gchandle);
+ static void release_script_gchandle(MonoGCHandleData &p_gchandle);
+ static void release_script_gchandle(MonoObject *p_expected_obj, MonoGCHandleData &p_gchandle);
bool debug_break(const String &p_error, bool p_allow_continue = true);
bool debug_break_parse(const String &p_file, int p_line, const String &p_error);
@@ -410,6 +441,8 @@ public:
return scripts_metadata;
}
+ _FORCE_INLINE_ ManagedCallableMiddleman *get_managed_callable_middleman() const { return managed_callable_middleman; }
+
virtual String get_name() const;
/* LANGUAGE FUNCTIONS */
@@ -426,7 +459,7 @@ public:
virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const;
virtual bool is_using_templates();
virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script);
- /* TODO */ virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, List<ScriptLanguage::Warning> *r_warnings = NULL, Set<int> *r_safe_lines = NULL) const { return true; }
+ /* TODO */ virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, List<ScriptLanguage::Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const { return true; }
virtual String validate_path(const String &p_path) const;
virtual Script *create_script() const;
virtual bool has_named_classes() const;
@@ -458,7 +491,7 @@ public:
virtual void frame();
/* TODO? */ virtual void get_public_functions(List<MethodInfo> *p_functions) const {}
- /* TODO? */ virtual void get_public_constants(List<Pair<String, Variant> > *p_constants) const {}
+ /* TODO? */ virtual void get_public_constants(List<Pair<String, Variant>> *p_constants) const {}
virtual void reload_all_scripts();
virtual void reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload);
@@ -497,7 +530,7 @@ public:
class ResourceFormatLoaderCSharpScript : public ResourceFormatLoader {
public:
- virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL, bool p_use_sub_threads = false, float *r_progress = nullptr);
+ virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
diff --git a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/Properties/AssemblyInfo.cs b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/Properties/AssemblyInfo.cs
index 8717c4901e..4374f21cfa 100644
--- a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/Properties/AssemblyInfo.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/Properties/AssemblyInfo.cs
@@ -1,4 +1,4 @@
-using System.Reflection;
+using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
diff --git a/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs b/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs
index b531b6aeee..326c49f096 100644
--- a/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs
@@ -30,7 +30,7 @@ namespace GodotTools.Core
path = string.Join(Path.DirectorySeparatorChar.ToString(), parts).Trim();
- return rooted ? Path.DirectorySeparatorChar.ToString() + path : path;
+ return rooted ? Path.DirectorySeparatorChar + path : path;
}
private static readonly string driveRoot = Path.GetPathRoot(Environment.CurrentDirectory);
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeConnection/Properties/AssemblyInfo.cs b/modules/mono/editor/GodotTools/GodotTools.IdeConnection/Properties/AssemblyInfo.cs
index c7c00e66a2..0806d02ca0 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeConnection/Properties/AssemblyInfo.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeConnection/Properties/AssemblyInfo.cs
@@ -1,4 +1,4 @@
-using System.Reflection;
+using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs
index 76cb249acf..9afd9adeb1 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs
@@ -1,6 +1,8 @@
using GodotTools.Core;
using System.Collections.Generic;
using System.IO;
+using System.Linq;
+using System.Text.RegularExpressions;
namespace GodotTools.ProjectEditor
{
@@ -118,5 +120,40 @@ EndProject";
const string ProjectPlatformsConfig =
@" {{{0}}}.{1}|Any CPU.ActiveCfg = {1}|Any CPU
{{{0}}}.{1}|Any CPU.Build.0 = {1}|Any CPU";
+
+ public static void MigrateFromOldConfigNames(string slnPath)
+ {
+ if (!File.Exists(slnPath))
+ return;
+
+ var input = File.ReadAllText(slnPath);
+
+ if (!Regex.IsMatch(input, Regex.Escape("Tools|Any CPU")))
+ return;
+
+ // This method renames old configurations in solutions to the new ones.
+ //
+ // This is the order configs appear in the solution and what we want to rename them to:
+ // Debug|Any CPU = Debug|Any CPU -> ExportDebug|Any CPU = ExportDebug|Any CPU
+ // Tools|Any CPU = Tools|Any CPU -> Debug|Any CPU = Debug|Any CPU
+ //
+ // But we want to move Tools (now Debug) to the top, so it's easier to rename like this:
+ // Debug|Any CPU = Debug|Any CPU -> Debug|Any CPU = Debug|Any CPU
+ // Release|Any CPU = Release|Any CPU -> ExportDebug|Any CPU = ExportDebug|Any CPU
+ // Tools|Any CPU = Tools|Any CPU -> ExportRelease|Any CPU = ExportRelease|Any CPU
+
+ var dict = new Dictionary<string, string>
+ {
+ {"Debug|Any CPU", "Debug|Any CPU"},
+ {"Release|Any CPU", "ExportDebug|Any CPU"},
+ {"Tools|Any CPU", "ExportRelease|Any CPU"}
+ };
+
+ var regex = new Regex(string.Join("|",dict.Keys.Select(Regex.Escape)));
+ var result = regex.Replace(input,m => dict[m.Value]);
+
+ if (result != input)
+ File.WriteAllText(slnPath, result);
+ }
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectExtensions.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectExtensions.cs
index 36961eb45e..f0e0d1b33d 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectExtensions.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectExtensions.cs
@@ -1,5 +1,7 @@
using GodotTools.Core;
using System;
+using System.Collections.Generic;
+using System.IO;
using DotNet.Globbing;
using Microsoft.Build.Construction;
@@ -7,16 +9,15 @@ namespace GodotTools.ProjectEditor
{
public static class ProjectExtensions
{
- public static bool HasItem(this ProjectRootElement root, string itemType, string include)
+ public static ProjectItemElement FindItemOrNull(this ProjectRootElement root, string itemType, string include, bool noCondition = false)
{
- GlobOptions globOptions = new GlobOptions();
- globOptions.Evaluation.CaseInsensitive = false;
+ GlobOptions globOptions = new GlobOptions {Evaluation = {CaseInsensitive = false}};
string normalizedInclude = include.NormalizePath();
foreach (var itemGroup in root.ItemGroups)
{
- if (itemGroup.Condition.Length != 0)
+ if (noCondition && itemGroup.Condition.Length != 0)
continue;
foreach (var item in itemGroup.Items)
@@ -27,18 +28,65 @@ namespace GodotTools.ProjectEditor
var glob = Glob.Parse(item.Include.NormalizePath(), globOptions);
if (glob.IsMatch(normalizedInclude))
- {
- return true;
- }
+ return item;
}
}
- return false;
+ return null;
+ }
+ public static ProjectItemElement FindItemOrNullAbs(this ProjectRootElement root, string itemType, string include, bool noCondition = false)
+ {
+ GlobOptions globOptions = new GlobOptions {Evaluation = {CaseInsensitive = false}};
+
+ string normalizedInclude = Path.GetFullPath(include).NormalizePath();
+
+ foreach (var itemGroup in root.ItemGroups)
+ {
+ if (noCondition && itemGroup.Condition.Length != 0)
+ continue;
+
+ foreach (var item in itemGroup.Items)
+ {
+ if (item.ItemType != itemType)
+ continue;
+
+ var glob = Glob.Parse(Path.GetFullPath(item.Include).NormalizePath(), globOptions);
+
+ if (glob.IsMatch(normalizedInclude))
+ return item;
+ }
+ }
+
+ return null;
+ }
+
+ public static IEnumerable<ProjectItemElement> FindAllItemsInFolder(this ProjectRootElement root, string itemType, string folder)
+ {
+ string absFolderNormalizedWithSep = Path.GetFullPath(folder).NormalizePath() + Path.DirectorySeparatorChar;
+
+ foreach (var itemGroup in root.ItemGroups)
+ {
+ foreach (var item in itemGroup.Items)
+ {
+ if (item.ItemType != itemType)
+ continue;
+
+ string absPathNormalized = Path.GetFullPath(item.Include).NormalizePath();
+
+ if (absPathNormalized.StartsWith(absFolderNormalizedWithSep))
+ yield return item;
+ }
+ }
+ }
+
+ public static bool HasItem(this ProjectRootElement root, string itemType, string include, bool noCondition = false)
+ {
+ return root.FindItemOrNull(itemType, include, noCondition) != null;
}
public static bool AddItemChecked(this ProjectRootElement root, string itemType, string include)
{
- if (!root.HasItem(itemType, include))
+ if (!root.HasItem(itemType, include, noCondition: true))
{
root.AddItem(itemType, include);
return true;
@@ -47,6 +95,18 @@ namespace GodotTools.ProjectEditor
return false;
}
+ public static bool RemoveItemChecked(this ProjectRootElement root, string itemType, string include)
+ {
+ var item = root.FindItemOrNullAbs(itemType, include);
+ if (item != null)
+ {
+ item.Parent.RemoveChild(item);
+ return true;
+ }
+
+ return false;
+ }
+
public static Guid GetGuid(this ProjectRootElement root)
{
foreach (var property in root.Properties)
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs
index 28b7832f90..cbe3afaedd 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs
@@ -17,30 +17,30 @@ namespace GodotTools.ProjectEditor
string path = Path.Combine(dir, name + ".csproj");
ProjectPropertyGroupElement mainGroup;
- var root = CreateLibraryProject(name, "Tools", out mainGroup);
+ var root = CreateLibraryProject(name, "Debug", out mainGroup);
mainGroup.SetProperty("OutputPath", Path.Combine(".mono", "temp", "bin", "$(Configuration)"));
mainGroup.SetProperty("BaseIntermediateOutputPath", Path.Combine(".mono", "temp", "obj"));
mainGroup.SetProperty("IntermediateOutputPath", Path.Combine("$(BaseIntermediateOutputPath)", "$(Configuration)"));
- mainGroup.SetProperty("ApiConfiguration", "Debug").Condition = " '$(Configuration)' != 'Release' ";
- mainGroup.SetProperty("ApiConfiguration", "Release").Condition = " '$(Configuration)' == 'Release' ";
-
- var toolsGroup = root.AddPropertyGroup();
- toolsGroup.Condition = " '$(Configuration)|$(Platform)' == 'Tools|AnyCPU' ";
- toolsGroup.AddProperty("DebugSymbols", "true");
- toolsGroup.AddProperty("DebugType", "portable");
- toolsGroup.AddProperty("Optimize", "false");
- toolsGroup.AddProperty("DefineConstants", "$(GodotDefineConstants);GODOT;DEBUG;TOOLS;");
- toolsGroup.AddProperty("ErrorReport", "prompt");
- toolsGroup.AddProperty("WarningLevel", "4");
- toolsGroup.AddProperty("ConsolePause", "false");
+ mainGroup.SetProperty("ApiConfiguration", "Debug").Condition = " '$(Configuration)' != 'ExportRelease' ";
+ mainGroup.SetProperty("ApiConfiguration", "Release").Condition = " '$(Configuration)' == 'ExportRelease' ";
+
+ var debugGroup = root.AddPropertyGroup();
+ debugGroup.Condition = " '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ";
+ debugGroup.AddProperty("DebugSymbols", "true");
+ debugGroup.AddProperty("DebugType", "portable");
+ debugGroup.AddProperty("Optimize", "false");
+ debugGroup.AddProperty("DefineConstants", "$(GodotDefineConstants);GODOT;DEBUG;TOOLS;");
+ debugGroup.AddProperty("ErrorReport", "prompt");
+ debugGroup.AddProperty("WarningLevel", "4");
+ debugGroup.AddProperty("ConsolePause", "false");
var coreApiRef = root.AddItem("Reference", CoreApiProjectName);
coreApiRef.AddMetadata("HintPath", Path.Combine("$(ProjectDir)", ".mono", "assemblies", "$(ApiConfiguration)", CoreApiProjectName + ".dll"));
coreApiRef.AddMetadata("Private", "False");
var editorApiRef = root.AddItem("Reference", EditorApiProjectName);
- editorApiRef.Condition = " '$(Configuration)' == 'Tools' ";
+ editorApiRef.Condition = " '$(Configuration)' == 'Debug' ";
editorApiRef.AddMetadata("HintPath", Path.Combine("$(ProjectDir)", ".mono", "assemblies", "$(ApiConfiguration)", EditorApiProjectName + ".dll"));
editorApiRef.AddMetadata("Private", "False");
@@ -103,24 +103,24 @@ namespace GodotTools.ProjectEditor
mainGroup.AddProperty("TargetFrameworkVersion", "v4.7");
mainGroup.AddProperty("GodotProjectGeneratorVersion", Assembly.GetExecutingAssembly().GetName().Version.ToString());
- var debugGroup = root.AddPropertyGroup();
- debugGroup.Condition = " '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ";
- debugGroup.AddProperty("DebugSymbols", "true");
- debugGroup.AddProperty("DebugType", "portable");
- debugGroup.AddProperty("Optimize", "false");
- debugGroup.AddProperty("DefineConstants", "$(GodotDefineConstants);GODOT;DEBUG;");
- debugGroup.AddProperty("ErrorReport", "prompt");
- debugGroup.AddProperty("WarningLevel", "4");
- debugGroup.AddProperty("ConsolePause", "false");
-
- var releaseGroup = root.AddPropertyGroup();
- releaseGroup.Condition = " '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ";
- releaseGroup.AddProperty("DebugType", "portable");
- releaseGroup.AddProperty("Optimize", "true");
- releaseGroup.AddProperty("DefineConstants", "$(GodotDefineConstants);GODOT;");
- releaseGroup.AddProperty("ErrorReport", "prompt");
- releaseGroup.AddProperty("WarningLevel", "4");
- releaseGroup.AddProperty("ConsolePause", "false");
+ var exportDebugGroup = root.AddPropertyGroup();
+ exportDebugGroup.Condition = " '$(Configuration)|$(Platform)' == 'ExportDebug|AnyCPU' ";
+ exportDebugGroup.AddProperty("DebugSymbols", "true");
+ exportDebugGroup.AddProperty("DebugType", "portable");
+ exportDebugGroup.AddProperty("Optimize", "false");
+ exportDebugGroup.AddProperty("DefineConstants", "$(GodotDefineConstants);GODOT;DEBUG;");
+ exportDebugGroup.AddProperty("ErrorReport", "prompt");
+ exportDebugGroup.AddProperty("WarningLevel", "4");
+ exportDebugGroup.AddProperty("ConsolePause", "false");
+
+ var exportReleaseGroup = root.AddPropertyGroup();
+ exportReleaseGroup.Condition = " '$(Configuration)|$(Platform)' == 'ExportRelease|AnyCPU' ";
+ exportReleaseGroup.AddProperty("DebugType", "portable");
+ exportReleaseGroup.AddProperty("Optimize", "true");
+ exportReleaseGroup.AddProperty("DefineConstants", "$(GodotDefineConstants);GODOT;");
+ exportReleaseGroup.AddProperty("ErrorReport", "prompt");
+ exportReleaseGroup.AddProperty("WarningLevel", "4");
+ exportReleaseGroup.AddProperty("ConsolePause", "false");
// References
var referenceGroup = root.AddItemGroup();
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs
index 233aab45b3..1776b46e6a 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
+using System.Reflection;
using DotNet.Globbing;
using Microsoft.Build.Construction;
@@ -22,6 +23,79 @@ namespace GodotTools.ProjectEditor
root.Save();
}
+ public static void RenameItemInProjectChecked(string projectPath, string itemType, string oldInclude, string newInclude)
+ {
+ var dir = Directory.GetParent(projectPath).FullName;
+ var root = ProjectRootElement.Open(projectPath);
+ Debug.Assert(root != null);
+
+ var normalizedOldInclude = oldInclude.NormalizePath();
+ var normalizedNewInclude = newInclude.NormalizePath();
+
+ var item = root.FindItemOrNullAbs(itemType, normalizedOldInclude);
+
+ if (item == null)
+ return;
+
+ item.Include = normalizedNewInclude.RelativeToPath(dir).Replace("/", "\\");
+ root.Save();
+ }
+
+ public static void RemoveItemFromProjectChecked(string projectPath, string itemType, string include)
+ {
+ var dir = Directory.GetParent(projectPath).FullName;
+ var root = ProjectRootElement.Open(projectPath);
+ Debug.Assert(root != null);
+
+ var normalizedInclude = include.NormalizePath();
+
+ if (root.RemoveItemChecked(itemType, normalizedInclude))
+ root.Save();
+ }
+
+ public static void RenameItemsToNewFolderInProjectChecked(string projectPath, string itemType, string oldFolder, string newFolder)
+ {
+ var dir = Directory.GetParent(projectPath).FullName;
+ var root = ProjectRootElement.Open(projectPath);
+ Debug.Assert(root != null);
+
+ bool dirty = false;
+
+ var oldFolderNormalized = oldFolder.NormalizePath();
+ var newFolderNormalized = newFolder.NormalizePath();
+ string absOldFolderNormalized = Path.GetFullPath(oldFolderNormalized).NormalizePath();
+ string absNewFolderNormalized = Path.GetFullPath(newFolderNormalized).NormalizePath();
+
+ foreach (var item in root.FindAllItemsInFolder(itemType, oldFolderNormalized))
+ {
+ string absPathNormalized = Path.GetFullPath(item.Include).NormalizePath();
+ string absNewIncludeNormalized = absNewFolderNormalized + absPathNormalized.Substring(absOldFolderNormalized.Length);
+ item.Include = absNewIncludeNormalized.RelativeToPath(dir).Replace("/", "\\");
+ dirty = true;
+ }
+
+ if (dirty)
+ root.Save();
+ }
+
+ public static void RemoveItemsInFolderFromProjectChecked(string projectPath, string itemType, string folder)
+ {
+ var root = ProjectRootElement.Open(projectPath);
+ Debug.Assert(root != null);
+
+ var folderNormalized = folder.NormalizePath();
+
+ var itemsToRemove = root.FindAllItemsInFolder(itemType, folderNormalized).ToList();
+
+ if (itemsToRemove.Count > 0)
+ {
+ foreach (var item in itemsToRemove)
+ item.Parent.RemoveChild(item);
+
+ root.Save();
+ }
+ }
+
private static string[] GetAllFilesRecursive(string rootDirectory, string mask)
{
string[] files = Directory.GetFiles(rootDirectory, mask, SearchOption.AllDirectories);
@@ -44,6 +118,7 @@ namespace GodotTools.ProjectEditor
globOptions.Evaluation.CaseInsensitive = false;
var root = ProjectRootElement.Open(projectPath);
+ Debug.Assert(root != null);
foreach (var itemGroup in root.ItemGroups)
{
@@ -85,35 +160,35 @@ namespace GodotTools.ProjectEditor
void AddPropertyIfNotPresent(string name, string condition, string value)
{
if (root.PropertyGroups
- .Any(g => (g.Condition == string.Empty || g.Condition == condition) &&
+ .Any(g => (g.Condition == string.Empty || g.Condition.Trim() == condition) &&
g.Properties
.Any(p => p.Name == name &&
p.Value == value &&
- (p.Condition == condition || g.Condition == condition))))
+ (p.Condition.Trim() == condition || g.Condition.Trim() == condition))))
{
return;
}
- root.AddProperty(name, value).Condition = condition;
+ root.AddProperty(name, value).Condition = " " + condition + " ";
dirty = true;
}
AddPropertyIfNotPresent(name: "ApiConfiguration",
- condition: " '$(Configuration)' != 'Release' ",
+ condition: "'$(Configuration)' != 'ExportRelease'",
value: "Debug");
AddPropertyIfNotPresent(name: "ApiConfiguration",
- condition: " '$(Configuration)' == 'Release' ",
+ condition: "'$(Configuration)' == 'ExportRelease'",
value: "Release");
void SetReferenceHintPath(string referenceName, string condition, string hintPath)
{
foreach (var itemGroup in root.ItemGroups.Where(g =>
- g.Condition == string.Empty || g.Condition == condition))
+ g.Condition.Trim() == string.Empty || g.Condition.Trim() == condition))
{
var references = itemGroup.Items.Where(item =>
item.ItemType == "Reference" &&
item.Include == referenceName &&
- (item.Condition == condition || itemGroup.Condition == condition));
+ (item.Condition.Trim() == condition || itemGroup.Condition.Trim() == condition));
var referencesWithHintPath = references.Where(reference =>
reference.Metadata.Any(m => m.Name == "HintPath"));
@@ -152,7 +227,7 @@ namespace GodotTools.ProjectEditor
}
// Found no Reference item at all. Add it.
- root.AddItem("Reference", referenceName).Condition = condition;
+ root.AddItem("Reference", referenceName).Condition = " " + condition + " ";
dirty = true;
}
@@ -160,7 +235,7 @@ namespace GodotTools.ProjectEditor
const string editorProjectName = "GodotSharpEditor";
const string coreCondition = "";
- const string editorCondition = " '$(Configuration)' == 'Tools' ";
+ const string editorCondition = "'$(Configuration)' == 'Debug'";
var coreHintPath = $"$(ProjectDir)/.mono/assemblies/$(ApiConfiguration)/{coreProjectName}.dll";
var editorHintPath = $"$(ProjectDir)/.mono/assemblies/$(ApiConfiguration)/{editorProjectName}.dll";
@@ -171,5 +246,104 @@ namespace GodotTools.ProjectEditor
if (dirty)
root.Save();
}
+
+ public static void MigrateFromOldConfigNames(string projectPath)
+ {
+ var root = ProjectRootElement.Open(projectPath);
+ Debug.Assert(root != null);
+
+ bool dirty = false;
+
+ bool hasGodotProjectGeneratorVersion = false;
+ bool foundOldConfiguration = false;
+
+ foreach (var propertyGroup in root.PropertyGroups.Where(g => g.Condition == string.Empty))
+ {
+ if (!hasGodotProjectGeneratorVersion && propertyGroup.Properties.Any(p => p.Name == "GodotProjectGeneratorVersion"))
+ hasGodotProjectGeneratorVersion = true;
+
+ foreach (var configItem in propertyGroup.Properties
+ .Where(p => p.Condition.Trim() == "'$(Configuration)' == ''" && p.Value == "Tools"))
+ {
+ configItem.Value = "Debug";
+ foundOldConfiguration = true;
+ dirty = true;
+ }
+ }
+
+ if (!hasGodotProjectGeneratorVersion)
+ {
+ root.PropertyGroups.First(g => g.Condition == string.Empty)?
+ .AddProperty("GodotProjectGeneratorVersion", Assembly.GetExecutingAssembly().GetName().Version.ToString());
+ dirty = true;
+ }
+
+ if (!foundOldConfiguration)
+ {
+ var toolsConditions = new[]
+ {
+ "'$(Configuration)|$(Platform)' == 'Tools|AnyCPU'",
+ "'$(Configuration)|$(Platform)' != 'Tools|AnyCPU'",
+ "'$(Configuration)' == 'Tools'",
+ "'$(Configuration)' != 'Tools'"
+ };
+
+ foundOldConfiguration = root.PropertyGroups
+ .Any(g => toolsConditions.Any(c => c == g.Condition.Trim()));
+ }
+
+ if (foundOldConfiguration)
+ {
+ void MigrateConfigurationConditions(string oldConfiguration, string newConfiguration)
+ {
+ void MigrateConditions(string oldCondition, string newCondition)
+ {
+ foreach (var propertyGroup in root.PropertyGroups.Where(g => g.Condition.Trim() == oldCondition))
+ {
+ propertyGroup.Condition = " " + newCondition + " ";
+ dirty = true;
+ }
+
+ foreach (var propertyGroup in root.PropertyGroups)
+ {
+ foreach (var prop in propertyGroup.Properties.Where(p => p.Condition.Trim() == oldCondition))
+ {
+ prop.Condition = " " + newCondition + " ";
+ dirty = true;
+ }
+ }
+
+ foreach (var itemGroup in root.ItemGroups.Where(g => g.Condition.Trim() == oldCondition))
+ {
+ itemGroup.Condition = " " + newCondition + " ";
+ dirty = true;
+ }
+
+ foreach (var itemGroup in root.ItemGroups)
+ {
+ foreach (var item in itemGroup.Items.Where(item => item.Condition.Trim() == oldCondition))
+ {
+ item.Condition = " " + newCondition + " ";
+ dirty = true;
+ }
+ }
+ }
+
+ foreach (var op in new[] {"==", "!="})
+ {
+ MigrateConditions($"'$(Configuration)|$(Platform)' {op} '{oldConfiguration}|AnyCPU'", $"'$(Configuration)|$(Platform)' {op} '{newConfiguration}|AnyCPU'");
+ MigrateConditions($"'$(Configuration)' {op} '{oldConfiguration}'", $"'$(Configuration)' {op} '{newConfiguration}'");
+ }
+ }
+
+ MigrateConfigurationConditions("Debug", "ExportDebug");
+ MigrateConfigurationConditions("Release", "ExportRelease");
+ MigrateConfigurationConditions("Tools", "Debug"); // Must be last
+ }
+
+
+ if (dirty)
+ root.Save();
+ }
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/Properties/AssemblyInfo.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/Properties/AssemblyInfo.cs
index 09333850fc..3a0464c9bc 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/Properties/AssemblyInfo.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/Properties/AssemblyInfo.cs
@@ -1,4 +1,4 @@
-using System.Reflection;
+using System.Reflection;
using System.Runtime.CompilerServices;
// Information about this assembly is defined by the following attributes.
diff --git a/modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs b/modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs
index bd7eb59913..3cf495f025 100644
--- a/modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs
@@ -166,7 +166,7 @@ namespace GodotTools
Internal.GodotIs32Bits() ? "32" : "64"
};
- bool buildSuccess = BuildManager.BuildProjectBlocking("Tools", godotDefines);
+ bool buildSuccess = BuildManager.BuildProjectBlocking("Debug", godotDefines);
if (!buildSuccess)
return;
@@ -205,9 +205,9 @@ namespace GodotTools
if (what == EditorSettings.NotificationEditorSettingsChanged)
{
var editorBaseControl = editorInterface.GetBaseControl();
- panelTabs.AddStyleboxOverride("panel", editorBaseControl.GetStylebox("DebuggerPanel", "EditorStyles"));
- panelTabs.AddStyleboxOverride("tab_fg", editorBaseControl.GetStylebox("DebuggerTabFG", "EditorStyles"));
- panelTabs.AddStyleboxOverride("tab_bg", editorBaseControl.GetStylebox("DebuggerTabBG", "EditorStyles"));
+ panelTabs.AddThemeStyleboxOverride("panel", editorBaseControl.GetThemeStylebox("DebuggerPanel", "EditorStyles"));
+ panelTabs.AddThemeStyleboxOverride("tab_fg", editorBaseControl.GetThemeStylebox("DebuggerTabFG", "EditorStyles"));
+ panelTabs.AddThemeStyleboxOverride("tab_bg", editorBaseControl.GetThemeStylebox("DebuggerTabBG", "EditorStyles"));
}
}
@@ -258,9 +258,9 @@ namespace GodotTools
RectMinSize = new Vector2(0, 228) * EditorScale,
SizeFlagsVertical = (int)SizeFlags.ExpandFill
};
- panelTabs.AddStyleboxOverride("panel", editorBaseControl.GetStylebox("DebuggerPanel", "EditorStyles"));
- panelTabs.AddStyleboxOverride("tab_fg", editorBaseControl.GetStylebox("DebuggerTabFG", "EditorStyles"));
- panelTabs.AddStyleboxOverride("tab_bg", editorBaseControl.GetStylebox("DebuggerTabBG", "EditorStyles"));
+ panelTabs.AddThemeStyleboxOverride("panel", editorBaseControl.GetThemeStylebox("DebuggerPanel", "EditorStyles"));
+ panelTabs.AddThemeStyleboxOverride("tab_fg", editorBaseControl.GetThemeStylebox("DebuggerTabFG", "EditorStyles"));
+ panelTabs.AddThemeStyleboxOverride("tab_bg", editorBaseControl.GetThemeStylebox("DebuggerTabBG", "EditorStyles"));
AddChild(panelTabs);
{
@@ -280,7 +280,7 @@ namespace GodotTools
Text = "Build Project".TTR(),
FocusMode = FocusModeEnum.None
};
- buildProjectBtn.Connect("pressed", this, nameof(BuildProjectPressed));
+ buildProjectBtn.PressedSignal += BuildProjectPressed;
toolBarHBox.AddChild(buildProjectBtn);
toolBarHBox.AddSpacer(begin: false);
@@ -293,7 +293,7 @@ namespace GodotTools
Visible = false,
FocusMode = FocusModeEnum.None
};
- warningsBtn.Connect("toggled", this, nameof(_WarningsToggled));
+ warningsBtn.Toggled += _WarningsToggled;
toolBarHBox.AddChild(warningsBtn);
errorsBtn = new ToolButton
@@ -304,7 +304,7 @@ namespace GodotTools
Visible = false,
FocusMode = FocusModeEnum.None
};
- errorsBtn.Connect("toggled", this, nameof(_ErrorsToggled));
+ errorsBtn.Toggled += _ErrorsToggled;
toolBarHBox.AddChild(errorsBtn);
toolBarHBox.AddSpacer(begin: false);
@@ -315,7 +315,7 @@ namespace GodotTools
FocusMode = FocusModeEnum.None,
Visible = false
};
- viewLogBtn.Connect("pressed", this, nameof(_ViewLogPressed));
+ viewLogBtn.PressedSignal += _ViewLogPressed;
toolBarHBox.AddChild(viewLogBtn);
var hsc = new HSplitContainer
@@ -326,8 +326,8 @@ namespace GodotTools
panelBuildsTab.AddChild(hsc);
buildTabsList = new ItemList { SizeFlagsHorizontal = (int)SizeFlags.ExpandFill };
- buildTabsList.Connect("item_selected", this, nameof(_BuildTabsItemSelected));
- buildTabsList.Connect("nothing_selected", this, nameof(_BuildTabsNothingSelected));
+ buildTabsList.ItemSelected += _BuildTabsItemSelected;
+ buildTabsList.NothingSelected += _BuildTabsNothingSelected;
hsc.AddChild(buildTabsList);
buildTabs = new TabContainer
diff --git a/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs b/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs
index 69a8c9cf4a..520e665595 100644
--- a/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs
@@ -166,7 +166,7 @@ namespace GodotTools
// Make sure the API assemblies are up to date before building the project.
// We may not have had the chance to update the release API assemblies, and the debug ones
// may have been deleted by the user at some point after they were loaded by the Godot editor.
- string apiAssembliesUpdateError = Internal.UpdateApiAssembliesFromPrebuilt(config == "Release" ? "Release" : "Debug");
+ string apiAssembliesUpdateError = Internal.UpdateApiAssembliesFromPrebuilt(config == "ExportRelease" ? "Release" : "Debug");
if (!string.IsNullOrEmpty(apiAssembliesUpdateError))
{
@@ -242,7 +242,7 @@ namespace GodotTools
Internal.GodotIs32Bits() ? "32" : "64"
};
- return BuildProjectBlocking("Tools", godotDefines);
+ return BuildProjectBlocking("Debug", godotDefines);
}
public static void Initialize()
@@ -251,12 +251,12 @@ namespace GodotTools
var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
var msbuild = BuildTool.MsBuildMono;
if (OS.IsWindows)
- msbuild = RiderPathManager.IsRider((string) editorSettings.GetSetting(RiderPathManager.EditorPathSettingName))
+ msbuild = RiderPathManager.IsExternalEditorSetToRider(editorSettings)
? BuildTool.JetBrainsMsBuild
: BuildTool.MsBuildVs;
EditorDef("mono/builds/build_tool", msbuild);
-
+
editorSettings.AddPropertyInfo(new Godot.Collections.Dictionary
{
["type"] = Godot.Variant.Type.Int,
diff --git a/modules/mono/editor/GodotTools/GodotTools/BuildTab.cs b/modules/mono/editor/GodotTools/GodotTools/BuildTab.cs
index f75fe239e3..938c3d8be1 100644
--- a/modules/mono/editor/GodotTools/GodotTools/BuildTab.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/BuildTab.cs
@@ -46,12 +46,12 @@ namespace GodotTools
get
{
if (!BuildExited)
- return GetIcon("Stop", "EditorIcons");
+ return GetThemeIcon("Stop", "EditorIcons");
if (BuildResult == BuildResults.Error)
- return GetIcon("StatusError", "EditorIcons");
+ return GetThemeIcon("StatusError", "EditorIcons");
- return GetIcon("StatusSuccess", "EditorIcons");
+ return GetThemeIcon("StatusSuccess", "EditorIcons");
}
}
@@ -145,8 +145,8 @@ namespace GodotTools
{
issuesList.Clear();
- using (var warningIcon = GetIcon("Warning", "EditorIcons"))
- using (var errorIcon = GetIcon("Error", "EditorIcons"))
+ using (var warningIcon = GetThemeIcon("Warning", "EditorIcons"))
+ using (var errorIcon = GetThemeIcon("Error", "EditorIcons"))
{
for (int i = 0; i < issues.Count; i++)
{
@@ -251,7 +251,7 @@ namespace GodotTools
base._Ready();
issuesList = new ItemList { SizeFlagsVertical = (int)SizeFlags.ExpandFill };
- issuesList.Connect("item_activated", this, nameof(_IssueActivated));
+ issuesList.ItemActivated += _IssueActivated;
AddChild(issuesList);
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/CsProjOperations.cs b/modules/mono/editor/GodotTools/GodotTools/CsProjOperations.cs
index 9abfda4538..421729cc11 100644
--- a/modules/mono/editor/GodotTools/GodotTools/CsProjOperations.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/CsProjOperations.cs
@@ -32,18 +32,6 @@ namespace GodotTools
ProjectUtils.AddItemToProjectChecked(projectPath, itemType, include);
}
- public static void FixApiHintPath(string projectPath)
- {
- try
- {
- ProjectUtils.FixApiHintPath(projectPath);
- }
- catch (Exception e)
- {
- GD.PushError(e.ToString());
- }
- }
-
private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
private static ulong ConvertToTimestamp(this DateTime value)
diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs b/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs
new file mode 100755
index 0000000000..f1765f7e19
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs
@@ -0,0 +1,618 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Text;
+using GodotTools.Internals;
+using Directory = GodotTools.Utils.Directory;
+using File = GodotTools.Utils.File;
+using OS = GodotTools.Utils.OS;
+using Path = System.IO.Path;
+
+namespace GodotTools.Export
+{
+ public struct AotOptions
+ {
+ public bool EnableLLVM;
+ public bool LLVMOnly;
+ public string LLVMPath;
+ public string LLVMOutputPath;
+
+ public bool FullAot;
+
+ private bool _useInterpreter;
+ public bool UseInterpreter { get => _useInterpreter && !LLVMOnly; set => _useInterpreter = value; }
+
+ public string[] ExtraAotOptions;
+ public string[] ExtraOptimizerOptions;
+
+ public string ToolchainPath;
+ }
+
+ public static class AotBuilder
+ {
+ public static void CompileAssemblies(ExportPlugin exporter, AotOptions aotOpts, string[] features, string platform, bool isDebug, string bclDir, string outputDir, string outputDataDir, IDictionary<string, string> assemblies)
+ {
+ // TODO: WASM
+
+ string aotTempDir = Path.Combine(Path.GetTempPath(), $"godot-aot-{Process.GetCurrentProcess().Id}");
+
+ if (!Directory.Exists(aotTempDir))
+ Directory.CreateDirectory(aotTempDir);
+
+ var assembliesPrepared = new Dictionary<string, string>();
+
+ foreach (var dependency in assemblies)
+ {
+ 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
+ assembliesPrepared.Add(assemblyName, assemblyPathInBcl);
+ }
+ else
+ {
+ string tempAssemblyPath = Path.Combine(aotTempDir, assemblyName + ".dll");
+ File.Copy(assemblyPath, tempAssemblyPath);
+ assembliesPrepared.Add(assemblyName, tempAssemblyPath);
+ }
+ }
+
+ if (platform == OS.Platforms.iOS)
+ {
+ var architectures = GetEnablediOSArchs(features).ToArray();
+ CompileAssembliesForiOS(exporter, isDebug, architectures, aotOpts, aotTempDir, assembliesPrepared, bclDir);
+ }
+ else if (platform == OS.Platforms.Android)
+ {
+ var abis = GetEnabledAndroidAbis(features).ToArray();
+ CompileAssembliesForAndroid(exporter, isDebug, abis, aotOpts, aotTempDir, assembliesPrepared, bclDir);
+ }
+ else
+ {
+ string bits = features.Contains("64") ? "64" : features.Contains("32") ? "32" : null;
+ CompileAssembliesForDesktop(exporter, platform, isDebug, bits, aotOpts, aotTempDir, outputDataDir, assembliesPrepared, bclDir);
+ }
+ }
+
+ public static void CompileAssembliesForAndroid(ExportPlugin exporter, bool isDebug, string[] abis, AotOptions aotOpts, string aotTempDir, IDictionary<string, string> assemblies, string bclDir)
+ {
+
+ foreach (var assembly in assemblies)
+ {
+ string assemblyName = assembly.Key;
+ string assemblyPath = assembly.Value;
+
+ // 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.
+ string outputFileName = "lib-aot-" + assemblyName + ".dll.so";
+
+ foreach (string abi in abis)
+ {
+ string aotAbiTempDir = Path.Combine(aotTempDir, abi);
+ string soFilePath = Path.Combine(aotAbiTempDir, outputFileName);
+
+ var compilerArgs = GetAotCompilerArgs(OS.Platforms.Android, isDebug, abi, aotOpts, assemblyPath, soFilePath);
+
+ // Make sure the output directory exists
+ Directory.CreateDirectory(aotAbiTempDir);
+
+ string compilerDirPath = Path.Combine(GodotSharpDirs.DataEditorToolsDir, "aot-compilers", $"{OS.Platforms.Android}-{abi}");
+
+ ExecuteCompiler(FindCrossCompiler(compilerDirPath), compilerArgs, bclDir);
+
+ // The Godot exporter expects us to pass the abi in the tags parameter
+ exporter.AddSharedObject(soFilePath, tags: new[] { abi });
+ }
+ }
+ }
+
+ public static void CompileAssembliesForDesktop(ExportPlugin exporter, string platform, bool isDebug, string bits, AotOptions aotOpts, string aotTempDir, string outputDataDir, IDictionary<string, string> assemblies, string bclDir)
+ {
+ foreach (var assembly in assemblies)
+ {
+ string assemblyName = assembly.Key;
+ string assemblyPath = assembly.Value;
+
+ string outputFileExtension = platform == OS.Platforms.Windows ? ".dll" :
+ platform == OS.Platforms.OSX ? ".dylib" :
+ ".so";
+
+ string outputFileName = assemblyName + ".dll" + outputFileExtension;
+ string tempOutputFilePath = Path.Combine(aotTempDir, outputFileName);
+
+ var compilerArgs = GetAotCompilerArgs(platform, isDebug, bits, aotOpts, assemblyPath, tempOutputFilePath);
+
+ string compilerDirPath = GetMonoCrossDesktopDirName(platform, bits);
+
+ ExecuteCompiler(FindCrossCompiler(compilerDirPath), compilerArgs, bclDir);
+
+ if (platform == OS.Platforms.OSX)
+ {
+ exporter.AddSharedObject(tempOutputFilePath, tags: null);
+ }
+ else
+ {
+ string outputDataLibDir = Path.Combine(outputDataDir, "Mono", platform == OS.Platforms.Windows ? "bin" : "lib");
+ File.Copy(tempOutputFilePath, Path.Combine(outputDataLibDir, outputFileName));
+ }
+ }
+ }
+
+ public static void CompileAssembliesForiOS(ExportPlugin exporter, bool isDebug, string[] architectures, AotOptions aotOpts, string aotTempDir, IDictionary<string, string> assemblies, string bclDir)
+ {
+ var cppCode = new StringBuilder();
+ var aotModuleInfoSymbols = new List<string>(assemblies.Count);
+
+ // {arch: paths}
+ var objFilePathsForiOSArch = architectures.ToDictionary(arch => arch, arch => new List<string>(assemblies.Count));
+
+ foreach (var assembly in assemblies)
+ {
+ string assemblyName = assembly.Key;
+ string assemblyPath = assembly.Value;
+
+ string asmFileName = assemblyName + ".dll.S";
+ string objFileName = assemblyName + ".dll.o";
+
+ foreach (string arch in architectures)
+ {
+ string aotArchTempDir = Path.Combine(aotTempDir, arch);
+ string asmFilePath = Path.Combine(aotArchTempDir, asmFileName);
+
+ var compilerArgs = GetAotCompilerArgs(OS.Platforms.iOS, isDebug, arch, aotOpts, assemblyPath, asmFilePath);
+
+ // Make sure the output directory exists
+ Directory.CreateDirectory(aotArchTempDir);
+
+ string compilerDirPath = Path.Combine(GodotSharpDirs.DataEditorToolsDir, "aot-compilers", $"{OS.Platforms.iOS}-{arch}");
+
+ ExecuteCompiler(FindCrossCompiler(compilerDirPath), compilerArgs, bclDir);
+
+ // Assembling
+ bool isSim = arch == "i386" || arch == "x86_64"; // Shouldn't really happen as we don't do AOT for the simulator
+ string versionMinName = isSim ? "iphonesimulator" : "iphoneos";
+ string iOSPlatformName = isSim ? "iPhoneSimulator" : "iPhoneOS";
+ const string versionMin = "10.0"; // TODO: Turn this hard-coded version into an exporter setting
+ string iOSSdkPath = Path.Combine(XcodeHelper.XcodePath,
+ $"Contents/Developer/Platforms/{iOSPlatformName}.platform/Developer/SDKs/{iOSPlatformName}.sdk");
+
+ string objFilePath = Path.Combine(aotArchTempDir, objFileName);
+
+ var clangArgs = new List<string>()
+ {
+ "-isysroot", iOSSdkPath,
+ "-Qunused-arguments",
+ $"-m{versionMinName}-version-min={versionMin}",
+ "-arch", arch,
+ "-c",
+ "-o", objFilePath,
+ "-x", "assembler"
+ };
+
+ if (isDebug)
+ clangArgs.Add("-DDEBUG");
+
+ clangArgs.Add(asmFilePath);
+
+ int clangExitCode = OS.ExecuteCommand(XcodeHelper.FindXcodeTool("clang"), clangArgs);
+ if (clangExitCode != 0)
+ throw new Exception($"Command 'clang' exited with code: {clangExitCode}");
+
+ objFilePathsForiOSArch[arch].Add(objFilePath);
+ }
+
+ aotModuleInfoSymbols.Add($"mono_aot_module_{AssemblyNameToAotSymbol(assemblyName)}_info");
+ }
+
+ // Generate driver code
+ cppCode.AppendLine("#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__)");
+ cppCode.AppendLine("#define IOS_DEVICE");
+ cppCode.AppendLine("#endif");
+
+ cppCode.AppendLine("#ifdef IOS_DEVICE");
+ cppCode.AppendLine("extern \"C\" {");
+ cppCode.AppendLine("// Mono API");
+ cppCode.AppendLine(@"
+typedef enum {
+MONO_AOT_MODE_NONE,
+MONO_AOT_MODE_NORMAL,
+MONO_AOT_MODE_HYBRID,
+MONO_AOT_MODE_FULL,
+MONO_AOT_MODE_LLVMONLY,
+MONO_AOT_MODE_INTERP,
+MONO_AOT_MODE_INTERP_LLVMONLY,
+MONO_AOT_MODE_LLVMONLY_INTERP,
+MONO_AOT_MODE_LAST = 1000,
+} MonoAotMode;");
+ cppCode.AppendLine("void mono_jit_set_aot_mode(MonoAotMode);");
+ cppCode.AppendLine("void mono_aot_register_module(void *);");
+
+ if (aotOpts.UseInterpreter)
+ {
+ cppCode.AppendLine("void mono_ee_interp_init(const char *);");
+ cppCode.AppendLine("void mono_icall_table_init();");
+ cppCode.AppendLine("void mono_marshal_ilgen_init();");
+ cppCode.AppendLine("void mono_method_builder_ilgen_init();");
+ cppCode.AppendLine("void mono_sgen_mono_ilgen_init();");
+ }
+
+ foreach (string symbol in aotModuleInfoSymbols)
+ cppCode.AppendLine($"extern void *{symbol};");
+
+ cppCode.AppendLine("void gd_mono_setup_aot() {");
+
+ foreach (string symbol in aotModuleInfoSymbols)
+ cppCode.AppendLine($"\tmono_aot_register_module({symbol});");
+
+ if (aotOpts.UseInterpreter)
+ {
+ cppCode.AppendLine("\tmono_icall_table_init();");
+ cppCode.AppendLine("\tmono_marshal_ilgen_init();");
+ cppCode.AppendLine("\tmono_method_builder_ilgen_init();");
+ cppCode.AppendLine("\tmono_sgen_mono_ilgen_init();");
+ cppCode.AppendLine("\tmono_ee_interp_init(0);");
+ }
+
+ string aotModeStr = null;
+
+ if (aotOpts.LLVMOnly)
+ {
+ aotModeStr = "MONO_AOT_MODE_LLVMONLY"; // --aot=llvmonly
+ }
+ else
+ {
+ if (aotOpts.UseInterpreter)
+ aotModeStr = "MONO_AOT_MODE_INTERP"; // --aot=interp or --aot=interp,full
+ else if (aotOpts.FullAot)
+ aotModeStr = "MONO_AOT_MODE_FULL"; // --aot=full
+ }
+
+ // One of the options above is always set for iOS
+ Debug.Assert(aotModeStr != null);
+
+ cppCode.AppendLine($"\tmono_jit_set_aot_mode({aotModeStr});");
+
+ cppCode.AppendLine("} // gd_mono_setup_aot");
+ cppCode.AppendLine("} // extern \"C\"");
+ cppCode.AppendLine("#endif // IOS_DEVICE");
+
+ // Add the driver code to the Xcode project
+ exporter.AddIosCppCode(cppCode.ToString());
+
+ // Archive the AOT object files into a static library
+
+ var arFilePathsForAllArchs = new List<string>();
+ string projectAssemblyName = GodotSharpEditor.ProjectAssemblyName;
+
+ foreach (var archPathsPair in objFilePathsForiOSArch)
+ {
+ string arch = archPathsPair.Key;
+ var objFilePaths = archPathsPair.Value;
+
+ string arOutputFilePath = Path.Combine(aotTempDir, $"lib-aot-{projectAssemblyName}.{arch}.a");
+
+ var arArgs = new List<string>()
+ {
+ "cr",
+ arOutputFilePath
+ };
+
+ foreach (string objFilePath in objFilePaths)
+ arArgs.Add(objFilePath);
+
+ int arExitCode = OS.ExecuteCommand(XcodeHelper.FindXcodeTool("ar"), arArgs);
+ if (arExitCode != 0)
+ throw new Exception($"Command 'ar' exited with code: {arExitCode}");
+
+ arFilePathsForAllArchs.Add(arOutputFilePath);
+ }
+
+ // It's lipo time
+
+ string fatOutputFileName = $"lib-aot-{projectAssemblyName}.fat.a";
+ string fatOutputFilePath = Path.Combine(aotTempDir, fatOutputFileName);
+
+ var lipoArgs = new List<string>();
+ lipoArgs.Add("-create");
+ lipoArgs.AddRange(arFilePathsForAllArchs);
+ lipoArgs.Add("-output");
+ lipoArgs.Add(fatOutputFilePath);
+
+ int lipoExitCode = OS.ExecuteCommand(XcodeHelper.FindXcodeTool("lipo"), lipoArgs);
+ if (lipoExitCode != 0)
+ throw new Exception($"Command 'lipo' exited with code: {lipoExitCode}");
+
+ // TODO: Add the AOT lib and interpreter libs as device only to supress warnings when targeting the simulator
+
+ // Add the fat AOT static library to the Xcode project
+ exporter.AddIosProjectStaticLib(fatOutputFilePath);
+
+ // Add the required Mono libraries to the Xcode project
+
+ string MonoLibFile(string libFileName) => libFileName + ".iphone.fat.a";
+
+ string MonoLibFromTemplate(string libFileName) =>
+ Path.Combine(Internal.FullTemplatesDir, "iphone-mono-libs", MonoLibFile(libFileName));
+
+ exporter.AddIosProjectStaticLib(MonoLibFromTemplate("libmonosgen-2.0"));
+
+ exporter.AddIosProjectStaticLib(MonoLibFromTemplate("libmono-native"));
+
+ if (aotOpts.UseInterpreter)
+ {
+ exporter.AddIosProjectStaticLib(MonoLibFromTemplate("libmono-ee-interp"));
+ exporter.AddIosProjectStaticLib(MonoLibFromTemplate("libmono-icall-table"));
+ exporter.AddIosProjectStaticLib(MonoLibFromTemplate("libmono-ilgen"));
+ }
+
+ // TODO: Turn into an exporter option
+ bool enableProfiling = false;
+ if (enableProfiling)
+ exporter.AddIosProjectStaticLib(MonoLibFromTemplate("libmono-profiler-log"));
+
+ // Add frameworks required by Mono to the Xcode project
+ exporter.AddIosFramework("libiconv.tbd");
+ exporter.AddIosFramework("GSS.framework");
+ exporter.AddIosFramework("CFNetwork.framework");
+
+ // Force load and export dynamic are needed for the linker to not strip required symbols.
+ // In theory we shouldn't be relying on this for P/Invoked functions (as is the case with
+ // functions in System.Native/libmono-native). Instead, we should use cecil to search for
+ // DllImports in assemblies and pass them to 'ld' as '-u/--undefined {pinvoke_symbol}'.
+ exporter.AddIosLinkerFlags("-rdynamic");
+ exporter.AddIosLinkerFlags($"-force_load \"$(SRCROOT)/{MonoLibFile("libmono-native")}\"");
+ }
+
+ /// Converts an assembly name to a valid symbol name in the same way the AOT compiler does
+ private static string AssemblyNameToAotSymbol(string assemblyName)
+ {
+ var builder = new StringBuilder();
+
+ foreach (var charByte in Encoding.UTF8.GetBytes(assemblyName))
+ {
+ char @char = (char)charByte;
+ builder.Append(Char.IsLetterOrDigit(@char) || @char == '_' ? @char : '_');
+ }
+
+ return builder.ToString();
+ }
+
+ private static IEnumerable<string> GetAotCompilerArgs(string platform, bool isDebug, string target, AotOptions aotOpts, string assemblyPath, string outputFilePath)
+ {
+ // TODO: LLVM
+
+ bool aotSoftDebug = isDebug && !aotOpts.EnableLLVM;
+ bool aotDwarfDebug = platform == OS.Platforms.iOS;
+
+ var aotOptions = new List<string>();
+ var optimizerOptions = new List<string>();
+
+ if (aotOpts.LLVMOnly)
+ {
+ aotOptions.Add("llvmonly");
+ }
+ else
+ {
+ // Can be both 'interp' and 'full'
+ if (aotOpts.UseInterpreter)
+ aotOptions.Add("interp");
+ if (aotOpts.FullAot)
+ aotOptions.Add("full");
+ }
+
+ aotOptions.Add(aotSoftDebug ? "soft-debug" : "nodebug");
+
+ if (aotDwarfDebug)
+ aotOptions.Add("dwarfdebug");
+
+ if (platform == OS.Platforms.Android)
+ {
+ string abi = target;
+
+ string androidToolchain = aotOpts.ToolchainPath;
+
+ 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}");
+ }
+ else if (platform == OS.Platforms.iOS)
+ {
+ if (!aotOpts.LLVMOnly && !aotOpts.UseInterpreter)
+ optimizerOptions.Add("gsharedvt");
+
+ aotOptions.Add("static");
+
+ // I couldn't get the Mono cross-compiler to do assembling, so we'll have to do it ourselves
+ aotOptions.Add("asmonly");
+
+ aotOptions.Add("direct-icalls");
+
+ if (aotSoftDebug)
+ aotOptions.Add("no-direct-calls");
+
+ if (aotOpts.LLVMOnly || !aotOpts.UseInterpreter)
+ aotOptions.Add("direct-pinvoke");
+
+ string arch = target;
+ aotOptions.Add($"mtriple={arch}-ios");
+ }
+
+ aotOptions.Add($"outfile={outputFilePath}");
+
+ if (aotOpts.EnableLLVM)
+ {
+ aotOptions.Add($"llvm-path={aotOpts.LLVMPath}");
+ aotOptions.Add($"llvm-outfile={aotOpts.LLVMOutputPath}");
+ }
+
+ if (aotOpts.ExtraAotOptions.Length > 0)
+ aotOptions.AddRange(aotOpts.ExtraAotOptions);
+
+ if (aotOpts.ExtraOptimizerOptions.Length > 0)
+ optimizerOptions.AddRange(aotOpts.ExtraOptimizerOptions);
+
+ string EscapeOption(string option) => option.Contains(',') ? $"\"{option}\"" : option;
+ string OptionsToString(IEnumerable<string> options) => string.Join(",", options.Select(EscapeOption));
+
+ var runtimeArgs = new List<string>();
+
+ // The '--debug' runtime option is required when using the 'soft-debug' and 'dwarfdebug' AOT options
+ if (aotSoftDebug || aotDwarfDebug)
+ runtimeArgs.Add("--debug");
+
+ if (aotOpts.EnableLLVM)
+ runtimeArgs.Add("--llvm");
+
+ runtimeArgs.Add(aotOptions.Count > 0 ? $"--aot={OptionsToString(aotOptions)}" : "--aot");
+
+ if (optimizerOptions.Count > 0)
+ runtimeArgs.Add($"-O={OptionsToString(optimizerOptions)}");
+
+ runtimeArgs.Add(assemblyPath);
+
+ return runtimeArgs;
+ }
+
+ private static void ExecuteCompiler(string compiler, IEnumerable<string> compilerArgs, string bclDir)
+ {
+ // 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(compiler, CmdLineArgsToString(compilerArgs))
+ {
+ UseShellExecute = false
+ };
+
+ process.StartInfo.EnvironmentVariables.Remove("MONO_ENV_OPTIONS");
+ process.StartInfo.EnvironmentVariables.Remove("MONO_THREADS_SUSPEND");
+ process.StartInfo.EnvironmentVariables.Add("MONO_PATH", bclDir);
+
+ 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 code: {process.ExitCode}");
+ }
+ }
+
+ private static IEnumerable<string> GetEnablediOSArchs(string[] features)
+ {
+ var iosArchs = new[]
+ {
+ "armv7",
+ "arm64"
+ };
+
+ return iosArchs.Where(features.Contains);
+ }
+
+ 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 string GetMonoCrossDesktopDirName(string platform, string bits)
+ {
+ switch (platform)
+ {
+ case OS.Platforms.Windows:
+ case OS.Platforms.UWP:
+ {
+ string arch = bits == "64" ? "x86_64" : "i686";
+ return $"windows-{arch}";
+ }
+ case OS.Platforms.OSX:
+ {
+ Debug.Assert(bits == null || bits == "64");
+ string arch = "x86_64";
+ return $"{platform}-{arch}";
+ }
+ case OS.Platforms.X11:
+ case OS.Platforms.Server:
+ {
+ string arch = bits == "64" ? "x86_64" : "i686";
+ return $"linux-{arch}";
+ }
+ case OS.Platforms.Haiku:
+ {
+ string arch = bits == "64" ? "x86_64" : "i686";
+ return $"{platform}-{arch}";
+ }
+ default:
+ throw new NotSupportedException($"Platform not supported: {platform}");
+ }
+ }
+
+ // TODO: Replace this for a specific path for each platform
+ private static string FindCrossCompiler(string monoCrossBin)
+ {
+ string exeExt = OS.IsWindows ? ".exe" : string.Empty;
+
+ var files = new DirectoryInfo(monoCrossBin).GetFiles($"*mono-sgen{exeExt}", SearchOption.TopDirectoryOnly);
+ if (files.Length > 0)
+ return Path.Combine(monoCrossBin, files[0].Name);
+
+ throw new FileNotFoundException($"Cannot find the mono runtime executable in {monoCrossBin}");
+ }
+ }
+}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
index 3e2a8c22a9..d782d4e61b 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
@@ -29,15 +29,13 @@ namespace GodotTools.Export
All = CJK | MidEast | Other | Rare | West
}
- private void AddI18NAssemblies(Godot.Collections.Dictionary<string, string> assemblies, string platform)
+ private void AddI18NAssemblies(Godot.Collections.Dictionary<string, string> assemblies, string bclDir)
{
- var codesets = (I18NCodesets) ProjectSettings.GetSetting("mono/export/i18n_codesets");
+ var codesets = (I18NCodesets)ProjectSettings.GetSetting("mono/export/i18n_codesets");
if (codesets == I18NCodesets.None)
return;
- string bclDir = DeterminePlatformBclDir(platform) ?? typeof(object).Assembly.Location.GetBaseDir();
-
void AddI18NAssembly(string name) => assemblies.Add(name, Path.Combine(bclDir, $"{name}.dll"));
AddI18NAssembly("I18N");
@@ -73,6 +71,7 @@ namespace GodotTools.Export
GlobalDef("mono/export/aot/enabled", false);
GlobalDef("mono/export/aot/full_aot", false);
+ GlobalDef("mono/export/aot/use_interpreter", true);
// --aot or --aot=opt1,opt2 (use 'mono --aot=help AuxAssembly.dll' to list AOT options)
GlobalDef("mono/export/aot/extra_aot_options", new string[] { });
@@ -86,9 +85,11 @@ namespace GodotTools.Export
private void AddFile(string srcPath, string dstPath, bool remap = false)
{
+ // Add file to the PCK
AddFile(dstPath.Replace("\\", "/"), File.ReadAllBytes(srcPath), remap);
}
+ // With this method we can override how a file is exported in the PCK
public override void _ExportFile(string path, string type, string[] features)
{
base._ExportFile(path, type, features);
@@ -96,7 +97,7 @@ namespace GodotTools.Export
if (type != Internal.CSharpLanguageType)
return;
- if (Path.GetExtension(path) != $".{Internal.CSharpLanguageExtension}")
+ 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
@@ -110,6 +111,8 @@ namespace GodotTools.Export
// 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);
+
+ // Tell the Godot exporter that we already took care of the file
Skip();
}
}
@@ -150,7 +153,7 @@ namespace GodotTools.Export
string outputDir = new FileInfo(path).Directory?.FullName ??
throw new FileNotFoundException("Base directory not found");
- string buildConfig = isDebug ? "Debug" : "Release";
+ string buildConfig = isDebug ? "ExportDebug" : "ExportRelease";
string scriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, $"scripts_metadata.{(isDebug ? "debug" : "release")}");
CsProjOperations.GenerateScriptsMetadata(GodotSharpDirs.ProjectCsProjPath, scriptsMetadataPath);
@@ -167,12 +170,7 @@ namespace GodotTools.Export
var dependencies = new Godot.Collections.Dictionary<string, string>();
- var projectDllName = (string)ProjectSettings.GetSetting("application/config/name");
- if (projectDllName.Empty())
- {
- projectDllName = "UnnamedProject";
- }
-
+ string projectDllName = GodotSharpEditor.ProjectAssemblyName;
string projectDllSrcDir = Path.Combine(GodotSharpDirs.ResTempAssembliesBaseDir, buildConfig);
string projectDllSrcPath = Path.Combine(projectDllSrcDir, $"{projectDllName}.dll");
@@ -189,10 +187,12 @@ namespace GodotTools.Export
dependencies["Mono.Android"] = monoAndroidAssemblyPath;
}
+ string bclDir = DeterminePlatformBclDir(platform);
+
var initialDependencies = dependencies.Duplicate();
- internal_GetExportedAssemblyDependencies(initialDependencies, buildConfig, DeterminePlatformBclDir(platform), dependencies);
+ internal_GetExportedAssemblyDependencies(initialDependencies, buildConfig, bclDir, dependencies);
- AddI18NAssemblies(dependencies, platform);
+ AddI18NAssemblies(dependencies, bclDir);
string outputDataDir = null;
@@ -227,11 +227,34 @@ namespace GodotTools.Export
}
}
- // AOT
+ // AOT compilation
+ bool aotEnabled = platform == OS.Platforms.iOS || (bool)ProjectSettings.GetSetting("mono/export/aot/enabled");
- if ((bool)ProjectSettings.GetSetting("mono/export/aot/enabled"))
+ if (aotEnabled)
{
- AotCompileDependencies(features, platform, isDebug, outputDir, outputDataDir, dependencies);
+ string aotToolchainPath = null;
+
+ if (platform == OS.Platforms.Android)
+ aotToolchainPath = (string)ProjectSettings.GetSetting("mono/export/aot/android_toolchain_path");
+
+ if (aotToolchainPath == string.Empty)
+ aotToolchainPath = null; // Don't risk it being used as current working dir
+
+ // TODO: LLVM settings are hard-coded and disabled for now
+ var aotOpts = new AotOptions
+ {
+ EnableLLVM = false,
+ LLVMOnly = false,
+ LLVMPath = "",
+ LLVMOutputPath = "",
+ FullAot = platform == OS.Platforms.iOS || (bool)(ProjectSettings.GetSetting("mono/export/aot/full_aot") ?? false),
+ UseInterpreter = (bool)ProjectSettings.GetSetting("mono/export/aot/use_interpreter"),
+ ExtraAotOptions = (string[])ProjectSettings.GetSetting("mono/export/aot/extra_aot_options") ?? new string[] { },
+ ExtraOptimizerOptions = (string[])ProjectSettings.GetSetting("mono/export/aot/extra_optimizer_options") ?? new string[] { },
+ ToolchainPath = aotToolchainPath
+ };
+
+ AotBuilder.CompileAssemblies(this, aotOpts, features, platform, isDebug, bclDir, outputDir, outputDataDir, dependencies);
}
}
@@ -258,7 +281,8 @@ namespace GodotTools.Export
{
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.
+ // NOTE: Bits is ok for now as all platforms with a data directory only have one or two architectures.
+ // However, this may change in the future if we add arm linux or windows desktop templates.
string bits = features.Contains("64") ? "64" : "32";
string TemplateDirName() => $"data.mono.{platform}.{bits}.{target}";
@@ -284,7 +308,7 @@ namespace GodotTools.Export
if (!validTemplatePathFound)
throw new FileNotFoundException("Data template directory not found", templateDirPath);
- string outputDataDir = Path.Combine(outputDir, DataDirName);
+ string outputDataDir = Path.Combine(outputDir, DetermineDataDirNameForProject());
if (Directory.Exists(outputDataDir))
Directory.Delete(outputDataDir, recursive: true); // Clean first
@@ -304,333 +328,10 @@ namespace GodotTools.Export
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($"Platform not supported: {platform}");
- }
- }
-
- 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);
+ return !new[] { OS.Platforms.OSX, OS.Platforms.Android, OS.Platforms.iOS, OS.Platforms.HTML5 }.Contains(platform);
}
private static string DeterminePlatformFromFeatures(IEnumerable<string> features)
@@ -665,7 +366,7 @@ namespace GodotTools.Export
if (PlatformRequiresCustomBcl(platform))
throw new FileNotFoundException($"Missing BCL (Base Class Library) for platform: {platform}");
- platformBclDir = null; // Use the one we're running on
+ platformBclDir = typeof(object).Assembly.Location; // Use the one we're running on
}
}
@@ -678,7 +379,7 @@ namespace GodotTools.Export
/// </summary>
private static bool PlatformRequiresCustomBcl(string platform)
{
- if (new[] { OS.Platforms.Android, OS.Platforms.HTML5 }.Contains(platform))
+ if (new[] { OS.Platforms.Android, OS.Platforms.iOS, OS.Platforms.HTML5 }.Contains(platform))
return true;
// The 'net_4_x' BCL is not compatible between Windows and the other platforms.
@@ -707,6 +408,8 @@ namespace GodotTools.Export
return "net_4_x";
case OS.Platforms.Android:
return "monodroid";
+ case OS.Platforms.iOS:
+ return "monotouch";
case OS.Platforms.HTML5:
return "wasm";
default:
@@ -714,14 +417,11 @@ namespace GodotTools.Export
}
}
- private static string DataDirName
+ private static string DetermineDataDirNameForProject()
{
- get
- {
- var appName = (string)ProjectSettings.GetSetting("application/config/name");
- string appNameSafe = appName.ToSafeDirName(allowDirSeparator: false);
- return $"data_{appNameSafe}";
- }
+ var appName = (string)ProjectSettings.GetSetting("application/config/name");
+ string appNameSafe = appName.ToSafeDirName(allowDirSeparator: false);
+ return $"data_{appNameSafe}";
}
[MethodImpl(MethodImplOptions.InternalCall)]
diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/XcodeHelper.cs b/modules/mono/editor/GodotTools/GodotTools/Export/XcodeHelper.cs
new file mode 100755
index 0000000000..219b7a698a
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools/Export/XcodeHelper.cs
@@ -0,0 +1,93 @@
+using System;
+using System.IO;
+
+namespace GodotTools.Export
+{
+ public static class XcodeHelper
+ {
+ private static string _XcodePath = null;
+
+ public static string XcodePath
+ {
+ get
+ {
+ if (_XcodePath == null)
+ {
+ _XcodePath = FindXcode();
+
+ if (_XcodePath == null)
+ throw new Exception("Could not find Xcode");
+ }
+
+ return _XcodePath;
+ }
+ }
+
+ private static string FindSelectedXcode()
+ {
+ var outputWrapper = new Godot.Collections.Array();
+
+ int exitCode = Godot.OS.Execute("xcode-select", new string[] { "--print-path" }, blocking: true, output: outputWrapper);
+
+ if (exitCode == 0)
+ {
+ string output = (string)outputWrapper[0];
+ return output.Trim();
+ }
+
+ Console.Error.WriteLine($"'xcode-select --print-path' exited with code: {exitCode}");
+
+ return null;
+ }
+
+ public static string FindXcode()
+ {
+ string selectedXcode = FindSelectedXcode();
+ if (selectedXcode != null)
+ {
+ if (Directory.Exists(Path.Combine(selectedXcode, "Contents", "Developer")))
+ return selectedXcode;
+
+ // The path already pointed to Contents/Developer
+ var dirInfo = new DirectoryInfo(selectedXcode);
+ if (dirInfo.Name != "Developer" || dirInfo.Parent.Name != "Contents")
+ {
+ Console.WriteLine(Path.GetDirectoryName(selectedXcode));
+ Console.WriteLine(System.IO.Directory.GetParent(selectedXcode).Name);
+ Console.Error.WriteLine("Unrecognized path for selected Xcode");
+ }
+ else
+ {
+ return System.IO.Path.GetFullPath($"{selectedXcode}/../..");
+ }
+ }
+ else
+ {
+ Console.Error.WriteLine("Could not find the selected Xcode; trying with a hint path");
+ }
+
+ const string XcodeHintPath = "/Applications/Xcode.app";
+
+ if (Directory.Exists(XcodeHintPath))
+ {
+ if (Directory.Exists(Path.Combine(XcodeHintPath, "Contents", "Developer")))
+ return XcodeHintPath;
+
+ Console.Error.WriteLine($"Found Xcode at '{XcodeHintPath}' but it's missing the 'Contents/Developer' sub-directory");
+ }
+
+ return null;
+ }
+
+ public static string FindXcodeTool(string toolName)
+ {
+ string XcodeDefaultToolchain = Path.Combine(XcodePath, "Contents", "Developer", "Toolchains", "XcodeDefault.xctoolchain");
+
+ string path = Path.Combine(XcodeDefaultToolchain, "usr", "bin", toolName);
+ if (File.Exists(path))
+ return path;
+
+ throw new FileNotFoundException($"Cannot find Xcode tool: {toolName}");
+ }
+ }
+}
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
index 147bc95bb8..c9d7dd26f8 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
@@ -13,6 +13,7 @@ using JetBrains.Annotations;
using static GodotTools.Internals.Globals;
using File = GodotTools.Utils.File;
using OS = GodotTools.Utils.OS;
+using Path = System.IO.Path;
namespace GodotTools
{
@@ -35,6 +36,17 @@ namespace GodotTools
public BottomPanel BottomPanel { get; private set; }
+ public static string ProjectAssemblyName
+ {
+ get
+ {
+ var projectAssemblyName = (string)ProjectSettings.GetSetting("application/config/name");
+ if (string.IsNullOrEmpty(projectAssemblyName))
+ projectAssemblyName = "UnnamedProject";
+ return projectAssemblyName;
+ }
+ }
+
private bool CreateProjectSolution()
{
using (var pr = new EditorProgress("create_csharp_solution", "Generating solution...".TTR(), 3))
@@ -44,9 +56,7 @@ namespace GodotTools
string resourceDir = ProjectSettings.GlobalizePath("res://");
string path = resourceDir;
- string name = (string)ProjectSettings.GetSetting("application/config/name");
- if (name.Empty())
- name = "UnnamedProject";
+ string name = ProjectAssemblyName;
string guid = CsProjOperations.GenerateGameProject(path, name);
@@ -61,7 +71,7 @@ namespace GodotTools
{
Guid = guid,
PathRelativeToSolution = name + ".csproj",
- Configs = new List<string> { "Debug", "Release", "Tools" }
+ Configs = new List<string> {"Debug", "ExportDebug", "ExportRelease"}
};
solution.AddNewProject(name, projectInfo);
@@ -118,19 +128,12 @@ namespace GodotTools
{
bool showOnStart = (bool)editorSettings.GetSetting("mono/editor/show_info_on_start");
aboutDialogCheckBox.Pressed = showOnStart;
- aboutDialog.PopupCenteredMinsize();
- }
-
- private void _ToggleAboutDialogOnStart(bool enabled)
- {
- bool showOnStart = (bool)editorSettings.GetSetting("mono/editor/show_info_on_start");
- if (showOnStart != enabled)
- editorSettings.SetSetting("mono/editor/show_info_on_start", enabled);
+ aboutDialog.PopupCentered();
}
- private void _MenuOptionPressed(MenuOptions id)
+ private void _MenuOptionPressed(int id)
{
- switch (id)
+ switch ((MenuOptions)id)
{
case MenuOptions.CreateSln:
CreateProjectSolution();
@@ -163,11 +166,41 @@ namespace GodotTools
bool showInfoDialog = (bool)editorSettings.GetSetting("mono/editor/show_info_on_start");
if (showInfoDialog)
{
- aboutDialog.PopupExclusive = true;
+ aboutDialog.Exclusive = true;
_ShowAboutDialog();
// Once shown a first time, it can be seen again via the Mono menu - it doesn't have to be exclusive from that time on.
- aboutDialog.PopupExclusive = false;
+ aboutDialog.Exclusive = false;
}
+
+ var fileSystemDock = GetEditorInterface().GetFileSystemDock();
+
+ fileSystemDock.FilesMoved += (file, newFile) =>
+ {
+ if (Path.GetExtension(file) == Internal.CSharpLanguageExtension)
+ {
+ ProjectUtils.RenameItemInProjectChecked(GodotSharpDirs.ProjectCsProjPath, "Compile",
+ ProjectSettings.GlobalizePath(file), ProjectSettings.GlobalizePath(newFile));
+ }
+ };
+
+ fileSystemDock.FileRemoved += file =>
+ {
+ if (Path.GetExtension(file) == Internal.CSharpLanguageExtension)
+ ProjectUtils.RemoveItemFromProjectChecked(GodotSharpDirs.ProjectCsProjPath, "Compile",
+ ProjectSettings.GlobalizePath(file));
+ };
+
+ fileSystemDock.FolderMoved += (oldFolder, newFolder) =>
+ {
+ ProjectUtils.RenameItemsToNewFolderInProjectChecked(GodotSharpDirs.ProjectCsProjPath, "Compile",
+ ProjectSettings.GlobalizePath(oldFolder), ProjectSettings.GlobalizePath(newFolder));
+ };
+
+ fileSystemDock.FolderRemoved += oldFolder =>
+ {
+ ProjectUtils.RemoveItemsInFolderFromProjectChecked(GodotSharpDirs.ProjectCsProjPath, "Compile",
+ ProjectSettings.GlobalizePath(oldFolder));
+ };
}
}
@@ -179,9 +212,9 @@ namespace GodotTools
public void ShowErrorDialog(string message, string title = "Error")
{
- errorDialog.WindowTitle = title;
+ errorDialog.Title = title;
errorDialog.DialogText = message;
- errorDialog.PopupCenteredMinsize();
+ errorDialog.PopupCentered();
}
private static string _vsCodePath = string.Empty;
@@ -210,7 +243,7 @@ namespace GodotTools
string scriptPath = ProjectSettings.GlobalizePath(script.ResourcePath);
RiderPathManager.OpenFile(GodotSharpDirs.ProjectSlnPath, scriptPath, line);
return Error.Ok;
- }
+ }
case ExternalEditorId.MonoDevelop:
{
string scriptPath = ProjectSettings.GlobalizePath(script.ResourcePath);
@@ -346,11 +379,10 @@ namespace GodotTools
bottomPanelBtn = AddControlToBottomPanel(BottomPanel, "Mono".TTR());
- AddChild(new HotReloadAssemblyWatcher { Name = "HotReloadAssemblyWatcher" });
+ AddChild(new HotReloadAssemblyWatcher {Name = "HotReloadAssemblyWatcher"});
menuPopup = new PopupMenu();
menuPopup.Hide();
- menuPopup.SetAsToplevel(true);
AddToolSubmenuItem("Mono", menuPopup);
@@ -359,7 +391,7 @@ namespace GodotTools
menuPopup.AddItem("About C# support".TTR(), (int)MenuOptions.AboutCSharp);
aboutDialog = new AcceptDialog();
editorBaseControl.AddChild(aboutDialog);
- aboutDialog.WindowTitle = "Important: C# support is not feature-complete";
+ aboutDialog.Title = "Important: C# support is not feature-complete";
// We don't use DialogText as the default AcceptDialog Label doesn't play well with the TextureRect and CheckBox
// we'll add. Instead we add containers and a new autowrapped Label inside.
@@ -373,7 +405,7 @@ namespace GodotTools
aboutVBox.AddChild(aboutHBox);
var aboutIcon = new TextureRect();
- aboutIcon.Texture = aboutIcon.GetIcon("NodeWarning", "EditorIcons");
+ aboutIcon.Texture = aboutIcon.GetThemeIcon("NodeWarning", "EditorIcons");
aboutHBox.AddChild(aboutIcon);
var aboutLabel = new Label();
@@ -394,15 +426,34 @@ namespace GodotTools
EditorDef("mono/editor/show_info_on_start", true);
// CheckBox in main container
- aboutDialogCheckBox = new CheckBox { Text = "Show this warning when starting the editor" };
- aboutDialogCheckBox.Connect("toggled", this, nameof(_ToggleAboutDialogOnStart));
+ aboutDialogCheckBox = new CheckBox {Text = "Show this warning when starting the editor"};
+ aboutDialogCheckBox.Toggled += enabled =>
+ {
+ bool showOnStart = (bool)editorSettings.GetSetting("mono/editor/show_info_on_start");
+ if (showOnStart != enabled)
+ editorSettings.SetSetting("mono/editor/show_info_on_start", enabled);
+ };
aboutVBox.AddChild(aboutDialogCheckBox);
}
if (File.Exists(GodotSharpDirs.ProjectSlnPath) && File.Exists(GodotSharpDirs.ProjectCsProjPath))
{
- // Make sure the existing project has Api assembly references configured correctly
- CsProjOperations.FixApiHintPath(GodotSharpDirs.ProjectCsProjPath);
+ try
+ {
+ // Migrate solution from old configuration names to: Debug, ExportDebug and ExportRelease
+ DotNetSolution.MigrateFromOldConfigNames(GodotSharpDirs.ProjectSlnPath);
+ // Migrate csproj from old configuration names to: Debug, ExportDebug and ExportRelease
+ ProjectUtils.MigrateFromOldConfigNames(GodotSharpDirs.ProjectCsProjPath);
+
+ // Apply the other fixes after configurations are migrated
+
+ // Make sure the existing project has Api assembly references configured correctly
+ ProjectUtils.FixApiHintPath(GodotSharpDirs.ProjectCsProjPath);
+ }
+ catch (Exception e)
+ {
+ GD.PushError(e.ToString());
+ }
}
else
{
@@ -410,7 +461,7 @@ namespace GodotTools
menuPopup.AddItem("Create C# solution".TTR(), (int)MenuOptions.CreateSln);
}
- menuPopup.Connect("id_pressed", this, nameof(_MenuOptionPressed));
+ menuPopup.IdPressed += _MenuOptionPressed;
var buildButton = new ToolButton
{
@@ -418,7 +469,7 @@ namespace GodotTools
HintTooltip = "Build solution",
FocusMode = Control.FocusModeEnum.None
};
- buildButton.Connect("pressed", this, nameof(_BuildSolutionPressed));
+ buildButton.PressedSignal += _BuildSolutionPressed;
AddControlToContainer(CustomControlContainer.Toolbar, buildButton);
// External editor settings
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
index 379dfd9f7d..ac9379adf8 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
@@ -51,7 +51,9 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Build\MsBuildFinder.cs" />
+ <Compile Include="Export\AotBuilder.cs" />
<Compile Include="Export\ExportPlugin.cs" />
+ <Compile Include="Export\XcodeHelper.cs" />
<Compile Include="ExternalEditorId.cs" />
<Compile Include="Ides\GodotIdeManager.cs" />
<Compile Include="Ides\GodotIdeServer.cs" />
diff --git a/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs b/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs
index 0ed567afd1..ae05710f4f 100644
--- a/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs
@@ -10,7 +10,7 @@ namespace GodotTools
public override void _Notification(int what)
{
- if (what == MainLoop.NotificationWmFocusIn)
+ if (what == Node.NotificationWmFocusIn)
{
RestartTimer();
@@ -40,7 +40,7 @@ namespace GodotTools
OneShot = false,
WaitTime = (float)EditorDef("mono/assembly_watch_interval_sec", 0.5)
};
- watchTimer.Connect("timeout", this, nameof(TimerTimeout));
+ watchTimer.Timeout += TimerTimeout;
AddChild(watchTimer);
watchTimer.Start();
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs
index 77740f0e53..e3a4fa7b45 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs
@@ -162,7 +162,7 @@ namespace GodotTools.Ides.Rider
private static string GetToolboxRiderRootPath(string localAppData)
{
- var toolboxPath = Path.Combine(localAppData, @"JetBrains\Toolbox");
+ var toolboxPath = Path.Combine(localAppData, @"JetBrains/Toolbox");
var settingsJson = Path.Combine(toolboxPath, ".settings.json");
if (File.Exists(settingsJson))
@@ -172,7 +172,7 @@ namespace GodotTools.Ides.Rider
toolboxPath = path;
}
- var toolboxRiderRootPath = Path.Combine(toolboxPath, @"apps\Rider");
+ var toolboxRiderRootPath = Path.Combine(toolboxPath, @"apps/Rider");
return toolboxRiderRootPath;
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs
index ee5677a6a8..16f91a0925 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs
@@ -55,6 +55,11 @@ namespace GodotTools.Ides.Rider
}
}
+ public static bool IsExternalEditorSetToRider(EditorSettings editorSettings)
+ {
+ return editorSettings.HasSetting(EditorPathSettingName) && IsRider((string) editorSettings.GetSetting(EditorPathSettingName));
+ }
+
public static bool IsRider(string path)
{
if (string.IsNullOrEmpty(path))
diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
index 2e121ba879..026a7db89c 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
@@ -8,7 +8,7 @@ namespace GodotTools.Internals
public static class Internal
{
public const string CSharpLanguageType = "CSharpScript";
- public const string CSharpLanguageExtension = "cs";
+ public const string CSharpLanguageExtension = ".cs";
public static string UpdateApiAssembliesFromPrebuilt(string config) =>
internal_UpdateApiAssembliesFromPrebuilt(config);
diff --git a/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs b/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs
index 279e67b3eb..b057ac12c6 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs
@@ -5,6 +5,7 @@ using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
+using JetBrains.Annotations;
namespace GodotTools.Utils
{
@@ -26,6 +27,7 @@ namespace GodotTools.Utils
public const string UWP = "UWP";
public const string Haiku = "Haiku";
public const string Android = "Android";
+ public const string iOS = "iOS";
public const string HTML5 = "HTML5";
}
@@ -33,11 +35,12 @@ namespace GodotTools.Utils
{
public const string Windows = "windows";
public const string OSX = "osx";
- public const string X11 = "x11";
+ public const string X11 = "linuxbsd";
public const string Server = "server";
public const string UWP = "uwp";
public const string Haiku = "haiku";
public const string Android = "android";
+ public const string iOS = "iphone";
public const string HTML5 = "javascript";
}
@@ -50,6 +53,7 @@ namespace GodotTools.Utils
[Names.UWP] = Platforms.UWP,
[Names.Haiku] = Platforms.Haiku,
[Names.Android] = Platforms.Android,
+ [Names.iOS] = Platforms.iOS,
[Names.HTML5] = Platforms.HTML5
};
@@ -65,6 +69,7 @@ namespace GodotTools.Utils
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> _isiOS = new Lazy<bool>(() => IsOS(Names.iOS));
private static readonly Lazy<bool> _isHTML5 = new Lazy<bool>(() => IsOS(Names.HTML5));
public static bool IsWindows => _isWindows.Value || IsUWP;
@@ -74,10 +79,11 @@ namespace GodotTools.Utils
public static bool IsUWP => _isUWP.Value;
public static bool IsHaiku => _isHaiku.Value;
public static bool IsAndroid => _isAndroid.Value;
+ public static bool IsiOS => _isiOS.Value;
public static bool IsHTML5 => _isHTML5.Value;
private static bool? _isUnixCache;
- private static readonly string[] UnixLikePlatforms = { Names.OSX, Names.X11, Names.Server, Names.Haiku, Names.Android };
+ private static readonly string[] UnixLikePlatforms = { Names.OSX, Names.X11, Names.Server, Names.Haiku, Names.Android, Names.iOS };
public static bool IsUnixLike()
{
@@ -91,12 +97,12 @@ namespace GodotTools.Utils
public static char PathSep => IsWindows ? ';' : ':';
- public static string PathWhich(string name)
+ public static string PathWhich([NotNull] string name)
{
return IsWindows ? PathWhichWindows(name) : PathWhichUnix(name);
}
- private static string PathWhichWindows(string name)
+ private static string PathWhichWindows([NotNull] string name)
{
string[] windowsExts = Environment.GetEnvironmentVariable("PATHEXT")?.Split(PathSep) ?? new string[] { };
string[] pathDirs = Environment.GetEnvironmentVariable("PATH")?.Split(PathSep);
@@ -121,7 +127,7 @@ namespace GodotTools.Utils
select path + ext).FirstOrDefault(File.Exists);
}
- private static string PathWhichUnix(string name)
+ private static string PathWhichUnix([NotNull] string name)
{
string[] pathDirs = Environment.GetEnvironmentVariable("PATH")?.Split(PathSep);
@@ -163,5 +169,33 @@ namespace GodotTools.Utils
User32Dll.AllowSetForegroundWindow(process.Id); // allows application to focus itself
}
}
+
+ public static int ExecuteCommand(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));
+ }
+
+ var startInfo = new ProcessStartInfo(command, CmdLineArgsToString(arguments));
+
+ Console.WriteLine($"Executing: \"{startInfo.FileName}\" {startInfo.Arguments}");
+
+ // Print the output
+ startInfo.RedirectStandardOutput = false;
+ startInfo.RedirectStandardError = false;
+
+ startInfo.UseShellExecute = false;
+
+ using (var process = new Process { StartInfo = startInfo })
+ {
+ process.Start();
+ process.WaitForExit();
+
+ return process.ExitCode;
+ }
+ }
}
}
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 908c72c591..71bb8ff851 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -76,7 +76,7 @@
#define GLUE_HEADER_FILE "glue_header.h"
#define ICALL_PREFIX "godot_icall_"
#define SINGLETON_ICALL_SUFFIX "_get_singleton"
-#define ICALL_GET_METHODBIND ICALL_PREFIX "Object_ClassDB_get_method"
+#define ICALL_GET_METHODBIND "__ClassDB_get_method"
#define C_LOCAL_RET "ret"
#define C_LOCAL_VARARG_RET "vararg_ret"
@@ -95,6 +95,10 @@
#define C_METHOD_MONOSTR_FROM_GODOT C_NS_MONOMARSHAL "::mono_string_from_godot"
#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 C_METHOD_MANAGED_TO_CALLABLE C_NS_MONOMARSHAL "::managed_to_callable"
+#define C_METHOD_MANAGED_FROM_CALLABLE C_NS_MONOMARSHAL "::callable_to_managed"
+#define C_METHOD_MANAGED_TO_SIGNAL C_NS_MONOMARSHAL "::signal_info_to_callable"
+#define C_METHOD_MANAGED_FROM_SIGNAL C_NS_MONOMARSHAL "::callable_to_signal_info"
#define BINDINGS_GENERATOR_VERSION UINT32_C(11)
@@ -407,7 +411,7 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
xml_output.append("\"/>");
} else {
// Try to find as global enum constant
- const EnumInterface *target_ienum = NULL;
+ const EnumInterface *target_ienum = nullptr;
for (const List<EnumInterface>::Element *E = global_enums.front(); E; E = E->next()) {
target_ienum = &E->get();
@@ -445,7 +449,7 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
xml_output.append("\"/>");
} else {
// Try to find as enum constant in the current class
- const EnumInterface *target_ienum = NULL;
+ const EnumInterface *target_ienum = nullptr;
for (const List<EnumInterface>::Element *E = target_itype->enums.front(); E; E = E->next()) {
target_ienum = &E->get();
@@ -504,23 +508,23 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
xml_output.append(tag);
xml_output.append("</c>");
} else if (tag == "PackedByteArray") {
- xml_output.append("<see cref=\"byte\"/>");
+ xml_output.append("<see cref=\"T:byte[]\"/>");
} else if (tag == "PackedInt32Array") {
- xml_output.append("<see cref=\"int\"/>");
+ xml_output.append("<see cref=\"T:int[]\"/>");
+ } else if (tag == "PackedInt64Array") {
+ xml_output.append("<see cref=\"T:long[]\"/>");
} else if (tag == "PackedFloat32Array") {
-#ifdef REAL_T_IS_DOUBLE
- xml_output.append("<see cref=\"double\"/>");
-#else
- xml_output.append("<see cref=\"float\"/>");
-#endif
+ xml_output.append("<see cref=\"T:float[]\"/>");
+ } else if (tag == "PackedFloat64Array") {
+ xml_output.append("<see cref=\"T:double[]\"/>");
} else if (tag == "PackedStringArray") {
- xml_output.append("<see cref=\"string\"/>");
+ xml_output.append("<see cref=\"T:string[]\"/>");
} else if (tag == "PackedVector2Array") {
- xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".Vector2\"/>");
+ xml_output.append("<see cref=\"T:" BINDINGS_NAMESPACE ".Vector2[]\"/>");
} else if (tag == "PackedVector3Array") {
- xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".Vector3\"/>");
+ xml_output.append("<see cref=\"T:" BINDINGS_NAMESPACE ".Vector3[]\"/>");
} else if (tag == "PackedColorArray") {
- xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".Color\"/>");
+ xml_output.append("<see cref=\"T:" BINDINGS_NAMESPACE ".Color[]\"/>");
} else {
const TypeInterface *target_itype = _get_type_or_null(TypeReference(tag));
@@ -778,7 +782,7 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
const ConstantInterface &iconstant = E->get();
if (iconstant.const_doc && iconstant.const_doc->description.size()) {
- String xml_summary = bbcode_to_xml(fix_doc_description(iconstant.const_doc->description), NULL);
+ String xml_summary = bbcode_to_xml(fix_doc_description(iconstant.const_doc->description), nullptr);
Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
if (summary_lines.size()) {
@@ -839,7 +843,7 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
const ConstantInterface &iconstant = F->get();
if (iconstant.const_doc && iconstant.const_doc->description.size()) {
- String xml_summary = bbcode_to_xml(fix_doc_description(iconstant.const_doc->description), NULL);
+ String xml_summary = bbcode_to_xml(fix_doc_description(iconstant.const_doc->description), nullptr);
Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
if (summary_lines.size()) {
@@ -932,7 +936,7 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir) {
"using System.Runtime.CompilerServices;\n"
"\n");
cs_icalls_content.append("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK);
- cs_icalls_content.append(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS "\n" INDENT1 OPEN_BLOCK);
+ cs_icalls_content.append(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS "\n" INDENT1 "{");
cs_icalls_content.append(MEMBER_BEGIN "internal static ulong godot_api_hash = ");
cs_icalls_content.append(String::num_uint64(GDMono::get_singleton()->get_api_core_hash()) + ";\n");
@@ -944,7 +948,7 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir) {
#define ADD_INTERNAL_CALL(m_icall) \
if (!m_icall.editor_only) { \
cs_icalls_content.append(MEMBER_BEGIN "[MethodImpl(MethodImplOptions.InternalCall)]\n"); \
- cs_icalls_content.append(INDENT2 "internal extern static "); \
+ cs_icalls_content.append(INDENT2 "internal static extern "); \
cs_icalls_content.append(m_icall.im_type_out + " "); \
cs_icalls_content.append(m_icall.name + "("); \
cs_icalls_content.append(m_icall.im_sig + ");\n"); \
@@ -1046,7 +1050,7 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_proj_dir) {
#define ADD_INTERNAL_CALL(m_icall) \
if (m_icall.editor_only) { \
cs_icalls_content.append(INDENT2 "[MethodImpl(MethodImplOptions.InternalCall)]\n"); \
- cs_icalls_content.append(INDENT2 "internal extern static "); \
+ cs_icalls_content.append(INDENT2 "internal static extern "); \
cs_icalls_content.append(m_icall.im_type_out + " "); \
cs_icalls_content.append(m_icall.name + "("); \
cs_icalls_content.append(m_icall.im_sig + ");\n"); \
@@ -1312,7 +1316,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
output.append(itype.proxy_name);
output.append(").Name);\n" INDENT4 "return singleton;\n" INDENT3 "}\n" INDENT2 "}\n");
- output.append(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \"");
+ output.append(MEMBER_BEGIN "private static StringName " BINDINGS_NATIVE_NAME_FIELD " = \"");
output.append(itype.name);
output.append("\";\n");
@@ -1324,7 +1328,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
} else if (is_derived_type) {
// Add member fields
- output.append(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \"");
+ output.append(MEMBER_BEGIN "private static StringName " BINDINGS_NATIVE_NAME_FIELD " = \"");
output.append(itype.name);
output.append("\";\n");
@@ -1363,6 +1367,13 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
"Failed to generate method '" + imethod.name + "' for class '" + itype.name + "'.");
}
+ for (const List<SignalInterface>::Element *E = itype.signals_.front(); E; E = E->next()) {
+ const SignalInterface &isignal = E->get();
+ Error method_err = _generate_cs_signal(itype, isignal, output);
+ ERR_FAIL_COND_V_MSG(method_err != OK, method_err,
+ "Failed to generate signal '" + isignal.name + "' for class '" + itype.name + "'.");
+ }
+
if (itype.is_singleton) {
InternalCall singleton_icall = InternalCall(itype.api_type, ICALL_PREFIX + itype.name + SINGLETON_ICALL_SUFFIX, "IntPtr");
@@ -1395,7 +1406,7 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
const TypeInterface *current_type = &p_itype;
while (!setter && current_type->base_name != StringName()) {
OrderedHashMap<StringName, TypeInterface>::Element base_match = obj_types.find(current_type->base_name);
- ERR_FAIL_COND_V(!base_match, ERR_BUG);
+ ERR_FAIL_COND_V_MSG(!base_match, ERR_BUG, "Type not found '" + current_type->base_name + "'. Inherited by '" + current_type->name + "'.");
current_type = &base_match.get();
setter = current_type->find_method_by_name(p_iprop.setter);
}
@@ -1406,7 +1417,7 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
current_type = &p_itype;
while (!getter && current_type->base_name != StringName()) {
OrderedHashMap<StringName, TypeInterface>::Element base_match = obj_types.find(current_type->base_name);
- ERR_FAIL_COND_V(!base_match, ERR_BUG);
+ ERR_FAIL_COND_V_MSG(!base_match, ERR_BUG, "Type not found '" + current_type->base_name + "'. Inherited by '" + current_type->name + "'.");
current_type = &base_match.get();
getter = current_type->find_method_by_name(p_iprop.getter);
}
@@ -1424,7 +1435,16 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
}
if (getter && setter) {
- ERR_FAIL_COND_V(getter->return_type.cname != setter->arguments.back()->get().type.cname, ERR_BUG);
+ const ArgumentInterface &setter_first_arg = setter->arguments.back()->get();
+ if (getter->return_type.cname != setter_first_arg.type.cname) {
+ // Special case for Node::set_name
+ bool whitelisted = getter->return_type.cname == name_cache.type_StringName &&
+ setter_first_arg.type.cname == name_cache.type_String;
+
+ ERR_FAIL_COND_V_MSG(!whitelisted, ERR_BUG,
+ "Return type from getter doesn't match first argument of setter for property: '" +
+ p_itype.name + "." + String(p_iprop.cname) + "'.");
+ }
}
const TypeReference &proptype_name = getter ? getter->return_type : setter->arguments.back()->get().type;
@@ -1474,7 +1494,7 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
if (idx_arg.type.cname != name_cache.type_int) {
// Assume the index parameter is an enum
const TypeInterface *idx_arg_type = _get_type_or_null(idx_arg.type);
- CRASH_COND(idx_arg_type == NULL);
+ CRASH_COND(idx_arg_type == nullptr);
p_output.append("(" + idx_arg_type->proxy_name + ")" + itos(p_iprop.index));
} else {
p_output.append(itos(p_iprop.index));
@@ -1502,7 +1522,7 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
if (idx_arg.type.cname != name_cache.type_int) {
// Assume the index parameter is an enum
const TypeInterface *idx_arg_type = _get_type_or_null(idx_arg.type);
- CRASH_COND(idx_arg_type == NULL);
+ CRASH_COND(idx_arg_type == nullptr);
p_output.append("(" + idx_arg_type->proxy_name + ")" + itos(p_iprop.index) + ", ");
} else {
p_output.append(itos(p_iprop.index) + ", ");
@@ -1525,7 +1545,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
const TypeInterface *return_type = _get_type_or_placeholder(p_imethod.return_type);
- String method_bind_field = "method_bind_" + itos(p_method_bind_count);
+ String method_bind_field = "__method_bind_" + itos(p_method_bind_count);
String arguments_sig;
String cs_in_statements;
@@ -1611,8 +1631,9 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
// Generate method
{
if (!p_imethod.is_virtual && !p_imethod.requires_object_call) {
- p_output.append(MEMBER_BEGIN "[DebuggerBrowsable(DebuggerBrowsableState.Never)]" MEMBER_BEGIN "private static IntPtr ");
- p_output.append(method_bind_field + " = Object." ICALL_GET_METHODBIND "(" BINDINGS_NATIVE_NAME_FIELD ", \"");
+ p_output.append(MEMBER_BEGIN "[DebuggerBrowsable(DebuggerBrowsableState.Never)]" MEMBER_BEGIN "private static readonly IntPtr ");
+ p_output.append(method_bind_field);
+ p_output.append(" = Object." ICALL_GET_METHODBIND "(" BINDINGS_NATIVE_NAME_FIELD ", \"");
p_output.append(p_imethod.name);
p_output.append("\");\n");
}
@@ -1726,6 +1747,106 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
return OK;
}
+Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::SignalInterface &p_isignal, StringBuilder &p_output) {
+ String arguments_sig;
+
+ // Retrieve information from the arguments
+ for (const List<ArgumentInterface>::Element *F = p_isignal.arguments.front(); F; F = F->next()) {
+ const ArgumentInterface &iarg = F->get();
+ const TypeInterface *arg_type = _get_type_or_placeholder(iarg.type);
+
+ // Add the current arguments to the signature
+
+ if (F != p_isignal.arguments.front())
+ arguments_sig += ", ";
+
+ arguments_sig += arg_type->cs_type;
+ arguments_sig += " ";
+ arguments_sig += iarg.name;
+ }
+
+ // Generate signal
+ {
+ if (p_isignal.method_doc && p_isignal.method_doc->description.size()) {
+ String xml_summary = bbcode_to_xml(fix_doc_description(p_isignal.method_doc->description), &p_itype);
+ Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
+
+ if (summary_lines.size()) {
+ p_output.append(MEMBER_BEGIN "/// <summary>\n");
+
+ for (int i = 0; i < summary_lines.size(); i++) {
+ p_output.append(INDENT2 "/// ");
+ p_output.append(summary_lines[i]);
+ p_output.append("\n");
+ }
+
+ p_output.append(INDENT2 "/// </summary>");
+ }
+ }
+
+ if (p_isignal.is_deprecated) {
+ if (p_isignal.deprecation_message.empty())
+ WARN_PRINT("An empty deprecation message is discouraged. Signal: '" + p_isignal.proxy_name + "'.");
+
+ p_output.append(MEMBER_BEGIN "[Obsolete(\"");
+ p_output.append(p_isignal.deprecation_message);
+ p_output.append("\")]");
+ }
+
+ String delegate_name = p_isignal.proxy_name;
+ delegate_name += "Handler"; // Delegate name is [SignalName]Handler
+
+ // Generate delegate
+ p_output.append(MEMBER_BEGIN "public delegate void ");
+ p_output.append(delegate_name);
+ p_output.append("(");
+ p_output.append(arguments_sig);
+ p_output.append(");\n");
+
+ // TODO:
+ // Could we assume the StringName instance of signal name will never be freed (it's stored in ClassDB) before the managed world is unloaded?
+ // If so, we could store the pointer we get from `data_unique_pointer()` instead of allocating StringName here.
+
+ // Cached signal name (StringName)
+ p_output.append(MEMBER_BEGIN "[DebuggerBrowsable(DebuggerBrowsableState.Never)]" MEMBER_BEGIN "private static StringName __signal_name_");
+ p_output.append(p_isignal.name);
+ p_output.append(" = \"");
+ p_output.append(p_isignal.name);
+ p_output.append("\";\n");
+
+ // Generate event
+ p_output.append(MEMBER_BEGIN "[Signal]" MEMBER_BEGIN "public ");
+
+ if (p_itype.is_singleton)
+ p_output.append("static ");
+
+ p_output.append("event ");
+ p_output.append(delegate_name);
+ p_output.append(" ");
+ p_output.append(p_isignal.proxy_name);
+ p_output.append("\n" OPEN_BLOCK_L2);
+
+ if (p_itype.is_singleton)
+ p_output.append("add => Singleton.Connect(__signal_name_");
+ else
+ p_output.append("add => Connect(__signal_name_");
+
+ p_output.append(p_isignal.name);
+ p_output.append(", new Callable(value));\n");
+
+ if (p_itype.is_singleton)
+ p_output.append(INDENT3 "remove => Singleton.Disconnect(__signal_name_");
+ else
+ p_output.append(INDENT3 "remove => Disconnect(__signal_name_");
+
+ p_output.append(p_isignal.name);
+ p_output.append(", new Callable(value));\n");
+ p_output.append(CLOSE_BLOCK_L2);
+ }
+
+ return OK;
+}
+
Error BindingsGenerator::generate_glue(const String &p_output_dir) {
ERR_FAIL_COND_V(!initialized, ERR_UNCONFIGURED);
@@ -2000,7 +2121,7 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte
if (return_type->is_object_type) {
ptrcall_return_type = return_type->is_reference ? "Ref<Reference>" : return_type->c_type;
- initialization = return_type->is_reference ? "" : " = NULL";
+ initialization = return_type->is_reference ? "" : " = nullptr";
} else {
ptrcall_return_type = return_type->c_type;
}
@@ -2009,10 +2130,10 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte
p_output.append(" " C_LOCAL_RET);
p_output.append(initialization + ";\n");
- String fail_ret = return_type->c_type_out.ends_with("*") && !return_type->ret_as_byref_arg ? "NULL" : return_type->c_type_out + "()";
+ String fail_ret = return_type->c_type_out.ends_with("*") && !return_type->ret_as_byref_arg ? "nullptr" : 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("\tif (" CS_PARAM_INSTANCE " == nullptr) { *arg_ret = ");
p_output.append(fail_ret);
p_output.append("; ERR_FAIL_MSG(\"Parameter ' arg_ret ' is null.\"); }\n");
} else {
@@ -2066,7 +2187,7 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte
}
p_output.append(CS_PARAM_METHODBIND "->call(" CS_PARAM_INSTANCE ", ");
- p_output.append(p_imethod.arguments.size() ? C_LOCAL_PTRCALL_ARGS ".ptr()" : "NULL");
+ p_output.append(p_imethod.arguments.size() ? C_LOCAL_PTRCALL_ARGS ".ptr()" : "nullptr");
p_output.append(", total_length, vcall_error);\n");
if (!ret_void) {
@@ -2077,8 +2198,8 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte
}
} else {
p_output.append("\t" CS_PARAM_METHODBIND "->ptrcall(" CS_PARAM_INSTANCE ", ");
- p_output.append(p_imethod.arguments.size() ? C_LOCAL_PTRCALL_ARGS ", " : "NULL, ");
- p_output.append(!ret_void ? "&" C_LOCAL_RET ");\n" : "NULL);\n");
+ p_output.append(p_imethod.arguments.size() ? C_LOCAL_PTRCALL_ARGS ", " : "nullptr, ");
+ p_output.append(!ret_void ? "&" C_LOCAL_RET ");\n" : "nullptr);\n");
}
if (!ret_void) {
@@ -2120,11 +2241,11 @@ const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_null(con
// Enum not found. Most likely because none of its constants were bound, so it's empty. That's fine. Use int instead.
const Map<StringName, TypeInterface>::Element *int_match = builtin_types.find(name_cache.type_int);
- ERR_FAIL_NULL_V(int_match, NULL);
+ ERR_FAIL_NULL_V(int_match, nullptr);
return &int_match->get();
}
- return NULL;
+ return nullptr;
}
const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_placeholder(const TypeReference &p_typeref) {
@@ -2291,7 +2412,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
iprop.proxy_name = iprop.proxy_name.replace("/", "__"); // Some members have a slash...
- iprop.prop_doc = NULL;
+ iprop.prop_doc = nullptr;
for (int i = 0; i < itype.class_doc->properties.size(); i++) {
const DocData::PropertyDoc &prop_doc = itype.class_doc->properties[i];
@@ -2336,7 +2457,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
PropertyInfo return_info = method_info.return_val;
- MethodBind *m = imethod.is_virtual ? NULL : ClassDB::get_method(type_cname, method_info.name);
+ MethodBind *m = imethod.is_virtual ? nullptr : ClassDB::get_method(type_cname, method_info.name);
imethod.is_vararg = m && m->is_vararg();
@@ -2479,13 +2600,92 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
}
}
+ // Populate signals
+
+ const HashMap<StringName, MethodInfo> &signal_map = class_info->signal_map;
+ const StringName *k = nullptr;
+
+ while ((k = signal_map.next(k))) {
+ SignalInterface isignal;
+
+ const MethodInfo &method_info = signal_map.get(*k);
+
+ isignal.name = method_info.name;
+ isignal.cname = method_info.name;
+
+ int argc = method_info.arguments.size();
+
+ for (int i = 0; i < argc; i++) {
+ PropertyInfo arginfo = method_info.arguments[i];
+
+ String orig_arg_name = arginfo.name;
+
+ ArgumentInterface iarg;
+ iarg.name = orig_arg_name;
+
+ if (arginfo.type == Variant::INT && arginfo.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
+ iarg.type.cname = arginfo.class_name;
+ iarg.type.is_enum = true;
+ } else if (arginfo.class_name != StringName()) {
+ iarg.type.cname = arginfo.class_name;
+ } else if (arginfo.hint == PROPERTY_HINT_RESOURCE_TYPE) {
+ iarg.type.cname = arginfo.hint_string;
+ } else if (arginfo.type == Variant::NIL) {
+ iarg.type.cname = name_cache.type_Variant;
+ } else {
+ if (arginfo.type == Variant::INT) {
+ iarg.type.cname = _get_int_type_name_from_meta(GodotTypeInfo::METADATA_NONE);
+ } else if (arginfo.type == Variant::FLOAT) {
+ iarg.type.cname = _get_float_type_name_from_meta(GodotTypeInfo::METADATA_NONE);
+ } else {
+ iarg.type.cname = Variant::get_type_name(arginfo.type);
+ }
+ }
+
+ iarg.name = escape_csharp_keyword(snake_to_camel_case(iarg.name));
+
+ isignal.add_argument(iarg);
+ }
+
+ isignal.proxy_name = escape_csharp_keyword(snake_to_pascal_case(isignal.name));
+
+ // Prevent the signal and its enclosing type from sharing the same name
+ if (isignal.proxy_name == itype.proxy_name) {
+ _log("Name of signal '%s' is ambiguous with the name of its enclosing class '%s'. Renaming signal to '%s_'\n",
+ isignal.proxy_name.utf8().get_data(), itype.proxy_name.utf8().get_data(), isignal.proxy_name.utf8().get_data());
+
+ isignal.proxy_name += "_";
+ }
+
+ if (itype.find_property_by_proxy_name(isignal.proxy_name) || itype.find_method_by_proxy_name(isignal.proxy_name)) {
+ // ClassDB allows signal names that conflict with method or property names.
+ // While registering a signal with a conflicting name is considered wrong,
+ // it may still happen and it may take some time until someone fixes the name.
+ // We can't allow the bindings to be in a broken state while we wait for a fix;
+ // that's why we must handle this possibility by renaming the signal.
+ isignal.proxy_name += "Signal";
+ }
+
+ if (itype.class_doc) {
+ for (int i = 0; i < itype.class_doc->signals.size(); i++) {
+ const DocData::MethodDoc &signal_doc = itype.class_doc->signals[i];
+ if (signal_doc.name == isignal.name) {
+ isignal.method_doc = &signal_doc;
+ break;
+ }
+ }
+ }
+
+ itype.signals_.push_back(isignal);
+ }
+
// Populate enums and constants
List<String> constants;
ClassDB::get_integer_constant_list(type_cname, &constants, true);
- const HashMap<StringName, List<StringName> > &enum_map = class_info->enum_map;
- const StringName *k = NULL;
+ const HashMap<StringName, List<StringName>> &enum_map = class_info->enum_map;
+ k = nullptr;
while ((k = enum_map.next(k))) {
StringName enum_proxy_cname = *k;
@@ -2507,7 +2707,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
ConstantInterface iconstant(constant_name, snake_to_pascal_case(constant_name, true), *value);
- iconstant.const_doc = NULL;
+ iconstant.const_doc = nullptr;
for (int i = 0; i < itype.class_doc->constants.size(); i++) {
const DocData::ConstantDoc &const_doc = itype.class_doc->constants[i];
@@ -2542,7 +2742,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
ConstantInterface iconstant(constant_name, snake_to_pascal_case(constant_name, true), *value);
- iconstant.const_doc = NULL;
+ iconstant.const_doc = nullptr;
for (int i = 0; i < itype.class_doc->constants.size(); i++) {
const DocData::ConstantDoc &const_doc = itype.class_doc->constants[i];
@@ -2587,8 +2787,15 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
#endif
break;
case Variant::STRING:
+ case Variant::STRING_NAME:
case Variant::NODE_PATH:
- r_iarg.default_argument = "\"" + r_iarg.default_argument + "\"";
+ if (r_iarg.type.cname == name_cache.type_StringName || r_iarg.type.cname == name_cache.type_NodePath) {
+ r_iarg.default_argument = "(%s)\"" + r_iarg.default_argument + "\"";
+ r_iarg.def_param_mode = ArgumentInterface::NULLABLE_REF;
+ } else {
+ CRASH_COND(r_iarg.type.cname != name_cache.type_String);
+ r_iarg.default_argument = "\"" + r_iarg.default_argument + "\"";
+ }
break;
case Variant::TRANSFORM:
if (p_val.operator Transform() == Transform())
@@ -2603,8 +2810,11 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
break;
case Variant::VECTOR2:
+ case Variant::VECTOR2I:
case Variant::RECT2:
+ case Variant::RECT2I:
case Variant::VECTOR3:
+ case Variant::VECTOR3I:
r_iarg.default_argument = "new %s" + r_iarg.default_argument;
r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
break;
@@ -2630,8 +2840,8 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
case Variant::ARRAY:
case Variant::PACKED_BYTE_ARRAY:
case Variant::PACKED_INT32_ARRAY:
- case Variant::PACKED_FLOAT32_ARRAY:
case Variant::PACKED_INT64_ARRAY:
+ case Variant::PACKED_FLOAT32_ARRAY:
case Variant::PACKED_FLOAT64_ARRAY:
case Variant::PACKED_STRING_ARRAY:
case Variant::PACKED_VECTOR2_ARRAY:
@@ -2646,8 +2856,13 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
r_iarg.default_argument = Variant::get_type_name(p_val.get_type()) + ".Identity";
r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
break;
- default: {
- }
+ case Variant::CALLABLE:
+ case Variant::SIGNAL:
+ CRASH_NOW_MSG("Parameter of type '" + String(r_iarg.type.cname) + "' cannot have a default value.");
+ break;
+ default:
+ CRASH_NOW_MSG("Unexpected Variant type: " + itos(p_val.get_type()));
+ break;
}
if (r_iarg.def_param_mode == ArgumentInterface::CONSTANT && r_iarg.type.cname == name_cache.type_Variant && r_iarg.default_argument != "null")
@@ -2672,16 +2887,19 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.c_type_out = "GDMonoMarshal::M_" #m_type; \
itype.cs_in = "ref %s"; \
/* in cs_out, im_type_out (%3) includes the 'out ' part */ \
- itype.cs_out = "%0(%1, %3 argRet); return (%2)argRet;"; \
+ itype.cs_out = "%0(%1, %3 argRet); return 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)
+ INSERT_STRUCT_TYPE(Vector2i)
INSERT_STRUCT_TYPE(Rect2)
+ INSERT_STRUCT_TYPE(Rect2i)
INSERT_STRUCT_TYPE(Transform2D)
INSERT_STRUCT_TYPE(Vector3)
+ INSERT_STRUCT_TYPE(Vector3i)
INSERT_STRUCT_TYPE(Basis)
INSERT_STRUCT_TYPE(Quat)
INSERT_STRUCT_TYPE(Transform)
@@ -2749,7 +2967,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
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.cs_out = "%0(%1, %3 argRet); return argRet;";
itype.ret_as_byref_arg = true;
builtin_types.insert(itype.cname, itype);
@@ -2766,7 +2984,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
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.cs_out = "%0(%1, %3 argRet); return argRet;";
itype.ret_as_byref_arg = true;
builtin_types.insert(itype.cname, itype);
}
@@ -2792,7 +3010,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
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.cs_out = "%0(%1, %3 argRet); return argRet;";
itype.ret_as_byref_arg = true;
builtin_types.insert(itype.cname, itype);
@@ -2814,7 +3032,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
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.cs_out = "%0(%1, %3 argRet); return argRet;";
itype.ret_as_byref_arg = true;
builtin_types.insert(itype.cname, itype);
}
@@ -2835,6 +3053,24 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.im_type_out = itype.proxy_name;
builtin_types.insert(itype.cname, itype);
+ // StringName
+ itype = TypeInterface();
+ itype.name = "StringName";
+ itype.cname = itype.name;
+ itype.proxy_name = "StringName";
+ itype.c_in = "\t%0 %1_in = %1 ? *%1 : StringName();\n";
+ itype.c_out = "\treturn memnew(StringName(%1));\n";
+ itype.c_arg_in = "&%s_in";
+ itype.c_type = itype.name;
+ itype.c_type_in = itype.c_type + "*";
+ itype.c_type_out = itype.c_type + "*";
+ itype.cs_type = itype.proxy_name;
+ itype.cs_in = "StringName." CS_SMETHOD_GETINSTANCE "(%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);
+
// NodePath
itype = TypeInterface();
itype.name = "NodePath";
@@ -2883,6 +3119,40 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.im_type_out = itype.proxy_name;
builtin_types.insert(itype.cname, itype);
+ // Callable
+ itype = TypeInterface::create_value_type(String("Callable"));
+ itype.c_in = "\t%0 %1_in = " C_METHOD_MANAGED_TO_CALLABLE "(*%1);\n";
+ itype.c_out = "\t*%3 = " C_METHOD_MANAGED_FROM_CALLABLE "(%1);\n";
+ itype.c_arg_in = "&%s_in";
+ itype.c_type_in = "GDMonoMarshal::M_Callable*";
+ itype.c_type_out = "GDMonoMarshal::M_Callable";
+ itype.cs_in = "ref %s";
+ /* in cs_out, im_type_out (%3) includes the 'out ' part */
+ itype.cs_out = "%0(%1, %3 argRet); return argRet;";
+ itype.im_type_out = "out " + itype.cs_type;
+ itype.ret_as_byref_arg = true;
+ builtin_types.insert(itype.cname, itype);
+
+ // Signal
+ itype = TypeInterface();
+ itype.name = "Signal";
+ itype.cname = itype.name;
+ itype.proxy_name = "SignalInfo";
+ itype.c_in = "\t%0 %1_in = " C_METHOD_MANAGED_TO_SIGNAL "(*%1);\n";
+ itype.c_out = "\t*%3 = " C_METHOD_MANAGED_FROM_SIGNAL "(%1);\n";
+ itype.c_arg_in = "&%s_in";
+ itype.c_type = itype.name;
+ itype.c_type_in = "GDMonoMarshal::M_SignalInfo*";
+ itype.c_type_out = "GDMonoMarshal::M_SignalInfo";
+ itype.cs_in = "ref %s";
+ /* in cs_out, im_type_out (%3) includes the 'out ' part */
+ itype.cs_out = "%0(%1, %3 argRet); return argRet;";
+ itype.cs_type = itype.proxy_name;
+ itype.im_type_in = "ref " + itype.cs_type;
+ itype.im_type_out = "out " + itype.cs_type;
+ itype.ret_as_byref_arg = true;
+ builtin_types.insert(itype.cname, itype);
+
// VarArg (fictitious type to represent variable arguments)
itype = TypeInterface();
itype.name = "VarArg";
@@ -2917,13 +3187,11 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
#define INSERT_ARRAY(m_type, m_proxy_t) INSERT_ARRAY_FULL(m_type, m_type, m_proxy_t)
INSERT_ARRAY(PackedInt32Array, int);
+ INSERT_ARRAY(PackedInt64Array, long);
INSERT_ARRAY_FULL(PackedByteArray, PackedByteArray, byte);
-#ifdef REAL_T_IS_DOUBLE
- INSERT_ARRAY(PackedFloat32Array, double);
-#else
INSERT_ARRAY(PackedFloat32Array, float);
-#endif
+ INSERT_ARRAY(PackedFloat64Array, double);
INSERT_ARRAY(PackedStringArray, string);
@@ -2994,7 +3262,7 @@ void BindingsGenerator::_populate_global_constants() {
String constant_name = GlobalConstants::get_global_constant_name(i);
- const DocData::ConstantDoc *const_doc = NULL;
+ const DocData::ConstantDoc *const_doc = nullptr;
for (int j = 0; j < global_scope_doc.constants.size(); j++) {
const DocData::ConstantDoc &curr_const_doc = global_scope_doc.constants[j];
@@ -3052,7 +3320,10 @@ void BindingsGenerator::_populate_global_constants() {
// HARDCODED
List<StringName> hardcoded_enums;
+ hardcoded_enums.push_back("Vector2.Axis");
+ hardcoded_enums.push_back("Vector2i.Axis");
hardcoded_enums.push_back("Vector3.Axis");
+ hardcoded_enums.push_back("Vector3i.Axis");
for (List<StringName>::Element *E = hardcoded_enums.front(); E; E = E->next()) {
// These enums are not generated and must be written manually (e.g.: Vector3.Axis)
// Here, we assume core types do not begin with underscore
diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h
index d3a8937313..7c87c688db 100644
--- a/modules/mono/editor/bindings_generator.h
+++ b/modules/mono/editor/bindings_generator.h
@@ -33,7 +33,7 @@
#include "core/class_db.h"
#include "core/string_builder.h"
-#include "editor/doc/doc_data.h"
+#include "editor/doc_data.h"
#include "editor/editor_help.h"
#if defined(DEBUG_METHODS_ENABLED) && defined(TOOLS_ENABLED)
@@ -107,9 +107,15 @@ class BindingsGenerator {
TypeReference type;
String name;
- String default_argument;
DefaultParamMode def_param_mode;
+ /**
+ * Determines the expression for the parameter default value.
+ * Formatting elements:
+ * %0 or %s: [cs_type] of the argument type
+ */
+ String default_argument;
+
ArgumentInterface() {
def_param_mode = CONSTANT;
}
@@ -170,7 +176,33 @@ class BindingsGenerator {
is_virtual = false;
requires_object_call = false;
is_internal = false;
- method_doc = NULL;
+ method_doc = nullptr;
+ is_deprecated = false;
+ }
+ };
+
+ struct SignalInterface {
+ String name;
+ StringName cname;
+
+ /**
+ * Name of the C# method
+ */
+ String proxy_name;
+
+ List<ArgumentInterface> arguments;
+
+ const DocData::MethodDoc *method_doc;
+
+ bool is_deprecated;
+ String deprecation_message;
+
+ void add_argument(const ArgumentInterface &argument) {
+ arguments.push_back(argument);
+ }
+
+ SignalInterface() {
+ method_doc = nullptr;
is_deprecated = false;
}
};
@@ -336,6 +368,7 @@ class BindingsGenerator {
List<EnumInterface> enums;
List<PropertyInterface> properties;
List<MethodInterface> methods;
+ List<SignalInterface> signals_;
const MethodInterface *find_method_by_name(const StringName &p_cname) const {
for (const List<MethodInterface>::Element *E = methods.front(); E; E = E->next()) {
@@ -343,7 +376,7 @@ class BindingsGenerator {
return &E->get();
}
- return NULL;
+ return nullptr;
}
const PropertyInterface *find_property_by_name(const StringName &p_cname) const {
@@ -352,7 +385,7 @@ class BindingsGenerator {
return &E->get();
}
- return NULL;
+ return nullptr;
}
const PropertyInterface *find_property_by_proxy_name(const String &p_proxy_name) const {
@@ -361,7 +394,16 @@ class BindingsGenerator {
return &E->get();
}
- return NULL;
+ return nullptr;
+ }
+
+ const MethodInterface *find_method_by_proxy_name(const String &p_proxy_name) const {
+ for (const List<MethodInterface>::Element *E = methods.front(); E; E = E->next()) {
+ if (E->get().proxy_name == p_proxy_name)
+ return &E->get();
+ }
+
+ return nullptr;
}
private:
@@ -456,7 +498,7 @@ class BindingsGenerator {
c_arg_in = "%s";
- class_doc = NULL;
+ class_doc = nullptr;
}
};
@@ -510,7 +552,7 @@ class BindingsGenerator {
List<InternalCall> core_custom_icalls;
List<InternalCall> editor_custom_icalls;
- Map<StringName, List<StringName> > blacklisted_methods;
+ Map<StringName, List<StringName>> blacklisted_methods;
void _initialize_blacklisted_methods();
@@ -524,6 +566,8 @@ class BindingsGenerator {
StringName type_Reference;
StringName type_RID;
StringName type_String;
+ StringName type_StringName;
+ StringName type_NodePath;
StringName type_at_GlobalScope;
StringName enum_Error;
@@ -548,6 +592,8 @@ class BindingsGenerator {
type_Reference = StaticCString::create("Reference");
type_RID = StaticCString::create("RID");
type_String = StaticCString::create("String");
+ type_StringName = StaticCString::create("StringName");
+ type_NodePath = StaticCString::create("NodePath");
type_at_GlobalScope = StaticCString::create("@GlobalScope");
enum_Error = StaticCString::create("Error");
@@ -576,7 +622,7 @@ class BindingsGenerator {
if (it->get().name == p_name) return it;
it = it->next();
}
- return NULL;
+ return nullptr;
}
const ConstantInterface *find_constant_by_name(const String &p_name, const List<ConstantInterface> &p_constants) const {
@@ -585,7 +631,7 @@ class BindingsGenerator {
return &E->get();
}
- return NULL;
+ return nullptr;
}
inline String get_unique_sig(const TypeInterface &p_type) {
@@ -623,6 +669,7 @@ class BindingsGenerator {
Error _generate_cs_property(const TypeInterface &p_itype, const PropertyInterface &p_iprop, StringBuilder &p_output);
Error _generate_cs_method(const TypeInterface &p_itype, const MethodInterface &p_imethod, int &p_method_bind_count, StringBuilder &p_output);
+ Error _generate_cs_signal(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::SignalInterface &p_isignal, StringBuilder &p_output);
void _generate_global_constants(StringBuilder &p_output);
diff --git a/modules/mono/editor/csharp_project.cpp b/modules/mono/editor/csharp_project.cpp
index 872f45ba91..e5c2d023d3 100644
--- a/modules/mono/editor/csharp_project.cpp
+++ b/modules/mono/editor/csharp_project.cpp
@@ -57,8 +57,8 @@ void add_item(const String &p_project_path, const String &p_item_type, const Str
Variant item_type = p_item_type;
Variant include = p_include;
const Variant *args[3] = { &project_path, &item_type, &include };
- MonoException *exc = NULL;
- klass->get_method("AddItemToProjectChecked", 3)->invoke(NULL, args, &exc);
+ MonoException *exc = nullptr;
+ klass->get_method("AddItemToProjectChecked", 3)->invoke(nullptr, args, &exc);
if (exc) {
GDMonoUtils::debug_print_unhandled_exception(exc);
diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp
index 31996a03d0..283d4beb8e 100644
--- a/modules/mono/editor/editor_internal_calls.cpp
+++ b/modules/mono/editor/editor_internal_calls.cpp
@@ -95,7 +95,7 @@ MonoString *godot_icall_GodotSharpDirs_MonoSolutionsDir() {
#ifdef TOOLS_ENABLED
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_mono_solutions_dir());
#else
- return NULL;
+ return nullptr;
#endif
}
@@ -103,7 +103,7 @@ MonoString *godot_icall_GodotSharpDirs_BuildLogsDirs() {
#ifdef TOOLS_ENABLED
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_build_logs_dir());
#else
- return NULL;
+ return nullptr;
#endif
}
@@ -111,7 +111,7 @@ MonoString *godot_icall_GodotSharpDirs_ProjectSlnPath() {
#ifdef TOOLS_ENABLED
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_project_sln_path());
#else
- return NULL;
+ return nullptr;
#endif
}
@@ -119,7 +119,7 @@ MonoString *godot_icall_GodotSharpDirs_ProjectCsProjPath() {
#ifdef TOOLS_ENABLED
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_project_csproj_path());
#else
- return NULL;
+ return nullptr;
#endif
}
@@ -127,7 +127,7 @@ MonoString *godot_icall_GodotSharpDirs_DataEditorToolsDir() {
#ifdef TOOLS_ENABLED
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_editor_tools_dir());
#else
- return NULL;
+ return nullptr;
#endif
}
@@ -135,7 +135,7 @@ MonoString *godot_icall_GodotSharpDirs_DataEditorPrebuiltApiDir() {
#ifdef TOOLS_ENABLED
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_editor_prebuilt_api_dir());
#else
- return NULL;
+ return nullptr;
#endif
}
@@ -151,7 +151,7 @@ MonoString *godot_icall_GodotSharpDirs_DataMonoBinDir() {
#ifdef WINDOWS_ENABLED
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_mono_bin_dir());
#else
- return NULL;
+ return nullptr;
#endif
}
@@ -202,7 +202,7 @@ uint32_t godot_icall_BindingsGenerator_CsGlueVersion() {
}
int32_t godot_icall_ScriptClassParser_ParseFile(MonoString *p_filepath, MonoObject *p_classes, MonoString **r_error_str) {
- *r_error_str = NULL;
+ *r_error_str = nullptr;
String filepath = GDMonoMarshal::mono_string_to_godot(p_filepath);
@@ -335,7 +335,7 @@ MonoString *godot_icall_Internal_MonoWindowsInstallRoot() {
String install_root_dir = GDMono::get_singleton()->get_mono_reg_info().install_root_dir;
return GDMonoMarshal::mono_string_from_godot(install_root_dir);
#else
- return NULL;
+ return nullptr;
#endif
}
diff --git a/modules/mono/editor/godotsharp_export.cpp b/modules/mono/editor/godotsharp_export.cpp
index ce0b6ad0e6..324013e5e2 100644
--- a/modules/mono/editor/godotsharp_export.cpp
+++ b/modules/mono/editor/godotsharp_export.cpp
@@ -59,7 +59,7 @@ Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String>
if (r_dependencies.has(ref_name))
continue;
- GDMonoAssembly *ref_assembly = NULL;
+ GDMonoAssembly *ref_assembly = nullptr;
String path;
bool has_extension = ref_name.ends_with(".dll") || ref_name.ends_with(".exe");
@@ -70,21 +70,21 @@ Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String>
path = search_dir.plus_file(ref_name);
if (FileAccess::exists(path)) {
GDMono::get_singleton()->load_assembly_from(ref_name.get_basename(), path, &ref_assembly, true);
- if (ref_assembly != NULL)
+ if (ref_assembly != nullptr)
break;
}
} else {
path = search_dir.plus_file(ref_name + ".dll");
if (FileAccess::exists(path)) {
GDMono::get_singleton()->load_assembly_from(ref_name, path, &ref_assembly, true);
- if (ref_assembly != NULL)
+ if (ref_assembly != nullptr)
break;
}
path = search_dir.plus_file(ref_name + ".exe");
if (FileAccess::exists(path)) {
GDMono::get_singleton()->load_assembly_from(ref_name, path, &ref_assembly, true);
- if (ref_assembly != NULL)
+ if (ref_assembly != nullptr)
break;
}
}
@@ -92,7 +92,8 @@ Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String>
ERR_FAIL_COND_V_MSG(!ref_assembly, ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + ref_name + "'.");
- r_dependencies[ref_name] = ref_assembly->get_path();
+ // Use the path we got from the search. Don't try to get the path from the loaded assembly as we can't trust it will be from the selected BCL dir.
+ r_dependencies[ref_name] = path;
Error err = get_assembly_dependencies(ref_assembly, p_search_dirs, r_dependencies);
ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot load one of the dependencies for the assembly: '" + ref_name + "'.");
@@ -116,7 +117,7 @@ Error get_exported_assembly_dependencies(const Dictionary &p_initial_dependencie
String assembly_name = *key;
String assembly_path = p_initial_dependencies[*key];
- GDMonoAssembly *assembly = NULL;
+ GDMonoAssembly *assembly = nullptr;
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 + "'.");
diff --git a/modules/mono/glue/GodotSharp/GodotSharp.sln b/modules/mono/glue/GodotSharp/GodotSharp.sln
index a496e36da3..4896d0a07d 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp.sln
+++ b/modules/mono/glue/GodotSharp/GodotSharp.sln
@@ -8,8 +8,6 @@ Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{AEBF0036-DA76-4341-B651-A3F2856AB2FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs
index 3957387be9..39d5782db8 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs
@@ -2,7 +2,7 @@ using System;
namespace Godot
{
- [AttributeUsage(AttributeTargets.Delegate)]
+ [AttributeUsage(AttributeTargets.Delegate | AttributeTargets.Event)]
public class SignalAttribute : Attribute
{
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs
new file mode 100644
index 0000000000..c85cc1884c
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs
@@ -0,0 +1,31 @@
+using System;
+
+namespace Godot
+{
+ public struct Callable
+ {
+ private readonly Object _target;
+ private readonly StringName _method;
+ private readonly Delegate _delegate;
+
+ public Object Target => _target;
+ public StringName Method => _method;
+ public Delegate Delegate => _delegate;
+
+ public static implicit operator Callable(Delegate @delegate) => new Callable(@delegate);
+
+ public Callable(Object target, StringName method)
+ {
+ _target = target;
+ _method = method;
+ _delegate = null;
+ }
+
+ public Callable(Delegate @delegate)
+ {
+ _target = null;
+ _method = null;
+ _delegate = @delegate;
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
new file mode 100644
index 0000000000..785e87a043
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
@@ -0,0 +1,395 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+namespace Godot
+{
+ internal static class DelegateUtils
+ {
+ private enum TargetKind : uint
+ {
+ Static,
+ GodotObject,
+ CompilerGenerated
+ }
+
+ internal static bool TrySerializeDelegate(Delegate @delegate, Collections.Array serializedData)
+ {
+ if (@delegate is MulticastDelegate multicastDelegate)
+ {
+ bool someDelegatesSerialized = false;
+
+ Delegate[] invocationList = multicastDelegate.GetInvocationList();
+
+ if (invocationList.Length > 1)
+ {
+ var multiCastData = new Collections.Array();
+
+ foreach (Delegate oneDelegate in invocationList)
+ someDelegatesSerialized |= TrySerializeDelegate(oneDelegate, multiCastData);
+
+ if (!someDelegatesSerialized)
+ return false;
+
+ serializedData.Add(multiCastData);
+ return true;
+ }
+ }
+
+ if (TrySerializeSingleDelegate(@delegate, out byte[] buffer))
+ {
+ serializedData.Add(buffer);
+ return true;
+ }
+
+ return false;
+ }
+
+ private static bool TrySerializeSingleDelegate(Delegate @delegate, out byte[] buffer)
+ {
+ buffer = null;
+
+ object target = @delegate.Target;
+
+ switch (target)
+ {
+ case null:
+ {
+ using (var stream = new MemoryStream())
+ using (var writer = new BinaryWriter(stream))
+ {
+ writer.Write((ulong) TargetKind.Static);
+
+ SerializeType(writer, @delegate.GetType());
+
+ if (!TrySerializeMethodInfo(writer, @delegate.Method))
+ return false;
+
+ buffer = stream.ToArray();
+ return true;
+ }
+ }
+ case Godot.Object godotObject:
+ {
+ using (var stream = new MemoryStream())
+ using (var writer = new BinaryWriter(stream))
+ {
+ writer.Write((ulong) TargetKind.GodotObject);
+ writer.Write((ulong) godotObject.GetInstanceId());
+
+ SerializeType(writer, @delegate.GetType());
+
+ if (!TrySerializeMethodInfo(writer, @delegate.Method))
+ return false;
+
+ buffer = stream.ToArray();
+ return true;
+ }
+ }
+ default:
+ {
+ Type targetType = target.GetType();
+
+ if (targetType.GetCustomAttribute(typeof(CompilerGeneratedAttribute), true) != null)
+ {
+ // Compiler generated. Probably a closure. Try to serialize it.
+
+ using (var stream = new MemoryStream())
+ using (var writer = new BinaryWriter(stream))
+ {
+ writer.Write((ulong) TargetKind.CompilerGenerated);
+ SerializeType(writer, targetType);
+
+ SerializeType(writer, @delegate.GetType());
+
+ if (!TrySerializeMethodInfo(writer, @delegate.Method))
+ return false;
+
+ FieldInfo[] fields = targetType.GetFields(BindingFlags.Instance | BindingFlags.Public);
+
+ writer.Write(fields.Length);
+
+ foreach (FieldInfo field in fields)
+ {
+ Type fieldType = field.GetType();
+
+ Variant.Type variantType = GD.TypeToVariantType(fieldType);
+
+ if (variantType == Variant.Type.Nil)
+ return false;
+
+ writer.Write(field.Name);
+ byte[] valueBuffer = GD.Var2Bytes(field.GetValue(target));
+ writer.Write(valueBuffer.Length);
+ writer.Write(valueBuffer);
+ }
+
+ buffer = stream.ToArray();
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+ }
+
+ private static bool TrySerializeMethodInfo(BinaryWriter writer, MethodInfo methodInfo)
+ {
+ if (methodInfo == null)
+ return false;
+
+ SerializeType(writer, methodInfo.DeclaringType);
+
+ writer.Write(methodInfo.Name);
+
+ int flags = 0;
+
+ if (methodInfo.IsPublic)
+ flags |= (int) BindingFlags.Public;
+ else
+ flags |= (int) BindingFlags.NonPublic;
+
+ if (methodInfo.IsStatic)
+ flags |= (int) BindingFlags.Static;
+ else
+ flags |= (int) BindingFlags.Instance;
+
+ writer.Write(flags);
+
+ Type returnType = methodInfo.ReturnType;
+ bool hasReturn = methodInfo.ReturnType != typeof(void);
+
+ writer.Write(hasReturn);
+ if (hasReturn)
+ SerializeType(writer, returnType);
+
+ ParameterInfo[] parameters = methodInfo.GetParameters();
+
+ writer.Write(parameters.Length);
+
+ if (parameters.Length > 0)
+ {
+ for (int i = 0; i < parameters.Length; i++)
+ SerializeType(writer, parameters[i].ParameterType);
+ }
+
+ return true;
+ }
+
+ private static void SerializeType(BinaryWriter writer, Type type)
+ {
+ if (type == null)
+ {
+ int genericArgumentsCount = -1;
+ writer.Write(genericArgumentsCount);
+ }
+ else if (type.IsGenericType)
+ {
+ Type genericTypeDef = type.GetGenericTypeDefinition();
+ Type[] genericArgs = type.GetGenericArguments();
+
+ int genericArgumentsCount = genericArgs.Length;
+ writer.Write(genericArgumentsCount);
+
+ string assemblyQualifiedName = genericTypeDef.AssemblyQualifiedName;
+ Debug.Assert(assemblyQualifiedName != null);
+ writer.Write(assemblyQualifiedName);
+
+ for (int i = 0; i < genericArgs.Length; i++)
+ SerializeType(writer, genericArgs[i]);
+ }
+ else
+ {
+ int genericArgumentsCount = 0;
+ writer.Write(genericArgumentsCount);
+
+ string assemblyQualifiedName = type.AssemblyQualifiedName;
+ Debug.Assert(assemblyQualifiedName != null);
+ writer.Write(assemblyQualifiedName);
+ }
+ }
+
+ private static bool TryDeserializeDelegate(Collections.Array serializedData, out Delegate @delegate)
+ {
+ if (serializedData.Count == 1)
+ {
+ object elem = serializedData[0];
+
+ if (elem is Collections.Array multiCastData)
+ return TryDeserializeDelegate(multiCastData, out @delegate);
+
+ return TryDeserializeSingleDelegate((byte[])elem, out @delegate);
+ }
+
+ @delegate = null;
+
+ var delegates = new List<Delegate>(serializedData.Count);
+
+ foreach (object elem in serializedData)
+ {
+ if (elem is Collections.Array multiCastData)
+ {
+ if (TryDeserializeDelegate(multiCastData, out Delegate oneDelegate))
+ delegates.Add(oneDelegate);
+ }
+ else
+ {
+ if (TryDeserializeSingleDelegate((byte[]) elem, out Delegate oneDelegate))
+ delegates.Add(oneDelegate);
+ }
+ }
+
+ if (delegates.Count <= 0)
+ return false;
+
+ @delegate = delegates.Count == 1 ? delegates[0] : Delegate.Combine(delegates.ToArray());
+ return true;
+ }
+
+ private static bool TryDeserializeSingleDelegate(byte[] buffer, out Delegate @delegate)
+ {
+ @delegate = null;
+
+ using (var stream = new MemoryStream(buffer, writable: false))
+ using (var reader = new BinaryReader(stream))
+ {
+ var targetKind = (TargetKind) reader.ReadUInt64();
+
+ switch (targetKind)
+ {
+ case TargetKind.Static:
+ {
+ Type delegateType = DeserializeType(reader);
+ if (delegateType == null)
+ return false;
+
+ if (!TryDeserializeMethodInfo(reader, out MethodInfo methodInfo))
+ return false;
+
+ @delegate = Delegate.CreateDelegate(delegateType, null, methodInfo);
+ return true;
+ }
+ case TargetKind.GodotObject:
+ {
+ ulong objectId = reader.ReadUInt64();
+ Godot.Object godotObject = GD.InstanceFromId(objectId);
+ if (godotObject == null)
+ return false;
+
+ Type delegateType = DeserializeType(reader);
+ if (delegateType == null)
+ return false;
+
+ if (!TryDeserializeMethodInfo(reader, out MethodInfo methodInfo))
+ return false;
+
+ @delegate = Delegate.CreateDelegate(delegateType, godotObject, methodInfo);
+ return true;
+ }
+ case TargetKind.CompilerGenerated:
+ {
+ Type targetType = DeserializeType(reader);
+ if (targetType == null)
+ return false;
+
+ Type delegateType = DeserializeType(reader);
+ if (delegateType == null)
+ return false;
+
+ if (!TryDeserializeMethodInfo(reader, out MethodInfo methodInfo))
+ return false;
+
+ int fieldCount = reader.ReadInt32();
+
+ object recreatedTarget = Activator.CreateInstance(targetType);
+
+ for (int i = 0; i < fieldCount; i++)
+ {
+ string name = reader.ReadString();
+ int valueBufferLength = reader.ReadInt32();
+ byte[] valueBuffer = reader.ReadBytes(valueBufferLength);
+
+ FieldInfo fieldInfo = targetType.GetField(name, BindingFlags.Instance | BindingFlags.Public);
+ fieldInfo?.SetValue(recreatedTarget, GD.Bytes2Var(valueBuffer));
+ }
+
+ @delegate = Delegate.CreateDelegate(delegateType, recreatedTarget, methodInfo);
+ return true;
+ }
+ default:
+ return false;
+ }
+ }
+ }
+
+ private static bool TryDeserializeMethodInfo(BinaryReader reader, out MethodInfo methodInfo)
+ {
+ methodInfo = null;
+
+ Type declaringType = DeserializeType(reader);
+
+ string methodName = reader.ReadString();
+
+ int flags = reader.ReadInt32();
+
+ bool hasReturn = reader.ReadBoolean();
+ Type returnType = hasReturn ? DeserializeType(reader) : typeof(void);
+
+ int parametersCount = reader.ReadInt32();
+
+ if (parametersCount > 0)
+ {
+ var parameterTypes = new Type[parametersCount];
+
+ for (int i = 0; i < parametersCount; i++)
+ {
+ Type parameterType = DeserializeType(reader);
+ if (parameterType == null)
+ return false;
+ parameterTypes[i] = parameterType;
+ }
+
+ methodInfo = declaringType.GetMethod(methodName, (BindingFlags) flags, null, parameterTypes, null);
+ return methodInfo != null && methodInfo.ReturnType == returnType;
+ }
+
+ methodInfo = declaringType.GetMethod(methodName, (BindingFlags) flags);
+ return methodInfo != null && methodInfo.ReturnType == returnType;
+ }
+
+ private static Type DeserializeType(BinaryReader reader)
+ {
+ int genericArgumentsCount = reader.ReadInt32();
+
+ if (genericArgumentsCount == -1)
+ return null;
+
+ string assemblyQualifiedName = reader.ReadString();
+ var type = Type.GetType(assemblyQualifiedName);
+
+ if (type == null)
+ return null; // Type not found
+
+ if (genericArgumentsCount != 0)
+ {
+ var genericArgumentTypes = new Type[genericArgumentsCount];
+
+ for (int i = 0; i < genericArgumentsCount; i++)
+ {
+ Type genericArgumentType = DeserializeType(reader);
+ if (genericArgumentType == null)
+ return null;
+ genericArgumentTypes[i] = genericArgumentType;
+ }
+
+ type = type.MakeGenericType(genericArgumentTypes);
+ }
+
+ return type;
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DynamicObject.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DynamicObject.cs
index a0f105d55e..c4c911b863 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DynamicObject.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DynamicObject.cs
@@ -23,7 +23,7 @@ namespace Godot
/// <example>
/// This sample shows how to use <see cref="Godot.DynamicGodotObject"/> to dynamically access the engine members of a <see cref="Godot.Object"/>.
/// <code>
- /// dynamic sprite = GetNode("Sprite").DynamicGodotObject;
+ /// dynamic sprite = GetNode("Sprite2D").DynamicGodotObject;
/// sprite.add_child(this);
///
/// if ((sprite.hframes * sprite.vframes) &gt; 0)
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ResourceLoaderExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ResourceLoaderExtensions.cs
index 684d160b57..5f64c09a89 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ResourceLoaderExtensions.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ResourceLoaderExtensions.cs
@@ -2,9 +2,9 @@ namespace Godot
{
public static partial class ResourceLoader
{
- public static T Load<T>(string path) where T : class
+ public static T Load<T>(string path, string typeHint = null, bool noCache = false) where T : class
{
- return (T)(object)Load(path);
+ return (T)(object)Load(path, typeHint, noCache);
}
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
index 2a9c2d73b1..9384da0e48 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
@@ -5,6 +5,7 @@ using System.Runtime.CompilerServices;
using real_t = System.Double;
#else
using real_t = System.Single;
+
#endif
// TODO: Add comments describing what this class does. It is not obvious.
@@ -13,9 +14,9 @@ namespace Godot
{
public static partial class GD
{
- public static object Bytes2Var(byte[] bytes, bool allow_objects = false)
+ public static object Bytes2Var(byte[] bytes, bool allowObjects = false)
{
- return godot_icall_GD_bytes2var(bytes, allow_objects);
+ return godot_icall_GD_bytes2var(bytes, allowObjects);
}
public static object Convert(object what, Variant.Type type)
@@ -25,7 +26,7 @@ namespace Godot
public static real_t Db2Linear(real_t db)
{
- return (real_t)Math.Exp(db * 0.11512925464970228420089957273422);
+ return (real_t) Math.Exp(db * 0.11512925464970228420089957273422);
}
public static real_t DecTime(real_t value, real_t amount, real_t step)
@@ -38,11 +39,11 @@ namespace Godot
return val * sgn;
}
- public static FuncRef FuncRef(Object instance, string funcname)
+ public static FuncRef FuncRef(Object instance, StringName funcName)
{
var ret = new FuncRef();
ret.SetInstance(instance);
- ret.SetFunction(funcname);
+ ret.SetFunction(funcName);
return ret;
}
@@ -58,7 +59,7 @@ namespace Godot
public static real_t Linear2Db(real_t linear)
{
- return (real_t)(Math.Log(linear) * 8.6858896380650365530225783783321);
+ return (real_t) (Math.Log(linear) * 8.6858896380650365530225783783321);
}
public static Resource Load(string path)
@@ -181,14 +182,14 @@ namespace Godot
return godot_icall_GD_str2var(str);
}
- public static bool TypeExists(string type)
+ public static bool TypeExists(StringName type)
{
- return godot_icall_GD_type_exists(type);
+ return godot_icall_GD_type_exists(StringName.GetPtr(type));
}
- public static byte[] Var2Bytes(object var, bool full_objects = false)
+ public static byte[] Var2Bytes(object var, bool fullObjects = false)
{
- return godot_icall_GD_var2bytes(var, full_objects);
+ return godot_icall_GD_var2bytes(var, fullObjects);
}
public static string Var2Str(object var)
@@ -196,8 +197,13 @@ namespace Godot
return godot_icall_GD_var2str(var);
}
+ public static Variant.Type TypeToVariantType(Type type)
+ {
+ return godot_icall_TypeToVariantType(type);
+ }
+
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static object godot_icall_GD_bytes2var(byte[] bytes, bool allow_objects);
+ internal extern static object godot_icall_GD_bytes2var(byte[] bytes, bool allowObjects);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static object godot_icall_GD_convert(object what, Variant.Type type);
@@ -206,7 +212,7 @@ namespace Godot
internal extern static int godot_icall_GD_hash(object var);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static Object godot_icall_GD_instance_from_id(ulong instance_id);
+ internal extern static Object godot_icall_GD_instance_from_id(ulong instanceId);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static void godot_icall_GD_print(object[] what);
@@ -249,10 +255,10 @@ namespace Godot
internal extern static object godot_icall_GD_str2var(string str);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static bool godot_icall_GD_type_exists(string type);
+ internal extern static bool godot_icall_GD_type_exists(IntPtr type);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static byte[] godot_icall_GD_var2bytes(object what, bool full_objects);
+ internal extern static byte[] godot_icall_GD_var2bytes(object what, bool fullObjects);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static string godot_icall_GD_var2str(object var);
@@ -262,5 +268,8 @@ namespace Godot
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static void godot_icall_GD_pushwarning(string type);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ private static extern Variant.Type godot_icall_TypeToVariantType(Type type);
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs
index 8c5872ba5a..4ecc55f94e 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs
@@ -7,7 +7,7 @@ namespace Godot
{
private bool disposed = false;
- internal IntPtr ptr;
+ private IntPtr ptr;
internal static IntPtr GetPtr(NodePath instance)
{
@@ -50,104 +50,93 @@ namespace Godot
this.ptr = ptr;
}
- public IntPtr NativeInstance
- {
- get { return ptr; }
- }
-
public NodePath() : this(string.Empty) {}
public NodePath(string path)
{
- this.ptr = godot_icall_NodePath_Ctor(path);
+ ptr = godot_icall_NodePath_Ctor(path);
}
- public static implicit operator NodePath(string from)
- {
- return new NodePath(from);
- }
+ public static implicit operator NodePath(string from) => new NodePath(from);
- public static implicit operator string(NodePath from)
- {
- return godot_icall_NodePath_operator_String(NodePath.GetPtr(from));
- }
+ public static implicit operator string(NodePath from) => from.ToString();
public override string ToString()
{
- return (string)this;
+ return godot_icall_NodePath_operator_String(GetPtr(this));
}
public NodePath GetAsPropertyPath()
{
- return new NodePath(godot_icall_NodePath_get_as_property_path(NodePath.GetPtr(this)));
+ return new NodePath(godot_icall_NodePath_get_as_property_path(GetPtr(this)));
}
public string GetConcatenatedSubnames()
{
- return godot_icall_NodePath_get_concatenated_subnames(NodePath.GetPtr(this));
+ return godot_icall_NodePath_get_concatenated_subnames(GetPtr(this));
}
public string GetName(int idx)
{
- return godot_icall_NodePath_get_name(NodePath.GetPtr(this), idx);
+ return godot_icall_NodePath_get_name(GetPtr(this), idx);
}
public int GetNameCount()
{
- return godot_icall_NodePath_get_name_count(NodePath.GetPtr(this));
+ return godot_icall_NodePath_get_name_count(GetPtr(this));
}
public string GetSubname(int idx)
{
- return godot_icall_NodePath_get_subname(NodePath.GetPtr(this), idx);
+ return godot_icall_NodePath_get_subname(GetPtr(this), idx);
}
public int GetSubnameCount()
{
- return godot_icall_NodePath_get_subname_count(NodePath.GetPtr(this));
+ return godot_icall_NodePath_get_subname_count(GetPtr(this));
}
public bool IsAbsolute()
{
- return godot_icall_NodePath_is_absolute(NodePath.GetPtr(this));
+ return godot_icall_NodePath_is_absolute(GetPtr(this));
}
public bool IsEmpty()
{
- return godot_icall_NodePath_is_empty(NodePath.GetPtr(this));
+ return godot_icall_NodePath_is_empty(GetPtr(this));
}
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static IntPtr godot_icall_NodePath_Ctor(string path);
+ private static extern IntPtr godot_icall_NodePath_Ctor(string path);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_NodePath_Dtor(IntPtr ptr);
+ private static extern void godot_icall_NodePath_Dtor(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static string godot_icall_NodePath_operator_String(IntPtr ptr);
+ private static extern string godot_icall_NodePath_operator_String(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static IntPtr godot_icall_NodePath_get_as_property_path(IntPtr ptr);
+ private static extern IntPtr godot_icall_NodePath_get_as_property_path(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static string godot_icall_NodePath_get_concatenated_subnames(IntPtr ptr);
+ private static extern string godot_icall_NodePath_get_concatenated_subnames(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static string godot_icall_NodePath_get_name(IntPtr ptr, int arg1);
+ private static extern string godot_icall_NodePath_get_name(IntPtr ptr, int arg1);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static int godot_icall_NodePath_get_name_count(IntPtr ptr);
+ private static extern int godot_icall_NodePath_get_name_count(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static string godot_icall_NodePath_get_subname(IntPtr ptr, int arg1);
+ private static extern string godot_icall_NodePath_get_subname(IntPtr ptr, int arg1);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static int godot_icall_NodePath_get_subname_count(IntPtr ptr);
+ private static extern int godot_icall_NodePath_get_subname_count(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static bool godot_icall_NodePath_is_absolute(IntPtr ptr);
+ private static extern bool godot_icall_NodePath_is_absolute(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static bool godot_icall_NodePath_is_empty(IntPtr ptr);
+ private static extern bool godot_icall_NodePath_is_empty(IntPtr ptr);
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs
index de80f7fddc..42610c5ef7 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs
@@ -7,7 +7,7 @@ namespace Godot
{
private bool disposed = false;
- private const string nativeName = "Object";
+ private static StringName nativeName = "Object";
internal IntPtr ptr;
internal bool memoryOwn;
@@ -15,7 +15,14 @@ namespace Godot
public Object() : this(false)
{
if (ptr == IntPtr.Zero)
+ {
ptr = godot_icall_Object_Ctor(this);
+ }
+ else
+ {
+ // This is called inside godot_icall_Object_Ctor, so we must call it as well in this case.
+ godot_icall_Object_ConnectEventSignals(ptr);
+ }
}
internal Object(bool memoryOwn)
@@ -101,7 +108,7 @@ namespace Godot
/// }
/// </code>
/// </example>
- public SignalAwaiter ToSignal(Object source, string signal)
+ public SignalAwaiter ToSignal(Object source, StringName signal)
{
return new SignalAwaiter(source, signal, this);
}
@@ -111,20 +118,28 @@ namespace Godot
/// </summary>
public dynamic DynamicObject => new DynamicGodotObject(this);
+ internal static IntPtr __ClassDB_get_method(StringName type, string method)
+ {
+ return godot_icall_Object_ClassDB_get_method(StringName.GetPtr(type), method);
+ }
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal static extern IntPtr godot_icall_Object_Ctor(Object obj);
+
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static IntPtr godot_icall_Object_Ctor(Object obj);
+ internal static extern void godot_icall_Object_Disposed(Object obj, IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_Object_Disposed(Object obj, IntPtr ptr);
+ internal static extern void godot_icall_Reference_Disposed(Object obj, IntPtr ptr, bool isFinalizer);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_Reference_Disposed(Object obj, IntPtr ptr, bool isFinalizer);
+ internal static extern void godot_icall_Object_ConnectEventSignals(IntPtr obj);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static string godot_icall_Object_ToString(IntPtr ptr);
+ internal static extern string godot_icall_Object_ToString(IntPtr ptr);
// Used by the generated API
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static IntPtr godot_icall_Object_ClassDB_get_method(string type, string method);
+ internal static extern IntPtr godot_icall_Object_ClassDB_get_method(IntPtr type, string method);
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs
new file mode 100644
index 0000000000..bc2cad8713
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs
@@ -0,0 +1,262 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Godot
+{
+ [Serializable]
+ [StructLayout(LayoutKind.Sequential)]
+ public struct Rect2i : IEquatable<Rect2i>
+ {
+ private Vector2i _position;
+ private Vector2i _size;
+
+ public Vector2i Position
+ {
+ get { return _position; }
+ set { _position = value; }
+ }
+
+ public Vector2i Size
+ {
+ get { return _size; }
+ set { _size = value; }
+ }
+
+ public Vector2i End
+ {
+ get { return _position + _size; }
+ set { _size = value - _position; }
+ }
+
+ public int Area
+ {
+ get { return GetArea(); }
+ }
+
+ public Rect2i Abs()
+ {
+ Vector2i end = End;
+ Vector2i topLeft = new Vector2i(Mathf.Min(_position.x, end.x), Mathf.Min(_position.y, end.y));
+ return new Rect2i(topLeft, _size.Abs());
+ }
+
+ public Rect2i Clip(Rect2i b)
+ {
+ var newRect = b;
+
+ if (!Intersects(newRect))
+ return new Rect2i();
+
+ newRect._position.x = Mathf.Max(b._position.x, _position.x);
+ newRect._position.y = Mathf.Max(b._position.y, _position.y);
+
+ Vector2i bEnd = b._position + b._size;
+ Vector2i end = _position + _size;
+
+ newRect._size.x = Mathf.Min(bEnd.x, end.x) - newRect._position.x;
+ newRect._size.y = Mathf.Min(bEnd.y, end.y) - newRect._position.y;
+
+ return newRect;
+ }
+
+ public bool Encloses(Rect2i b)
+ {
+ return b._position.x >= _position.x && b._position.y >= _position.y &&
+ b._position.x + b._size.x < _position.x + _size.x &&
+ b._position.y + b._size.y < _position.y + _size.y;
+ }
+
+ public Rect2i Expand(Vector2i to)
+ {
+ var expanded = this;
+
+ Vector2i begin = expanded._position;
+ Vector2i end = expanded._position + expanded._size;
+
+ if (to.x < begin.x)
+ begin.x = to.x;
+ if (to.y < begin.y)
+ begin.y = to.y;
+
+ if (to.x > end.x)
+ end.x = to.x;
+ if (to.y > end.y)
+ end.y = to.y;
+
+ expanded._position = begin;
+ expanded._size = end - begin;
+
+ return expanded;
+ }
+
+ public int GetArea()
+ {
+ return _size.x * _size.y;
+ }
+
+ public Rect2i Grow(int by)
+ {
+ var g = this;
+
+ g._position.x -= by;
+ g._position.y -= by;
+ g._size.x += by * 2;
+ g._size.y += by * 2;
+
+ return g;
+ }
+
+ public Rect2i GrowIndividual(int left, int top, int right, int bottom)
+ {
+ var g = this;
+
+ g._position.x -= left;
+ g._position.y -= top;
+ g._size.x += left + right;
+ g._size.y += top + bottom;
+
+ return g;
+ }
+
+ public Rect2i GrowMargin(Margin margin, int by)
+ {
+ var g = this;
+
+ g.GrowIndividual(Margin.Left == margin ? by : 0,
+ Margin.Top == margin ? by : 0,
+ Margin.Right == margin ? by : 0,
+ Margin.Bottom == margin ? by : 0);
+
+ return g;
+ }
+
+ public bool HasNoArea()
+ {
+ return _size.x <= 0 || _size.y <= 0;
+ }
+
+ public bool HasPoint(Vector2i point)
+ {
+ if (point.x < _position.x)
+ return false;
+ if (point.y < _position.y)
+ return false;
+
+ if (point.x >= _position.x + _size.x)
+ return false;
+ if (point.y >= _position.y + _size.y)
+ return false;
+
+ return true;
+ }
+
+ public bool Intersects(Rect2i b)
+ {
+ if (_position.x >= b._position.x + b._size.x)
+ return false;
+ if (_position.x + _size.x <= b._position.x)
+ return false;
+ if (_position.y >= b._position.y + b._size.y)
+ return false;
+ if (_position.y + _size.y <= b._position.y)
+ return false;
+
+ return true;
+ }
+
+ public Rect2i Merge(Rect2i b)
+ {
+ Rect2i newRect;
+
+ newRect._position.x = Mathf.Min(b._position.x, _position.x);
+ newRect._position.y = Mathf.Min(b._position.y, _position.y);
+
+ newRect._size.x = Mathf.Max(b._position.x + b._size.x, _position.x + _size.x);
+ newRect._size.y = Mathf.Max(b._position.y + b._size.y, _position.y + _size.y);
+
+ newRect._size = newRect._size - newRect._position; // Make relative again
+
+ return newRect;
+ }
+
+ // Constructors
+ public Rect2i(Vector2i position, Vector2i size)
+ {
+ _position = position;
+ _size = size;
+ }
+ public Rect2i(Vector2i position, int width, int height)
+ {
+ _position = position;
+ _size = new Vector2i(width, height);
+ }
+ public Rect2i(int x, int y, Vector2i size)
+ {
+ _position = new Vector2i(x, y);
+ _size = size;
+ }
+ public Rect2i(int x, int y, int width, int height)
+ {
+ _position = new Vector2i(x, y);
+ _size = new Vector2i(width, height);
+ }
+
+ public static bool operator ==(Rect2i left, Rect2i right)
+ {
+ return left.Equals(right);
+ }
+
+ public static bool operator !=(Rect2i left, Rect2i right)
+ {
+ return !left.Equals(right);
+ }
+
+ public static implicit operator Rect2(Rect2i value)
+ {
+ return new Rect2(value._position, value._size);
+ }
+
+ public static explicit operator Rect2i(Rect2 value)
+ {
+ return new Rect2i((Vector2i)value.Position, (Vector2i)value.Size);
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (obj is Rect2i)
+ {
+ return Equals((Rect2i)obj);
+ }
+
+ return false;
+ }
+
+ public bool Equals(Rect2i other)
+ {
+ return _position.Equals(other._position) && _size.Equals(other._size);
+ }
+
+ public override int GetHashCode()
+ {
+ return _position.GetHashCode() ^ _size.GetHashCode();
+ }
+
+ public override string ToString()
+ {
+ return String.Format("{0}, {1}", new object[]
+ {
+ _position.ToString(),
+ _size.ToString()
+ });
+ }
+
+ public string ToString(string format)
+ {
+ return String.Format("{0}, {1}", new object[]
+ {
+ _position.ToString(format),
+ _size.ToString(format)
+ });
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs
index 9483b6ffb4..4dc630238b 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs
@@ -9,13 +9,13 @@ namespace Godot
private object[] result;
private Action action;
- public SignalAwaiter(Object source, string signal, Object target)
+ public SignalAwaiter(Object source, StringName signal, Object target)
{
- godot_icall_SignalAwaiter_connect(Object.GetPtr(source), signal, Object.GetPtr(target), this);
+ godot_icall_SignalAwaiter_connect(Object.GetPtr(source), StringName.GetPtr(signal), Object.GetPtr(target), this);
}
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static Error godot_icall_SignalAwaiter_connect(IntPtr source, string signal, IntPtr target, SignalAwaiter awaiter);
+ internal extern static Error godot_icall_SignalAwaiter_connect(IntPtr source, IntPtr signal, IntPtr target, SignalAwaiter awaiter);
public bool IsCompleted
{
@@ -50,11 +50,5 @@ namespace Godot
action();
}
}
-
- internal void FailureCallback()
- {
- action = null;
- completed = true;
- }
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs
new file mode 100644
index 0000000000..dc92de7a61
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs
@@ -0,0 +1,17 @@
+namespace Godot
+{
+ public struct SignalInfo
+ {
+ private readonly Object _owner;
+ private readonly StringName _signalName;
+
+ public Object Owner => _owner;
+ public StringName Name => _signalName;
+
+ public SignalInfo(Object owner, StringName name)
+ {
+ _owner = owner;
+ _signalName = name;
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs
new file mode 100644
index 0000000000..7700b6d4ed
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs
@@ -0,0 +1,82 @@
+using System;
+using System.Runtime.CompilerServices;
+
+namespace Godot
+{
+ public sealed partial class StringName : IDisposable
+ {
+ private IntPtr ptr;
+
+ internal static IntPtr GetPtr(StringName instance)
+ {
+ if (instance == null)
+ throw new NullReferenceException($"The instance of type {nameof(StringName)} is null.");
+
+ if (instance.ptr == IntPtr.Zero)
+ throw new ObjectDisposedException(instance.GetType().FullName);
+
+ return instance.ptr;
+ }
+
+ ~StringName()
+ {
+ Dispose(false);
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ private void Dispose(bool disposing)
+ {
+ if (ptr != IntPtr.Zero)
+ {
+ godot_icall_StringName_Dtor(ptr);
+ ptr = IntPtr.Zero;
+ }
+ }
+
+ internal StringName(IntPtr ptr)
+ {
+ this.ptr = ptr;
+ }
+
+ public StringName()
+ {
+ ptr = IntPtr.Zero;
+ }
+
+ public StringName(string path)
+ {
+ ptr = path == null ? IntPtr.Zero : godot_icall_StringName_Ctor(path);
+ }
+
+ public static implicit operator StringName(string from) => new StringName(from);
+
+ public static implicit operator string(StringName from) => from.ToString();
+
+ public override string ToString()
+ {
+ return ptr == IntPtr.Zero ? string.Empty : godot_icall_StringName_operator_String(GetPtr(this));
+ }
+
+ public bool IsEmpty()
+ {
+ return ptr == IntPtr.Zero || godot_icall_StringName_is_empty(GetPtr(this));
+ }
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ private static extern IntPtr godot_icall_StringName_Ctor(string path);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ private static extern void godot_icall_StringName_Dtor(IntPtr ptr);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ private static extern string godot_icall_StringName_operator_String(IntPtr ptr);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ private static extern bool godot_icall_StringName_is_empty(IntPtr ptr);
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
index 385bfed122..f7b13198f8 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
@@ -76,11 +76,6 @@ namespace Godot
}
}
- public real_t Cross(Vector2 b)
- {
- return x * b.y - y * b.x;
- }
-
public Vector2 Abs()
{
return new Vector2(Mathf.Abs(x), Mathf.Abs(y));
@@ -130,6 +125,11 @@ namespace Godot
return v;
}
+ public real_t Cross(Vector2 b)
+ {
+ return x * b.y - y * b.x;
+ }
+
public Vector2 CubicInterpolate(Vector2 b, Vector2 preA, Vector2 postB, real_t t)
{
var p0 = preA;
@@ -234,7 +234,7 @@ namespace Godot
public Vector2 Reflect(Vector2 n)
{
- return 2.0f * n * Dot(n) - this;
+ return 2 * Dot(n) * n - this;
}
public Vector2 Rotated(real_t phi)
@@ -352,18 +352,18 @@ namespace Godot
return left;
}
- public static Vector2 operator /(Vector2 vec, real_t scale)
+ public static Vector2 operator /(Vector2 vec, real_t divisor)
{
- vec.x /= scale;
- vec.y /= scale;
+ vec.x /= divisor;
+ vec.y /= divisor;
return vec;
}
- public static Vector2 operator /(Vector2 left, Vector2 right)
+ public static Vector2 operator /(Vector2 vec, Vector2 divisorv)
{
- left.x /= right.x;
- left.y /= right.y;
- return left;
+ vec.x /= divisorv.x;
+ vec.y /= divisorv.y;
+ return vec;
}
public static Vector2 operator %(Vector2 vec, real_t divisor)
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs
new file mode 100644
index 0000000000..7dc22d7918
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs
@@ -0,0 +1,380 @@
+using System;
+using System.Runtime.InteropServices;
+
+#if REAL_T_IS_DOUBLE
+using real_t = System.Double;
+#else
+using real_t = System.Single;
+#endif
+
+namespace Godot
+{
+ /// <summary>
+ /// 2-element structure that can be used to represent 2D grid coordinates or pairs of integers.
+ /// </summary>
+ [Serializable]
+ [StructLayout(LayoutKind.Sequential)]
+ public struct Vector2i : IEquatable<Vector2i>
+ {
+ public enum Axis
+ {
+ X = 0,
+ Y
+ }
+
+ public int x;
+ public int y;
+
+ public int this[int index]
+ {
+ get
+ {
+ switch (index)
+ {
+ case 0:
+ return x;
+ case 1:
+ return y;
+ default:
+ throw new IndexOutOfRangeException();
+ }
+ }
+ set
+ {
+ switch (index)
+ {
+ case 0:
+ x = value;
+ return;
+ case 1:
+ y = value;
+ return;
+ default:
+ throw new IndexOutOfRangeException();
+ }
+ }
+ }
+
+ public Vector2i Abs()
+ {
+ return new Vector2i(Mathf.Abs(x), Mathf.Abs(y));
+ }
+
+ public real_t Angle()
+ {
+ return Mathf.Atan2(y, x);
+ }
+
+ public real_t AngleTo(Vector2i to)
+ {
+ return Mathf.Atan2(Cross(to), Dot(to));
+ }
+
+ public real_t AngleToPoint(Vector2i to)
+ {
+ return Mathf.Atan2(y - to.y, x - to.x);
+ }
+
+ public real_t Aspect()
+ {
+ return x / (real_t)y;
+ }
+
+ public Vector2i Bounce(Vector2i n)
+ {
+ return -Reflect(n);
+ }
+
+ public int Cross(Vector2i b)
+ {
+ return x * b.y - y * b.x;
+ }
+
+ public int DistanceSquaredTo(Vector2i b)
+ {
+ return (b - this).LengthSquared();
+ }
+
+ public real_t DistanceTo(Vector2i b)
+ {
+ return (b - this).Length();
+ }
+
+ public int Dot(Vector2i b)
+ {
+ return x * b.x + y * b.y;
+ }
+
+ public real_t Length()
+ {
+ int x2 = x * x;
+ int y2 = y * y;
+
+ return Mathf.Sqrt(x2 + y2);
+ }
+
+ public int LengthSquared()
+ {
+ int x2 = x * x;
+ int y2 = y * y;
+
+ return x2 + y2;
+ }
+
+ public Axis MaxAxis()
+ {
+ return x < y ? Axis.Y : Axis.X;
+ }
+
+ public Axis MinAxis()
+ {
+ return x > y ? Axis.Y : Axis.X;
+ }
+
+ public Vector2i PosMod(int mod)
+ {
+ Vector2i v = this;
+ v.x = Mathf.PosMod(v.x, mod);
+ v.y = Mathf.PosMod(v.y, mod);
+ return v;
+ }
+
+ public Vector2i PosMod(Vector2i modv)
+ {
+ Vector2i v = this;
+ v.x = Mathf.PosMod(v.x, modv.x);
+ v.y = Mathf.PosMod(v.y, modv.y);
+ return v;
+ }
+
+ public Vector2i Reflect(Vector2i n)
+ {
+ return 2 * Dot(n) * n - this;
+ }
+
+ public Vector2i Sign()
+ {
+ Vector2i v = this;
+ v.x = Mathf.Sign(v.x);
+ v.y = Mathf.Sign(v.y);
+ return v;
+ }
+
+ public Vector2i Tangent()
+ {
+ return new Vector2i(y, -x);
+ }
+
+ // Constants
+ private static readonly Vector2i _zero = new Vector2i(0, 0);
+ private static readonly Vector2i _one = new Vector2i(1, 1);
+
+ private static readonly Vector2i _up = new Vector2i(0, -1);
+ private static readonly Vector2i _down = new Vector2i(0, 1);
+ private static readonly Vector2i _right = new Vector2i(1, 0);
+ private static readonly Vector2i _left = new Vector2i(-1, 0);
+
+ public static Vector2i Zero { get { return _zero; } }
+ public static Vector2i One { get { return _one; } }
+
+ public static Vector2i Up { get { return _up; } }
+ public static Vector2i Down { get { return _down; } }
+ public static Vector2i Right { get { return _right; } }
+ public static Vector2i Left { get { return _left; } }
+
+ // Constructors
+ public Vector2i(int x, int y)
+ {
+ this.x = x;
+ this.y = y;
+ }
+ public Vector2i(Vector2i vi)
+ {
+ this.x = vi.x;
+ this.y = vi.y;
+ }
+ public Vector2i(Vector2 v)
+ {
+ this.x = Mathf.RoundToInt(v.x);
+ this.y = Mathf.RoundToInt(v.y);
+ }
+
+ public static Vector2i operator +(Vector2i left, Vector2i right)
+ {
+ left.x += right.x;
+ left.y += right.y;
+ return left;
+ }
+
+ public static Vector2i operator -(Vector2i left, Vector2i right)
+ {
+ left.x -= right.x;
+ left.y -= right.y;
+ return left;
+ }
+
+ public static Vector2i operator -(Vector2i vec)
+ {
+ vec.x = -vec.x;
+ vec.y = -vec.y;
+ return vec;
+ }
+
+ public static Vector2i operator *(Vector2i vec, int scale)
+ {
+ vec.x *= scale;
+ vec.y *= scale;
+ return vec;
+ }
+
+ public static Vector2i operator *(int scale, Vector2i vec)
+ {
+ vec.x *= scale;
+ vec.y *= scale;
+ return vec;
+ }
+
+ public static Vector2i operator *(Vector2i left, Vector2i right)
+ {
+ left.x *= right.x;
+ left.y *= right.y;
+ return left;
+ }
+
+ public static Vector2i operator /(Vector2i vec, int divisor)
+ {
+ vec.x /= divisor;
+ vec.y /= divisor;
+ return vec;
+ }
+
+ public static Vector2i operator /(Vector2i vec, Vector2i divisorv)
+ {
+ vec.x /= divisorv.x;
+ vec.y /= divisorv.y;
+ return vec;
+ }
+
+ public static Vector2i operator %(Vector2i vec, int divisor)
+ {
+ vec.x %= divisor;
+ vec.y %= divisor;
+ return vec;
+ }
+
+ public static Vector2i operator %(Vector2i vec, Vector2i divisorv)
+ {
+ vec.x %= divisorv.x;
+ vec.y %= divisorv.y;
+ return vec;
+ }
+
+ public static Vector2i operator &(Vector2i vec, int and)
+ {
+ vec.x &= and;
+ vec.y &= and;
+ return vec;
+ }
+
+ public static Vector2i operator &(Vector2i vec, Vector2i andv)
+ {
+ vec.x &= andv.x;
+ vec.y &= andv.y;
+ return vec;
+ }
+
+ public static bool operator ==(Vector2i left, Vector2i right)
+ {
+ return left.Equals(right);
+ }
+
+ public static bool operator !=(Vector2i left, Vector2i right)
+ {
+ return !left.Equals(right);
+ }
+
+ public static bool operator <(Vector2i left, Vector2i right)
+ {
+ if (left.x.Equals(right.x))
+ {
+ return left.y < right.y;
+ }
+ return left.x < right.x;
+ }
+
+ public static bool operator >(Vector2i left, Vector2i right)
+ {
+ if (left.x.Equals(right.x))
+ {
+ return left.y > right.y;
+ }
+ return left.x > right.x;
+ }
+
+ public static bool operator <=(Vector2i left, Vector2i right)
+ {
+ if (left.x.Equals(right.x))
+ {
+ return left.y <= right.y;
+ }
+ return left.x <= right.x;
+ }
+
+ public static bool operator >=(Vector2i left, Vector2i right)
+ {
+ if (left.x.Equals(right.x))
+ {
+ return left.y >= right.y;
+ }
+ return left.x >= right.x;
+ }
+
+ public static implicit operator Vector2(Vector2i value)
+ {
+ return new Vector2(value.x, value.y);
+ }
+
+ public static explicit operator Vector2i(Vector2 value)
+ {
+ return new Vector2i(value);
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (obj is Vector2i)
+ {
+ return Equals((Vector2i)obj);
+ }
+
+ return false;
+ }
+
+ public bool Equals(Vector2i other)
+ {
+ return x == other.x && y == other.y;
+ }
+
+ public override int GetHashCode()
+ {
+ return y.GetHashCode() ^ x.GetHashCode();
+ }
+
+ public override string ToString()
+ {
+ return String.Format("({0}, {1})", new object[]
+ {
+ this.x.ToString(),
+ this.y.ToString()
+ });
+ }
+
+ public string ToString(string format)
+ {
+ return String.Format("({0}, {1})", new object[]
+ {
+ this.x.ToString(format),
+ this.y.ToString(format)
+ });
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
index 390036c654..a43836e985 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
@@ -400,20 +400,20 @@ namespace Godot
return left;
}
- public static Vector3 operator /(Vector3 vec, real_t scale)
+ public static Vector3 operator /(Vector3 vec, real_t divisor)
{
- vec.x /= scale;
- vec.y /= scale;
- vec.z /= scale;
+ vec.x /= divisor;
+ vec.y /= divisor;
+ vec.z /= divisor;
return vec;
}
- public static Vector3 operator /(Vector3 left, Vector3 right)
+ public static Vector3 operator /(Vector3 vec, Vector3 divisorv)
{
- left.x /= right.x;
- left.y /= right.y;
- left.z /= right.z;
- return left;
+ vec.x /= divisorv.x;
+ vec.y /= divisorv.y;
+ vec.z /= divisorv.z;
+ return vec;
}
public static Vector3 operator %(Vector3 vec, real_t divisor)
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs
new file mode 100644
index 0000000000..c17f900131
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs
@@ -0,0 +1,402 @@
+using System;
+using System.Runtime.InteropServices;
+
+#if REAL_T_IS_DOUBLE
+using real_t = System.Double;
+#else
+using real_t = System.Single;
+#endif
+
+namespace Godot
+{
+ /// <summary>
+ /// 3-element structure that can be used to represent 3D grid coordinates or sets of integers.
+ /// </summary>
+ [Serializable]
+ [StructLayout(LayoutKind.Sequential)]
+ public struct Vector3i : IEquatable<Vector3i>
+ {
+ public enum Axis
+ {
+ X = 0,
+ Y,
+ Z
+ }
+
+ public int x;
+ public int y;
+ public int z;
+
+ public int this[int index]
+ {
+ get
+ {
+ switch (index)
+ {
+ case 0:
+ return x;
+ case 1:
+ return y;
+ case 2:
+ return z;
+ default:
+ throw new IndexOutOfRangeException();
+ }
+ }
+ set
+ {
+ switch (index)
+ {
+ case 0:
+ x = value;
+ return;
+ case 1:
+ y = value;
+ return;
+ case 2:
+ z = value;
+ return;
+ default:
+ throw new IndexOutOfRangeException();
+ }
+ }
+ }
+
+ public Vector3i Abs()
+ {
+ Vector3i v = this;
+ if (v.x < 0)
+ {
+ v.x = -v.x;
+ }
+ if (v.y < 0)
+ {
+ v.y = -v.y;
+ }
+ if (v.z < 0)
+ {
+ v.z = -v.z;
+ }
+ return v;
+ }
+
+ public int DistanceSquaredTo(Vector3i b)
+ {
+ return (b - this).LengthSquared();
+ }
+
+ public real_t DistanceTo(Vector3i b)
+ {
+ return (b - this).Length();
+ }
+
+ public int Dot(Vector3i b)
+ {
+ return x * b.x + y * b.y + z * b.z;
+ }
+
+ public real_t Length()
+ {
+ int x2 = x * x;
+ int y2 = y * y;
+ int z2 = z * z;
+
+ return Mathf.Sqrt(x2 + y2 + z2);
+ }
+
+ public int LengthSquared()
+ {
+ int x2 = x * x;
+ int y2 = y * y;
+ int z2 = z * z;
+
+ return x2 + y2 + z2;
+ }
+
+ public Axis MaxAxis()
+ {
+ return x < y ? (y < z ? Axis.Z : Axis.Y) : (x < z ? Axis.Z : Axis.X);
+ }
+
+ public Axis MinAxis()
+ {
+ return x < y ? (x < z ? Axis.X : Axis.Z) : (y < z ? Axis.Y : Axis.Z);
+ }
+
+ public Vector3i PosMod(int mod)
+ {
+ Vector3i v = this;
+ v.x = Mathf.PosMod(v.x, mod);
+ v.y = Mathf.PosMod(v.y, mod);
+ v.z = Mathf.PosMod(v.z, mod);
+ return v;
+ }
+
+ public Vector3i PosMod(Vector3i modv)
+ {
+ Vector3i v = this;
+ v.x = Mathf.PosMod(v.x, modv.x);
+ v.y = Mathf.PosMod(v.y, modv.y);
+ v.z = Mathf.PosMod(v.z, modv.z);
+ return v;
+ }
+
+ public Vector3i Sign()
+ {
+ Vector3i v = this;
+ v.x = Mathf.Sign(v.x);
+ v.y = Mathf.Sign(v.y);
+ v.z = Mathf.Sign(v.z);
+ return v;
+ }
+
+ // Constants
+ private static readonly Vector3i _zero = new Vector3i(0, 0, 0);
+ private static readonly Vector3i _one = new Vector3i(1, 1, 1);
+
+ private static readonly Vector3i _up = new Vector3i(0, 1, 0);
+ private static readonly Vector3i _down = new Vector3i(0, -1, 0);
+ private static readonly Vector3i _right = new Vector3i(1, 0, 0);
+ private static readonly Vector3i _left = new Vector3i(-1, 0, 0);
+ private static readonly Vector3i _forward = new Vector3i(0, 0, -1);
+ private static readonly Vector3i _back = new Vector3i(0, 0, 1);
+
+ public static Vector3i Zero { get { return _zero; } }
+ public static Vector3i One { get { return _one; } }
+
+ public static Vector3i Up { get { return _up; } }
+ public static Vector3i Down { get { return _down; } }
+ public static Vector3i Right { get { return _right; } }
+ public static Vector3i Left { get { return _left; } }
+ public static Vector3i Forward { get { return _forward; } }
+ public static Vector3i Back { get { return _back; } }
+
+ // Constructors
+ public Vector3i(int x, int y, int z)
+ {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ }
+ public Vector3i(Vector3i vi)
+ {
+ this.x = vi.x;
+ this.y = vi.y;
+ this.z = vi.z;
+ }
+ public Vector3i(Vector3 v)
+ {
+ this.x = Mathf.RoundToInt(v.x);
+ this.y = Mathf.RoundToInt(v.y);
+ this.z = Mathf.RoundToInt(v.z);
+ }
+
+ public static Vector3i operator +(Vector3i left, Vector3i right)
+ {
+ left.x += right.x;
+ left.y += right.y;
+ left.z += right.z;
+ return left;
+ }
+
+ public static Vector3i operator -(Vector3i left, Vector3i right)
+ {
+ left.x -= right.x;
+ left.y -= right.y;
+ left.z -= right.z;
+ return left;
+ }
+
+ public static Vector3i operator -(Vector3i vec)
+ {
+ vec.x = -vec.x;
+ vec.y = -vec.y;
+ vec.z = -vec.z;
+ return vec;
+ }
+
+ public static Vector3i operator *(Vector3i vec, int scale)
+ {
+ vec.x *= scale;
+ vec.y *= scale;
+ vec.z *= scale;
+ return vec;
+ }
+
+ public static Vector3i operator *(int scale, Vector3i vec)
+ {
+ vec.x *= scale;
+ vec.y *= scale;
+ vec.z *= scale;
+ return vec;
+ }
+
+ public static Vector3i operator *(Vector3i left, Vector3i right)
+ {
+ left.x *= right.x;
+ left.y *= right.y;
+ left.z *= right.z;
+ return left;
+ }
+
+ public static Vector3i operator /(Vector3i vec, int divisor)
+ {
+ vec.x /= divisor;
+ vec.y /= divisor;
+ vec.z /= divisor;
+ return vec;
+ }
+
+ public static Vector3i operator /(Vector3i vec, Vector3i divisorv)
+ {
+ vec.x /= divisorv.x;
+ vec.y /= divisorv.y;
+ vec.z /= divisorv.z;
+ return vec;
+ }
+
+ public static Vector3i operator %(Vector3i vec, int divisor)
+ {
+ vec.x %= divisor;
+ vec.y %= divisor;
+ vec.z %= divisor;
+ return vec;
+ }
+
+ public static Vector3i operator %(Vector3i vec, Vector3i divisorv)
+ {
+ vec.x %= divisorv.x;
+ vec.y %= divisorv.y;
+ vec.z %= divisorv.z;
+ return vec;
+ }
+
+ public static Vector3i operator &(Vector3i vec, int and)
+ {
+ vec.x &= and;
+ vec.y &= and;
+ vec.z &= and;
+ return vec;
+ }
+
+ public static Vector3i operator &(Vector3i vec, Vector3i andv)
+ {
+ vec.x &= andv.x;
+ vec.y &= andv.y;
+ vec.z &= andv.z;
+ return vec;
+ }
+
+ public static bool operator ==(Vector3i left, Vector3i right)
+ {
+ return left.Equals(right);
+ }
+
+ public static bool operator !=(Vector3i left, Vector3i right)
+ {
+ return !left.Equals(right);
+ }
+
+ public static bool operator <(Vector3i left, Vector3i right)
+ {
+ if (left.x == right.x)
+ {
+ if (left.y == right.y)
+ return left.z < right.z;
+ else
+ return left.y < right.y;
+ }
+
+ return left.x < right.x;
+ }
+
+ public static bool operator >(Vector3i left, Vector3i right)
+ {
+ if (left.x == right.x)
+ {
+ if (left.y == right.y)
+ return left.z > right.z;
+ else
+ return left.y > right.y;
+ }
+
+ return left.x > right.x;
+ }
+
+ public static bool operator <=(Vector3i left, Vector3i right)
+ {
+ if (left.x == right.x)
+ {
+ if (left.y == right.y)
+ return left.z <= right.z;
+ else
+ return left.y < right.y;
+ }
+
+ return left.x < right.x;
+ }
+
+ public static bool operator >=(Vector3i left, Vector3i right)
+ {
+ if (left.x == right.x)
+ {
+ if (left.y == right.y)
+ return left.z >= right.z;
+ else
+ return left.y > right.y;
+ }
+
+ return left.x > right.x;
+ }
+
+ public static implicit operator Vector3(Vector3i value)
+ {
+ return new Vector3(value.x, value.y, value.z);
+ }
+
+ public static explicit operator Vector3i(Vector3 value)
+ {
+ return new Vector3i(value);
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (obj is Vector3i)
+ {
+ return Equals((Vector3i)obj);
+ }
+
+ return false;
+ }
+
+ public bool Equals(Vector3i other)
+ {
+ return x == other.x && y == other.y && z == other.z;
+ }
+
+ public override int GetHashCode()
+ {
+ return y.GetHashCode() ^ x.GetHashCode() ^ z.GetHashCode();
+ }
+
+ public override string ToString()
+ {
+ return String.Format("({0}, {1}, {2})", new object[]
+ {
+ this.x.ToString(),
+ this.y.ToString(),
+ this.z.ToString()
+ });
+ }
+
+ public string ToString(string format)
+ {
+ return String.Format("({0}, {1}, {2})", new object[]
+ {
+ this.x.ToString(format),
+ this.y.ToString(format),
+ this.z.ToString(format)
+ });
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
index 5419cd06e6..ba0bbd7630 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
+++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
@@ -41,9 +41,11 @@
<Compile Include="Core\Attributes\SignalAttribute.cs" />
<Compile Include="Core\Attributes\ToolAttribute.cs" />
<Compile Include="Core\Basis.cs" />
+ <Compile Include="Core\Callable.cs" />
<Compile Include="Core\Color.cs" />
<Compile Include="Core\Colors.cs" />
<Compile Include="Core\DebuggingUtils.cs" />
+ <Compile Include="Core\DelegateUtils.cs" />
<Compile Include="Core\Dictionary.cs" />
<Compile Include="Core\Dispatcher.cs" />
<Compile Include="Core\DynamicObject.cs" />
@@ -65,13 +67,18 @@
<Compile Include="Core\Plane.cs" />
<Compile Include="Core\Quat.cs" />
<Compile Include="Core\Rect2.cs" />
+ <Compile Include="Core\Rect2i.cs" />
<Compile Include="Core\RID.cs" />
+ <Compile Include="Core\SignalInfo.cs" />
<Compile Include="Core\SignalAwaiter.cs" />
<Compile Include="Core\StringExtensions.cs" />
+ <Compile Include="Core\StringName.cs" />
<Compile Include="Core\Transform.cs" />
<Compile Include="Core\Transform2D.cs" />
<Compile Include="Core\Vector2.cs" />
+ <Compile Include="Core\Vector2i.cs" />
<Compile Include="Core\Vector3.cs" />
+ <Compile Include="Core\Vector3i.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<!--
diff --git a/modules/mono/glue/base_object_glue.cpp b/modules/mono/glue/base_object_glue.cpp
index 8c77220b85..f370883320 100644
--- a/modules/mono/glue/base_object_glue.cpp
+++ b/modules/mono/glue/base_object_glue.cpp
@@ -51,7 +51,7 @@ Object *godot_icall_Object_Ctor(MonoObject *p_obj) {
void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr) {
#ifdef DEBUG_ENABLED
- CRASH_COND(p_ptr == NULL);
+ CRASH_COND(p_ptr == nullptr);
#endif
if (p_ptr->get_script_instance()) {
@@ -59,7 +59,7 @@ void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr) {
if (cs_instance) {
if (!cs_instance->is_destructing_script_instance()) {
cs_instance->mono_object_disposed(p_obj);
- p_ptr->set_script_instance(NULL);
+ p_ptr->set_script_instance(nullptr);
}
return;
}
@@ -70,8 +70,8 @@ void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr) {
if (data) {
CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
if (script_binding.inited) {
- Ref<MonoGCHandle> &gchandle = script_binding.gchandle;
- if (gchandle.is_valid()) {
+ MonoGCHandleData &gchandle = script_binding.gchandle;
+ if (!gchandle.is_released()) {
CSharpLanguage::release_script_gchandle(p_obj, gchandle);
}
}
@@ -80,7 +80,7 @@ void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr) {
void godot_icall_Reference_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoolean p_is_finalizer) {
#ifdef DEBUG_ENABLED
- CRASH_COND(p_ptr == NULL);
+ CRASH_COND(p_ptr == nullptr);
// This is only called with Reference derived classes
CRASH_COND(!Object::cast_to<Reference>(p_ptr));
#endif
@@ -99,7 +99,7 @@ void godot_icall_Reference_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoolea
if (delete_owner) {
memdelete(ref);
} else if (remove_script_instance) {
- ref->set_script_instance(NULL);
+ ref->set_script_instance(nullptr);
}
}
return;
@@ -117,8 +117,8 @@ void godot_icall_Reference_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoolea
if (data) {
CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
if (script_binding.inited) {
- Ref<MonoGCHandle> &gchandle = script_binding.gchandle;
- if (gchandle.is_valid()) {
+ MonoGCHandleData &gchandle = script_binding.gchandle;
+ if (!gchandle.is_released()) {
CSharpLanguage::release_script_gchandle(p_obj, gchandle);
}
}
@@ -126,37 +126,44 @@ void godot_icall_Reference_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoolea
}
}
-MethodBind *godot_icall_Object_ClassDB_get_method(MonoString *p_type, MonoString *p_method) {
- StringName type(GDMonoMarshal::mono_string_to_godot(p_type));
+void godot_icall_Object_ConnectEventSignals(Object *p_ptr) {
+ CSharpInstance *csharp_instance = CAST_CSHARP_INSTANCE(p_ptr->get_script_instance());
+ if (csharp_instance) {
+ csharp_instance->connect_event_signals();
+ }
+}
+
+MethodBind *godot_icall_Object_ClassDB_get_method(StringName *p_type, MonoString *p_method) {
+ StringName type = p_type ? *p_type : StringName();
StringName method(GDMonoMarshal::mono_string_to_godot(p_method));
return ClassDB::get_method(type, method);
}
-MonoObject *godot_icall_Object_weakref(Object *p_obj) {
- if (!p_obj)
- return NULL;
+MonoObject *godot_icall_Object_weakref(Object *p_ptr) {
+ if (!p_ptr)
+ return nullptr;
Ref<WeakRef> wref;
- Reference *ref = Object::cast_to<Reference>(p_obj);
+ Reference *ref = Object::cast_to<Reference>(p_ptr);
if (ref) {
REF r = ref;
if (!r.is_valid())
- return NULL;
+ return nullptr;
wref.instance();
wref->set_ref(r);
} else {
wref.instance();
- wref->set_obj(p_obj);
+ wref->set_obj(p_ptr);
}
return GDMonoUtils::unmanaged_get_managed(wref.ptr());
}
-Error godot_icall_SignalAwaiter_connect(Object *p_source, MonoString *p_signal, Object *p_target, MonoObject *p_awaiter) {
- String signal = GDMonoMarshal::mono_string_to_godot(p_signal);
- return SignalAwaiterUtils::connect_signal_awaiter(p_source, signal, p_target, p_awaiter);
+Error godot_icall_SignalAwaiter_connect(Object *p_source, StringName *p_signal, Object *p_target, MonoObject *p_awaiter) {
+ StringName signal = p_signal ? *p_signal : StringName();
+ return gd_mono_connect_signal_awaiter(p_source, signal, p_target, p_awaiter);
}
MonoArray *godot_icall_DynamicGodotObject_SetMemberList(Object *p_ptr) {
@@ -223,10 +230,10 @@ MonoBoolean godot_icall_DynamicGodotObject_SetMember(Object *p_ptr, MonoString *
MonoString *godot_icall_Object_ToString(Object *p_ptr) {
#ifdef DEBUG_ENABLED
// Cannot happen in C#; would get an ObjectDisposedException instead.
- CRASH_COND(p_ptr == NULL);
+ CRASH_COND(p_ptr == nullptr);
#endif
-
- String result = p_ptr->to_string();
+ // Can't call 'Object::to_string()' here, as that can end up calling 'ToString' again resulting in an endless circular loop.
+ String result = "[" + p_ptr->get_class() + ":" + itos(p_ptr->get_instance_id()) + "]";
return GDMonoMarshal::mono_string_from_godot(result);
}
diff --git a/modules/mono/glue/base_object_glue.h b/modules/mono/glue/base_object_glue.h
index 22532dcff9..67769f3061 100644
--- a/modules/mono/glue/base_object_glue.h
+++ b/modules/mono/glue/base_object_glue.h
@@ -44,11 +44,13 @@ void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr);
void godot_icall_Reference_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoolean p_is_finalizer);
-MethodBind *godot_icall_Object_ClassDB_get_method(MonoString *p_type, MonoString *p_method);
+void godot_icall_Object_ConnectEventSignals(Object *p_ptr);
-MonoObject *godot_icall_Object_weakref(Object *p_obj);
+MethodBind *godot_icall_Object_ClassDB_get_method(StringName *p_type, MonoString *p_method);
-Error godot_icall_SignalAwaiter_connect(Object *p_source, MonoString *p_signal, Object *p_target, MonoObject *p_awaiter);
+MonoObject *godot_icall_Object_weakref(Object *p_ptr);
+
+Error godot_icall_SignalAwaiter_connect(Object *p_source, StringName *p_signal, Object *p_target, MonoObject *p_awaiter);
// DynamicGodotObject
diff --git a/modules/mono/glue/collections_glue.cpp b/modules/mono/glue/collections_glue.cpp
index b7fa7fcab2..4e3dc9c4ea 100644
--- a/modules/mono/glue/collections_glue.cpp
+++ b/modules/mono/glue/collections_glue.cpp
@@ -49,7 +49,7 @@ void godot_icall_Array_Dtor(Array *ptr) {
MonoObject *godot_icall_Array_At(Array *ptr, int index) {
if (index < 0 || index >= ptr->size()) {
GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range());
- return NULL;
+ return nullptr;
}
return GDMonoMarshal::variant_to_mono_object(ptr->operator[](index));
}
@@ -57,7 +57,7 @@ MonoObject *godot_icall_Array_At(Array *ptr, int index) {
MonoObject *godot_icall_Array_At_Generic(Array *ptr, int index, uint32_t type_encoding, GDMonoClass *type_class) {
if (index < 0 || index >= ptr->size()) {
GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range());
- return NULL;
+ return nullptr;
}
return GDMonoMarshal::variant_to_mono_object(ptr->operator[](index), ManagedType(type_encoding, type_class));
}
@@ -162,28 +162,28 @@ void godot_icall_Dictionary_Dtor(Dictionary *ptr) {
MonoObject *godot_icall_Dictionary_GetValue(Dictionary *ptr, MonoObject *key) {
Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key));
- if (ret == NULL) {
+ if (ret == nullptr) {
MonoObject *exc = mono_object_new(mono_domain_get(), CACHED_CLASS(KeyNotFoundException)->get_mono_ptr());
#ifdef DEBUG_ENABLED
CRASH_COND(!exc);
#endif
GDMonoUtils::runtime_object_init(exc, CACHED_CLASS(KeyNotFoundException));
GDMonoUtils::set_pending_exception((MonoException *)exc);
- return NULL;
+ return nullptr;
}
return GDMonoMarshal::variant_to_mono_object(ret);
}
MonoObject *godot_icall_Dictionary_GetValue_Generic(Dictionary *ptr, MonoObject *key, uint32_t type_encoding, GDMonoClass *type_class) {
Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key));
- if (ret == NULL) {
+ if (ret == nullptr) {
MonoObject *exc = mono_object_new(mono_domain_get(), CACHED_CLASS(KeyNotFoundException)->get_mono_ptr());
#ifdef DEBUG_ENABLED
CRASH_COND(!exc);
#endif
GDMonoUtils::runtime_object_init(exc, CACHED_CLASS(KeyNotFoundException));
GDMonoUtils::set_pending_exception((MonoException *)exc);
- return NULL;
+ return nullptr;
}
return GDMonoMarshal::variant_to_mono_object(ret, ManagedType(type_encoding, type_class));
}
@@ -207,7 +207,7 @@ int godot_icall_Dictionary_Count(Dictionary *ptr) {
void godot_icall_Dictionary_Add(Dictionary *ptr, MonoObject *key, MonoObject *value) {
Variant varKey = GDMonoMarshal::mono_object_to_variant(key);
Variant *ret = ptr->getptr(varKey);
- if (ret != NULL) {
+ if (ret != nullptr) {
GDMonoUtils::set_pending_exception(mono_get_exception_argument("key", "An element with the same key already exists"));
return;
}
@@ -221,7 +221,7 @@ void godot_icall_Dictionary_Clear(Dictionary *ptr) {
MonoBoolean godot_icall_Dictionary_Contains(Dictionary *ptr, MonoObject *key, MonoObject *value) {
// no dupes
Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key));
- return ret != NULL && *ret == GDMonoMarshal::mono_object_to_variant(value);
+ return ret != nullptr && *ret == GDMonoMarshal::mono_object_to_variant(value);
}
MonoBoolean godot_icall_Dictionary_ContainsKey(Dictionary *ptr, MonoObject *key) {
@@ -241,7 +241,7 @@ MonoBoolean godot_icall_Dictionary_Remove(Dictionary *ptr, MonoObject *key, Mono
// no dupes
Variant *ret = ptr->getptr(varKey);
- if (ret != NULL && *ret == GDMonoMarshal::mono_object_to_variant(value)) {
+ if (ret != nullptr && *ret == GDMonoMarshal::mono_object_to_variant(value)) {
ptr->erase(varKey);
return true;
}
@@ -251,8 +251,8 @@ MonoBoolean godot_icall_Dictionary_Remove(Dictionary *ptr, MonoObject *key, Mono
MonoBoolean godot_icall_Dictionary_TryGetValue(Dictionary *ptr, MonoObject *key, MonoObject **value) {
Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key));
- if (ret == NULL) {
- *value = NULL;
+ if (ret == nullptr) {
+ *value = nullptr;
return false;
}
*value = GDMonoMarshal::variant_to_mono_object(ret);
@@ -261,8 +261,8 @@ MonoBoolean godot_icall_Dictionary_TryGetValue(Dictionary *ptr, MonoObject *key,
MonoBoolean godot_icall_Dictionary_TryGetValue_Generic(Dictionary *ptr, MonoObject *key, MonoObject **value, uint32_t type_encoding, GDMonoClass *type_class) {
Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key));
- if (ret == NULL) {
- *value = NULL;
+ if (ret == nullptr) {
+ *value = nullptr;
return false;
}
*value = GDMonoMarshal::variant_to_mono_object(ret, ManagedType(type_encoding, type_class));
diff --git a/modules/mono/glue/gd_glue.cpp b/modules/mono/glue/gd_glue.cpp
index cdacd90538..2da39a916a 100644
--- a/modules/mono/glue/gd_glue.cpp
+++ b/modules/mono/glue/gd_glue.cpp
@@ -45,7 +45,7 @@
MonoObject *godot_icall_GD_bytes2var(MonoArray *p_bytes, MonoBoolean p_allow_objects) {
Variant ret;
PackedByteArray varr = GDMonoMarshal::mono_array_to_PackedByteArray(p_bytes);
- Error err = decode_variant(ret, varr.ptr(), varr.size(), NULL, p_allow_objects);
+ Error err = decode_variant(ret, varr.ptr(), varr.size(), nullptr, p_allow_objects);
if (err != OK) {
ret = RTR("Not enough bytes for decoding bytes, or invalid format.");
}
@@ -57,7 +57,7 @@ MonoObject *godot_icall_GD_convert(MonoObject *p_what, int32_t p_type) {
const Variant *args[1] = { &what };
Callable::CallError ce;
Variant ret = Variant::construct(Variant::Type(p_type), args, 1, ce);
- ERR_FAIL_COND_V(ce.error != Callable::CallError::CALL_OK, NULL);
+ ERR_FAIL_COND_V(ce.error != Callable::CallError::CALL_OK, nullptr);
return GDMonoMarshal::variant_to_mono_object(ret);
}
@@ -76,7 +76,7 @@ void godot_icall_GD_print(MonoArray *p_what) {
for (int i = 0; i < length; i++) {
MonoObject *elem = mono_array_get(p_what, MonoObject *, i);
- MonoException *exc = NULL;
+ MonoException *exc = nullptr;
String elem_str = GDMonoMarshal::mono_object_to_variant_string(elem, &exc);
if (exc) {
@@ -98,7 +98,7 @@ void godot_icall_GD_printerr(MonoArray *p_what) {
for (int i = 0; i < length; i++) {
MonoObject *elem = mono_array_get(p_what, MonoObject *, i);
- MonoException *exc = NULL;
+ MonoException *exc = nullptr;
String elem_str = GDMonoMarshal::mono_object_to_variant_string(elem, &exc);
if (exc) {
@@ -119,7 +119,7 @@ void godot_icall_GD_printraw(MonoArray *p_what) {
for (int i = 0; i < length; i++) {
MonoObject *elem = mono_array_get(p_what, MonoObject *, i);
- MonoException *exc = NULL;
+ MonoException *exc = nullptr;
String elem_str = GDMonoMarshal::mono_object_to_variant_string(elem, &exc);
if (exc) {
@@ -140,7 +140,7 @@ void godot_icall_GD_prints(MonoArray *p_what) {
for (int i = 0; i < length; i++) {
MonoObject *elem = mono_array_get(p_what, MonoObject *, i);
- MonoException *exc = NULL;
+ MonoException *exc = nullptr;
String elem_str = GDMonoMarshal::mono_object_to_variant_string(elem, &exc);
if (exc) {
@@ -164,7 +164,7 @@ void godot_icall_GD_printt(MonoArray *p_what) {
for (int i = 0; i < length; i++) {
MonoObject *elem = mono_array_get(p_what, MonoObject *, i);
- MonoException *exc = NULL;
+ MonoException *exc = nullptr;
String elem_str = GDMonoMarshal::mono_object_to_variant_string(elem, &exc);
if (exc) {
@@ -241,8 +241,9 @@ MonoObject *godot_icall_GD_str2var(MonoString *p_str) {
return GDMonoMarshal::variant_to_mono_object(ret);
}
-MonoBoolean godot_icall_GD_type_exists(MonoString *p_type) {
- return ClassDB::class_exists(GDMonoMarshal::mono_string_to_godot(p_type));
+MonoBoolean godot_icall_GD_type_exists(StringName *p_type) {
+ StringName type = p_type ? *p_type : StringName();
+ return ClassDB::class_exists(type);
}
void godot_icall_GD_pusherror(MonoString *p_str) {
@@ -258,8 +259,8 @@ MonoArray *godot_icall_GD_var2bytes(MonoObject *p_var, MonoBoolean p_full_object
PackedByteArray barr;
int len;
- Error err = encode_variant(var, NULL, len, p_full_objects);
- ERR_FAIL_COND_V_MSG(err != OK, NULL, "Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID).");
+ Error err = encode_variant(var, nullptr, len, p_full_objects);
+ ERR_FAIL_COND_V_MSG(err != OK, nullptr, "Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID).");
barr.resize(len);
encode_variant(var, barr.ptrw(), len, p_full_objects);
@@ -273,6 +274,10 @@ MonoString *godot_icall_GD_var2str(MonoObject *p_var) {
return GDMonoMarshal::mono_string_from_godot(vars);
}
+uint32_t godot_icall_TypeToVariantType(MonoReflectionType *p_refl_type) {
+ return (uint32_t)GDMonoMarshal::managed_to_variant_type(ManagedType::from_reftype(p_refl_type));
+}
+
MonoObject *godot_icall_DefaultGodotTaskScheduler() {
return GDMonoCache::cached_data.task_scheduler_handle->get_target();
}
@@ -300,6 +305,7 @@ void godot_register_gd_icalls() {
mono_add_internal_call("Godot.GD::godot_icall_GD_type_exists", (void *)godot_icall_GD_type_exists);
mono_add_internal_call("Godot.GD::godot_icall_GD_var2bytes", (void *)godot_icall_GD_var2bytes);
mono_add_internal_call("Godot.GD::godot_icall_GD_var2str", (void *)godot_icall_GD_var2str);
+ mono_add_internal_call("Godot.GD::godot_icall_TypeToVariantType", (void *)godot_icall_TypeToVariantType);
// Dispatcher
mono_add_internal_call("Godot.Dispatcher::godot_icall_DefaultGodotTaskScheduler", (void *)godot_icall_DefaultGodotTaskScheduler);
diff --git a/modules/mono/glue/gd_glue.h b/modules/mono/glue/gd_glue.h
index f00e2efc5d..3ad6058205 100644
--- a/modules/mono/glue/gd_glue.h
+++ b/modules/mono/glue/gd_glue.h
@@ -69,7 +69,7 @@ MonoString *godot_icall_GD_str(MonoArray *p_what);
MonoObject *godot_icall_GD_str2var(MonoString *p_str);
-MonoBoolean godot_icall_GD_type_exists(MonoString *p_type);
+MonoBoolean godot_icall_GD_type_exists(StringName *p_type);
MonoArray *godot_icall_GD_var2bytes(MonoObject *p_var, MonoBoolean p_full_objects);
diff --git a/modules/mono/glue/glue_header.h b/modules/mono/glue/glue_header.h
index 758b71f719..ee99a300b9 100644
--- a/modules/mono/glue/glue_header.h
+++ b/modules/mono/glue/glue_header.h
@@ -36,6 +36,7 @@
#include "nodepath_glue.h"
#include "rid_glue.h"
#include "string_glue.h"
+#include "string_name_glue.h"
/**
* Registers internal calls that were not generated. This function is called
@@ -44,6 +45,7 @@
void godot_register_glue_header_icalls() {
godot_register_collections_icalls();
godot_register_gd_icalls();
+ godot_register_string_name_icalls();
godot_register_nodepath_icalls();
godot_register_object_icalls();
godot_register_rid_icalls();
@@ -68,7 +70,7 @@ void godot_register_glue_header_icalls() {
#include "../mono_gd/gd_mono_utils.h"
#define GODOTSHARP_INSTANCE_OBJECT(m_instance, m_type) \
- static ClassDB::ClassInfo *ci = NULL; \
+ static ClassDB::ClassInfo *ci = nullptr; \
if (!ci) { \
ci = ClassDB::classes.getptr(m_type); \
} \
diff --git a/modules/mono/glue/string_name_glue.cpp b/modules/mono/glue/string_name_glue.cpp
new file mode 100644
index 0000000000..81006e5849
--- /dev/null
+++ b/modules/mono/glue/string_name_glue.cpp
@@ -0,0 +1,61 @@
+/*************************************************************************/
+/* string_name_glue.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 "string_name_glue.h"
+
+#ifdef MONO_GLUE_ENABLED
+
+#include "core/ustring.h"
+
+StringName *godot_icall_StringName_Ctor(MonoString *p_path) {
+ return memnew(StringName(GDMonoMarshal::mono_string_to_godot(p_path)));
+}
+
+void godot_icall_StringName_Dtor(StringName *p_ptr) {
+ ERR_FAIL_NULL(p_ptr);
+ memdelete(p_ptr);
+}
+
+MonoString *godot_icall_StringName_operator_String(StringName *p_np) {
+ return GDMonoMarshal::mono_string_from_godot(p_np->operator String());
+}
+
+MonoBoolean godot_icall_StringName_is_empty(StringName *p_ptr) {
+ return (MonoBoolean)(p_ptr == StringName());
+}
+
+void godot_register_string_name_icalls() {
+ mono_add_internal_call("Godot.StringName::godot_icall_StringName_Ctor", (void *)godot_icall_StringName_Ctor);
+ mono_add_internal_call("Godot.StringName::godot_icall_StringName_Dtor", (void *)godot_icall_StringName_Dtor);
+ mono_add_internal_call("Godot.StringName::godot_icall_StringName_operator_String", (void *)godot_icall_StringName_operator_String);
+ mono_add_internal_call("Godot.StringName::godot_icall_StringName_is_empty", (void *)godot_icall_StringName_is_empty);
+}
+
+#endif // MONO_GLUE_ENABLED
diff --git a/modules/mono/glue/string_name_glue.h b/modules/mono/glue/string_name_glue.h
new file mode 100644
index 0000000000..88354ddd84
--- /dev/null
+++ b/modules/mono/glue/string_name_glue.h
@@ -0,0 +1,54 @@
+/*************************************************************************/
+/* string_name_glue.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 STRING_NAME_GLUE_H
+#define STRING_NAME_GLUE_H
+
+#ifdef MONO_GLUE_ENABLED
+
+#include "core/string_name.h"
+
+#include "../mono_gd/gd_mono_marshal.h"
+
+StringName *godot_icall_StringName_Ctor(MonoString *p_path);
+
+void godot_icall_StringName_Dtor(StringName *p_ptr);
+
+MonoString *godot_icall_StringName_operator_String(StringName *p_np);
+
+MonoBoolean godot_icall_StringName_is_empty(StringName *p_ptr);
+
+// Register internal calls
+
+void godot_register_string_name_icalls();
+
+#endif // MONO_GLUE_ENABLED
+
+#endif // STRING_NAME_GLUE_H
diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp
index 47eb432490..fe8b925257 100644
--- a/modules/mono/godotsharp_dirs.cpp
+++ b/modules/mono/godotsharp_dirs.cpp
@@ -40,7 +40,7 @@
#endif
#ifdef ANDROID_ENABLED
-#include "mono_gd/gd_mono_android.h"
+#include "mono_gd/support/mono-support.h"
#endif
#include "mono_gd/gd_mono.h"
@@ -49,13 +49,13 @@ namespace GodotSharpDirs {
String _get_expected_build_config() {
#ifdef TOOLS_ENABLED
- return "Tools";
+ return "Debug";
#else
#ifdef DEBUG_ENABLED
- return "Debug";
+ return "ExportDebug";
#else
- return "Release";
+ return "ExportRelease";
#endif
#endif
@@ -169,7 +169,7 @@ private:
data_mono_etc_dir = data_mono_root_dir.plus_file("etc");
#ifdef ANDROID_ENABLED
- data_mono_lib_dir = GDMonoAndroid::get_app_native_lib_dir();
+ data_mono_lib_dir = gdmono::android::support::get_app_native_lib_dir();
#else
data_mono_lib_dir = data_mono_root_dir.plus_file("lib");
#endif
@@ -206,7 +206,7 @@ private:
data_mono_etc_dir = data_mono_root_dir.plus_file("etc");
#ifdef ANDROID_ENABLED
- data_mono_lib_dir = GDMonoAndroid::get_app_native_lib_dir();
+ data_mono_lib_dir = gdmono::android::support::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");
diff --git a/modules/mono/icons/icon_c_sharp_script.svg b/modules/mono/icons/CSharpScript.svg
index 69664ca553..69664ca553 100644
--- a/modules/mono/icons/icon_c_sharp_script.svg
+++ b/modules/mono/icons/CSharpScript.svg
diff --git a/modules/mono/managed_callable.cpp b/modules/mono/managed_callable.cpp
new file mode 100644
index 0000000000..dfd78a8244
--- /dev/null
+++ b/modules/mono/managed_callable.cpp
@@ -0,0 +1,145 @@
+/*************************************************************************/
+/* managed_callable.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 "managed_callable.h"
+
+#include "csharp_script.h"
+#include "mono_gd/gd_mono_marshal.h"
+#include "mono_gd/gd_mono_utils.h"
+
+#ifdef GD_MONO_HOT_RELOAD
+SelfList<ManagedCallable>::List ManagedCallable::instances;
+Map<ManagedCallable *, Array> ManagedCallable::instances_pending_reload;
+Mutex ManagedCallable::instances_mutex;
+#endif
+
+bool ManagedCallable::compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) {
+ const ManagedCallable *a = static_cast<const ManagedCallable *>(p_a);
+ const ManagedCallable *b = static_cast<const ManagedCallable *>(p_b);
+
+ MonoDelegate *delegate_a = (MonoDelegate *)a->delegate_handle.get_target();
+ MonoDelegate *delegate_b = (MonoDelegate *)b->delegate_handle.get_target();
+
+ if (!delegate_a || !delegate_b) {
+ if (!delegate_a && !delegate_b)
+ return true;
+ return false;
+ }
+
+ // Call Delegate's 'Equals'
+ return GDMonoUtils::mono_delegate_equal(delegate_a, delegate_b);
+}
+
+bool ManagedCallable::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) {
+ if (compare_equal(p_a, p_b))
+ return false;
+ return p_a < p_b;
+}
+
+uint32_t ManagedCallable::hash() const {
+ // hmm
+ uint32_t hash = delegate_invoke->get_name().hash();
+ return hash_djb2_one_64(delegate_handle.handle, hash);
+}
+
+String ManagedCallable::get_as_text() const {
+ return "Delegate::Invoke";
+}
+
+CallableCustom::CompareEqualFunc ManagedCallable::get_compare_equal_func() const {
+ return compare_equal_func_ptr;
+}
+
+CallableCustom::CompareLessFunc ManagedCallable::get_compare_less_func() const {
+ return compare_less_func_ptr;
+}
+
+ObjectID ManagedCallable::get_object() const {
+ return CSharpLanguage::get_singleton()->get_managed_callable_middleman()->get_instance_id();
+}
+
+void ManagedCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
+ r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; // Can't find anything better
+ r_return_value = Variant();
+
+#ifdef GD_MONO_HOT_RELOAD
+ // Lost during hot-reload
+ ERR_FAIL_NULL(delegate_invoke);
+ ERR_FAIL_COND(delegate_handle.is_released());
+#endif
+
+ ERR_FAIL_COND(delegate_invoke->get_parameters_count() < p_argcount);
+
+ MonoObject *delegate = delegate_handle.get_target();
+
+ MonoException *exc = nullptr;
+ MonoObject *ret = delegate_invoke->invoke(delegate, p_arguments, &exc);
+
+ if (exc) {
+ GDMonoUtils::set_pending_exception(exc);
+ } else {
+ r_return_value = GDMonoMarshal::mono_object_to_variant(ret);
+ r_call_error.error = Callable::CallError::CALL_OK;
+ }
+}
+
+void ManagedCallable::set_delegate(MonoDelegate *p_delegate) {
+ delegate_handle = MonoGCHandleData::new_strong_handle((MonoObject *)p_delegate);
+ MonoMethod *delegate_invoke_raw = mono_get_delegate_invoke(mono_object_get_class((MonoObject *)p_delegate));
+ const StringName &delegate_invoke_name = CSharpLanguage::get_singleton()->get_string_names().delegate_invoke_method_name;
+ delegate_invoke = memnew(GDMonoMethod(delegate_invoke_name, delegate_invoke_raw)); // TODO: Use pooling for this GDMonoMethod instances
+}
+
+ManagedCallable::ManagedCallable(MonoDelegate *p_delegate) {
+#ifdef DEBUG_ENABLED
+ CRASH_COND(p_delegate == nullptr);
+#endif
+
+ set_delegate(p_delegate);
+
+#ifdef GD_MONO_HOT_RELOAD
+ {
+ MutexLock lock(instances_mutex);
+ instances.add(&self_instance);
+ }
+#endif
+}
+
+ManagedCallable::~ManagedCallable() {
+#ifdef GD_MONO_HOT_RELOAD
+ {
+ MutexLock lock(instances_mutex);
+ instances.remove(&self_instance);
+ instances_pending_reload.erase(this);
+ }
+#endif
+
+ delegate_handle.release();
+}
diff --git a/modules/mono/managed_callable.h b/modules/mono/managed_callable.h
new file mode 100644
index 0000000000..4f71e14a2f
--- /dev/null
+++ b/modules/mono/managed_callable.h
@@ -0,0 +1,77 @@
+/*************************************************************************/
+/* managed_callable.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 MANAGED_CALLABLE_H
+#define MANAGED_CALLABLE_H
+
+#include <mono/metadata/object.h>
+
+#include "core/callable.h"
+#include "core/os/mutex.h"
+#include "core/self_list.h"
+
+#include "mono_gc_handle.h"
+#include "mono_gd/gd_mono_method.h"
+
+class ManagedCallable : public CallableCustom {
+ friend class CSharpLanguage;
+ MonoGCHandleData delegate_handle;
+ GDMonoMethod *delegate_invoke;
+
+#ifdef GD_MONO_HOT_RELOAD
+ SelfList<ManagedCallable> self_instance = this;
+ static SelfList<ManagedCallable>::List instances;
+ static Map<ManagedCallable *, Array> instances_pending_reload;
+ static Mutex instances_mutex;
+#endif
+
+public:
+ uint32_t hash() const override;
+ String get_as_text() const override;
+ CompareEqualFunc get_compare_equal_func() const override;
+ CompareLessFunc get_compare_less_func() const override;
+ ObjectID get_object() const override;
+ void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
+
+ _FORCE_INLINE_ MonoDelegate *get_delegate() { return (MonoDelegate *)delegate_handle.get_target(); }
+
+ void set_delegate(MonoDelegate *p_delegate);
+
+ static bool compare_equal(const CallableCustom *p_a, const CallableCustom *p_b);
+ static bool compare_less(const CallableCustom *p_a, const CallableCustom *p_b);
+
+ static constexpr CompareEqualFunc compare_equal_func_ptr = &ManagedCallable::compare_equal;
+ static constexpr CompareEqualFunc compare_less_func_ptr = &ManagedCallable::compare_less;
+
+ ManagedCallable(MonoDelegate *p_delegate);
+ ~ManagedCallable();
+};
+
+#endif // MANAGED_CALLABLE_H
diff --git a/modules/mono/mono_gc_handle.cpp b/modules/mono/mono_gc_handle.cpp
index feeea848ee..e362d5affb 100644
--- a/modules/mono/mono_gc_handle.cpp
+++ b/modules/mono/mono_gc_handle.cpp
@@ -32,56 +32,35 @@
#include "mono_gd/gd_mono.h"
-uint32_t MonoGCHandle::new_strong_handle(MonoObject *p_object) {
-
- return mono_gchandle_new(p_object, /* pinned: */ false);
-}
-
-uint32_t MonoGCHandle::new_strong_handle_pinned(MonoObject *p_object) {
-
- return mono_gchandle_new(p_object, /* pinned: */ true);
-}
-
-uint32_t MonoGCHandle::new_weak_handle(MonoObject *p_object) {
-
- return mono_gchandle_new_weakref(p_object, /* track_resurrection: */ false);
-}
-
-void MonoGCHandle::free_handle(uint32_t p_gchandle) {
+void MonoGCHandleData::release() {
+#ifdef DEBUG_ENABLED
+ CRASH_COND(handle && GDMono::get_singleton() == nullptr);
+#endif
- mono_gchandle_free(p_gchandle);
+ if (handle && GDMono::get_singleton()->is_runtime_initialized()) {
+ GDMonoUtils::free_gchandle(handle);
+ handle = 0;
+ }
}
-Ref<MonoGCHandle> MonoGCHandle::create_strong(MonoObject *p_object) {
-
- return memnew(MonoGCHandle(new_strong_handle(p_object), STRONG_HANDLE));
+MonoGCHandleData MonoGCHandleData::new_strong_handle(MonoObject *p_object) {
+ return MonoGCHandleData(GDMonoUtils::new_strong_gchandle(p_object), gdmono::GCHandleType::STRONG_HANDLE);
}
-Ref<MonoGCHandle> MonoGCHandle::create_weak(MonoObject *p_object) {
-
- return memnew(MonoGCHandle(new_weak_handle(p_object), WEAK_HANDLE));
+MonoGCHandleData MonoGCHandleData::new_strong_handle_pinned(MonoObject *p_object) {
+ return MonoGCHandleData(GDMonoUtils::new_strong_gchandle_pinned(p_object), gdmono::GCHandleType::STRONG_HANDLE);
}
-void MonoGCHandle::release() {
-
-#ifdef DEBUG_ENABLED
- CRASH_COND(!released && GDMono::get_singleton() == NULL);
-#endif
-
- if (!released && GDMono::get_singleton()->is_runtime_initialized()) {
- free_handle(handle);
- released = true;
- }
+MonoGCHandleData MonoGCHandleData::new_weak_handle(MonoObject *p_object) {
+ return MonoGCHandleData(GDMonoUtils::new_weak_gchandle(p_object), gdmono::GCHandleType::WEAK_HANDLE);
}
-MonoGCHandle::MonoGCHandle(uint32_t p_handle, HandleType p_handle_type) {
+Ref<MonoGCHandleRef> MonoGCHandleRef::create_strong(MonoObject *p_object) {
- released = false;
- weak = p_handle_type == WEAK_HANDLE;
- handle = p_handle;
+ return memnew(MonoGCHandleRef(MonoGCHandleData::new_strong_handle(p_object)));
}
-MonoGCHandle::~MonoGCHandle() {
+Ref<MonoGCHandleRef> MonoGCHandleRef::create_weak(MonoObject *p_object) {
- release();
+ return memnew(MonoGCHandleRef(MonoGCHandleData::new_weak_handle(p_object)));
}
diff --git a/modules/mono/mono_gc_handle.h b/modules/mono/mono_gc_handle.h
index 37fc7d8a17..fbcb405b0d 100644
--- a/modules/mono/mono_gc_handle.h
+++ b/modules/mono/mono_gc_handle.h
@@ -35,42 +35,79 @@
#include "core/reference.h"
-class MonoGCHandle : public Reference {
+namespace gdmono {
- GDCLASS(MonoGCHandle, Reference);
+enum class GCHandleType : char {
+ NIL,
+ STRONG_HANDLE,
+ WEAK_HANDLE
+};
+
+}
- bool released;
- bool weak;
+// Manual release of the GC handle must be done when using this struct
+struct MonoGCHandleData {
uint32_t handle;
+ gdmono::GCHandleType type;
-public:
- enum HandleType {
- STRONG_HANDLE,
- WEAK_HANDLE
- };
+ _FORCE_INLINE_ bool is_released() const { return !handle; }
+ _FORCE_INLINE_ bool is_weak() const { return type == gdmono::GCHandleType::WEAK_HANDLE; }
- static uint32_t new_strong_handle(MonoObject *p_object);
- static uint32_t new_strong_handle_pinned(MonoObject *p_object);
- static uint32_t new_weak_handle(MonoObject *p_object);
- static void free_handle(uint32_t p_gchandle);
+ _FORCE_INLINE_ MonoObject *get_target() const { return handle ? mono_gchandle_get_target(handle) : nullptr; }
- static Ref<MonoGCHandle> create_strong(MonoObject *p_object);
- static Ref<MonoGCHandle> create_weak(MonoObject *p_object);
+ void release();
- _FORCE_INLINE_ bool is_released() { return released; }
- _FORCE_INLINE_ bool is_weak() { return weak; }
+ MonoGCHandleData &operator=(const MonoGCHandleData &p_other) {
+#ifdef DEBUG_ENABLED
+ CRASH_COND(!is_released());
+#endif
+ handle = p_other.handle;
+ type = p_other.type;
+ return *this;
+ }
- _FORCE_INLINE_ MonoObject *get_target() const { return released ? NULL : mono_gchandle_get_target(handle); }
+ MonoGCHandleData(const MonoGCHandleData &) = default;
- _FORCE_INLINE_ void set_handle(uint32_t p_handle, HandleType p_handle_type) {
- released = false;
- weak = p_handle_type == WEAK_HANDLE;
- handle = p_handle;
+ MonoGCHandleData() :
+ handle(0),
+ type(gdmono::GCHandleType::NIL) {
}
- void release();
- MonoGCHandle(uint32_t p_handle, HandleType p_handle_type);
- ~MonoGCHandle();
+ MonoGCHandleData(uint32_t p_handle, gdmono::GCHandleType p_type) :
+ handle(p_handle),
+ type(p_type) {
+ }
+
+ static MonoGCHandleData new_strong_handle(MonoObject *p_object);
+ static MonoGCHandleData new_strong_handle_pinned(MonoObject *p_object);
+ static MonoGCHandleData new_weak_handle(MonoObject *p_object);
+};
+
+class MonoGCHandleRef : public Reference {
+
+ GDCLASS(MonoGCHandleRef, Reference);
+
+ MonoGCHandleData data;
+
+public:
+ static Ref<MonoGCHandleRef> create_strong(MonoObject *p_object);
+ static Ref<MonoGCHandleRef> create_weak(MonoObject *p_object);
+
+ _FORCE_INLINE_ bool is_released() const { return data.is_released(); }
+ _FORCE_INLINE_ bool is_weak() const { return data.is_weak(); }
+
+ _FORCE_INLINE_ MonoObject *get_target() const { return data.get_target(); }
+
+ void release() { data.release(); }
+
+ _FORCE_INLINE_ void set_handle(uint32_t p_handle, gdmono::GCHandleType p_handle_type) {
+ data = MonoGCHandleData(p_handle, p_handle_type);
+ }
+
+ MonoGCHandleRef(const MonoGCHandleData &p_gc_handle_data) :
+ data(p_gc_handle_data) {
+ }
+ ~MonoGCHandleRef() { release(); }
};
#endif // CSHARP_GC_HANDLE_H
diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp
index 895393537f..306a1997ab 100644
--- a/modules/mono/mono_gd/gd_mono.cpp
+++ b/modules/mono/mono_gd/gd_mono.cpp
@@ -37,6 +37,7 @@
#include <mono/metadata/mono-gc.h>
#include <mono/metadata/profiler.h>
+#include "core/debugger/engine_debugger.h"
#include "core/os/dir_access.h"
#include "core/os/file_access.h"
#include "core/os/os.h"
@@ -57,14 +58,25 @@
#ifdef ANDROID_ENABLED
#include "android_mono_config.h"
-#include "gd_mono_android.h"
+#include "support/android_support.h"
+#elif defined(IPHONE_ENABLED)
+#include "support/ios_support.h"
+#endif
+
+#if defined(TOOL_ENABLED) && defined(GD_MONO_SINGLE_APPDOMAIN)
+// This will no longer be the case if we replace appdomains with AssemblyLoadContext
+#error "Editor build requires support for multiple appdomains"
+#endif
+
+#if defined(GD_MONO_HOT_RELOAD) && defined(GD_MONO_SINGLE_APPDOMAIN)
+#error "Hot reloading requires multiple appdomains"
#endif
// TODO:
// This has 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;
+GDMono *GDMono::singleton = nullptr;
namespace {
@@ -177,7 +189,14 @@ MonoDomain *gd_initialize_mono_runtime() {
gd_mono_debug_init();
#endif
- return mono_jit_init_version("GodotEngine.RootDomain", "v4.0.30319");
+#if defined(IPHONE_ENABLED) || defined(ANDROID_ENABLED)
+ // I don't know whether this actually matters or not
+ const char *runtime_version = "mobile";
+#else
+ const char *runtime_version = "v4.0.30319";
+#endif
+
+ return mono_jit_init_version("GodotEngine.RootDomain", runtime_version);
}
#endif
@@ -313,14 +332,22 @@ void GDMono::initialize() {
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);
+ mono_set_dirs(assembly_rootdir.length() ? assembly_rootdir.utf8().get_data() : nullptr,
+ config_dir.length() ? config_dir.utf8().get_data() : nullptr);
add_mono_shared_libs_dir_to_path();
#endif
+#ifdef ANDROID_ENABLED
+ mono_config_parse_memory(get_godot_android_mono_config().utf8().get_data());
+#else
+ mono_config_parse(nullptr);
+#endif
+
#if defined(ANDROID_ENABLED)
- GDMonoAndroid::initialize();
+ gdmono::android::support::initialize();
+#elif defined(IPHONE_ENABLED)
+ gdmono::ios::support::initialize();
#endif
GDMonoAssembly::initialize();
@@ -329,13 +356,7 @@ void GDMono::initialize() {
gd_mono_profiler_init();
#endif
-#ifdef ANDROID_ENABLED
- mono_config_parse_memory(get_godot_android_mono_config().utf8().get_data());
-#else
- mono_config_parse(NULL);
-#endif
-
- mono_install_unhandled_exception_hook(&unhandled_exception_hook, NULL);
+ mono_install_unhandled_exception_hook(&unhandled_exception_hook, nullptr);
#ifndef TOOLS_ENABLED
// Exported games that don't use C# must still work. They likely don't ship with mscorlib.
@@ -354,7 +375,7 @@ void GDMono::initialize() {
#endif
// NOTE: Internal calls must be registered after the Mono runtime initialization.
- // Otherwise registration fails with the error: 'assertion 'hash != NULL' failed'.
+ // Otherwise registration fails with the error: 'assertion 'hash != nullptr' failed'.
root_domain = gd_initialize_mono_runtime();
ERR_FAIL_NULL_MSG(root_domain, "Mono: Failed to initialize runtime.");
@@ -370,15 +391,19 @@ void GDMono::initialize() {
print_verbose("Mono: Runtime initialized");
#if defined(ANDROID_ENABLED)
- GDMonoAndroid::register_internal_calls();
+ gdmono::android::support::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.");
+#ifndef GD_MONO_SINGLE_APPDOMAIN
Error domain_load_err = _load_scripts_domain();
ERR_FAIL_COND_MSG(domain_load_err != OK, "Mono: Failed to load scripts domain.");
+#else
+ scripts_domain = root_domain;
+#endif
_register_internal_calls();
@@ -490,11 +515,15 @@ void GDMono::add_assembly(uint32_t p_domain_id, GDMonoAssembly *p_assembly) {
assemblies[p_domain_id][p_assembly->get_name()] = p_assembly;
}
-GDMonoAssembly **GDMono::get_loaded_assembly(const String &p_name) {
+GDMonoAssembly *GDMono::get_loaded_assembly(const String &p_name) {
+
+ if (p_name == "mscorlib")
+ return get_corlib_assembly();
MonoDomain *domain = mono_domain_get();
uint32_t domain_id = domain ? mono_domain_get_id(domain) : 0;
- return assemblies[domain_id].getptr(p_name);
+ GDMonoAssembly **result = assemblies[domain_id].getptr(p_name);
+ return result ? *result : nullptr;
}
bool GDMono::load_assembly(const String &p_name, GDMonoAssembly **r_assembly, bool p_refonly) {
@@ -516,7 +545,7 @@ bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMo
print_verbose("Mono: Loading assembly " + p_name + (p_refonly ? " (refonly)" : "") + "...");
MonoImageOpenStatus status = MONO_IMAGE_OK;
- MonoAssembly *assembly = mono_assembly_load_full(p_aname, NULL, &status, p_refonly);
+ MonoAssembly *assembly = mono_assembly_load_full(p_aname, nullptr, &status, p_refonly);
if (!assembly)
return false;
@@ -527,7 +556,7 @@ bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMo
GDMonoAssembly **stored_assembly = assemblies[domain_id].getptr(p_name);
- ERR_FAIL_COND_V(stored_assembly == NULL, false);
+ ERR_FAIL_COND_V(stored_assembly == nullptr, false);
ERR_FAIL_COND_V((*stored_assembly)->get_assembly() != assembly, false);
*r_assembly = *stored_assembly;
@@ -548,14 +577,6 @@ bool GDMono::load_assembly_from(const String &p_name, const String &p_path, GDMo
if (!assembly)
return false;
-#ifdef DEBUG_ENABLED
- uint32_t domain_id = mono_domain_get_id(mono_domain_get());
- GDMonoAssembly **stored_assembly = assemblies[domain_id].getptr(p_name);
-
- ERR_FAIL_COND_V(stored_assembly == NULL, false);
- ERR_FAIL_COND_V(*stored_assembly != assembly, false);
-#endif
-
*r_assembly = assembly;
print_verbose("Mono: Assembly " + p_name + (p_refonly ? " (refonly)" : "") + " loaded from path: " + (*r_assembly)->get_path());
@@ -575,15 +596,15 @@ ApiAssemblyInfo::Version ApiAssemblyInfo::Version::get_from_loaded_assembly(GDMo
if (nativecalls_klass) {
GDMonoField *api_hash_field = nativecalls_klass->get_field("godot_api_hash");
if (api_hash_field)
- api_assembly_version.godot_api_hash = GDMonoMarshal::unbox<uint64_t>(api_hash_field->get_value(NULL));
+ api_assembly_version.godot_api_hash = GDMonoMarshal::unbox<uint64_t>(api_hash_field->get_value(nullptr));
GDMonoField *binds_ver_field = nativecalls_klass->get_field("bindings_version");
if (binds_ver_field)
- api_assembly_version.bindings_version = GDMonoMarshal::unbox<uint32_t>(binds_ver_field->get_value(NULL));
+ api_assembly_version.bindings_version = GDMonoMarshal::unbox<uint32_t>(binds_ver_field->get_value(nullptr));
GDMonoField *cs_glue_ver_field = nativecalls_klass->get_field("cs_glue_version");
if (cs_glue_ver_field)
- api_assembly_version.cs_glue_version = GDMonoMarshal::unbox<uint32_t>(cs_glue_ver_field->get_value(NULL));
+ api_assembly_version.cs_glue_version = GDMonoMarshal::unbox<uint32_t>(cs_glue_ver_field->get_value(nullptr));
}
return api_assembly_version;
@@ -714,7 +735,7 @@ bool GDMono::_temp_domain_load_are_assemblies_out_of_sync(const String &p_config
GDMono::LoadedApiAssembly temp_editor_api_assembly;
if (!_try_load_api_assemblies(temp_core_api_assembly, temp_editor_api_assembly,
- p_config, /* refonly: */ true, /* loaded_callback: */ NULL)) {
+ p_config, /* refonly: */ true, /* loaded_callback: */ nullptr)) {
return temp_core_api_assembly.out_of_sync || temp_editor_api_assembly.out_of_sync;
}
@@ -893,8 +914,8 @@ void GDMono::_load_api_assemblies() {
bool api_assemblies_loaded = _try_load_api_assemblies_preset();
+#if defined(TOOLS_ENABLED) && !defined(GD_MONO_SINGLE_APPDOMAIN)
if (!api_assemblies_loaded) {
-#ifdef TOOLS_ENABLED
// The API assemblies are out of sync or some other error happened. Fine, try one more time, but
// this time update them from the prebuilt assemblies directory before trying to load them again.
@@ -915,8 +936,8 @@ void GDMono::_load_api_assemblies() {
// 4. Try loading the updated assemblies
api_assemblies_loaded = _try_load_api_assemblies_preset();
-#endif
}
+#endif
if (!api_assemblies_loaded) {
// welp... too bad
@@ -981,8 +1002,8 @@ void GDMono::_install_trace_listener() {
GDMonoClass *debug_utils = get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, "DebuggingUtils");
GDMonoMethod *install_func = debug_utils->get_method("InstallTraceListener");
- MonoException *exc = NULL;
- install_func->invoke_raw(NULL, NULL, &exc);
+ MonoException *exc = nullptr;
+ install_func->invoke_raw(nullptr, nullptr, &exc);
if (exc) {
GDMonoUtils::debug_print_unhandled_exception(exc);
ERR_PRINT("Failed to install 'System.Diagnostics.Trace' listener.");
@@ -990,9 +1011,10 @@ void GDMono::_install_trace_listener() {
#endif
}
+#ifndef GD_MONO_SINGLE_APPDOMAIN
Error GDMono::_load_scripts_domain() {
- ERR_FAIL_COND_V(scripts_domain != NULL, ERR_BUG);
+ ERR_FAIL_COND_V(scripts_domain != nullptr, ERR_BUG);
print_verbose("Mono: Loading scripts domain...");
@@ -1009,7 +1031,7 @@ Error GDMono::_unload_scripts_domain() {
ERR_FAIL_NULL_V(scripts_domain, ERR_BUG);
- print_verbose("Mono: Unloading scripts domain...");
+ print_verbose("Mono: Finalizing scripts domain...");
if (mono_domain_get() != root_domain)
mono_domain_set(root_domain, true);
@@ -1028,21 +1050,23 @@ Error GDMono::_unload_scripts_domain() {
_domain_assemblies_cleanup(mono_domain_get_id(scripts_domain));
- core_api_assembly.assembly = NULL;
+ core_api_assembly.assembly = nullptr;
#ifdef TOOLS_ENABLED
- editor_api_assembly.assembly = NULL;
+ editor_api_assembly.assembly = nullptr;
#endif
- project_assembly = NULL;
+ project_assembly = nullptr;
#ifdef TOOLS_ENABLED
- tools_assembly = NULL;
- tools_project_editor_assembly = NULL;
+ tools_assembly = nullptr;
+ tools_project_editor_assembly = nullptr;
#endif
MonoDomain *domain = scripts_domain;
- scripts_domain = NULL;
+ scripts_domain = nullptr;
+
+ print_verbose("Mono: Unloading scripts domain...");
- MonoException *exc = NULL;
+ MonoException *exc = nullptr;
mono_domain_try_unload(domain, (MonoObject **)&exc);
if (exc) {
@@ -1053,6 +1077,7 @@ Error GDMono::_unload_scripts_domain() {
return OK;
}
+#endif
#ifdef GD_MONO_HOT_RELOAD
Error GDMono::reload_scripts_domain() {
@@ -1091,9 +1116,10 @@ Error GDMono::reload_scripts_domain() {
}
#endif
+#ifndef GD_MONO_SINGLE_APPDOMAIN
Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) {
- CRASH_COND(p_domain == NULL);
+ CRASH_COND(p_domain == nullptr);
CRASH_COND(p_domain == GDMono::get_singleton()->get_scripts_domain()); // Should use _unload_scripts_domain() instead
String domain_name = mono_domain_get_friendly_name(p_domain);
@@ -1111,7 +1137,7 @@ Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) {
_domain_assemblies_cleanup(mono_domain_get_id(p_domain));
- MonoException *exc = NULL;
+ MonoException *exc = nullptr;
mono_domain_try_unload(p_domain, (MonoObject **)&exc);
if (exc) {
@@ -1122,6 +1148,7 @@ Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) {
return OK;
}
+#endif
GDMonoClass *GDMono::get_class(MonoClass *p_raw_class) {
@@ -1133,7 +1160,7 @@ GDMonoClass *GDMono::get_class(MonoClass *p_raw_class) {
uint32_t domain_id = mono_domain_get_id(mono_domain_get());
HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[domain_id];
- const String *k = NULL;
+ const String *k = nullptr;
while ((k = domain_assemblies.next(k))) {
GDMonoAssembly *assembly = domain_assemblies.get(*k);
if (assembly->get_image() == image) {
@@ -1144,30 +1171,34 @@ GDMonoClass *GDMono::get_class(MonoClass *p_raw_class) {
}
}
- return NULL;
+ return nullptr;
}
GDMonoClass *GDMono::get_class(const StringName &p_namespace, const StringName &p_name) {
+ GDMonoClass *klass = corlib_assembly->get_class(p_namespace, p_name);
+ if (klass)
+ return klass;
+
uint32_t domain_id = mono_domain_get_id(mono_domain_get());
HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[domain_id];
- const String *k = NULL;
+ const String *k = nullptr;
while ((k = domain_assemblies.next(k))) {
GDMonoAssembly *assembly = domain_assemblies.get(*k);
- GDMonoClass *klass = assembly->get_class(p_namespace, p_name);
+ klass = assembly->get_class(p_namespace, p_name);
if (klass)
return klass;
}
- return NULL;
+ return nullptr;
}
void GDMono::_domain_assemblies_cleanup(uint32_t p_domain_id) {
HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[p_domain_id];
- const String *k = NULL;
+ const String *k = nullptr;
while ((k = domain_assemblies.next(k))) {
memdelete(domain_assemblies.get(*k));
}
@@ -1183,8 +1214,8 @@ void GDMono::unhandled_exception_hook(MonoObject *p_exc, void *) {
#ifdef DEBUG_ENABLED
GDMonoUtils::debug_send_unhandled_exception_error((MonoException *)p_exc);
- if (ScriptDebugger::get_singleton())
- ScriptDebugger::get_singleton()->idle_poll();
+ if (EngineDebugger::is_active())
+ EngineDebugger::get_singleton()->poll_events(false);
#endif
exit(mono_environment_exitcode_get());
@@ -1201,14 +1232,14 @@ GDMono::GDMono() {
runtime_initialized = false;
finalizing_scripts_domain = false;
- root_domain = NULL;
- scripts_domain = NULL;
+ root_domain = nullptr;
+ scripts_domain = nullptr;
- corlib_assembly = NULL;
- project_assembly = NULL;
+ corlib_assembly = nullptr;
+ project_assembly = nullptr;
#ifdef TOOLS_ENABLED
- tools_assembly = NULL;
- tools_project_editor_assembly = NULL;
+ tools_assembly = nullptr;
+ tools_project_editor_assembly = nullptr;
#endif
api_core_hash = 0;
@@ -1222,18 +1253,50 @@ GDMono::GDMono() {
GDMono::~GDMono() {
if (is_runtime_initialized()) {
+#ifndef GD_MONO_SINGLE_APPDOMAIN
if (scripts_domain) {
Error err = _unload_scripts_domain();
if (err != OK) {
ERR_PRINT("Mono: Failed to unload scripts domain.");
}
}
+#else
+ CRASH_COND(scripts_domain != root_domain);
+
+ print_verbose("Mono: Finalizing scripts domain...");
+
+ if (mono_domain_get() != root_domain)
+ mono_domain_set(root_domain, true);
+
+ finalizing_scripts_domain = true;
+
+ if (!mono_domain_finalize(root_domain, 2000)) {
+ ERR_PRINT("Mono: Domain finalization timeout.");
+ }
+
+ finalizing_scripts_domain = false;
- const uint32_t *k = NULL;
+ mono_gc_collect(mono_gc_max_generation());
+
+ GDMonoCache::clear_godot_api_cache();
+
+ _domain_assemblies_cleanup(mono_domain_get_id(root_domain));
+
+ core_api_assembly.assembly = nullptr;
+
+ project_assembly = nullptr;
+
+ root_domain = nullptr;
+ scripts_domain = nullptr;
+
+ // Leave the rest to 'mono_jit_cleanup'
+#endif
+
+ const uint32_t *k = nullptr;
while ((k = assemblies.next(k))) {
HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies.get(*k);
- const String *kk = NULL;
+ const String *kk = nullptr;
while ((kk = domain_assemblies.next(kk))) {
memdelete(domain_assemblies.get(*kk));
}
@@ -1244,22 +1307,22 @@ GDMono::~GDMono() {
mono_jit_cleanup(root_domain);
-#if defined(ANDROID_ENABLED)
- GDMonoAndroid::cleanup();
-#endif
-
print_verbose("Mono: Finalized");
runtime_initialized = false;
}
+#if defined(ANDROID_ENABLED)
+ gdmono::android::support::cleanup();
+#endif
+
if (gdmono_log)
memdelete(gdmono_log);
- singleton = NULL;
+ singleton = nullptr;
}
-_GodotSharp *_GodotSharp::singleton = NULL;
+_GodotSharp *_GodotSharp::singleton = nullptr;
void _GodotSharp::attach_thread() {
@@ -1287,7 +1350,7 @@ int32_t _GodotSharp::get_scripts_domain_id() {
bool _GodotSharp::is_scripts_domain_loaded() {
- return GDMono::get_singleton()->is_runtime_initialized() && GDMono::get_singleton()->get_scripts_domain() != NULL;
+ return GDMono::get_singleton()->is_runtime_initialized() && GDMono::get_singleton()->get_scripts_domain() != nullptr;
}
bool _GodotSharp::_is_domain_finalizing_for_unload(int32_t p_domain_id) {
@@ -1352,5 +1415,5 @@ _GodotSharp::_GodotSharp() {
_GodotSharp::~_GodotSharp() {
- singleton = NULL;
+ singleton = nullptr;
}
diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h
index 306fa15f12..4898833e8e 100644
--- a/modules/mono/mono_gd/gd_mono.h
+++ b/modules/mono/mono_gd/gd_mono.h
@@ -91,7 +91,7 @@ public:
bool out_of_sync;
LoadedApiAssembly() :
- assembly(NULL),
+ assembly(nullptr),
out_of_sync(false) {
}
};
@@ -105,7 +105,7 @@ private:
MonoDomain *root_domain;
MonoDomain *scripts_domain;
- HashMap<uint32_t, HashMap<String, GDMonoAssembly *> > assemblies;
+ HashMap<uint32_t, HashMap<String, GDMonoAssembly *>> assemblies;
GDMonoAssembly *corlib_assembly;
GDMonoAssembly *project_assembly;
@@ -144,8 +144,10 @@ private:
void _register_internal_calls();
+#ifndef GD_MONO_SINGLE_APPDOMAIN
Error _load_scripts_domain();
Error _unload_scripts_domain();
+#endif
void _domain_assemblies_cleanup(uint32_t p_domain_id);
@@ -198,18 +200,18 @@ public:
#ifdef TOOLS_ENABLED
bool copy_prebuilt_api_assembly(ApiAssemblyInfo::Type p_api_type, const String &p_config);
- String update_api_assemblies_from_prebuilt(const String &p_config, const bool *p_core_api_out_of_sync = NULL, const bool *p_editor_api_out_of_sync = NULL);
+ String update_api_assemblies_from_prebuilt(const String &p_config, const bool *p_core_api_out_of_sync = nullptr, const bool *p_editor_api_out_of_sync = nullptr);
#endif
static GDMono *get_singleton() { return singleton; }
- GD_NORETURN static void unhandled_exception_hook(MonoObject *p_exc, void *p_user_data);
+ [[noreturn]] static void unhandled_exception_hook(MonoObject *p_exc, void *p_user_data);
UnhandledExceptionPolicy get_unhandled_exception_policy() const { return unhandled_exception_policy; }
// Do not use these, unless you know what you're doing
void add_assembly(uint32_t p_domain_id, GDMonoAssembly *p_assembly);
- GDMonoAssembly **get_loaded_assembly(const String &p_name);
+ GDMonoAssembly *get_loaded_assembly(const String &p_name);
_FORCE_INLINE_ bool is_runtime_initialized() const { return runtime_initialized && !mono_runtime_is_shutting_down() /* stays true after shutdown finished */; }
@@ -263,7 +265,7 @@ public:
this->prev_domain = prev_domain;
mono_domain_set(p_domain, false);
} else {
- this->prev_domain = NULL;
+ this->prev_domain = nullptr;
}
}
diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp
index 6cf5377e2c..8439769d84 100644
--- a/modules/mono/mono_gd/gd_mono_assembly.cpp
+++ b/modules/mono/mono_gd/gd_mono_assembly.cpp
@@ -42,9 +42,6 @@
#include "gd_mono_cache.h"
#include "gd_mono_class.h"
-bool GDMonoAssembly::no_search = false;
-bool GDMonoAssembly::in_preload = false;
-
Vector<String> GDMonoAssembly::search_dirs;
void GDMonoAssembly::fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config, const String &p_custom_bcl_dir) {
@@ -78,7 +75,7 @@ void GDMonoAssembly::fill_search_dirs(Vector<String> &r_search_dirs, const Strin
if (p_custom_config.empty()) {
r_search_dirs.push_back(GodotSharpDirs::get_res_assemblies_dir());
} else {
- String api_config = p_custom_config == "Release" ? "Release" : "Debug";
+ String api_config = p_custom_config == "ExportRelease" ? "Release" : "Debug";
r_search_dirs.push_back(GodotSharpDirs::get_res_assemblies_base_dir().plus_file(api_config));
}
@@ -94,19 +91,30 @@ void GDMonoAssembly::fill_search_dirs(Vector<String> &r_search_dirs, const Strin
#endif
}
+// This is how these assembly loading hooks work:
+//
+// - The 'search' hook checks if the assembly has already been loaded, to avoid loading again.
+// - The 'preload' hook does the actual loading and is only called if the
+// 'search' hook didn't find the assembly in the list of loaded assemblies.
+// - The 'load' hook is called after the assembly has been loaded. Its job is to add the
+// assembly to the list of loaded assemblies so that the 'search' hook can look it up.
+
void GDMonoAssembly::assembly_load_hook(MonoAssembly *assembly, void *user_data) {
- if (no_search)
- return;
-
- // If our search and preload hooks fail to load the assembly themselves, the mono runtime still might.
- // Just do Assembly.LoadFrom("/Full/Path/On/Disk.dll");
- // In this case, we wouldn't have the assembly known in GDMono, which causes crashes
- // if any class inside the assembly is looked up by Godot.
- // And causing a lookup like that is as easy as throwing an exception defined in it...
- // No, we can't make the assembly load hooks smart enough because they get passed a MonoAssemblyName* only,
- // not the disk path passed to say Assembly.LoadFrom().
- _wrap_mono_assembly(assembly);
+ String name = String::utf8(mono_assembly_name_get_name(mono_assembly_get_name(assembly)));
+
+ MonoImage *image = mono_assembly_get_image(assembly);
+
+ GDMonoAssembly *gdassembly = memnew(GDMonoAssembly(name, image, assembly));
+
+#ifdef GD_MONO_HOT_RELOAD
+ const char *path = mono_image_get_filename(image);
+ if (FileAccess::exists(path))
+ gdassembly->modified_time = FileAccess::get_modified_time(path);
+#endif
+
+ MonoDomain *domain = mono_domain_get();
+ GDMono::get_singleton()->add_assembly(domain ? mono_domain_get_id(domain) : 0, gdassembly);
}
MonoAssembly *GDMonoAssembly::assembly_search_hook(MonoAssemblyName *aname, void *user_data) {
@@ -132,71 +140,24 @@ MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_d
String name = String::utf8(mono_assembly_name_get_name(aname));
bool has_extension = name.ends_with(".dll") || name.ends_with(".exe");
- if (no_search)
- return NULL;
-
- GDMonoAssembly **loaded_asm = GDMono::get_singleton()->get_loaded_assembly(has_extension ? name.get_basename() : name);
+ GDMonoAssembly *loaded_asm = GDMono::get_singleton()->get_loaded_assembly(has_extension ? name.get_basename() : name);
if (loaded_asm)
- return (*loaded_asm)->get_assembly();
-
- no_search = true; // Avoid the recursion madness
-
- GDMonoAssembly *res = _load_assembly_search(name, search_dirs, refonly);
-
- no_search = false;
+ return loaded_asm->get_assembly();
- return res ? res->get_assembly() : NULL;
+ return nullptr;
}
-static _THREAD_LOCAL_(MonoImage *) image_corlib_loading = NULL;
-
MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **, void *user_data, bool refonly) {
(void)user_data; // UNUSED
- {
- // 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.
- // One of the load hooks is 'load_aot_module'. This hook can end up calling preload hooks
- // again for the same assembly in certain in certain circumstances (the 'do_load_image' part).
- // If this is the case and we return NULL due to the no_search condition below,
- // it will result in an internal crash later on. Therefore we need to return the assembly we didn't
- // get yet from 'mono_assembly_load_from_full'. Luckily we have the image, which already got it.
- // This must be done here. If done in search hooks, it would cause 'mono_assembly_load_from_full'
- // to think another MonoAssembly for this assembly was already loaded, making it delete its own,
- // when in fact both pointers were the same... This hooks thing is confusing.
- if (image_corlib_loading) {
- return mono_image_get_assembly(image_corlib_loading);
- }
- }
-
- if (no_search)
- return NULL;
-
- no_search = true;
- in_preload = true;
-
String name = String::utf8(mono_assembly_name_get_name(aname));
- bool has_extension = name.ends_with(".dll");
-
- GDMonoAssembly *res = NULL;
- if (has_extension ? name == "mscorlib.dll" : name == "mscorlib") {
- GDMonoAssembly **stored_assembly = GDMono::get_singleton()->get_loaded_assembly(has_extension ? name.get_basename() : name);
- if (stored_assembly)
- return (*stored_assembly)->get_assembly();
-
- res = _load_assembly_search("mscorlib.dll", search_dirs, refonly);
- }
-
- no_search = false;
- in_preload = false;
-
- return res ? res->get_assembly() : NULL;
+ return _load_assembly_search(name, search_dirs, refonly);
}
-GDMonoAssembly *GDMonoAssembly::_load_assembly_search(const String &p_name, const Vector<String> &p_search_dirs, bool p_refonly) {
+MonoAssembly *GDMonoAssembly::_load_assembly_search(const String &p_name, const Vector<String> &p_search_dirs, bool p_refonly) {
- GDMonoAssembly *res = NULL;
+ MonoAssembly *res = nullptr;
String path;
bool has_extension = p_name.ends_with(".dll") || p_name.ends_with(".exe");
@@ -207,28 +168,28 @@ GDMonoAssembly *GDMonoAssembly::_load_assembly_search(const String &p_name, cons
if (has_extension) {
path = search_dir.plus_file(p_name);
if (FileAccess::exists(path)) {
- res = _load_assembly_from(p_name.get_basename(), path, p_refonly);
- if (res != NULL)
+ res = _real_load_assembly_from(path, p_refonly);
+ if (res != nullptr)
return res;
}
} else {
path = search_dir.plus_file(p_name + ".dll");
if (FileAccess::exists(path)) {
- res = _load_assembly_from(p_name, path, p_refonly);
- if (res != NULL)
+ res = _real_load_assembly_from(path, p_refonly);
+ if (res != nullptr)
return res;
}
path = search_dir.plus_file(p_name + ".exe");
if (FileAccess::exists(path)) {
- res = _load_assembly_from(p_name, path, p_refonly);
- if (res != NULL)
+ res = _real_load_assembly_from(path, p_refonly);
+ if (res != nullptr)
return res;
}
}
}
- return NULL;
+ return nullptr;
}
String GDMonoAssembly::find_assembly(const String &p_name) {
@@ -258,91 +219,50 @@ String GDMonoAssembly::find_assembly(const String &p_name) {
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));
-
- Error err = assembly->load(p_refonly);
-
- if (err != OK) {
- memdelete(assembly);
- ERR_FAIL_V(NULL);
- }
-
- MonoDomain *domain = mono_domain_get();
- GDMono::get_singleton()->add_assembly(domain ? mono_domain_get_id(domain) : 0, assembly);
-
- return assembly;
-}
-
-void GDMonoAssembly::_wrap_mono_assembly(MonoAssembly *assembly) {
- String name = String::utf8(mono_assembly_name_get_name(mono_assembly_get_name(assembly)));
-
- MonoImage *image = mono_assembly_get_image(assembly);
-
- GDMonoAssembly *gdassembly = memnew(GDMonoAssembly(name, mono_image_get_filename(image)));
- Error err = gdassembly->wrapper_for_image(image);
-
- if (err != OK) {
- memdelete(gdassembly);
- ERR_FAIL();
- }
-
- MonoDomain *domain = mono_domain_get();
- GDMono::get_singleton()->add_assembly(domain ? mono_domain_get_id(domain) : 0, gdassembly);
-}
-
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);
- mono_install_assembly_refonly_preload_hook(&assembly_refonly_preload_hook, NULL);
- mono_install_assembly_load_hook(&assembly_load_hook, NULL);
+ mono_install_assembly_search_hook(&assembly_search_hook, nullptr);
+ mono_install_assembly_refonly_search_hook(&assembly_refonly_search_hook, nullptr);
+ mono_install_assembly_preload_hook(&assembly_preload_hook, nullptr);
+ mono_install_assembly_refonly_preload_hook(&assembly_refonly_preload_hook, nullptr);
+ mono_install_assembly_load_hook(&assembly_load_hook, nullptr);
}
-Error GDMonoAssembly::load(bool p_refonly) {
-
- ERR_FAIL_COND_V(loaded, ERR_FILE_ALREADY_IN_USE);
-
- refonly = p_refonly;
+MonoAssembly *GDMonoAssembly::_real_load_assembly_from(const String &p_path, bool p_refonly) {
- uint64_t last_modified_time = FileAccess::get_modified_time(path);
-
- Vector<uint8_t> data = FileAccess::get_file_as_array(path);
- ERR_FAIL_COND_V(data.empty(), ERR_FILE_CANT_READ);
+ Vector<uint8_t> data = FileAccess::get_file_as_array(p_path);
+ ERR_FAIL_COND_V_MSG(data.empty(), nullptr, "Could read the assembly in the specified location");
String image_filename;
#ifdef ANDROID_ENABLED
- if (path.begins_with("res://")) {
- image_filename = path.substr(6, path.length());
+ if (p_path.begins_with("res://")) {
+ image_filename = p_path.substr(6, p_path.length());
} else {
- image_filename = ProjectSettings::get_singleton()->globalize_path(path);
+ image_filename = ProjectSettings::get_singleton()->globalize_path(p_path);
}
#else
// FIXME: globalize_path does not work on exported games
- image_filename = ProjectSettings::get_singleton()->globalize_path(path);
+ image_filename = ProjectSettings::get_singleton()->globalize_path(p_path);
#endif
MonoImageOpenStatus status = MONO_IMAGE_OK;
- image = mono_image_open_from_data_with_name(
+ MonoImage *image = mono_image_open_from_data_with_name(
(char *)&data[0], data.size(),
- true, &status, refonly,
- image_filename.utf8().get_data());
+ true, &status, p_refonly,
+ image_filename.utf8());
- ERR_FAIL_COND_V(status != MONO_IMAGE_OK, ERR_FILE_CANT_OPEN);
- ERR_FAIL_NULL_V(image, ERR_FILE_CANT_OPEN);
+ ERR_FAIL_COND_V_MSG(status != MONO_IMAGE_OK || !image, nullptr, "Failed to open assembly image from the loaded data");
#ifdef DEBUG_ENABLED
Vector<uint8_t> pdb_data;
- String pdb_path(path + ".pdb");
+ String pdb_path(p_path + ".pdb");
if (!FileAccess::exists(pdb_path)) {
- pdb_path = path.get_basename() + ".pdb"; // without .dll
+ pdb_path = p_path.get_basename() + ".pdb"; // without .dll
if (!FileAccess::exists(pdb_path))
goto no_pdb;
@@ -357,44 +277,21 @@ no_pdb:
#endif
- bool is_corlib_preload = in_preload && name == "mscorlib";
-
- if (is_corlib_preload)
- image_corlib_loading = image;
+ status = MONO_IMAGE_OK;
- assembly = mono_assembly_load_from_full(image, image_filename.utf8().get_data(), &status, refonly);
+ MonoAssembly *assembly = mono_assembly_load_from_full(image, image_filename.utf8().get_data(), &status, p_refonly);
- if (is_corlib_preload)
- image_corlib_loading = NULL;
-
- ERR_FAIL_COND_V(status != MONO_IMAGE_OK || assembly == NULL, ERR_FILE_CANT_OPEN);
+ ERR_FAIL_COND_V_MSG(status != MONO_IMAGE_OK || !assembly, nullptr, "Failed to load assembly for image");
// Decrement refcount which was previously incremented by mono_image_open_from_data_with_name
mono_image_close(image);
- loaded = true;
- modified_time = last_modified_time;
-
- return OK;
-}
-
-Error GDMonoAssembly::wrapper_for_image(MonoImage *p_image) {
-
- ERR_FAIL_COND_V(loaded, ERR_FILE_ALREADY_IN_USE);
-
- assembly = mono_image_get_assembly(p_image);
- ERR_FAIL_NULL_V(assembly, FAILED);
-
- image = p_image;
-
- loaded = true;
-
- return OK;
+ return assembly;
}
void GDMonoAssembly::unload() {
- ERR_FAIL_COND(!loaded);
+ ERR_FAIL_NULL(image); // Should not be called if already unloaded
for (Map<MonoClass *, GDMonoClass *>::Element *E = cached_raw.front(); E; E = E->next()) {
memdelete(E->value());
@@ -403,14 +300,17 @@ void GDMonoAssembly::unload() {
cached_classes.clear();
cached_raw.clear();
- assembly = NULL;
- image = NULL;
- loaded = false;
+ assembly = nullptr;
+ image = nullptr;
+}
+
+String GDMonoAssembly::get_path() const {
+ return String::utf8(mono_image_get_filename(image));
}
GDMonoClass *GDMonoAssembly::get_class(const StringName &p_namespace, const StringName &p_name) {
- ERR_FAIL_COND_V(!loaded, NULL);
+ ERR_FAIL_NULL_V(image, nullptr);
ClassKey key(p_namespace, p_name);
@@ -422,7 +322,7 @@ GDMonoClass *GDMonoAssembly::get_class(const StringName &p_namespace, const Stri
MonoClass *mono_class = mono_class_from_name(image, String(p_namespace).utf8(), String(p_name).utf8());
if (!mono_class)
- return NULL;
+ return nullptr;
GDMonoClass *wrapped_class = memnew(GDMonoClass(p_namespace, p_name, mono_class, this));
@@ -434,7 +334,7 @@ GDMonoClass *GDMonoAssembly::get_class(const StringName &p_namespace, const Stri
GDMonoClass *GDMonoAssembly::get_class(MonoClass *p_mono_class) {
- ERR_FAIL_COND_V(!loaded, NULL);
+ ERR_FAIL_NULL_V(image, nullptr);
Map<MonoClass *, GDMonoClass *>::Element *match = cached_raw.find(p_mono_class);
@@ -454,7 +354,7 @@ GDMonoClass *GDMonoAssembly::get_class(MonoClass *p_mono_class) {
GDMonoClass *GDMonoAssembly::get_object_derived_class(const StringName &p_class) {
- GDMonoClass *match = NULL;
+ GDMonoClass *match = nullptr;
if (gdobject_class_cache_updated) {
Map<StringName, GDMonoClass *>::Element *result = gdobject_class_cache.find(p_class);
@@ -486,7 +386,7 @@ GDMonoClass *GDMonoAssembly::get_object_derived_class(const StringName &p_class)
GDMonoClass *current_nested = nested_classes.front()->get();
nested_classes.pop_back();
- void *iter = NULL;
+ void *iter = nullptr;
while (true) {
MonoClass *raw_nested = mono_class_get_nested_types(current_nested->get_mono_ptr(), &iter);
@@ -514,32 +414,38 @@ GDMonoClass *GDMonoAssembly::get_object_derived_class(const StringName &p_class)
GDMonoAssembly *GDMonoAssembly::load_from(const String &p_name, const String &p_path, bool p_refonly) {
- GDMonoAssembly **loaded_asm = GDMono::get_singleton()->get_loaded_assembly(p_name);
- if (loaded_asm)
- return *loaded_asm;
-#ifdef DEBUG_ENABLED
- CRASH_COND(!FileAccess::exists(p_path));
-#endif
- no_search = true;
- GDMonoAssembly *res = _load_assembly_from(p_name, p_path, p_refonly);
- no_search = false;
- return res;
-}
+ if (p_name == "mscorlib" || p_name == "mscorlib.dll")
+ return GDMono::get_singleton()->get_corlib_assembly();
-GDMonoAssembly::GDMonoAssembly(const String &p_name, const String &p_path) {
+ // We need to manually call the search hook in this case, as it won't be called in the next step
+ MonoAssemblyName *aname = mono_assembly_name_new(p_name.utf8());
+ MonoAssembly *assembly = mono_assembly_invoke_search_hook(aname);
+ mono_assembly_name_free(aname);
+ mono_free(aname);
+
+ if (!assembly) {
+ assembly = _real_load_assembly_from(p_path, p_refonly);
+ ERR_FAIL_NULL_V(assembly, nullptr);
+ }
- loaded = false;
- gdobject_class_cache_updated = false;
- name = p_name;
- path = p_path;
- refonly = false;
- modified_time = 0;
- assembly = NULL;
- image = NULL;
+ GDMonoAssembly *loaded_asm = GDMono::get_singleton()->get_loaded_assembly(p_name);
+ ERR_FAIL_NULL_V_MSG(loaded_asm, nullptr, "Loaded assembly missing from table. Did we not receive the load hook?");
+
+ return loaded_asm;
+}
+
+GDMonoAssembly::GDMonoAssembly(const String &p_name, MonoImage *p_image, MonoAssembly *p_assembly) :
+ name(p_name),
+ image(p_image),
+ assembly(p_assembly),
+#ifdef GD_MONO_HOT_RELOAD
+ modified_time(0),
+#endif
+ gdobject_class_cache_updated(false) {
}
GDMonoAssembly::~GDMonoAssembly() {
- if (loaded)
+ if (image)
unload();
}
diff --git a/modules/mono/mono_gd/gd_mono_assembly.h b/modules/mono/mono_gd/gd_mono_assembly.h
index 4740e10339..43c8225b74 100644
--- a/modules/mono/mono_gd/gd_mono_assembly.h
+++ b/modules/mono/mono_gd/gd_mono_assembly.h
@@ -68,24 +68,20 @@ class GDMonoAssembly {
StringName class_name;
};
- MonoAssembly *assembly;
+ String name;
MonoImage *image;
+ MonoAssembly *assembly;
- bool refonly;
- bool loaded;
-
- String name;
- String path;
+#ifdef GD_MONO_HOT_RELOAD
uint64_t modified_time;
-
- HashMap<ClassKey, GDMonoClass *, ClassKey::Hasher> cached_classes;
- Map<MonoClass *, GDMonoClass *> cached_raw;
+#endif
bool gdobject_class_cache_updated;
Map<StringName, GDMonoClass *> gdobject_class_cache;
- static bool no_search;
- static bool in_preload;
+ HashMap<ClassKey, GDMonoClass *, ClassKey::Hasher> cached_classes;
+ Map<MonoClass *, GDMonoClass *> cached_raw;
+
static Vector<String> search_dirs;
static void assembly_load_hook(MonoAssembly *assembly, void *user_data);
@@ -97,25 +93,24 @@ class GDMonoAssembly {
static MonoAssembly *_search_hook(MonoAssemblyName *aname, void *user_data, bool refonly);
static MonoAssembly *_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data, bool refonly);
- static GDMonoAssembly *_load_assembly_from(const String &p_name, const String &p_path, bool p_refonly);
- static GDMonoAssembly *_load_assembly_search(const String &p_name, const Vector<String> &p_search_dirs, bool p_refonly);
- static void _wrap_mono_assembly(MonoAssembly *assembly);
+ static MonoAssembly *_real_load_assembly_from(const String &p_path, bool p_refonly);
+ static MonoAssembly *_load_assembly_search(const String &p_name, const Vector<String> &p_search_dirs, bool p_refonly);
friend class GDMono;
static void initialize();
public:
- Error load(bool p_refonly);
- Error wrapper_for_image(MonoImage *p_image);
void unload();
- _FORCE_INLINE_ bool is_refonly() const { return refonly; }
- _FORCE_INLINE_ bool is_loaded() const { return loaded; }
_FORCE_INLINE_ MonoImage *get_image() const { return image; }
_FORCE_INLINE_ MonoAssembly *get_assembly() const { return assembly; }
_FORCE_INLINE_ String get_name() const { return name; }
- _FORCE_INLINE_ String get_path() const { return path; }
+
+#ifdef GD_MONO_HOT_RELOAD
_FORCE_INLINE_ uint64_t get_modified_time() const { return modified_time; }
+#endif
+
+ String get_path() const;
GDMonoClass *get_class(const StringName &p_namespace, const StringName &p_name);
GDMonoClass *get_class(MonoClass *p_mono_class);
@@ -128,7 +123,7 @@ public:
static GDMonoAssembly *load_from(const String &p_name, const String &p_path, bool p_refonly);
- GDMonoAssembly(const String &p_name, const String &p_path = String());
+ GDMonoAssembly(const String &p_name, MonoImage *p_image, MonoAssembly *p_assembly);
~GDMonoAssembly();
};
diff --git a/modules/mono/mono_gd/gd_mono_cache.cpp b/modules/mono/mono_gd/gd_mono_cache.cpp
index 0ad90a510e..facc0da780 100644
--- a/modules/mono/mono_gd/gd_mono_cache.cpp
+++ b/modules/mono/mono_gd/gd_mono_cache.cpp
@@ -40,11 +40,11 @@ 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_AND_CHECK(m_var, m_val) \
+ { \
+ CRASH_COND(m_var != nullptr); \
+ m_var = m_val; \
+ ERR_FAIL_COND_MSG(m_var == nullptr, "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)
@@ -54,12 +54,12 @@ CachedData cached_data;
#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_IMPL(m_var, m_val) \
+ { \
+ CRASH_COND(!m_var.is_null()); \
+ ERR_FAIL_COND_MSG(m_val == nullptr, "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)
@@ -68,94 +68,105 @@ 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;
+ class_MonoObject = nullptr;
+ class_bool = nullptr;
+ class_int8_t = nullptr;
+ class_int16_t = nullptr;
+ class_int32_t = nullptr;
+ class_int64_t = nullptr;
+ class_uint8_t = nullptr;
+ class_uint16_t = nullptr;
+ class_uint32_t = nullptr;
+ class_uint64_t = nullptr;
+ class_float = nullptr;
+ class_double = nullptr;
+ class_String = nullptr;
+ class_IntPtr = nullptr;
+
+ class_System_Collections_IEnumerable = nullptr;
+ class_System_Collections_IDictionary = nullptr;
#ifdef DEBUG_ENABLED
- class_System_Diagnostics_StackTrace = NULL;
+ class_System_Diagnostics_StackTrace = nullptr;
methodthunk_System_Diagnostics_StackTrace_GetFrames.nullify();
- method_System_Diagnostics_StackTrace_ctor_bool = NULL;
- method_System_Diagnostics_StackTrace_ctor_Exception_bool = NULL;
+ method_System_Diagnostics_StackTrace_ctor_bool = nullptr;
+ method_System_Diagnostics_StackTrace_ctor_Exception_bool = nullptr;
#endif
- class_KeyNotFoundException = NULL;
+ class_KeyNotFoundException = nullptr;
}
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;
+ rawclass_Dictionary = nullptr;
+
+ class_Vector2 = nullptr;
+ class_Vector2i = nullptr;
+ class_Rect2 = nullptr;
+ class_Rect2i = nullptr;
+ class_Transform2D = nullptr;
+ class_Vector3 = nullptr;
+ class_Vector3i = nullptr;
+ class_Basis = nullptr;
+ class_Quat = nullptr;
+ class_Transform = nullptr;
+ class_AABB = nullptr;
+ class_Color = nullptr;
+ class_Plane = nullptr;
+ class_StringName = nullptr;
+ class_NodePath = nullptr;
+ class_RID = nullptr;
+ class_GodotObject = nullptr;
+ class_GodotResource = nullptr;
+ class_Node = nullptr;
+ class_Control = nullptr;
+ class_Node3D = nullptr;
+ class_WeakRef = nullptr;
+ class_Callable = nullptr;
+ class_SignalInfo = nullptr;
+ class_Array = nullptr;
+ class_Dictionary = nullptr;
+ class_MarshalUtils = nullptr;
+ class_ISerializationListener = nullptr;
#ifdef DEBUG_ENABLED
- class_DebuggingUtils = NULL;
+ class_DebuggingUtils = nullptr;
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_MasterAttribute = NULL;
- class_PuppetAttribute = 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;
+ class_ExportAttribute = nullptr;
+ field_ExportAttribute_hint = nullptr;
+ field_ExportAttribute_hintString = nullptr;
+ class_SignalAttribute = nullptr;
+ class_ToolAttribute = nullptr;
+ class_RemoteAttribute = nullptr;
+ class_MasterAttribute = nullptr;
+ class_PuppetAttribute = nullptr;
+ class_RemoteSyncAttribute = nullptr;
+ class_MasterSyncAttribute = nullptr;
+ class_PuppetSyncAttribute = nullptr;
+ class_GodotMethodAttribute = nullptr;
+ field_GodotMethodAttribute_methodName = nullptr;
+
+ field_GodotObject_ptr = nullptr;
+ field_StringName_ptr = nullptr;
+ field_NodePath_ptr = nullptr;
+ field_Image_ptr = nullptr;
+ field_RID_ptr = nullptr;
methodthunk_GodotObject_Dispose.nullify();
methodthunk_Array_GetPtr.nullify();
methodthunk_Dictionary_GetPtr.nullify();
methodthunk_SignalAwaiter_SignalCallback.nullify();
- methodthunk_SignalAwaiter_FailureCallback.nullify();
methodthunk_GodotTaskScheduler_Activate.nullify();
+ methodthunk_Delegate_Equals.nullify();
+
+ methodthunk_DelegateUtils_TrySerializeDelegate.nullify();
+ methodthunk_DelegateUtils_TryDeserializeDelegate.nullify();
+
// Start of MarshalUtils methods
methodthunk_MarshalUtils_TypeIsGenericArray.nullify();
@@ -178,7 +189,7 @@ void CachedData::clear_godot_api_cache() {
// End of MarshalUtils methods
- task_scheduler_handle = Ref<MonoGCHandle>();
+ task_scheduler_handle = Ref<MonoGCHandleRef>();
}
#define GODOT_API_CLASS(m_class) (GDMono::get_singleton()->get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, #m_class))
@@ -211,6 +222,8 @@ void update_corlib_cache() {
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_METHOD_THUNK_AND_CHECK(Delegate, Equals, GDMono::get_singleton()->get_corlib_assembly()->get_class("System", "Delegate")->get_method_with_desc("System.Delegate:Equals(object)", 1));
+
CACHE_CLASS_AND_CHECK(KeyNotFoundException, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections.Generic", "KeyNotFoundException"));
cached_data.corlib_cache_updated = true;
@@ -219,23 +232,29 @@ void update_corlib_cache() {
void update_godot_api_cache() {
CACHE_CLASS_AND_CHECK(Vector2, GODOT_API_CLASS(Vector2));
+ CACHE_CLASS_AND_CHECK(Vector2i, GODOT_API_CLASS(Vector2i));
CACHE_CLASS_AND_CHECK(Rect2, GODOT_API_CLASS(Rect2));
+ CACHE_CLASS_AND_CHECK(Rect2i, GODOT_API_CLASS(Rect2i));
CACHE_CLASS_AND_CHECK(Transform2D, GODOT_API_CLASS(Transform2D));
CACHE_CLASS_AND_CHECK(Vector3, GODOT_API_CLASS(Vector3));
+ CACHE_CLASS_AND_CHECK(Vector3i, GODOT_API_CLASS(Vector3i));
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(StringName, GODOT_API_CLASS(StringName));
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(Node3D, GODOT_API_CLASS(Node3D));
CACHE_CLASS_AND_CHECK(WeakRef, GODOT_API_CLASS(WeakRef));
+ CACHE_CLASS_AND_CHECK(Callable, GODOT_API_CLASS(Callable));
+ CACHE_CLASS_AND_CHECK(SignalInfo, GODOT_API_CLASS(SignalInfo));
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));
@@ -261,6 +280,7 @@ void update_godot_api_cache() {
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(StringName, ptr, CACHED_CLASS(StringName)->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));
@@ -268,9 +288,11 @@ void update_godot_api_cache() {
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));
+ CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, TrySerializeDelegate, GODOT_API_CLASS(DelegateUtils)->get_method("TrySerializeDelegate", 2));
+ CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, TryDeserializeDelegate, GODOT_API_CLASS(DelegateUtils)->get_method("TryDeserializeDelegate", 2));
+
// Start of MarshalUtils methods
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericArray, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericArray", 1));
@@ -300,7 +322,7 @@ void update_godot_api_cache() {
// 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.task_scheduler_handle = MonoGCHandleRef::create_strong(task_scheduler);
cached_data.godot_api_cache_updated = true;
}
diff --git a/modules/mono/mono_gd/gd_mono_cache.h b/modules/mono/mono_gd/gd_mono_cache.h
index 0458e91240..21c8ed4efe 100644
--- a/modules/mono/mono_gd/gd_mono_cache.h
+++ b/modules/mono/mono_gd/gd_mono_cache.h
@@ -73,23 +73,29 @@ struct CachedData {
// -----------------------------------------------
GDMonoClass *class_Vector2;
+ GDMonoClass *class_Vector2i;
GDMonoClass *class_Rect2;
+ GDMonoClass *class_Rect2i;
GDMonoClass *class_Transform2D;
GDMonoClass *class_Vector3;
+ GDMonoClass *class_Vector3i;
GDMonoClass *class_Basis;
GDMonoClass *class_Quat;
GDMonoClass *class_Transform;
GDMonoClass *class_AABB;
GDMonoClass *class_Color;
GDMonoClass *class_Plane;
+ GDMonoClass *class_StringName;
GDMonoClass *class_NodePath;
GDMonoClass *class_RID;
GDMonoClass *class_GodotObject;
GDMonoClass *class_GodotResource;
GDMonoClass *class_Node;
GDMonoClass *class_Control;
- GDMonoClass *class_Spatial;
+ GDMonoClass *class_Node3D;
GDMonoClass *class_WeakRef;
+ GDMonoClass *class_Callable;
+ GDMonoClass *class_SignalInfo;
GDMonoClass *class_Array;
GDMonoClass *class_Dictionary;
GDMonoClass *class_MarshalUtils;
@@ -115,6 +121,7 @@ struct CachedData {
GDMonoField *field_GodotMethodAttribute_methodName;
GDMonoField *field_GodotObject_ptr;
+ GDMonoField *field_StringName_ptr;
GDMonoField *field_NodePath_ptr;
GDMonoField *field_Image_ptr;
GDMonoField *field_RID_ptr;
@@ -123,9 +130,13 @@ struct CachedData {
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;
+ GDMonoMethodThunkR<MonoBoolean, MonoObject *, MonoObject *> methodthunk_Delegate_Equals;
+
+ GDMonoMethodThunkR<MonoBoolean, MonoDelegate *, MonoObject *> methodthunk_DelegateUtils_TrySerializeDelegate;
+ GDMonoMethodThunkR<MonoBoolean, MonoObject *, MonoDelegate **> methodthunk_DelegateUtils_TryDeserializeDelegate;
+
// Start of MarshalUtils methods
GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericArray;
@@ -148,7 +159,7 @@ struct CachedData {
// End of MarshalUtils methods
- Ref<MonoGCHandle> task_scheduler_handle;
+ Ref<MonoGCHandleRef> task_scheduler_handle;
bool corlib_cache_updated;
bool godot_api_cache_updated;
@@ -193,10 +204,4 @@ _FORCE_INLINE_ bool tools_godot_api_check() {
#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 648f5f6c6b..ede4203e35 100644
--- a/modules/mono/mono_gd/gd_mono_class.cpp
+++ b/modules/mono/mono_gd/gd_mono_class.cpp
@@ -40,7 +40,7 @@ String GDMonoClass::get_full_name(MonoClass *p_mono_class) {
// mono_type_get_full_name is not exposed to embedders, but this seems to do the job
MonoReflectionType *type_obj = mono_type_get_object(mono_domain_get(), get_mono_type(p_mono_class));
- MonoException *exc = NULL;
+ MonoException *exc = nullptr;
MonoString *str = GDMonoUtils::object_to_string((MonoObject *)type_obj, &exc);
UNHANDLED_EXCEPTION(exc);
@@ -76,12 +76,12 @@ bool GDMonoClass::is_assignable_from(GDMonoClass *p_from) const {
GDMonoClass *GDMonoClass::get_parent_class() {
MonoClass *parent_mono_class = mono_class_get_parent(mono_class);
- return parent_mono_class ? GDMono::get_singleton()->get_class(parent_mono_class) : NULL;
+ return parent_mono_class ? GDMono::get_singleton()->get_class(parent_mono_class) : nullptr;
}
GDMonoClass *GDMonoClass::get_nesting_class() {
MonoClass *nesting_type = mono_class_get_nesting_type(mono_class);
- return nesting_type ? GDMono::get_singleton()->get_class(nesting_type) : NULL;
+ return nesting_type ? GDMono::get_singleton()->get_class(nesting_type) : nullptr;
}
#ifdef TOOLS_ENABLED
@@ -92,9 +92,9 @@ Vector<MonoClassField *> GDMonoClass::get_enum_fields() {
Vector<MonoClassField *> enum_fields;
- void *iter = NULL;
- MonoClassField *raw_field = NULL;
- while ((raw_field = mono_class_get_fields(get_mono_ptr(), &iter)) != NULL) {
+ void *iter = nullptr;
+ MonoClassField *raw_field = nullptr;
+ while ((raw_field = mono_class_get_fields(get_mono_ptr(), &iter)) != nullptr) {
uint32_t field_flags = mono_field_get_flags(raw_field);
// Enums have an instance field named value__ which holds the value of the enum.
@@ -126,21 +126,21 @@ bool GDMonoClass::has_attribute(GDMonoClass *p_attr_class) {
MonoObject *GDMonoClass::get_attribute(GDMonoClass *p_attr_class) {
#ifdef DEBUG_ENABLED
- ERR_FAIL_NULL_V(p_attr_class, NULL);
+ ERR_FAIL_NULL_V(p_attr_class, nullptr);
#endif
if (!attrs_fetched)
fetch_attributes();
if (!attributes)
- return NULL;
+ return nullptr;
return mono_custom_attrs_get_attr(attributes, p_attr_class->get_mono_ptr());
}
void GDMonoClass::fetch_attributes() {
- ERR_FAIL_COND(attributes != NULL);
+ ERR_FAIL_COND(attributes != nullptr);
attributes = mono_custom_attrs_from_class(get_mono_ptr());
attrs_fetched = true;
@@ -153,9 +153,9 @@ void GDMonoClass::fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base
if (methods_fetched)
return;
- void *iter = NULL;
- MonoMethod *raw_method = NULL;
- while ((raw_method = mono_class_get_methods(get_mono_ptr(), &iter)) != NULL) {
+ void *iter = nullptr;
+ MonoMethod *raw_method = nullptr;
+ while ((raw_method = mono_class_get_methods(get_mono_ptr(), &iter)) != nullptr) {
StringName name = mono_method_get_name(raw_method);
// get_method implicitly fetches methods and adds them to this->methods
@@ -198,7 +198,7 @@ void GDMonoClass::fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base
}
#endif
- uint32_t flags = mono_method_get_flags(method->mono_method, NULL);
+ uint32_t flags = mono_method_get_flags(method->mono_method, nullptr);
if (!(flags & MONO_METHOD_ATTR_VIRTUAL))
continue;
@@ -242,21 +242,21 @@ void GDMonoClass::fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base
GDMonoMethod *GDMonoClass::get_fetched_method_unknown_params(const StringName &p_name) {
- ERR_FAIL_COND_V(!methods_fetched, NULL);
+ ERR_FAIL_COND_V(!methods_fetched, nullptr);
- const MethodKey *k = NULL;
+ const MethodKey *k = nullptr;
while ((k = methods.next(k))) {
if (k->name == p_name)
return methods.get(*k);
}
- return NULL;
+ return nullptr;
}
bool GDMonoClass::has_fetched_method_unknown_params(const StringName &p_name) {
- return get_fetched_method_unknown_params(p_name) != NULL;
+ return get_fetched_method_unknown_params(p_name) != nullptr;
}
bool GDMonoClass::implements_interface(GDMonoClass *p_interface) {
@@ -274,7 +274,7 @@ GDMonoMethod *GDMonoClass::get_method(const StringName &p_name, int p_params_cou
return *match;
if (methods_fetched)
- return NULL;
+ return nullptr;
MonoMethod *raw_method = mono_class_get_method_from_name(mono_class, String(p_name).utf8().get_data(), p_params_count);
@@ -285,7 +285,7 @@ GDMonoMethod *GDMonoClass::get_method(const StringName &p_name, int p_params_cou
return method;
}
- return NULL;
+ return nullptr;
}
GDMonoMethod *GDMonoClass::get_method(MonoMethod *p_raw_method) {
@@ -307,7 +307,7 @@ GDMonoMethod *GDMonoClass::get_method(MonoMethod *p_raw_method, const StringName
GDMonoMethod *GDMonoClass::get_method(MonoMethod *p_raw_method, const StringName &p_name, int p_params_count) {
- ERR_FAIL_NULL_V(p_raw_method, NULL);
+ ERR_FAIL_NULL_V(p_raw_method, nullptr);
MethodKey key = MethodKey(p_name, p_params_count);
@@ -328,7 +328,7 @@ GDMonoMethod *GDMonoClass::get_method_with_desc(const String &p_description, boo
MonoMethod *method = mono_method_desc_search_in_class(desc, mono_class);
mono_method_desc_free(desc);
- ERR_FAIL_COND_V(mono_method_get_class(method) != mono_class, NULL);
+ ERR_FAIL_COND_V(mono_method_get_class(method) != mono_class, nullptr);
return get_method(method);
}
@@ -341,7 +341,7 @@ GDMonoField *GDMonoClass::get_field(const StringName &p_name) {
return result->value();
if (fields_fetched)
- return NULL;
+ return nullptr;
MonoClassField *raw_field = mono_class_get_field_from_name(mono_class, String(p_name).utf8().get_data());
@@ -352,7 +352,7 @@ GDMonoField *GDMonoClass::get_field(const StringName &p_name) {
return field;
}
- return NULL;
+ return nullptr;
}
const Vector<GDMonoField *> &GDMonoClass::get_all_fields() {
@@ -360,9 +360,9 @@ const Vector<GDMonoField *> &GDMonoClass::get_all_fields() {
if (fields_fetched)
return fields_list;
- void *iter = NULL;
- MonoClassField *raw_field = NULL;
- while ((raw_field = mono_class_get_fields(mono_class, &iter)) != NULL) {
+ void *iter = nullptr;
+ MonoClassField *raw_field = nullptr;
+ while ((raw_field = mono_class_get_fields(mono_class, &iter)) != nullptr) {
StringName name = mono_field_get_name(raw_field);
Map<StringName, GDMonoField *>::Element *match = fields.find(name);
@@ -389,7 +389,7 @@ GDMonoProperty *GDMonoClass::get_property(const StringName &p_name) {
return result->value();
if (properties_fetched)
- return NULL;
+ return nullptr;
MonoProperty *raw_property = mono_class_get_property_from_name(mono_class, String(p_name).utf8().get_data());
@@ -400,7 +400,7 @@ GDMonoProperty *GDMonoClass::get_property(const StringName &p_name) {
return property;
}
- return NULL;
+ return nullptr;
}
const Vector<GDMonoProperty *> &GDMonoClass::get_all_properties() {
@@ -408,9 +408,9 @@ const Vector<GDMonoProperty *> &GDMonoClass::get_all_properties() {
if (properties_fetched)
return properties_list;
- void *iter = NULL;
- MonoProperty *raw_property = NULL;
- while ((raw_property = mono_class_get_properties(mono_class, &iter)) != NULL) {
+ void *iter = nullptr;
+ MonoProperty *raw_property = nullptr;
+ while ((raw_property = mono_class_get_properties(mono_class, &iter)) != nullptr) {
StringName name = mono_property_get_name(raw_property);
Map<StringName, GDMonoProperty *>::Element *match = properties.find(name);
@@ -433,9 +433,9 @@ const Vector<GDMonoClass *> &GDMonoClass::get_all_delegates() {
if (delegates_fetched)
return delegates_list;
- void *iter = NULL;
- MonoClass *raw_class = NULL;
- while ((raw_class = mono_class_get_nested_types(mono_class, &iter)) != NULL) {
+ void *iter = nullptr;
+ MonoClass *raw_class = nullptr;
+ while ((raw_class = mono_class_get_nested_types(mono_class, &iter)) != nullptr) {
if (mono_class_is_delegate(raw_class)) {
StringName name = mono_class_get_name(raw_class);
@@ -459,9 +459,9 @@ const Vector<GDMonoClass *> &GDMonoClass::get_all_delegates() {
const Vector<GDMonoMethod *> &GDMonoClass::get_all_methods() {
if (!method_list_fetched) {
- void *iter = NULL;
- MonoMethod *raw_method = NULL;
- while ((raw_method = mono_class_get_methods(get_mono_ptr(), &iter)) != NULL) {
+ void *iter = nullptr;
+ MonoMethod *raw_method = nullptr;
+ while ((raw_method = mono_class_get_methods(get_mono_ptr(), &iter)) != nullptr) {
method_list.push_back(memnew(GDMonoMethod(mono_method_get_name(raw_method), raw_method)));
}
@@ -479,7 +479,7 @@ GDMonoClass::GDMonoClass(const StringName &p_namespace, const StringName &p_name
assembly = p_assembly;
attrs_fetched = false;
- attributes = NULL;
+ attributes = nullptr;
methods_fetched = false;
method_list_fetched = false;
@@ -512,7 +512,7 @@ GDMonoClass::~GDMonoClass() {
Vector<GDMonoMethod *> deleted_methods;
deleted_methods.resize(methods.size());
- const MethodKey *k = NULL;
+ const MethodKey *k = nullptr;
while ((k = methods.next(k))) {
GDMonoMethod *method = methods.get(*k);
diff --git a/modules/mono/mono_gd/gd_mono_field.cpp b/modules/mono/mono_gd/gd_mono_field.cpp
index 03b56c9949..3f4e5fe5ac 100644
--- a/modules/mono/mono_gd/gd_mono_field.cpp
+++ b/modules/mono/mono_gd/gd_mono_field.cpp
@@ -37,6 +37,10 @@
#include "gd_mono_marshal.h"
#include "gd_mono_utils.h"
+void GDMonoField::set_value(MonoObject *p_object, MonoObject *p_value) {
+ mono_field_set_value(p_object, mono_field, p_value);
+}
+
void GDMonoField::set_value_raw(MonoObject *p_object, void *p_ptr) {
mono_field_set_value(p_object, mono_field, &p_ptr);
}
@@ -112,7 +116,7 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
case MONO_TYPE_STRING: {
if (p_value.get_type() == Variant::NIL) {
// Otherwise, Variant -> String would return the string "Null"
- MonoString *mono_string = NULL;
+ MonoString *mono_string = nullptr;
mono_field_set_value(p_object, mono_field, mono_string);
} else {
MonoString *mono_string = GDMonoMarshal::mono_string_from_godot(p_value);
@@ -128,11 +132,21 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
break;
}
+ if (tclass == CACHED_CLASS(Vector2i)) {
+ SET_FROM_STRUCT(Vector2i);
+ break;
+ }
+
if (tclass == CACHED_CLASS(Rect2)) {
SET_FROM_STRUCT(Rect2);
break;
}
+ if (tclass == CACHED_CLASS(Rect2i)) {
+ SET_FROM_STRUCT(Rect2i);
+ break;
+ }
+
if (tclass == CACHED_CLASS(Transform2D)) {
SET_FROM_STRUCT(Transform2D);
break;
@@ -143,6 +157,11 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
break;
}
+ if (tclass == CACHED_CLASS(Vector3i)) {
+ SET_FROM_STRUCT(Vector3i);
+ break;
+ }
+
if (tclass == CACHED_CLASS(Basis)) {
SET_FROM_STRUCT(Basis);
break;
@@ -173,6 +192,18 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
break;
}
+ if (tclass == CACHED_CLASS(Callable)) {
+ GDMonoMarshal::M_Callable val = GDMonoMarshal::callable_to_managed(p_value.operator Callable());
+ mono_field_set_value(p_object, mono_field, &val);
+ break;
+ }
+
+ if (tclass == CACHED_CLASS(SignalInfo)) {
+ GDMonoMarshal::M_SignalInfo val = GDMonoMarshal::signal_info_to_managed(p_value.operator Signal());
+ mono_field_set_value(p_object, mono_field, &val);
+ break;
+ }
+
if (mono_class_is_enum(tclass->get_mono_ptr())) {
MonoType *enum_basetype = mono_class_enum_basetype(tclass->get_mono_ptr());
switch (mono_type_get_type(enum_basetype)) {
@@ -256,11 +287,21 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
break;
}
- if (array_type->eklass == REAL_T_MONOCLASS) {
+ if (array_type->eklass == CACHED_CLASS_RAW(int64_t)) {
+ SET_FROM_ARRAY(PackedInt64Array);
+ break;
+ }
+
+ if (array_type->eklass == CACHED_CLASS_RAW(float)) {
SET_FROM_ARRAY(PackedFloat32Array);
break;
}
+ if (array_type->eklass == CACHED_CLASS_RAW(double)) {
+ SET_FROM_ARRAY(PackedFloat64Array);
+ break;
+ }
+
if (array_type->eklass == CACHED_CLASS_RAW(String)) {
SET_FROM_ARRAY(PackedStringArray);
break;
@@ -294,6 +335,12 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
break;
}
+ if (CACHED_CLASS(StringName) == type_class) {
+ MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator StringName());
+ mono_field_set_value(p_object, mono_field, managed);
+ break;
+ }
+
if (CACHED_CLASS(NodePath) == type_class) {
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator NodePath());
mono_field_set_value(p_object, mono_field, managed);
@@ -386,12 +433,21 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
case Variant::VECTOR2: {
SET_FROM_STRUCT(Vector2);
} break;
+ case Variant::VECTOR2I: {
+ SET_FROM_STRUCT(Vector2i);
+ } break;
case Variant::RECT2: {
SET_FROM_STRUCT(Rect2);
} break;
+ case Variant::RECT2I: {
+ SET_FROM_STRUCT(Rect2i);
+ } break;
case Variant::VECTOR3: {
SET_FROM_STRUCT(Vector3);
} break;
+ case Variant::VECTOR3I: {
+ SET_FROM_STRUCT(Vector3i);
+ } break;
case Variant::TRANSFORM2D: {
SET_FROM_STRUCT(Transform2D);
} break;
@@ -413,6 +469,10 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
case Variant::COLOR: {
SET_FROM_STRUCT(Color);
} break;
+ case Variant::STRING_NAME: {
+ MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator StringName());
+ mono_field_set_value(p_object, mono_field, managed);
+ } break;
case Variant::NODE_PATH: {
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator NodePath());
mono_field_set_value(p_object, mono_field, managed);
@@ -424,8 +484,15 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
case Variant::OBJECT: {
MonoObject *managed = GDMonoUtils::unmanaged_get_managed(p_value.operator Object *());
mono_field_set_value(p_object, mono_field, managed);
- break;
- }
+ } break;
+ case Variant::CALLABLE: {
+ GDMonoMarshal::M_Callable val = GDMonoMarshal::callable_to_managed(p_value.operator Callable());
+ mono_field_set_value(p_object, mono_field, &val);
+ } break;
+ case Variant::SIGNAL: {
+ GDMonoMarshal::M_SignalInfo val = GDMonoMarshal::signal_info_to_managed(p_value.operator Signal());
+ mono_field_set_value(p_object, mono_field, &val);
+ } break;
case Variant::DICTIONARY: {
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary));
mono_field_set_value(p_object, mono_field, managed);
@@ -440,9 +507,15 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
case Variant::PACKED_INT32_ARRAY: {
SET_FROM_ARRAY(PackedInt32Array);
} break;
+ case Variant::PACKED_INT64_ARRAY: {
+ SET_FROM_ARRAY(PackedInt64Array);
+ } break;
case Variant::PACKED_FLOAT32_ARRAY: {
SET_FROM_ARRAY(PackedFloat32Array);
} break;
+ case Variant::PACKED_FLOAT64_ARRAY: {
+ SET_FROM_ARRAY(PackedFloat64Array);
+ } break;
case Variant::PACKED_STRING_ARRAY: {
SET_FROM_ARRAY(PackedStringArray);
} break;
@@ -550,19 +623,19 @@ bool GDMonoField::has_attribute(GDMonoClass *p_attr_class) {
}
MonoObject *GDMonoField::get_attribute(GDMonoClass *p_attr_class) {
- ERR_FAIL_NULL_V(p_attr_class, NULL);
+ ERR_FAIL_NULL_V(p_attr_class, nullptr);
if (!attrs_fetched)
fetch_attributes();
if (!attributes)
- return NULL;
+ return nullptr;
return mono_custom_attrs_get_attr(attributes, p_attr_class->get_mono_ptr());
}
void GDMonoField::fetch_attributes() {
- ERR_FAIL_COND(attributes != NULL);
+ ERR_FAIL_COND(attributes != nullptr);
attributes = mono_custom_attrs_from_field(owner->get_mono_ptr(), mono_field);
attrs_fetched = true;
}
@@ -598,7 +671,7 @@ GDMonoField::GDMonoField(MonoClassField *p_mono_field, GDMonoClass *p_owner) {
type.type_class = GDMono::get_singleton()->get_class(field_type_class);
attrs_fetched = false;
- attributes = NULL;
+ attributes = nullptr;
}
GDMonoField::~GDMonoField() {
diff --git a/modules/mono/mono_gd/gd_mono_field.h b/modules/mono/mono_gd/gd_mono_field.h
index 76ee0963c4..61f2c8f071 100644
--- a/modules/mono/mono_gd/gd_mono_field.h
+++ b/modules/mono/mono_gd/gd_mono_field.h
@@ -47,21 +47,22 @@ class GDMonoField : public IMonoClassMember {
MonoCustomAttrInfo *attributes;
public:
- virtual GDMonoClass *get_enclosing_class() const GD_FINAL { return owner; }
+ virtual GDMonoClass *get_enclosing_class() const final { return owner; }
- virtual MemberType get_member_type() const GD_FINAL { return MEMBER_TYPE_FIELD; }
+ virtual MemberType get_member_type() const final { return MEMBER_TYPE_FIELD; }
- virtual StringName get_name() const GD_FINAL { return name; }
+ virtual StringName get_name() const final { return name; }
- virtual bool is_static() GD_FINAL;
- virtual Visibility get_visibility() GD_FINAL;
+ virtual bool is_static() final;
+ virtual Visibility get_visibility() final;
- virtual bool has_attribute(GDMonoClass *p_attr_class) GD_FINAL;
- virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) GD_FINAL;
+ virtual bool has_attribute(GDMonoClass *p_attr_class) final;
+ virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) final;
void fetch_attributes();
_FORCE_INLINE_ ManagedType get_type() const { return type; }
+ void set_value(MonoObject *p_object, MonoObject *p_value);
void set_value_raw(MonoObject *p_object, void *p_ptr);
void set_value_from_variant(MonoObject *p_object, const Variant &p_value);
diff --git a/modules/mono/mono_gd/gd_mono_internals.cpp b/modules/mono/mono_gd/gd_mono_internals.cpp
index 74ffa90cb3..1898785699 100644
--- a/modules/mono/mono_gd/gd_mono_internals.cpp
+++ b/modules/mono/mono_gd/gd_mono_internals.cpp
@@ -33,11 +33,13 @@
#include "../csharp_script.h"
#include "../mono_gc_handle.h"
#include "../utils/macros.h"
-#include "../utils/thread_local.h"
#include "gd_mono_class.h"
#include "gd_mono_marshal.h"
#include "gd_mono_utils.h"
+#include "core/debugger/engine_debugger.h"
+#include "core/debugger/script_debugger.h"
+
#include <mono/metadata/exception.h>
namespace GDMonoInternals {
@@ -59,7 +61,7 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {
GDMonoClass *native = GDMonoUtils::get_class_native_base(klass);
- CRASH_COND(native == NULL);
+ CRASH_COND(native == nullptr);
if (native == klass) {
// If it's just a wrapper Godot class and not a custom inheriting class, then attach a
@@ -73,7 +75,7 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {
script_binding.inited = true;
script_binding.type_name = NATIVE_GDMONOCLASS_NAME(klass);
script_binding.wrapper_class = klass;
- script_binding.gchandle = ref ? MonoGCHandle::create_weak(managed) : MonoGCHandle::create_strong(managed);
+ script_binding.gchandle = ref ? MonoGCHandleData::new_weak_handle(managed) : MonoGCHandleData::new_strong_handle(managed);
script_binding.owner = unmanaged;
if (ref) {
@@ -99,15 +101,17 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {
return;
}
- Ref<MonoGCHandle> gchandle = ref ? MonoGCHandle::create_weak(managed) : MonoGCHandle::create_strong(managed);
+ MonoGCHandleData gchandle = ref ? MonoGCHandleData::new_weak_handle(managed) : MonoGCHandleData::new_strong_handle(managed);
Ref<CSharpScript> script = CSharpScript::create_for_managed_type(klass, native);
CRASH_COND(script.is_null());
- ScriptInstance *si = CSharpInstance::create_for_managed_type(unmanaged, script.ptr(), gchandle);
+ CSharpInstance *csharp_instance = CSharpInstance::create_for_managed_type(unmanaged, script.ptr(), gchandle);
+
+ unmanaged->set_script_and_instance(script, csharp_instance);
- unmanaged->set_script_and_instance(script, si);
+ csharp_instance->connect_event_signals();
}
void unhandled_exception(MonoException *p_exc) {
@@ -115,13 +119,13 @@ void unhandled_exception(MonoException *p_exc) {
if (GDMono::get_singleton()->get_unhandled_exception_policy() == GDMono::POLICY_TERMINATE_APP) {
// Too bad 'mono_invoke_unhandled_exception_hook' is not exposed to embedders
- GDMono::unhandled_exception_hook((MonoObject *)p_exc, NULL);
+ GDMono::unhandled_exception_hook((MonoObject *)p_exc, nullptr);
GD_UNREACHABLE();
} else {
#ifdef DEBUG_ENABLED
GDMonoUtils::debug_send_unhandled_exception_error((MonoException *)p_exc);
- if (ScriptDebugger::get_singleton())
- ScriptDebugger::get_singleton()->idle_poll();
+ if (EngineDebugger::is_active())
+ EngineDebugger::get_singleton()->poll_events(false);
#endif
}
}
diff --git a/modules/mono/mono_gd/gd_mono_log.cpp b/modules/mono/mono_gd/gd_mono_log.cpp
index 76828a66e0..ca16c2b76a 100644
--- a/modules/mono/mono_gd/gd_mono_log.cpp
+++ b/modules/mono/mono_gd/gd_mono_log.cpp
@@ -46,13 +46,13 @@ static CharString get_default_log_level() {
#endif
}
-GDMonoLog *GDMonoLog::singleton = NULL;
+GDMonoLog *GDMonoLog::singleton = nullptr;
-#if !defined(JAVASCRIPT_ENABLED)
+#ifdef GD_MONO_LOG_ENABLED
static int get_log_level_id(const char *p_log_level) {
- const char *valid_log_levels[] = { "error", "critical", "warning", "message", "info", "debug", NULL };
+ const char *valid_log_levels[] = { "error", "critical", "warning", "message", "info", "debug", nullptr };
int i = 0;
while (valid_log_levels[i]) {
@@ -191,7 +191,7 @@ GDMonoLog::GDMonoLog() {
GDMonoLog::~GDMonoLog() {
- singleton = NULL;
+ singleton = nullptr;
if (log_file) {
log_file->close();
@@ -213,7 +213,7 @@ GDMonoLog::GDMonoLog() {
GDMonoLog::~GDMonoLog() {
- singleton = NULL;
+ singleton = nullptr;
}
#endif // !defined(JAVASCRIPT_ENABLED)
diff --git a/modules/mono/mono_gd/gd_mono_log.h b/modules/mono/mono_gd/gd_mono_log.h
index ecf4c78b1a..1fc21f7df5 100644
--- a/modules/mono/mono_gd/gd_mono_log.h
+++ b/modules/mono/mono_gd/gd_mono_log.h
@@ -35,13 +35,18 @@
#include "core/typedefs.h"
-#if !defined(JAVASCRIPT_ENABLED)
+#if !defined(JAVASCRIPT_ENABLED) && !defined(IPHONE_ENABLED)
+// We have custom mono log callbacks for WASM and iOS
+#define GD_MONO_LOG_ENABLED
+#endif
+
+#ifdef GD_MONO_LOG_ENABLED
#include "core/os/file_access.h"
#endif
class GDMonoLog {
-#if !defined(JAVASCRIPT_ENABLED)
+#ifdef GD_MONO_LOG_ENABLED
int log_level_id;
FileAccess *log_file;
diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp
index 695be64d6e..1878038f44 100644
--- a/modules/mono/mono_gd/gd_mono_marshal.cpp
+++ b/modules/mono/mono_gd/gd_mono_marshal.cpp
@@ -30,13 +30,14 @@
#include "gd_mono_marshal.h"
+#include "../signal_awaiter_utils.h"
#include "gd_mono.h"
#include "gd_mono_cache.h"
#include "gd_mono_class.h"
namespace GDMonoMarshal {
-Variant::Type managed_to_variant_type(const ManagedType &p_type) {
+Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_variant) {
switch (p_type.type_encoding) {
case MONO_TYPE_BOOLEAN:
return Variant::BOOL;
@@ -74,15 +75,24 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
if (vtclass == CACHED_CLASS(Vector2))
return Variant::VECTOR2;
+ if (vtclass == CACHED_CLASS(Vector2i))
+ return Variant::VECTOR2I;
+
if (vtclass == CACHED_CLASS(Rect2))
return Variant::RECT2;
+ if (vtclass == CACHED_CLASS(Rect2i))
+ return Variant::RECT2I;
+
if (vtclass == CACHED_CLASS(Transform2D))
return Variant::TRANSFORM2D;
if (vtclass == CACHED_CLASS(Vector3))
return Variant::VECTOR3;
+ if (vtclass == CACHED_CLASS(Vector3i))
+ return Variant::VECTOR3I;
+
if (vtclass == CACHED_CLASS(Basis))
return Variant::BASIS;
@@ -101,6 +111,12 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
if (vtclass == CACHED_CLASS(Plane))
return Variant::PLANE;
+ if (vtclass == CACHED_CLASS(Callable))
+ return Variant::CALLABLE;
+
+ if (vtclass == CACHED_CLASS(SignalInfo))
+ return Variant::SIGNAL;
+
if (mono_class_is_enum(vtclass->get_mono_ptr()))
return Variant::INT;
} break;
@@ -118,9 +134,15 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
if (array_type->eklass == CACHED_CLASS_RAW(int32_t))
return Variant::PACKED_INT32_ARRAY;
- if (array_type->eklass == REAL_T_MONOCLASS)
+ if (array_type->eklass == CACHED_CLASS_RAW(int64_t))
+ return Variant::PACKED_INT64_ARRAY;
+
+ if (array_type->eklass == CACHED_CLASS_RAW(float))
return Variant::PACKED_FLOAT32_ARRAY;
+ if (array_type->eklass == CACHED_CLASS_RAW(double))
+ return Variant::PACKED_FLOAT64_ARRAY;
+
if (array_type->eklass == CACHED_CLASS_RAW(String))
return Variant::PACKED_STRING_ARRAY;
@@ -142,6 +164,10 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
return Variant::OBJECT;
}
+ if (CACHED_CLASS(StringName) == type_class) {
+ return Variant::STRING_NAME;
+ }
+
if (CACHED_CLASS(NodePath) == type_class) {
return Variant::NODE_PATH;
}
@@ -179,6 +205,12 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
}
} break;
+ case MONO_TYPE_OBJECT: {
+ if (r_nil_is_variant)
+ *r_nil_is_variant = true;
+ return Variant::NIL;
+ } break;
+
case MONO_TYPE_GENERICINST: {
MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type());
@@ -211,6 +243,9 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
} break;
}
+ if (r_nil_is_variant)
+ *r_nil_is_variant = false;
+
// Unknown
return Variant::NIL;
}
@@ -375,7 +410,7 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
case MONO_TYPE_STRING: {
if (p_var->get_type() == Variant::NIL)
- return NULL; // Otherwise, Variant -> String would return the string "Null"
+ return nullptr; // Otherwise, Variant -> String would return the string "Null"
return (MonoObject *)mono_string_from_godot(p_var->operator String());
} break;
@@ -387,11 +422,21 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector2), &from);
}
+ if (vtclass == CACHED_CLASS(Vector2i)) {
+ GDMonoMarshal::M_Vector2i from = MARSHALLED_OUT(Vector2i, p_var->operator ::Vector2i());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector2i), &from);
+ }
+
if (vtclass == CACHED_CLASS(Rect2)) {
GDMonoMarshal::M_Rect2 from = MARSHALLED_OUT(Rect2, p_var->operator ::Rect2());
return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Rect2), &from);
}
+ if (vtclass == CACHED_CLASS(Rect2i)) {
+ GDMonoMarshal::M_Rect2i from = MARSHALLED_OUT(Rect2i, p_var->operator ::Rect2i());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Rect2i), &from);
+ }
+
if (vtclass == CACHED_CLASS(Transform2D)) {
GDMonoMarshal::M_Transform2D from = MARSHALLED_OUT(Transform2D, p_var->operator ::Transform2D());
return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform2D), &from);
@@ -402,6 +447,11 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector3), &from);
}
+ if (vtclass == CACHED_CLASS(Vector3i)) {
+ GDMonoMarshal::M_Vector3i from = MARSHALLED_OUT(Vector3i, p_var->operator ::Vector3i());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector3i), &from);
+ }
+
if (vtclass == CACHED_CLASS(Basis)) {
GDMonoMarshal::M_Basis from = MARSHALLED_OUT(Basis, p_var->operator ::Basis());
return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Basis), &from);
@@ -432,6 +482,16 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Plane), &from);
}
+ if (vtclass == CACHED_CLASS(Callable)) {
+ GDMonoMarshal::M_Callable from = GDMonoMarshal::callable_to_managed(p_var->operator Callable());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Callable), &from);
+ }
+
+ if (vtclass == CACHED_CLASS(SignalInfo)) {
+ GDMonoMarshal::M_SignalInfo from = GDMonoMarshal::signal_info_to_managed(p_var->operator Signal());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(SignalInfo), &from);
+ }
+
if (mono_class_is_enum(vtclass->get_mono_ptr())) {
MonoType *enum_basetype = mono_class_enum_basetype(vtclass->get_mono_ptr());
MonoClass *enum_baseclass = mono_class_from_mono_type(enum_basetype);
@@ -477,7 +537,7 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
return BOX_ENUM(enum_baseclass, val);
}
default: {
- ERR_FAIL_V_MSG(NULL, "Attempted to convert Variant to a managed enum value of unmarshallable base type.");
+ ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to a managed enum value of unmarshallable base type.");
}
}
}
@@ -496,9 +556,15 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
if (array_type->eklass == CACHED_CLASS_RAW(int32_t))
return (MonoObject *)PackedInt32Array_to_mono_array(p_var->operator PackedInt32Array());
- if (array_type->eklass == REAL_T_MONOCLASS)
+ if (array_type->eklass == CACHED_CLASS_RAW(int64_t))
+ return (MonoObject *)PackedInt64Array_to_mono_array(p_var->operator PackedInt64Array());
+
+ if (array_type->eklass == CACHED_CLASS_RAW(float))
return (MonoObject *)PackedFloat32Array_to_mono_array(p_var->operator PackedFloat32Array());
+ if (array_type->eklass == CACHED_CLASS_RAW(double))
+ return (MonoObject *)PackedFloat64Array_to_mono_array(p_var->operator PackedFloat64Array());
+
if (array_type->eklass == CACHED_CLASS_RAW(String))
return (MonoObject *)PackedStringArray_to_mono_array(p_var->operator PackedStringArray());
@@ -511,7 +577,7 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
if (array_type->eklass == CACHED_CLASS_RAW(Color))
return (MonoObject *)PackedColorArray_to_mono_array(p_var->operator PackedColorArray());
- ERR_FAIL_V_MSG(NULL, "Attempted to convert Variant to a managed array of unmarshallable element type.");
+ ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to a managed array of unmarshallable element type.");
} break;
case MONO_TYPE_CLASS: {
@@ -522,6 +588,10 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
return GDMonoUtils::unmanaged_get_managed(p_var->operator Object *());
}
+ if (CACHED_CLASS(StringName) == type_class) {
+ return GDMonoUtils::create_managed_from(p_var->operator StringName());
+ }
+
if (CACHED_CLASS(NodePath) == type_class) {
return GDMonoUtils::create_managed_from(p_var->operator NodePath());
}
@@ -592,14 +662,26 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
GDMonoMarshal::M_Vector2 from = MARSHALLED_OUT(Vector2, p_var->operator ::Vector2());
return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector2), &from);
}
+ case Variant::VECTOR2I: {
+ GDMonoMarshal::M_Vector2i from = MARSHALLED_OUT(Vector2i, p_var->operator ::Vector2i());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector2i), &from);
+ }
case Variant::RECT2: {
GDMonoMarshal::M_Rect2 from = MARSHALLED_OUT(Rect2, p_var->operator ::Rect2());
return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Rect2), &from);
}
+ case Variant::RECT2I: {
+ GDMonoMarshal::M_Rect2i from = MARSHALLED_OUT(Rect2i, p_var->operator ::Rect2i());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Rect2i), &from);
+ }
case Variant::VECTOR3: {
GDMonoMarshal::M_Vector3 from = MARSHALLED_OUT(Vector3, p_var->operator ::Vector3());
return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector3), &from);
}
+ case Variant::VECTOR3I: {
+ GDMonoMarshal::M_Vector3i from = MARSHALLED_OUT(Vector3i, p_var->operator ::Vector3i());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector3i), &from);
+ }
case Variant::TRANSFORM2D: {
GDMonoMarshal::M_Transform2D from = MARSHALLED_OUT(Transform2D, p_var->operator ::Transform2D());
return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform2D), &from);
@@ -628,12 +710,22 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
GDMonoMarshal::M_Color from = MARSHALLED_OUT(Color, p_var->operator ::Color());
return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Color), &from);
}
+ case Variant::STRING_NAME:
+ return GDMonoUtils::create_managed_from(p_var->operator StringName());
case Variant::NODE_PATH:
return GDMonoUtils::create_managed_from(p_var->operator NodePath());
case Variant::_RID:
return GDMonoUtils::create_managed_from(p_var->operator RID());
case Variant::OBJECT:
return GDMonoUtils::unmanaged_get_managed(p_var->operator Object *());
+ case Variant::CALLABLE: {
+ GDMonoMarshal::M_Callable from = GDMonoMarshal::callable_to_managed(p_var->operator Callable());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Callable), &from);
+ }
+ case Variant::SIGNAL: {
+ GDMonoMarshal::M_SignalInfo from = GDMonoMarshal::signal_info_to_managed(p_var->operator Signal());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(SignalInfo), &from);
+ }
case Variant::DICTIONARY:
return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary));
case Variant::ARRAY:
@@ -642,8 +734,12 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
return (MonoObject *)PackedByteArray_to_mono_array(p_var->operator PackedByteArray());
case Variant::PACKED_INT32_ARRAY:
return (MonoObject *)PackedInt32Array_to_mono_array(p_var->operator PackedInt32Array());
+ case Variant::PACKED_INT64_ARRAY:
+ return (MonoObject *)PackedInt64Array_to_mono_array(p_var->operator PackedInt64Array());
case Variant::PACKED_FLOAT32_ARRAY:
return (MonoObject *)PackedFloat32Array_to_mono_array(p_var->operator PackedFloat32Array());
+ case Variant::PACKED_FLOAT64_ARRAY:
+ return (MonoObject *)PackedFloat64Array_to_mono_array(p_var->operator PackedFloat64Array());
case Variant::PACKED_STRING_ARRAY:
return (MonoObject *)PackedStringArray_to_mono_array(p_var->operator PackedStringArray());
case Variant::PACKED_VECTOR2_ARRAY:
@@ -653,7 +749,7 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
case Variant::PACKED_COLOR_ARRAY:
return (MonoObject *)PackedColorArray_to_mono_array(p_var->operator PackedColorArray());
default:
- return NULL;
+ return nullptr;
}
break;
case MONO_TYPE_GENERICINST: {
@@ -696,8 +792,8 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
} break;
}
- ERR_FAIL_V_MSG(NULL, "Attempted to convert Variant to an unmarshallable managed type. Name: '" +
- p_type.type_class->get_name() + "' Encoding: " + itos(p_type.type_encoding) + ".");
+ ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to an unmarshallable managed type. Name: '" +
+ p_type.type_class->get_name() + "' Encoding: " + itos(p_type.type_encoding) + ".");
}
Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type, bool p_fail_with_err = true) {
@@ -735,7 +831,7 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type
return unbox<double>(p_obj);
case MONO_TYPE_STRING: {
- if (p_obj == NULL)
+ if (p_obj == nullptr)
return Variant(); // NIL
return mono_string_to_godot_not_null((MonoString *)p_obj);
} break;
@@ -744,34 +840,49 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type
GDMonoClass *vtclass = p_type.type_class;
if (vtclass == CACHED_CLASS(Vector2))
- return MARSHALLED_IN(Vector2, (GDMonoMarshal::M_Vector2 *)mono_object_unbox(p_obj));
+ return MARSHALLED_IN(Vector2, unbox_addr<GDMonoMarshal::M_Vector2>(p_obj));
+
+ if (vtclass == CACHED_CLASS(Vector2i))
+ return MARSHALLED_IN(Vector2i, unbox_addr<GDMonoMarshal::M_Vector2i>(p_obj));
if (vtclass == CACHED_CLASS(Rect2))
- return MARSHALLED_IN(Rect2, (GDMonoMarshal::M_Rect2 *)mono_object_unbox(p_obj));
+ return MARSHALLED_IN(Rect2, unbox_addr<GDMonoMarshal::M_Rect2>(p_obj));
+
+ if (vtclass == CACHED_CLASS(Rect2i))
+ return MARSHALLED_IN(Rect2i, unbox_addr<GDMonoMarshal::M_Rect2i>(p_obj));
if (vtclass == CACHED_CLASS(Transform2D))
- return MARSHALLED_IN(Transform2D, (GDMonoMarshal::M_Transform2D *)mono_object_unbox(p_obj));
+ return MARSHALLED_IN(Transform2D, unbox_addr<GDMonoMarshal::M_Transform2D>(p_obj));
if (vtclass == CACHED_CLASS(Vector3))
- return MARSHALLED_IN(Vector3, (GDMonoMarshal::M_Vector3 *)mono_object_unbox(p_obj));
+ return MARSHALLED_IN(Vector3, unbox_addr<GDMonoMarshal::M_Vector3>(p_obj));
+
+ if (vtclass == CACHED_CLASS(Vector3i))
+ return MARSHALLED_IN(Vector3i, unbox_addr<GDMonoMarshal::M_Vector3i>(p_obj));
if (vtclass == CACHED_CLASS(Basis))
- return MARSHALLED_IN(Basis, (GDMonoMarshal::M_Basis *)mono_object_unbox(p_obj));
+ return MARSHALLED_IN(Basis, unbox_addr<GDMonoMarshal::M_Basis>(p_obj));
if (vtclass == CACHED_CLASS(Quat))
- return MARSHALLED_IN(Quat, (GDMonoMarshal::M_Quat *)mono_object_unbox(p_obj));
+ return MARSHALLED_IN(Quat, unbox_addr<GDMonoMarshal::M_Quat>(p_obj));
if (vtclass == CACHED_CLASS(Transform))
- return MARSHALLED_IN(Transform, (GDMonoMarshal::M_Transform *)mono_object_unbox(p_obj));
+ return MARSHALLED_IN(Transform, unbox_addr<GDMonoMarshal::M_Transform>(p_obj));
if (vtclass == CACHED_CLASS(AABB))
- return MARSHALLED_IN(AABB, (GDMonoMarshal::M_AABB *)mono_object_unbox(p_obj));
+ return MARSHALLED_IN(AABB, unbox_addr<GDMonoMarshal::M_AABB>(p_obj));
if (vtclass == CACHED_CLASS(Color))
- return MARSHALLED_IN(Color, (GDMonoMarshal::M_Color *)mono_object_unbox(p_obj));
+ return MARSHALLED_IN(Color, unbox_addr<GDMonoMarshal::M_Color>(p_obj));
if (vtclass == CACHED_CLASS(Plane))
- return MARSHALLED_IN(Plane, (GDMonoMarshal::M_Plane *)mono_object_unbox(p_obj));
+ return MARSHALLED_IN(Plane, unbox_addr<GDMonoMarshal::M_Plane>(p_obj));
+
+ if (vtclass == CACHED_CLASS(Callable))
+ return managed_to_callable(unbox<GDMonoMarshal::M_Callable>(p_obj));
+
+ if (vtclass == CACHED_CLASS(SignalInfo))
+ return managed_to_signal_info(unbox<GDMonoMarshal::M_SignalInfo>(p_obj));
if (mono_class_is_enum(vtclass->get_mono_ptr()))
return unbox<int32_t>(p_obj);
@@ -790,9 +901,15 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type
if (array_type->eklass == CACHED_CLASS_RAW(int32_t))
return mono_array_to_PackedInt32Array((MonoArray *)p_obj);
- if (array_type->eklass == REAL_T_MONOCLASS)
+ if (array_type->eklass == CACHED_CLASS_RAW(int64_t))
+ return mono_array_to_PackedInt64Array((MonoArray *)p_obj);
+
+ if (array_type->eklass == CACHED_CLASS_RAW(float))
return mono_array_to_PackedFloat32Array((MonoArray *)p_obj);
+ if (array_type->eklass == CACHED_CLASS_RAW(double))
+ return mono_array_to_PackedFloat64Array((MonoArray *)p_obj);
+
if (array_type->eklass == CACHED_CLASS_RAW(String))
return mono_array_to_PackedStringArray((MonoArray *)p_obj);
@@ -818,13 +935,18 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type
// GodotObject
if (CACHED_CLASS(GodotObject)->is_assignable_from(type_class)) {
Object *ptr = unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(p_obj));
- if (ptr != NULL) {
+ if (ptr != nullptr) {
Reference *ref = Object::cast_to<Reference>(ptr);
return ref ? Variant(Ref<Reference>(ref)) : Variant(ptr);
}
return Variant();
}
+ if (CACHED_CLASS(StringName) == type_class) {
+ StringName *ptr = unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(p_obj));
+ return ptr ? Variant(*ptr) : Variant();
+ }
+
if (CACHED_CLASS(NodePath) == type_class) {
NodePath *ptr = unbox<NodePath *>(CACHED_FIELD(NodePath, ptr)->get_value(p_obj));
return ptr ? Variant(*ptr) : Variant();
@@ -836,14 +958,14 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type
}
if (CACHED_CLASS(Array) == type_class) {
- MonoException *exc = NULL;
+ MonoException *exc = nullptr;
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;
+ MonoException *exc = nullptr;
Dictionary *ptr = CACHED_METHOD_THUNK(Dictionary, GetPtr).invoke(p_obj, &exc);
UNHANDLED_EXCEPTION(exc);
return ptr ? Variant(*ptr) : Variant();
@@ -874,14 +996,14 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type
MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type());
if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
- MonoException *exc = NULL;
+ MonoException *exc = nullptr;
MonoObject *ret = p_type.type_class->get_method("GetPtr")->invoke(p_obj, &exc);
UNHANDLED_EXCEPTION(exc);
return *unbox<Dictionary *>(ret);
}
if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) {
- MonoException *exc = NULL;
+ MonoException *exc = nullptr;
MonoObject *ret = p_type.type_class->get_method("GetPtr")->invoke(p_obj, &exc);
UNHANDLED_EXCEPTION(exc);
return *unbox<Array *>(ret);
@@ -942,9 +1064,9 @@ String mono_object_to_variant_string(MonoObject *p_obj, MonoException **r_exc) {
ManagedType type = ManagedType::from_class(mono_object_get_class(p_obj));
Variant var = GDMonoMarshal::mono_object_to_variant_no_err(p_obj, type);
- if (var.get_type() == Variant::NIL && p_obj != NULL) {
+ if (var.get_type() == Variant::NIL && p_obj != nullptr) {
// Cannot convert MonoObject* to Variant; fallback to 'ToString()'.
- MonoException *exc = NULL;
+ MonoException *exc = nullptr;
MonoString *mono_str = GDMonoUtils::object_to_string(p_obj, &exc);
if (exc) {
@@ -960,9 +1082,10 @@ String mono_object_to_variant_string(MonoObject *p_obj, MonoException **r_exc) {
}
MonoArray *Array_to_mono_array(const Array &p_array) {
- MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), p_array.size());
+ int length = p_array.size();
+ MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), length);
- for (int i = 0; i < p_array.size(); i++) {
+ for (int i = 0; i < length; i++) {
MonoObject *boxed = variant_to_mono_object(p_array[i]);
mono_array_setref(ret, i, boxed);
}
@@ -985,16 +1108,14 @@ Array mono_array_to_Array(MonoArray *p_array) {
return ret;
}
-// TODO: Use memcpy where possible
-
MonoArray *PackedInt32Array_to_mono_array(const PackedInt32Array &p_array) {
- const int *r = p_array.ptr();
+ const int32_t *src = p_array.ptr();
+ int length = p_array.size();
- MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(int32_t), p_array.size());
+ MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(int32_t), length);
- for (int i = 0; i < p_array.size(); i++) {
- mono_array_set(ret, int32_t, i, r[i]);
- }
+ int32_t *dst = (int32_t *)mono_array_addr(ret, int32_t, 0);
+ memcpy(dst, src, length);
return ret;
}
@@ -1005,23 +1126,48 @@ PackedInt32Array mono_array_to_PackedInt32Array(MonoArray *p_array) {
return ret;
int length = mono_array_length(p_array);
ret.resize(length);
- int *w = ret.ptrw();
+ int32_t *dst = ret.ptrw();
- for (int i = 0; i < length; i++) {
- w[i] = mono_array_get(p_array, int32_t, i);
- }
+ const int32_t *src = (const int32_t *)mono_array_addr(p_array, int32_t, 0);
+ memcpy(dst, src, length);
+
+ return ret;
+}
+
+MonoArray *PackedInt64Array_to_mono_array(const PackedInt64Array &p_array) {
+ const int64_t *src = p_array.ptr();
+ int length = p_array.size();
+
+ MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(int64_t), length);
+
+ int64_t *dst = (int64_t *)mono_array_addr(ret, int64_t, 0);
+ memcpy(dst, src, length);
+
+ return ret;
+}
+
+PackedInt64Array mono_array_to_PackedInt64Array(MonoArray *p_array) {
+ PackedInt64Array ret;
+ if (!p_array)
+ return ret;
+ int length = mono_array_length(p_array);
+ ret.resize(length);
+ int64_t *dst = ret.ptrw();
+
+ const int64_t *src = (const int64_t *)mono_array_addr(p_array, int64_t, 0);
+ memcpy(dst, src, length);
return ret;
}
MonoArray *PackedByteArray_to_mono_array(const PackedByteArray &p_array) {
- const uint8_t *r = p_array.ptr();
+ const uint8_t *src = p_array.ptr();
+ int length = p_array.size();
- MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(uint8_t), p_array.size());
+ MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(uint8_t), length);
- for (int i = 0; i < p_array.size(); i++) {
- mono_array_set(ret, uint8_t, i, r[i]);
- }
+ uint8_t *dst = (uint8_t *)mono_array_addr(ret, uint8_t, 0);
+ memcpy(dst, src, length);
return ret;
}
@@ -1032,23 +1178,22 @@ PackedByteArray mono_array_to_PackedByteArray(MonoArray *p_array) {
return ret;
int length = mono_array_length(p_array);
ret.resize(length);
- uint8_t *w = ret.ptrw();
+ uint8_t *dst = ret.ptrw();
- for (int i = 0; i < length; i++) {
- w[i] = mono_array_get(p_array, uint8_t, i);
- }
+ const uint8_t *src = (const uint8_t *)mono_array_addr(p_array, uint8_t, 0);
+ memcpy(dst, src, length);
return ret;
}
MonoArray *PackedFloat32Array_to_mono_array(const PackedFloat32Array &p_array) {
- const real_t *r = p_array.ptr();
+ const float *src = p_array.ptr();
+ int length = p_array.size();
- MonoArray *ret = mono_array_new(mono_domain_get(), REAL_T_MONOCLASS, p_array.size());
+ MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(float), length);
- for (int i = 0; i < p_array.size(); i++) {
- mono_array_set(ret, real_t, i, r[i]);
- }
+ float *dst = (float *)mono_array_addr(ret, float, 0);
+ memcpy(dst, src, length);
return ret;
}
@@ -1059,21 +1204,47 @@ PackedFloat32Array mono_array_to_PackedFloat32Array(MonoArray *p_array) {
return ret;
int length = mono_array_length(p_array);
ret.resize(length);
- real_t *w = ret.ptrw();
+ float *dst = ret.ptrw();
- for (int i = 0; i < length; i++) {
- w[i] = mono_array_get(p_array, real_t, i);
- }
+ const float *src = (const float *)mono_array_addr(p_array, float, 0);
+ memcpy(dst, src, length);
+
+ return ret;
+}
+
+MonoArray *PackedFloat64Array_to_mono_array(const PackedFloat64Array &p_array) {
+ const double *src = p_array.ptr();
+ int length = p_array.size();
+
+ MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(double), length);
+
+ double *dst = (double *)mono_array_addr(ret, double, 0);
+ memcpy(dst, src, length);
+
+ return ret;
+}
+
+PackedFloat64Array mono_array_to_PackedFloat64Array(MonoArray *p_array) {
+ PackedFloat64Array ret;
+ if (!p_array)
+ return ret;
+ int length = mono_array_length(p_array);
+ ret.resize(length);
+ double *dst = ret.ptrw();
+
+ const double *src = (const double *)mono_array_addr(p_array, double, 0);
+ memcpy(dst, src, length);
return ret;
}
MonoArray *PackedStringArray_to_mono_array(const PackedStringArray &p_array) {
const String *r = p_array.ptr();
+ int length = p_array.size();
- MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(String), p_array.size());
+ MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(String), length);
- for (int i = 0; i < p_array.size(); i++) {
+ for (int i = 0; i < length; i++) {
MonoString *boxed = mono_string_from_godot(r[i]);
mono_array_setref(ret, i, boxed);
}
@@ -1098,13 +1269,19 @@ PackedStringArray mono_array_to_PackedStringArray(MonoArray *p_array) {
}
MonoArray *PackedColorArray_to_mono_array(const PackedColorArray &p_array) {
- const Color *r = p_array.ptr();
+ const Color *src = p_array.ptr();
+ int length = p_array.size();
- MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Color), p_array.size());
+ MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Color), length);
- for (int i = 0; i < p_array.size(); i++) {
- M_Color *raw = (M_Color *)mono_array_addr_with_size(ret, sizeof(M_Color), i);
- *raw = MARSHALLED_OUT(Color, r[i]);
+ if constexpr (InteropLayout::MATCHES_Color) {
+ Color *dst = (Color *)mono_array_addr(ret, Color, 0);
+ memcpy(dst, src, length);
+ } else {
+ for (int i = 0; i < length; i++) {
+ M_Color *raw = (M_Color *)mono_array_addr_with_size(ret, sizeof(M_Color), i);
+ *raw = MARSHALLED_OUT(Color, src[i]);
+ }
}
return ret;
@@ -1116,23 +1293,34 @@ PackedColorArray mono_array_to_PackedColorArray(MonoArray *p_array) {
return ret;
int length = mono_array_length(p_array);
ret.resize(length);
- Color *w = ret.ptrw();
+ Color *dst = ret.ptrw();
- for (int i = 0; i < length; i++) {
- w[i] = MARSHALLED_IN(Color, (M_Color *)mono_array_addr_with_size(p_array, sizeof(M_Color), i));
+ if constexpr (InteropLayout::MATCHES_Color) {
+ const Color *src = (const Color *)mono_array_addr(p_array, Color, 0);
+ memcpy(dst, src, length);
+ } else {
+ for (int i = 0; i < length; i++) {
+ dst[i] = MARSHALLED_IN(Color, (M_Color *)mono_array_addr_with_size(p_array, sizeof(M_Color), i));
+ }
}
return ret;
}
MonoArray *PackedVector2Array_to_mono_array(const PackedVector2Array &p_array) {
- const Vector2 *r = p_array.ptr();
+ const Vector2 *src = p_array.ptr();
+ int length = p_array.size();
- MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Vector2), p_array.size());
+ MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Vector2), length);
- for (int i = 0; i < p_array.size(); i++) {
- M_Vector2 *raw = (M_Vector2 *)mono_array_addr_with_size(ret, sizeof(M_Vector2), i);
- *raw = MARSHALLED_OUT(Vector2, r[i]);
+ if constexpr (InteropLayout::MATCHES_Vector2) {
+ Vector2 *dst = (Vector2 *)mono_array_addr(ret, Vector2, 0);
+ memcpy(dst, src, length);
+ } else {
+ for (int i = 0; i < length; i++) {
+ M_Vector2 *raw = (M_Vector2 *)mono_array_addr_with_size(ret, sizeof(M_Vector2), i);
+ *raw = MARSHALLED_OUT(Vector2, src[i]);
+ }
}
return ret;
@@ -1144,23 +1332,34 @@ PackedVector2Array mono_array_to_PackedVector2Array(MonoArray *p_array) {
return ret;
int length = mono_array_length(p_array);
ret.resize(length);
- Vector2 *w = ret.ptrw();
+ Vector2 *dst = ret.ptrw();
- for (int i = 0; i < length; i++) {
- w[i] = MARSHALLED_IN(Vector2, (M_Vector2 *)mono_array_addr_with_size(p_array, sizeof(M_Vector2), i));
+ if constexpr (InteropLayout::MATCHES_Vector2) {
+ const Vector2 *src = (const Vector2 *)mono_array_addr(p_array, Vector2, 0);
+ memcpy(dst, src, length);
+ } else {
+ for (int i = 0; i < length; i++) {
+ dst[i] = MARSHALLED_IN(Vector2, (M_Vector2 *)mono_array_addr_with_size(p_array, sizeof(M_Vector2), i));
+ }
}
return ret;
}
MonoArray *PackedVector3Array_to_mono_array(const PackedVector3Array &p_array) {
- const Vector3 *r = p_array.ptr();
+ const Vector3 *src = p_array.ptr();
+ int length = p_array.size();
- MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Vector3), p_array.size());
+ MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Vector3), length);
- for (int i = 0; i < p_array.size(); i++) {
- M_Vector3 *raw = (M_Vector3 *)mono_array_addr_with_size(ret, sizeof(M_Vector3), i);
- *raw = MARSHALLED_OUT(Vector3, r[i]);
+ if constexpr (InteropLayout::MATCHES_Vector3) {
+ Vector3 *dst = (Vector3 *)mono_array_addr(ret, Vector3, 0);
+ memcpy(dst, src, length);
+ } else {
+ for (int i = 0; i < length; i++) {
+ M_Vector3 *raw = (M_Vector3 *)mono_array_addr_with_size(ret, sizeof(M_Vector3), i);
+ *raw = MARSHALLED_OUT(Vector3, src[i]);
+ }
}
return ret;
@@ -1172,13 +1371,85 @@ PackedVector3Array mono_array_to_PackedVector3Array(MonoArray *p_array) {
return ret;
int length = mono_array_length(p_array);
ret.resize(length);
- Vector3 *w = ret.ptrw();
+ Vector3 *dst = ret.ptrw();
- for (int i = 0; i < length; i++) {
- w[i] = MARSHALLED_IN(Vector3, (M_Vector3 *)mono_array_addr_with_size(p_array, sizeof(M_Vector3), i));
+ if constexpr (InteropLayout::MATCHES_Vector3) {
+ const Vector3 *src = (const Vector3 *)mono_array_addr(p_array, Vector3, 0);
+ memcpy(dst, src, length);
+ } else {
+ for (int i = 0; i < length; i++) {
+ dst[i] = MARSHALLED_IN(Vector3, (M_Vector3 *)mono_array_addr_with_size(p_array, sizeof(M_Vector3), i));
+ }
}
return ret;
}
+Callable managed_to_callable(const M_Callable &p_managed_callable) {
+ if (p_managed_callable.delegate) {
+ // TODO: Use pooling for ManagedCallable instances.
+ CallableCustom *managed_callable = memnew(ManagedCallable(p_managed_callable.delegate));
+ return Callable(managed_callable);
+ } else {
+ Object *target = p_managed_callable.target ?
+ unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(p_managed_callable.target)) :
+ nullptr;
+ StringName *method_ptr = unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(p_managed_callable.method_string_name));
+ StringName method = method_ptr ? *method_ptr : StringName();
+ return Callable(target, method);
+ }
+}
+
+M_Callable callable_to_managed(const Callable &p_callable) {
+ if (p_callable.is_custom()) {
+ CallableCustom *custom = p_callable.get_custom();
+ CallableCustom::CompareEqualFunc compare_equal_func = custom->get_compare_equal_func();
+
+ if (compare_equal_func == ManagedCallable::compare_equal_func_ptr) {
+ ManagedCallable *managed_callable = static_cast<ManagedCallable *>(custom);
+ return {
+ nullptr, nullptr,
+ managed_callable->get_delegate()
+ };
+ } else if (compare_equal_func == SignalAwaiterCallable::compare_equal_func_ptr) {
+ SignalAwaiterCallable *signal_awaiter_callable = static_cast<SignalAwaiterCallable *>(custom);
+ return {
+ GDMonoUtils::unmanaged_get_managed(ObjectDB::get_instance(signal_awaiter_callable->get_object())),
+ GDMonoUtils::create_managed_from(signal_awaiter_callable->get_signal()),
+ nullptr
+ };
+ } else if (compare_equal_func == EventSignalCallable::compare_equal_func_ptr) {
+ EventSignalCallable *event_signal_callable = static_cast<EventSignalCallable *>(custom);
+ return {
+ GDMonoUtils::unmanaged_get_managed(ObjectDB::get_instance(event_signal_callable->get_object())),
+ GDMonoUtils::create_managed_from(event_signal_callable->get_signal()),
+ nullptr
+ };
+ }
+
+ // Some other CallableCustom. We only support ManagedCallable.
+ return { nullptr, nullptr, nullptr };
+ } else {
+ MonoObject *target_managed = GDMonoUtils::unmanaged_get_managed(p_callable.get_object());
+ MonoObject *method_string_name_managed = GDMonoUtils::create_managed_from(p_callable.get_method());
+ return { target_managed, method_string_name_managed, nullptr };
+ }
+}
+
+Signal managed_to_signal_info(const M_SignalInfo &p_managed_signal) {
+ Object *owner = p_managed_signal.owner ?
+ unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(p_managed_signal.owner)) :
+ nullptr;
+ StringName *name_ptr = unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(p_managed_signal.name_string_name));
+ StringName name = name_ptr ? *name_ptr : StringName();
+ return Signal(owner, name);
+}
+
+M_SignalInfo signal_info_to_managed(const Signal &p_signal) {
+ Object *owner = p_signal.get_object();
+ MonoObject *owner_managed = GDMonoUtils::unmanaged_get_managed(owner);
+ MonoObject *name_string_name_managed = GDMonoUtils::create_managed_from(p_signal.get_name());
+ return { owner_managed, name_string_name_managed };
+}
+
} // namespace GDMonoMarshal
diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h
index 5db59522ce..8b405291a7 100644
--- a/modules/mono/mono_gd/gd_mono_marshal.h
+++ b/modules/mono/mono_gd/gd_mono_marshal.h
@@ -33,6 +33,7 @@
#include "core/variant.h"
+#include "../managed_callable.h"
#include "gd_mono.h"
#include "gd_mono_utils.h"
@@ -62,7 +63,7 @@ T *unbox_addr(MonoObject *p_obj) {
#define BOX_PTR(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(IntPtr), x)
#define BOX_ENUM(m_enum_class, x) mono_value_box(mono_domain_get(), m_enum_class, &x)
-Variant::Type managed_to_variant_type(const ManagedType &p_type);
+Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_variant = nullptr);
bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_elem_type);
bool try_get_dictionary_key_value_types(const ManagedType &p_dictionary_type, ManagedType &r_key_type, ManagedType &r_value_type);
@@ -80,7 +81,7 @@ _FORCE_INLINE_ String mono_string_to_godot_not_null(MonoString *p_mono_string) {
}
_FORCE_INLINE_ String mono_string_to_godot(MonoString *p_mono_string) {
- if (p_mono_string == NULL)
+ if (p_mono_string == nullptr)
return String();
return mono_string_to_godot_not_null(p_mono_string);
@@ -132,6 +133,11 @@ Array mono_array_to_Array(MonoArray *p_array);
MonoArray *PackedInt32Array_to_mono_array(const PackedInt32Array &p_array);
PackedInt32Array mono_array_to_PackedInt32Array(MonoArray *p_array);
+// PackedInt64Array
+
+MonoArray *PackedInt64Array_to_mono_array(const PackedInt64Array &p_array);
+PackedInt64Array mono_array_to_PackedInt64Array(MonoArray *p_array);
+
// PackedByteArray
MonoArray *PackedByteArray_to_mono_array(const PackedByteArray &p_array);
@@ -142,6 +148,11 @@ PackedByteArray mono_array_to_PackedByteArray(MonoArray *p_array);
MonoArray *PackedFloat32Array_to_mono_array(const PackedFloat32Array &p_array);
PackedFloat32Array mono_array_to_PackedFloat32Array(MonoArray *p_array);
+// PackedFloat64Array
+
+MonoArray *PackedFloat64Array_to_mono_array(const PackedFloat64Array &p_array);
+PackedFloat64Array mono_array_to_PackedFloat64Array(MonoArray *p_array);
+
// PackedStringArray
MonoArray *PackedStringArray_to_mono_array(const PackedStringArray &p_array);
@@ -162,11 +173,36 @@ PackedVector2Array mono_array_to_PackedVector2Array(MonoArray *p_array);
MonoArray *PackedVector3Array_to_mono_array(const PackedVector3Array &p_array);
PackedVector3Array mono_array_to_PackedVector3Array(MonoArray *p_array);
+#pragma pack(push, 1)
+
+struct M_Callable {
+ MonoObject *target;
+ MonoObject *method_string_name;
+ MonoDelegate *delegate;
+};
+
+struct M_SignalInfo {
+ MonoObject *owner;
+ MonoObject *name_string_name;
+};
+
+#pragma pack(pop)
+
+// Callable
+Callable managed_to_callable(const M_Callable &p_managed_callable);
+M_Callable callable_to_managed(const Callable &p_callable);
+
+// SignalInfo
+Signal managed_to_signal_info(const M_SignalInfo &p_managed_signal);
+M_SignalInfo signal_info_to_managed(const Signal &p_signal);
+
// Structures
namespace InteropLayout {
enum {
+ MATCHES_int = (sizeof(int32_t) == sizeof(uint32_t)),
+
MATCHES_float = (sizeof(float) == sizeof(uint32_t)),
MATCHES_double = (sizeof(double) == sizeof(uint64_t)),
@@ -181,10 +217,18 @@ enum {
offsetof(Vector2, x) == (sizeof(real_t) * 0) &&
offsetof(Vector2, y) == (sizeof(real_t) * 1)),
+ MATCHES_Vector2i = (MATCHES_int && (sizeof(Vector2i) == (sizeof(int32_t) * 2)) &&
+ offsetof(Vector2i, x) == (sizeof(int32_t) * 0) &&
+ offsetof(Vector2i, y) == (sizeof(int32_t) * 1)),
+
MATCHES_Rect2 = (MATCHES_Vector2 && (sizeof(Rect2) == (sizeof(Vector2) * 2)) &&
offsetof(Rect2, position) == (sizeof(Vector2) * 0) &&
offsetof(Rect2, size) == (sizeof(Vector2) * 1)),
+ MATCHES_Rect2i = (MATCHES_Vector2i && (sizeof(Rect2i) == (sizeof(Vector2i) * 2)) &&
+ offsetof(Rect2i, position) == (sizeof(Vector2i) * 0) &&
+ offsetof(Rect2i, size) == (sizeof(Vector2i) * 1)),
+
MATCHES_Transform2D = (MATCHES_Vector2 && (sizeof(Transform2D) == (sizeof(Vector2) * 3))), // No field offset required, it stores an array
MATCHES_Vector3 = (MATCHES_real_t && (sizeof(Vector3) == (sizeof(real_t) * 3)) &&
@@ -192,6 +236,11 @@ enum {
offsetof(Vector3, y) == (sizeof(real_t) * 1) &&
offsetof(Vector3, z) == (sizeof(real_t) * 2)),
+ MATCHES_Vector3i = (MATCHES_int && (sizeof(Vector3i) == (sizeof(int32_t) * 3)) &&
+ offsetof(Vector3i, x) == (sizeof(int32_t) * 0) &&
+ offsetof(Vector3i, y) == (sizeof(int32_t) * 1) &&
+ offsetof(Vector3i, z) == (sizeof(int32_t) * 2)),
+
MATCHES_Basis = (MATCHES_Vector3 && (sizeof(Basis) == (sizeof(Vector3) * 3))), // No field offset required, it stores an array
MATCHES_Quat = (MATCHES_real_t && (sizeof(Quat) == (sizeof(real_t) * 4)) &&
@@ -222,8 +271,9 @@ enum {
// In the future we may force this if we want to ref return these structs
#ifdef GD_MONO_FORCE_INTEROP_STRUCT_COPY
/* clang-format off */
-GD_STATIC_ASSERT(MATCHES_Vector2 && MATCHES_Rect2 && MATCHES_Transform2D && MATCHES_Vector3 &&
- MATCHES_Basis && MATCHES_Quat && MATCHES_Transform && MATCHES_AABB && MATCHES_Color &&MATCHES_Plane);
+static_assert(MATCHES_Vector2 && MATCHES_Rect2 && MATCHES_Transform2D && MATCHES_Vector3 &&
+ MATCHES_Basis && MATCHES_Quat && MATCHES_Transform && MATCHES_AABB && MATCHES_Color &&
+ MATCHES_Plane && MATCHES_Vector2i && MATCHES_Rect2i && MATCHES_Vector3i);
/* clang-format on */
#endif
@@ -244,6 +294,19 @@ struct M_Vector2 {
}
};
+struct M_Vector2i {
+ int32_t x, y;
+
+ static _FORCE_INLINE_ Vector2i convert_to(const M_Vector2i &p_from) {
+ return Vector2i(p_from.x, p_from.y);
+ }
+
+ static _FORCE_INLINE_ M_Vector2i convert_from(const Vector2i &p_from) {
+ M_Vector2i ret = { p_from.x, p_from.y };
+ return ret;
+ }
+};
+
struct M_Rect2 {
M_Vector2 position;
M_Vector2 size;
@@ -259,6 +322,21 @@ struct M_Rect2 {
}
};
+struct M_Rect2i {
+ M_Vector2i position;
+ M_Vector2i size;
+
+ static _FORCE_INLINE_ Rect2i convert_to(const M_Rect2i &p_from) {
+ return Rect2i(M_Vector2i::convert_to(p_from.position),
+ M_Vector2i::convert_to(p_from.size));
+ }
+
+ static _FORCE_INLINE_ M_Rect2i convert_from(const Rect2i &p_from) {
+ M_Rect2i ret = { M_Vector2i::convert_from(p_from.position), M_Vector2i::convert_from(p_from.size) };
+ return ret;
+ }
+};
+
struct M_Transform2D {
M_Vector2 elements[3];
@@ -291,6 +369,19 @@ struct M_Vector3 {
}
};
+struct M_Vector3i {
+ int32_t x, y, z;
+
+ static _FORCE_INLINE_ Vector3i convert_to(const M_Vector3i &p_from) {
+ return Vector3i(p_from.x, p_from.y, p_from.z);
+ }
+
+ static _FORCE_INLINE_ M_Vector3i convert_from(const Vector3i &p_from) {
+ M_Vector3i ret = { p_from.x, p_from.y, p_from.z };
+ return ret;
+ }
+};
+
struct M_Basis {
M_Vector3 elements[3];
@@ -416,9 +507,12 @@ struct M_Plane {
}
DECL_TYPE_MARSHAL_TEMPLATES(Vector2)
+DECL_TYPE_MARSHAL_TEMPLATES(Vector2i)
DECL_TYPE_MARSHAL_TEMPLATES(Rect2)
+DECL_TYPE_MARSHAL_TEMPLATES(Rect2i)
DECL_TYPE_MARSHAL_TEMPLATES(Transform2D)
DECL_TYPE_MARSHAL_TEMPLATES(Vector3)
+DECL_TYPE_MARSHAL_TEMPLATES(Vector3i)
DECL_TYPE_MARSHAL_TEMPLATES(Basis)
DECL_TYPE_MARSHAL_TEMPLATES(Quat)
DECL_TYPE_MARSHAL_TEMPLATES(Transform)
diff --git a/modules/mono/mono_gd/gd_mono_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp
index 971c5ac737..c8cc5247c6 100644
--- a/modules/mono/mono_gd/gd_mono_method.cpp
+++ b/modules/mono/mono_gd/gd_mono_method.cpp
@@ -58,9 +58,9 @@ void GDMonoMethod::_update_signature(MonoMethodSignature *p_method_sig) {
}
}
- void *iter = NULL;
+ void *iter = nullptr;
MonoType *param_raw_type;
- while ((param_raw_type = mono_signature_get_params(p_method_sig, &iter)) != NULL) {
+ while ((param_raw_type = mono_signature_get_params(p_method_sig, &iter)) != nullptr) {
ManagedType param_type;
param_type.type_encoding = mono_type_get_type(param_raw_type);
@@ -81,11 +81,11 @@ GDMonoClass *GDMonoMethod::get_enclosing_class() const {
}
bool GDMonoMethod::is_static() {
- return mono_method_get_flags(mono_method, NULL) & MONO_METHOD_ATTR_STATIC;
+ return mono_method_get_flags(mono_method, nullptr) & MONO_METHOD_ATTR_STATIC;
}
IMonoClassMember::Visibility GDMonoMethod::get_visibility() {
- switch (mono_method_get_flags(mono_method, NULL) & MONO_METHOD_ATTR_ACCESS_MASK) {
+ switch (mono_method_get_flags(mono_method, nullptr) & MONO_METHOD_ATTR_ACCESS_MASK) {
case MONO_METHOD_ATTR_PRIVATE:
return IMonoClassMember::PRIVATE;
case MONO_METHOD_ATTR_FAM_AND_ASSEM:
@@ -101,55 +101,46 @@ IMonoClassMember::Visibility GDMonoMethod::get_visibility() {
}
}
-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());
+MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc) const {
+ MonoException *exc = nullptr;
+ MonoObject *ret;
+
+ if (params_count > 0) {
+ MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), params_count);
for (int i = 0; i < params_count; i++) {
MonoObject *boxed_param = GDMonoMarshal::variant_to_mono_object(p_params[i], param_types[i]);
mono_array_setref(params, i, boxed_param);
}
- MonoException *exc = NULL;
- MonoObject *ret = GDMonoUtils::runtime_invoke_array(mono_method, p_object, params, &exc);
-
- if (exc) {
- ret = NULL;
- if (r_exc) {
- *r_exc = exc;
- } else {
- GDMonoUtils::set_pending_exception(exc);
- }
- }
-
- return ret;
+ ret = GDMonoUtils::runtime_invoke_array(mono_method, p_object, params, &exc);
} else {
- MonoException *exc = NULL;
- GDMonoUtils::runtime_invoke(mono_method, p_object, NULL, &exc);
-
- if (exc) {
- if (r_exc) {
- *r_exc = exc;
- } else {
- GDMonoUtils::set_pending_exception(exc);
- }
- }
+ ret = GDMonoUtils::runtime_invoke(mono_method, p_object, nullptr, &exc);
+ }
- return NULL;
+ if (exc) {
+ ret = nullptr;
+ if (r_exc) {
+ *r_exc = exc;
+ } else {
+ GDMonoUtils::set_pending_exception(exc);
+ }
}
+
+ return ret;
}
-MonoObject *GDMonoMethod::invoke(MonoObject *p_object, MonoException **r_exc) {
- ERR_FAIL_COND_V(get_parameters_count() > 0, NULL);
- return invoke_raw(p_object, NULL, r_exc);
+MonoObject *GDMonoMethod::invoke(MonoObject *p_object, MonoException **r_exc) const {
+ ERR_FAIL_COND_V(get_parameters_count() > 0, nullptr);
+ return invoke_raw(p_object, nullptr, r_exc);
}
-MonoObject *GDMonoMethod::invoke_raw(MonoObject *p_object, void **p_params, MonoException **r_exc) {
- MonoException *exc = NULL;
+MonoObject *GDMonoMethod::invoke_raw(MonoObject *p_object, void **p_params, MonoException **r_exc) const {
+ MonoException *exc = nullptr;
MonoObject *ret = GDMonoUtils::runtime_invoke(mono_method, p_object, p_params, &exc);
if (exc) {
- ret = NULL;
+ ret = nullptr;
if (r_exc) {
*r_exc = exc;
} else {
@@ -173,19 +164,19 @@ bool GDMonoMethod::has_attribute(GDMonoClass *p_attr_class) {
}
MonoObject *GDMonoMethod::get_attribute(GDMonoClass *p_attr_class) {
- ERR_FAIL_NULL_V(p_attr_class, NULL);
+ ERR_FAIL_NULL_V(p_attr_class, nullptr);
if (!attrs_fetched)
fetch_attributes();
if (!attributes)
- return NULL;
+ return nullptr;
return mono_custom_attrs_get_attr(attributes, p_attr_class->get_mono_ptr());
}
void GDMonoMethod::fetch_attributes() {
- ERR_FAIL_COND(attributes != NULL);
+ ERR_FAIL_COND(attributes != nullptr);
attributes = mono_custom_attrs_from_method(mono_method);
attrs_fetched = true;
}
@@ -247,7 +238,7 @@ void GDMonoMethod::get_parameter_names(Vector<StringName> &names) const {
}
void GDMonoMethod::get_parameter_types(Vector<ManagedType> &types) const {
- for (int i = 0; i < param_types.size(); ++i) {
+ for (int i = 0; i < params_count; ++i) {
types.push_back(param_types[i]);
}
}
@@ -256,13 +247,22 @@ const MethodInfo &GDMonoMethod::get_method_info() {
if (!method_info_fetched) {
method_info.name = name;
- method_info.return_val = PropertyInfo(GDMonoMarshal::managed_to_variant_type(return_type), "");
+
+ bool nil_is_variant = false;
+ method_info.return_val = PropertyInfo(GDMonoMarshal::managed_to_variant_type(return_type, &nil_is_variant), "");
+ if (method_info.return_val.type == Variant::NIL && nil_is_variant)
+ method_info.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
Vector<StringName> names;
get_parameter_names(names);
for (int i = 0; i < params_count; ++i) {
- method_info.arguments.push_back(PropertyInfo(GDMonoMarshal::managed_to_variant_type(param_types[i]), names[i]));
+ nil_is_variant = false;
+ PropertyInfo arg_info = PropertyInfo(GDMonoMarshal::managed_to_variant_type(param_types[i], &nil_is_variant), names[i]);
+ if (arg_info.type == Variant::NIL && nil_is_variant)
+ arg_info.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
+
+ method_info.arguments.push_back(arg_info);
}
// TODO: default arguments
@@ -281,7 +281,7 @@ GDMonoMethod::GDMonoMethod(StringName p_name, MonoMethod *p_method) {
method_info_fetched = false;
attrs_fetched = false;
- attributes = NULL;
+ attributes = nullptr;
_update_signature();
}
diff --git a/modules/mono/mono_gd/gd_mono_method.h b/modules/mono/mono_gd/gd_mono_method.h
index b47e42dec2..54b2eba3e8 100644
--- a/modules/mono/mono_gd/gd_mono_method.h
+++ b/modules/mono/mono_gd/gd_mono_method.h
@@ -57,28 +57,28 @@ class GDMonoMethod : public IMonoClassMember {
MonoMethod *mono_method;
public:
- virtual GDMonoClass *get_enclosing_class() const GD_FINAL;
+ virtual GDMonoClass *get_enclosing_class() const final;
- virtual MemberType get_member_type() const GD_FINAL { return MEMBER_TYPE_METHOD; }
+ virtual MemberType get_member_type() const final { return MEMBER_TYPE_METHOD; }
- virtual StringName get_name() const GD_FINAL { return name; }
+ virtual StringName get_name() const final { return name; }
- virtual bool is_static() GD_FINAL;
+ virtual bool is_static() final;
- virtual Visibility get_visibility() GD_FINAL;
+ virtual Visibility get_visibility() final;
- virtual bool has_attribute(GDMonoClass *p_attr_class) GD_FINAL;
- virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) GD_FINAL;
+ virtual bool has_attribute(GDMonoClass *p_attr_class) final;
+ virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) final;
void fetch_attributes();
- _FORCE_INLINE_ MonoMethod *get_mono_ptr() { return mono_method; }
+ _FORCE_INLINE_ MonoMethod *get_mono_ptr() const { return mono_method; }
- _FORCE_INLINE_ int get_parameters_count() { return params_count; }
- _FORCE_INLINE_ ManagedType get_return_type() { return return_type; }
+ _FORCE_INLINE_ int get_parameters_count() const { return params_count; }
+ _FORCE_INLINE_ ManagedType get_return_type() const { return return_type; }
- 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);
+ MonoObject *invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc = nullptr) const;
+ MonoObject *invoke(MonoObject *p_object, MonoException **r_exc = nullptr) const;
+ MonoObject *invoke_raw(MonoObject *p_object, void **p_params, MonoException **r_exc = nullptr) const;
String get_full_name(bool p_signature = false) const;
String get_full_name_no_class() const;
diff --git a/modules/mono/mono_gd/gd_mono_method_thunk.h b/modules/mono/mono_gd/gd_mono_method_thunk.h
index d8c9a5eb02..0e05e974e9 100644
--- a/modules/mono/mono_gd/gd_mono_method_thunk.h
+++ b/modules/mono/mono_gd/gd_mono_method_thunk.h
@@ -39,7 +39,7 @@
#include "gd_mono_method.h"
#include "gd_mono_utils.h"
-#if !defined(JAVASCRIPT_ENABLED)
+#if !defined(JAVASCRIPT_ENABLED) && !defined(IPHONE_ENABLED)
#define HAVE_METHOD_THUNKS
#endif
@@ -60,16 +60,16 @@ public:
}
_FORCE_INLINE_ bool is_null() {
- return mono_method_thunk == NULL;
+ return mono_method_thunk == nullptr;
}
_FORCE_INLINE_ void nullify() {
- mono_method_thunk = NULL;
+ mono_method_thunk = nullptr;
}
_FORCE_INLINE_ void set_from_method(GDMonoMethod *p_mono_method) {
#ifdef DEBUG_ENABLED
- CRASH_COND(p_mono_method == NULL);
+ CRASH_COND(p_mono_method == nullptr);
CRASH_COND(p_mono_method->get_return_type().type_encoding != MONO_TYPE_VOID);
if (p_mono_method->is_static()) {
@@ -82,7 +82,7 @@ public:
}
GDMonoMethodThunk() :
- mono_method_thunk(NULL) {
+ mono_method_thunk(nullptr) {
}
explicit GDMonoMethodThunk(GDMonoMethod *p_mono_method) {
@@ -106,16 +106,16 @@ public:
}
_FORCE_INLINE_ bool is_null() {
- return mono_method_thunk == NULL;
+ return mono_method_thunk == nullptr;
}
_FORCE_INLINE_ void nullify() {
- mono_method_thunk = NULL;
+ mono_method_thunk = nullptr;
}
_FORCE_INLINE_ void set_from_method(GDMonoMethod *p_mono_method) {
#ifdef DEBUG_ENABLED
- CRASH_COND(p_mono_method == NULL);
+ CRASH_COND(p_mono_method == nullptr);
CRASH_COND(p_mono_method->get_return_type().type_encoding == MONO_TYPE_VOID);
if (p_mono_method->is_static()) {
@@ -128,12 +128,12 @@ public:
}
GDMonoMethodThunkR() :
- mono_method_thunk(NULL) {
+ mono_method_thunk(nullptr) {
}
explicit GDMonoMethodThunkR(GDMonoMethod *p_mono_method) {
#ifdef DEBUG_ENABLED
- CRASH_COND(p_mono_method == NULL);
+ CRASH_COND(p_mono_method == nullptr);
#endif
mono_method_thunk = (M)mono_method_get_unmanaged_thunk(p_mono_method->get_mono_ptr());
}
@@ -146,7 +146,7 @@ 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);
+ p_mono_method->invoke_raw(nullptr, args, r_exc);
} else {
void *args[ThunkParamCount] = { p_args... };
p_mono_method->invoke_raw((MonoObject *)p_arg1, args, r_exc);
@@ -167,7 +167,7 @@ struct VariadicInvokeMonoMethod<0> {
#ifdef DEBUG_ENABLED
CRASH_COND(!p_mono_method->is_static());
#endif
- p_mono_method->invoke_raw(NULL, NULL, r_exc);
+ p_mono_method->invoke_raw(nullptr, nullptr, r_exc);
}
};
@@ -176,9 +176,9 @@ 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);
+ p_mono_method->invoke_raw(nullptr, args, r_exc);
} else {
- p_mono_method->invoke_raw((MonoObject *)p_arg1, NULL, r_exc);
+ p_mono_method->invoke_raw((MonoObject *)p_arg1, nullptr, r_exc);
}
}
};
@@ -203,7 +203,7 @@ 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);
+ MonoObject *r = p_mono_method->invoke_raw(nullptr, args, r_exc);
return unbox_if_needed<R>(r, p_mono_method->get_return_type());
} else {
void *args[ThunkParamCount] = { p_args... };
@@ -226,7 +226,7 @@ struct VariadicInvokeMonoMethodR<0, R> {
#ifdef DEBUG_ENABLED
CRASH_COND(!p_mono_method->is_static());
#endif
- MonoObject *r = p_mono_method->invoke_raw(NULL, NULL, r_exc);
+ MonoObject *r = p_mono_method->invoke_raw(nullptr, nullptr, r_exc);
return unbox_if_needed<R>(r, p_mono_method->get_return_type());
}
};
@@ -236,10 +236,10 @@ 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);
+ MonoObject *r = p_mono_method->invoke_raw(nullptr, 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);
+ MonoObject *r = p_mono_method->invoke_raw((MonoObject *)p_arg1, nullptr, r_exc);
return unbox_if_needed<R>(r, p_mono_method->get_return_type());
}
}
@@ -256,16 +256,16 @@ public:
}
_FORCE_INLINE_ bool is_null() {
- return mono_method == NULL;
+ return mono_method == nullptr;
}
_FORCE_INLINE_ void nullify() {
- mono_method = NULL;
+ mono_method = nullptr;
}
_FORCE_INLINE_ void set_from_method(GDMonoMethod *p_mono_method) {
#ifdef DEBUG_ENABLED
- CRASH_COND(p_mono_method == NULL);
+ CRASH_COND(p_mono_method == nullptr);
CRASH_COND(p_mono_method->get_return_type().type_encoding != MONO_TYPE_VOID);
if (p_mono_method->is_static()) {
@@ -278,7 +278,7 @@ public:
}
GDMonoMethodThunk() :
- mono_method(NULL) {
+ mono_method(nullptr) {
}
explicit GDMonoMethodThunk(GDMonoMethod *p_mono_method) {
@@ -297,16 +297,16 @@ public:
}
_FORCE_INLINE_ bool is_null() {
- return mono_method == NULL;
+ return mono_method == nullptr;
}
_FORCE_INLINE_ void nullify() {
- mono_method = NULL;
+ mono_method = nullptr;
}
_FORCE_INLINE_ void set_from_method(GDMonoMethod *p_mono_method) {
#ifdef DEBUG_ENABLED
- CRASH_COND(p_mono_method == NULL);
+ CRASH_COND(p_mono_method == nullptr);
CRASH_COND(p_mono_method->get_return_type().type_encoding == MONO_TYPE_VOID);
if (p_mono_method->is_static()) {
@@ -319,7 +319,7 @@ public:
}
GDMonoMethodThunkR() :
- mono_method(NULL) {
+ mono_method(nullptr) {
}
explicit GDMonoMethodThunkR(GDMonoMethod *p_mono_method) {
diff --git a/modules/mono/mono_gd/gd_mono_property.cpp b/modules/mono/mono_gd/gd_mono_property.cpp
index 3b5ce58d80..c3e7598f2d 100644
--- a/modules/mono/mono_gd/gd_mono_property.cpp
+++ b/modules/mono/mono_gd/gd_mono_property.cpp
@@ -57,7 +57,7 @@ GDMonoProperty::GDMonoProperty(MonoProperty *p_mono_property, GDMonoClass *p_own
MonoMethodSignature *setter_sig = mono_method_signature(prop_method);
- void *iter = NULL;
+ void *iter = nullptr;
MonoType *param_raw_type = mono_signature_get_params(setter_sig, &iter);
type.type_encoding = mono_type_get_type(param_raw_type);
@@ -66,7 +66,7 @@ GDMonoProperty::GDMonoProperty(MonoProperty *p_mono_property, GDMonoClass *p_own
}
attrs_fetched = false;
- attributes = NULL;
+ attributes = nullptr;
}
GDMonoProperty::~GDMonoProperty() {
@@ -77,17 +77,17 @@ GDMonoProperty::~GDMonoProperty() {
bool GDMonoProperty::is_static() {
MonoMethod *prop_method = mono_property_get_get_method(mono_property);
- if (prop_method == NULL)
+ if (prop_method == nullptr)
prop_method = mono_property_get_set_method(mono_property);
- return mono_method_get_flags(prop_method, NULL) & MONO_METHOD_ATTR_STATIC;
+ return mono_method_get_flags(prop_method, nullptr) & MONO_METHOD_ATTR_STATIC;
}
IMonoClassMember::Visibility GDMonoProperty::get_visibility() {
MonoMethod *prop_method = mono_property_get_get_method(mono_property);
- if (prop_method == NULL)
+ if (prop_method == nullptr)
prop_method = mono_property_get_set_method(mono_property);
- switch (mono_method_get_flags(prop_method, NULL) & MONO_METHOD_ATTR_ACCESS_MASK) {
+ switch (mono_method_get_flags(prop_method, nullptr) & MONO_METHOD_ATTR_ACCESS_MASK) {
case MONO_METHOD_ATTR_PRIVATE:
return IMonoClassMember::PRIVATE;
case MONO_METHOD_ATTR_FAM_AND_ASSEM:
@@ -116,36 +116,36 @@ bool GDMonoProperty::has_attribute(GDMonoClass *p_attr_class) {
}
MonoObject *GDMonoProperty::get_attribute(GDMonoClass *p_attr_class) {
- ERR_FAIL_NULL_V(p_attr_class, NULL);
+ ERR_FAIL_NULL_V(p_attr_class, nullptr);
if (!attrs_fetched)
fetch_attributes();
if (!attributes)
- return NULL;
+ return nullptr;
return mono_custom_attrs_get_attr(attributes, p_attr_class->get_mono_ptr());
}
void GDMonoProperty::fetch_attributes() {
- ERR_FAIL_COND(attributes != NULL);
+ ERR_FAIL_COND(attributes != nullptr);
attributes = mono_custom_attrs_from_property(owner->get_mono_ptr(), mono_property);
attrs_fetched = true;
}
bool GDMonoProperty::has_getter() {
- return mono_property_get_get_method(mono_property) != NULL;
+ return mono_property_get_get_method(mono_property) != nullptr;
}
bool GDMonoProperty::has_setter() {
- return mono_property_get_set_method(mono_property) != NULL;
+ return mono_property_get_set_method(mono_property) != nullptr;
}
void GDMonoProperty::set_value(MonoObject *p_object, MonoObject *p_value, MonoException **r_exc) {
MonoMethod *prop_method = mono_property_get_set_method(mono_property);
MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), 1);
mono_array_setref(params, 0, p_value);
- MonoException *exc = NULL;
+ MonoException *exc = nullptr;
GDMonoUtils::runtime_invoke_array(prop_method, p_object, params, &exc);
if (exc) {
if (r_exc) {
@@ -157,7 +157,7 @@ void GDMonoProperty::set_value(MonoObject *p_object, MonoObject *p_value, MonoEx
}
void GDMonoProperty::set_value(MonoObject *p_object, void **p_params, MonoException **r_exc) {
- MonoException *exc = NULL;
+ MonoException *exc = nullptr;
GDMonoUtils::property_set_value(mono_property, p_object, p_params, &exc);
if (exc) {
@@ -170,11 +170,11 @@ void GDMonoProperty::set_value(MonoObject *p_object, void **p_params, MonoExcept
}
MonoObject *GDMonoProperty::get_value(MonoObject *p_object, MonoException **r_exc) {
- MonoException *exc = NULL;
- MonoObject *ret = GDMonoUtils::property_get_value(mono_property, p_object, NULL, &exc);
+ MonoException *exc = nullptr;
+ MonoObject *ret = GDMonoUtils::property_get_value(mono_property, p_object, nullptr, &exc);
if (exc) {
- ret = NULL;
+ ret = nullptr;
if (r_exc) {
*r_exc = exc;
} else {
diff --git a/modules/mono/mono_gd/gd_mono_property.h b/modules/mono/mono_gd/gd_mono_property.h
index 692037f76a..4653758a86 100644
--- a/modules/mono/mono_gd/gd_mono_property.h
+++ b/modules/mono/mono_gd/gd_mono_property.h
@@ -47,17 +47,17 @@ class GDMonoProperty : public IMonoClassMember {
MonoCustomAttrInfo *attributes;
public:
- virtual GDMonoClass *get_enclosing_class() const GD_FINAL { return owner; }
+ virtual GDMonoClass *get_enclosing_class() const final { return owner; }
- virtual MemberType get_member_type() const GD_FINAL { return MEMBER_TYPE_PROPERTY; }
+ virtual MemberType get_member_type() const final { return MEMBER_TYPE_PROPERTY; }
- virtual StringName get_name() const GD_FINAL { return name; }
+ virtual StringName get_name() const final { return name; }
- virtual bool is_static() GD_FINAL;
- virtual Visibility get_visibility() GD_FINAL;
+ virtual bool is_static() final;
+ virtual Visibility get_visibility() final;
- virtual bool has_attribute(GDMonoClass *p_attr_class) GD_FINAL;
- virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) GD_FINAL;
+ virtual bool has_attribute(GDMonoClass *p_attr_class) final;
+ virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) final;
void fetch_attributes();
bool has_getter();
@@ -65,9 +65,9 @@ public:
_FORCE_INLINE_ ManagedType get_type() const { return type; }
- void set_value(MonoObject *p_object, MonoObject *p_value, MonoException **r_exc = NULL);
- void set_value(MonoObject *p_object, void **p_params, MonoException **r_exc = NULL);
- MonoObject *get_value(MonoObject *p_object, MonoException **r_exc = NULL);
+ void set_value(MonoObject *p_object, MonoObject *p_value, MonoException **r_exc = nullptr);
+ void set_value(MonoObject *p_object, void **p_params, MonoException **r_exc = nullptr);
+ MonoObject *get_value(MonoObject *p_object, MonoException **r_exc = nullptr);
bool get_bool_value(MonoObject *p_object);
int get_int_value(MonoObject *p_object);
diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp
index 41f49d8ac9..00119ced88 100644
--- a/modules/mono/mono_gd/gd_mono_utils.cpp
+++ b/modules/mono/mono_gd/gd_mono_utils.cpp
@@ -32,6 +32,8 @@
#include <mono/metadata/exception.h>
+#include "core/debugger/engine_debugger.h"
+#include "core/debugger/script_debugger.h"
#include "core/os/dir_access.h"
#include "core/os/mutex.h"
#include "core/os/os.h"
@@ -39,7 +41,7 @@
#include "core/reference.h"
#ifdef TOOLS_ENABLED
-#include "editor/debugger/script_editor_debugger.h"
+#include "editor/debugger/editor_debugger_node.h"
#endif
#include "../csharp_script.h"
@@ -55,7 +57,7 @@ namespace GDMonoUtils {
MonoObject *unmanaged_get_managed(Object *unmanaged) {
if (!unmanaged)
- return NULL;
+ return nullptr;
if (unmanaged->get_script_instance()) {
CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(unmanaged->get_script_instance());
@@ -69,7 +71,7 @@ MonoObject *unmanaged_get_managed(Object *unmanaged) {
void *data = unmanaged->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index());
- ERR_FAIL_NULL_V(data, NULL);
+ ERR_FAIL_NULL_V(data, nullptr);
CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->value();
@@ -80,14 +82,13 @@ MonoObject *unmanaged_get_managed(Object *unmanaged) {
// Already had a binding that needs to be setup
CSharpLanguage::get_singleton()->setup_csharp_script_binding(script_binding, unmanaged);
- ERR_FAIL_COND_V(!script_binding.inited, NULL);
+ ERR_FAIL_COND_V(!script_binding.inited, nullptr);
}
}
- Ref<MonoGCHandle> &gchandle = script_binding.gchandle;
- ERR_FAIL_COND_V(gchandle.is_null(), NULL);
+ MonoGCHandleData &gchandle = script_binding.gchandle;
- MonoObject *target = gchandle->get_target();
+ MonoObject *target = gchandle.get_target();
if (target)
return target;
@@ -98,13 +99,13 @@ MonoObject *unmanaged_get_managed(Object *unmanaged) {
#ifdef DEBUG_ENABLED
CRASH_COND(script_binding.type_name == StringName());
- CRASH_COND(script_binding.wrapper_class == NULL);
+ CRASH_COND(script_binding.wrapper_class == nullptr);
#endif
MonoObject *mono_object = GDMonoUtils::create_managed_for_godot_object(script_binding.wrapper_class, script_binding.type_name, unmanaged);
- ERR_FAIL_NULL_V(mono_object, NULL);
+ ERR_FAIL_NULL_V(mono_object, nullptr);
- gchandle->set_handle(MonoGCHandle::new_strong_handle(mono_object), MonoGCHandle::STRONG_HANDLE);
+ gchandle = MonoGCHandleData::new_strong_handle(mono_object);
// Tie managed to unmanaged
Reference *ref = Object::cast_to<Reference>(unmanaged);
@@ -126,10 +127,15 @@ void set_main_thread(MonoThread *p_thread) {
}
MonoThread *attach_current_thread() {
- ERR_FAIL_COND_V(!GDMono::get_singleton()->is_runtime_initialized(), NULL);
+ ERR_FAIL_COND_V(!GDMono::get_singleton()->is_runtime_initialized(), nullptr);
MonoDomain *scripts_domain = GDMono::get_singleton()->get_scripts_domain();
+#ifndef GD_MONO_SINGLE_APPDOMAIN
MonoThread *mono_thread = mono_thread_attach(scripts_domain ? scripts_domain : mono_get_root_domain());
- ERR_FAIL_NULL_V(mono_thread, NULL);
+#else
+ // The scripts domain is the root domain
+ MonoThread *mono_thread = mono_thread_attach(scripts_domain);
+#endif
+ ERR_FAIL_NULL_V(mono_thread, nullptr);
return mono_thread;
}
@@ -151,13 +157,36 @@ MonoThread *get_current_thread() {
}
bool is_thread_attached() {
- return mono_domain_get() != NULL;
+ return mono_domain_get() != nullptr;
+}
+
+uint32_t new_strong_gchandle(MonoObject *p_object) {
+ return mono_gchandle_new(p_object, /* pinned: */ false);
+}
+
+uint32_t new_strong_gchandle_pinned(MonoObject *p_object) {
+ return mono_gchandle_new(p_object, /* pinned: */ true);
+}
+
+uint32_t new_weak_gchandle(MonoObject *p_object) {
+ return mono_gchandle_new_weakref(p_object, /* track_resurrection: */ false);
+}
+
+void free_gchandle(uint32_t p_gchandle) {
+ mono_gchandle_free(p_gchandle);
}
void runtime_object_init(MonoObject *p_this_obj, GDMonoClass *p_class, MonoException **r_exc) {
GDMonoMethod *ctor = p_class->get_method(".ctor", 0);
ERR_FAIL_NULL(ctor);
- ctor->invoke_raw(p_this_obj, NULL, r_exc);
+ ctor->invoke_raw(p_this_obj, nullptr, r_exc);
+}
+
+bool mono_delegate_equal(MonoDelegate *p_a, MonoDelegate *p_b) {
+ MonoException *exc = nullptr;
+ MonoBoolean res = CACHED_METHOD_THUNK(Delegate, Equals).invoke((MonoObject *)p_a, (MonoObject *)p_b, &exc);
+ UNHANDLED_EXCEPTION(exc);
+ return (bool)res;
}
GDMonoClass *get_object_class(MonoObject *p_object) {
@@ -197,18 +226,18 @@ GDMonoClass *get_class_native_base(GDMonoClass *p_class) {
if (assembly == GDMono::get_singleton()->get_editor_api_assembly())
return klass;
#endif
- } while ((klass = klass->get_parent_class()) != NULL);
+ } while ((klass = klass->get_parent_class()) != nullptr);
- return NULL;
+ return nullptr;
}
MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringName &p_native, Object *p_object) {
bool parent_is_object_class = ClassDB::is_parent_class(p_object->get_class_name(), p_native);
- ERR_FAIL_COND_V_MSG(!parent_is_object_class, NULL,
+ ERR_FAIL_COND_V_MSG(!parent_is_object_class, nullptr,
"Type inherits from native type '" + p_native + "', so it can't be instanced in object of type: '" + p_object->get_class() + "'.");
MonoObject *mono_object = mono_object_new(mono_domain_get(), p_class->get_mono_ptr());
- ERR_FAIL_NULL_V(mono_object, NULL);
+ ERR_FAIL_NULL_V(mono_object, nullptr);
CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, p_object);
@@ -218,9 +247,21 @@ MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringNa
return mono_object;
}
+MonoObject *create_managed_from(const StringName &p_from) {
+ MonoObject *mono_object = mono_object_new(mono_domain_get(), CACHED_CLASS_RAW(StringName));
+ ERR_FAIL_NULL_V(mono_object, nullptr);
+
+ // Construct
+ GDMonoUtils::runtime_object_init(mono_object, CACHED_CLASS(StringName));
+
+ CACHED_FIELD(StringName, ptr)->set_value_raw(mono_object, memnew(StringName(p_from)));
+
+ return mono_object;
+}
+
MonoObject *create_managed_from(const NodePath &p_from) {
MonoObject *mono_object = mono_object_new(mono_domain_get(), CACHED_CLASS_RAW(NodePath));
- ERR_FAIL_NULL_V(mono_object, NULL);
+ ERR_FAIL_NULL_V(mono_object, nullptr);
// Construct
GDMonoUtils::runtime_object_init(mono_object, CACHED_CLASS(NodePath));
@@ -232,7 +273,7 @@ MonoObject *create_managed_from(const NodePath &p_from) {
MonoObject *create_managed_from(const RID &p_from) {
MonoObject *mono_object = mono_object_new(mono_domain_get(), CACHED_CLASS_RAW(RID));
- ERR_FAIL_NULL_V(mono_object, NULL);
+ ERR_FAIL_NULL_V(mono_object, nullptr);
// Construct
GDMonoUtils::runtime_object_init(mono_object, CACHED_CLASS(RID));
@@ -244,15 +285,15 @@ MonoObject *create_managed_from(const RID &p_from) {
MonoObject *create_managed_from(const Array &p_from, GDMonoClass *p_class) {
MonoObject *mono_object = mono_object_new(mono_domain_get(), p_class->get_mono_ptr());
- ERR_FAIL_NULL_V(mono_object, NULL);
+ ERR_FAIL_NULL_V(mono_object, nullptr);
// Search constructor that takes a pointer as parameter
MonoMethod *m;
- void *iter = NULL;
+ void *iter = nullptr;
while ((m = mono_class_get_methods(p_class->get_mono_ptr(), &iter))) {
if (strcmp(mono_method_get_name(m), ".ctor") == 0) {
MonoMethodSignature *sig = mono_method_signature(m);
- void *front = NULL;
+ void *front = nullptr;
if (mono_signature_get_param_count(sig) == 1 &&
mono_class_from_mono_type(mono_signature_get_params(sig, &front)) == CACHED_CLASS(IntPtr)->get_mono_ptr()) {
break;
@@ -260,12 +301,12 @@ MonoObject *create_managed_from(const Array &p_from, GDMonoClass *p_class) {
}
}
- CRASH_COND(m == NULL);
+ CRASH_COND(m == nullptr);
Array *new_array = memnew(Array(p_from));
void *args[1] = { &new_array };
- MonoException *exc = NULL;
+ MonoException *exc = nullptr;
GDMonoUtils::runtime_invoke(m, mono_object, args, &exc);
UNHANDLED_EXCEPTION(exc);
@@ -274,15 +315,15 @@ MonoObject *create_managed_from(const Array &p_from, GDMonoClass *p_class) {
MonoObject *create_managed_from(const Dictionary &p_from, GDMonoClass *p_class) {
MonoObject *mono_object = mono_object_new(mono_domain_get(), p_class->get_mono_ptr());
- ERR_FAIL_NULL_V(mono_object, NULL);
+ ERR_FAIL_NULL_V(mono_object, nullptr);
// Search constructor that takes a pointer as parameter
MonoMethod *m;
- void *iter = NULL;
+ void *iter = nullptr;
while ((m = mono_class_get_methods(p_class->get_mono_ptr(), &iter))) {
if (strcmp(mono_method_get_name(m), ".ctor") == 0) {
MonoMethodSignature *sig = mono_method_signature(m);
- void *front = NULL;
+ void *front = nullptr;
if (mono_signature_get_param_count(sig) == 1 &&
mono_class_from_mono_type(mono_signature_get_params(sig, &front)) == CACHED_CLASS(IntPtr)->get_mono_ptr()) {
break;
@@ -290,12 +331,12 @@ MonoObject *create_managed_from(const Dictionary &p_from, GDMonoClass *p_class)
}
}
- CRASH_COND(m == NULL);
+ CRASH_COND(m == nullptr);
Dictionary *new_dict = memnew(Dictionary(p_from));
void *args[1] = { &new_dict };
- MonoException *exc = NULL;
+ MonoException *exc = nullptr;
GDMonoUtils::runtime_invoke(m, mono_object, args, &exc);
UNHANDLED_EXCEPTION(exc);
@@ -305,7 +346,7 @@ MonoObject *create_managed_from(const Dictionary &p_from, GDMonoClass *p_class)
MonoDomain *create_domain(const String &p_friendly_name) {
print_verbose("Mono: Creating domain '" + p_friendly_name + "'...");
- MonoDomain *domain = mono_domain_create_appdomain((char *)p_friendly_name.utf8().get_data(), NULL);
+ MonoDomain *domain = mono_domain_create_appdomain((char *)p_friendly_name.utf8().get_data(), nullptr);
if (domain) {
// Workaround to avoid this exception:
@@ -330,7 +371,7 @@ String get_exception_name_and_message(MonoException *p_exc) {
res += ": ";
MonoProperty *prop = mono_class_get_property_from_name(klass, "Message");
- MonoString *msg = (MonoString *)property_get_value(prop, (MonoObject *)p_exc, NULL, NULL);
+ MonoString *msg = (MonoString *)property_get_value(prop, (MonoObject *)p_exc, nullptr, nullptr);
res += GDMonoMarshal::mono_string_to_godot(msg);
return res;
@@ -341,7 +382,7 @@ void set_exception_message(MonoException *p_exc, String message) {
MonoProperty *prop = mono_class_get_property_from_name(klass, "Message");
MonoString *msg = GDMonoMarshal::mono_string_from_godot(message);
void *params[1] = { msg };
- property_set_value(prop, (MonoObject *)p_exc, params, NULL);
+ property_set_value(prop, (MonoObject *)p_exc, params, nullptr);
}
void debug_print_unhandled_exception(MonoException *p_exc) {
@@ -351,7 +392,7 @@ void debug_print_unhandled_exception(MonoException *p_exc) {
void debug_send_unhandled_exception_error(MonoException *p_exc) {
#ifdef DEBUG_ENABLED
- if (!ScriptDebugger::get_singleton()) {
+ if (!EngineDebugger::is_active()) {
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
ERR_PRINT(GDMonoUtils::get_exception_name_and_message(p_exc));
@@ -360,7 +401,11 @@ void debug_send_unhandled_exception_error(MonoException *p_exc) {
return;
}
- _TLS_RECURSION_GUARD_;
+ static thread_local bool _recursion_flag_ = false;
+ if (_recursion_flag_)
+ return;
+ _recursion_flag_ = true;
+ SCOPE_EXIT { _recursion_flag_ = false; };
ScriptLanguage::StackInfo separator;
separator.file = String();
@@ -370,14 +415,14 @@ void debug_send_unhandled_exception_error(MonoException *p_exc) {
Vector<ScriptLanguage::StackInfo> si;
String exc_msg;
- while (p_exc != NULL) {
+ while (p_exc != nullptr) {
GDMonoClass *st_klass = CACHED_CLASS(System_Diagnostics_StackTrace);
MonoObject *stack_trace = mono_object_new(mono_domain_get(), st_klass->get_mono_ptr());
MonoBoolean need_file_info = true;
void *ctor_args[2] = { p_exc, &need_file_info };
- MonoException *unexpected_exc = NULL;
+ MonoException *unexpected_exc = nullptr;
CACHED_METHOD(System_Diagnostics_StackTrace, ctor_Exception_bool)->invoke_raw(stack_trace, ctor_args, &unexpected_exc);
if (unexpected_exc) {
@@ -386,7 +431,7 @@ void debug_send_unhandled_exception_error(MonoException *p_exc) {
}
Vector<ScriptLanguage::StackInfo> _si;
- if (stack_trace != NULL) {
+ if (stack_trace != nullptr) {
_si = CSharpLanguage::get_singleton()->stack_trace_get_info(stack_trace);
for (int i = _si.size() - 1; i >= 0; i--)
si.insert(0, _si[i]);
@@ -396,10 +441,10 @@ void debug_send_unhandled_exception_error(MonoException *p_exc) {
GDMonoClass *exc_class = GDMono::get_singleton()->get_class(mono_get_exception_class());
GDMonoProperty *inner_exc_prop = exc_class->get_property("InnerException");
- CRASH_COND(inner_exc_prop == NULL);
+ CRASH_COND(inner_exc_prop == nullptr);
MonoObject *inner_exc = inner_exc_prop->get_value((MonoObject *)p_exc);
- if (inner_exc != NULL)
+ if (inner_exc != nullptr)
si.insert(0, separator);
p_exc = (MonoException *)inner_exc;
@@ -410,7 +455,7 @@ void debug_send_unhandled_exception_error(MonoException *p_exc) {
int line = si.size() ? si[0].line : __LINE__;
String error_msg = "Unhandled exception";
- ScriptDebugger::get_singleton()->send_error(func, file, line, error_msg, exc_msg, ERR_HANDLER_ERROR, si);
+ EngineDebugger::get_script_debugger()->send_error(func, file, line, error_msg, exc_msg, ERR_HANDLER_ERROR, si);
#endif
}
@@ -437,8 +482,7 @@ void set_pending_exception(MonoException *p_exc) {
#endif
}
-_THREAD_LOCAL_(int)
-current_invoke_count = 0;
+thread_local int current_invoke_count = 0;
MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, MonoException **r_exc) {
GD_MONO_BEGIN_RUNTIME_INVOKE;
@@ -526,7 +570,7 @@ namespace Marshal {
bool type_is_generic_array(MonoReflectionType *p_reftype) {
NO_GLUE_RET(false);
- MonoException *exc = NULL;
+ MonoException *exc = nullptr;
MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray).invoke(p_reftype, &exc);
UNHANDLED_EXCEPTION(exc);
return (bool)res;
@@ -534,27 +578,27 @@ bool type_is_generic_array(MonoReflectionType *p_reftype) {
bool type_is_generic_dictionary(MonoReflectionType *p_reftype) {
NO_GLUE_RET(false);
- MonoException *exc = NULL;
+ MonoException *exc = nullptr;
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) {
- MonoException *exc = NULL;
+ MonoException *exc = nullptr;
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) {
- MonoException *exc = NULL;
+ MonoException *exc = nullptr;
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);
- MonoException *exc = NULL;
+ MonoException *exc = nullptr;
MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, GenericIEnumerableIsAssignableFromType).invoke(p_reftype, &exc);
UNHANDLED_EXCEPTION(exc);
return (bool)res;
@@ -562,7 +606,7 @@ bool generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype) {
bool generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype) {
NO_GLUE_RET(false);
- MonoException *exc = NULL;
+ MonoException *exc = nullptr;
MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryIsAssignableFromType).invoke(p_reftype, &exc);
UNHANDLED_EXCEPTION(exc);
return (bool)res;
@@ -570,7 +614,7 @@ bool generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype) {
bool generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_elem_reftype) {
NO_GLUE_RET(false);
- MonoException *exc = NULL;
+ MonoException *exc = nullptr;
MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, GenericIEnumerableIsAssignableFromType_with_info).invoke(p_reftype, r_elem_reftype, &exc);
UNHANDLED_EXCEPTION(exc);
return (bool)res;
@@ -578,7 +622,7 @@ bool generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype, MonoR
bool generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype) {
NO_GLUE_RET(false);
- MonoException *exc = NULL;
+ MonoException *exc = nullptr;
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;
@@ -587,7 +631,7 @@ bool generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype, MonoR
Array enumerable_to_array(MonoObject *p_enumerable) {
NO_GLUE_RET(Array());
Array result;
- MonoException *exc = NULL;
+ MonoException *exc = nullptr;
CACHED_METHOD_THUNK(MarshalUtils, EnumerableToArray).invoke(p_enumerable, &result, &exc);
UNHANDLED_EXCEPTION(exc);
return result;
@@ -596,7 +640,7 @@ Array enumerable_to_array(MonoObject *p_enumerable) {
Dictionary idictionary_to_dictionary(MonoObject *p_idictionary) {
NO_GLUE_RET(Dictionary());
Dictionary result;
- MonoException *exc = NULL;
+ MonoException *exc = nullptr;
CACHED_METHOD_THUNK(MarshalUtils, IDictionaryToDictionary).invoke(p_idictionary, &result, &exc);
UNHANDLED_EXCEPTION(exc);
return result;
@@ -605,23 +649,23 @@ Dictionary idictionary_to_dictionary(MonoObject *p_idictionary) {
Dictionary generic_idictionary_to_dictionary(MonoObject *p_generic_idictionary) {
NO_GLUE_RET(Dictionary());
Dictionary result;
- MonoException *exc = NULL;
+ MonoException *exc = nullptr;
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);
- MonoException *exc = NULL;
+ NO_GLUE_RET(nullptr);
+ MonoException *exc = nullptr;
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);
- MonoException *exc = NULL;
+ NO_GLUE_RET(nullptr);
+ MonoException *exc = nullptr;
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)));
@@ -630,7 +674,7 @@ GDMonoClass *make_generic_dictionary_type(MonoReflectionType *p_key_reftype, Mon
} // namespace Marshal
ScopeThreadAttach::ScopeThreadAttach() :
- mono_thread(NULL) {
+ mono_thread(nullptr) {
if (likely(GDMono::get_singleton()->is_runtime_initialized()) && unlikely(!mono_domain_get())) {
mono_thread = GDMonoUtils::attach_current_thread();
}
@@ -642,6 +686,10 @@ ScopeThreadAttach::~ScopeThreadAttach() {
}
}
-// namespace Marshal
+StringName get_native_godot_class_name(GDMonoClass *p_class) {
+ MonoObject *native_name_obj = p_class->get_field(BINDINGS_NATIVE_NAME_FIELD)->get_value(nullptr);
+ StringName *ptr = GDMonoMarshal::unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(native_name_obj));
+ return ptr ? *ptr : StringName();
+}
} // namespace GDMonoUtils
diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h
index db9f99bfdc..b850e1be9b 100644
--- a/modules/mono/mono_gd/gd_mono_utils.h
+++ b/modules/mono/mono_gd/gd_mono_utils.h
@@ -35,14 +35,13 @@
#include "../mono_gc_handle.h"
#include "../utils/macros.h"
-#include "../utils/thread_local.h"
#include "gd_mono_header.h"
#include "core/object.h"
#include "core/reference.h"
#define UNHANDLED_EXCEPTION(m_exc) \
- if (unlikely(m_exc != NULL)) { \
+ if (unlikely(m_exc != nullptr)) { \
GDMonoUtils::debug_unhandled_exception(m_exc); \
GD_UNREACHABLE(); \
}
@@ -78,7 +77,7 @@ _FORCE_INLINE_ void hash_combine(uint32_t &p_hash, const uint32_t &p_with_hash)
/**
* If the object has a csharp script, returns the target of the gchandle stored in the script instance
* Otherwise returns a newly constructed MonoObject* which is attached to the object
- * Returns NULL on error
+ * Returns nullptr on error
*/
MonoObject *unmanaged_get_managed(Object *unmanaged);
@@ -90,10 +89,17 @@ MonoThread *get_current_thread();
bool is_thread_attached();
_FORCE_INLINE_ bool is_main_thread() {
- return mono_domain_get() != NULL && mono_thread_get_main() == mono_thread_current();
+ return mono_domain_get() != nullptr && mono_thread_get_main() == mono_thread_current();
}
-void runtime_object_init(MonoObject *p_this_obj, GDMonoClass *p_class, MonoException **r_exc = NULL);
+uint32_t new_strong_gchandle(MonoObject *p_object);
+uint32_t new_strong_gchandle_pinned(MonoObject *p_object);
+uint32_t new_weak_gchandle(MonoObject *p_object);
+void free_gchandle(uint32_t p_gchandle);
+
+void runtime_object_init(MonoObject *p_this_obj, GDMonoClass *p_class, MonoException **r_exc = nullptr);
+
+bool mono_delegate_equal(MonoDelegate *p_a, MonoDelegate *p_b);
GDMonoClass *get_object_class(MonoObject *p_object);
GDMonoClass *type_get_proxy_class(const StringName &p_type);
@@ -101,6 +107,7 @@ GDMonoClass *get_class_native_base(GDMonoClass *p_class);
MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringName &p_native, Object *p_object);
+MonoObject *create_managed_from(const StringName &p_from);
MonoObject *create_managed_from(const NodePath &p_from);
MonoObject *create_managed_from(const RID &p_from);
MonoObject *create_managed_from(const Array &p_from, GDMonoClass *p_class);
@@ -123,7 +130,7 @@ void print_unhandled_exception(MonoException *p_exc);
*/
void set_pending_exception(MonoException *p_exc);
-extern _THREAD_LOCAL_(int) current_invoke_count;
+extern thread_local int current_invoke_count;
_FORCE_INLINE_ int get_runtime_invoke_count() {
return current_invoke_count;
@@ -152,9 +159,11 @@ private:
MonoThread *mono_thread;
};
+StringName get_native_godot_class_name(GDMonoClass *p_class);
+
} // namespace GDMonoUtils
-#define NATIVE_GDMONOCLASS_NAME(m_class) (GDMonoMarshal::mono_string_to_godot((MonoString *)m_class->get_field(BINDINGS_NATIVE_NAME_FIELD)->get_value(NULL)))
+#define NATIVE_GDMONOCLASS_NAME(m_class) (GDMonoUtils::get_native_godot_class_name(m_class))
#define GD_MONO_BEGIN_RUNTIME_INVOKE \
int &_runtime_invoke_count_ref = GDMonoUtils::get_runtime_invoke_count_ref(); \
diff --git a/modules/mono/mono_gd/managed_type.h b/modules/mono/mono_gd/managed_type.h
index 11b832d0cc..84d1837853 100644
--- a/modules/mono/mono_gd/managed_type.h
+++ b/modules/mono/mono_gd/managed_type.h
@@ -46,7 +46,7 @@ struct ManagedType {
ManagedType() :
type_encoding(0),
- type_class(NULL) {
+ type_class(nullptr) {
}
ManagedType(int p_type_encoding, GDMonoClass *p_type_class) :
diff --git a/modules/mono/mono_gd/gd_mono_android.cpp b/modules/mono/mono_gd/support/android_support.cpp
index 761368878f..8bcdeec9dd 100644..100755
--- a/modules/mono/mono_gd/gd_mono_android.cpp
+++ b/modules/mono/mono_gd/support/android_support.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* gd_mono_android.cpp */
+/* android_support.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "gd_mono_android.h"
+#include "android_support.h"
#if defined(ANDROID_ENABLED)
@@ -49,14 +49,16 @@
#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"
+#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 {
+namespace gdmono {
+namespace android {
+namespace support {
template <typename T>
struct ScopedLocalRef {
@@ -67,7 +69,7 @@ struct ScopedLocalRef {
_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_ operator bool() const { return local_ref != nullptr; }
_FORCE_INLINE_ bool operator==(std::nullptr_t) const {
return local_ref == nullptr;
@@ -122,7 +124,7 @@ String determine_app_native_lib_dir() {
String result;
- const char *const nativeLibraryDirUtf8 = env->GetStringUTFChars(nativeLibraryDir, NULL);
+ const char *const nativeLibraryDirUtf8 = env->GetStringUTFChars(nativeLibraryDir, nullptr);
if (nativeLibraryDirUtf8) {
result.parse_utf8(nativeLibraryDirUtf8);
env->ReleaseStringUTFChars(nativeLibraryDir, nativeLibraryDirUtf8);
@@ -150,21 +152,21 @@ int gd_mono_convert_dl_flags(int flags) {
return lflags;
}
-#ifndef GD_MONO_ANDROID_SO_NAME
-#define GD_MONO_ANDROID_SO_NAME "libmonosgen-2.0.so"
+#ifndef GD_MONO_SO_NAME
+#define GD_MONO_SO_NAME "libmonosgen-2.0.so"
#endif
-const char *mono_so_name = GD_MONO_ANDROID_SO_NAME;
+const char *mono_so_name = GD_MONO_SO_NAME;
const char *godot_so_name = "libgodot_android.so";
-void *mono_dl_handle = NULL;
-void *godot_dl_handle = NULL;
+void *mono_dl_handle = nullptr;
+void *godot_dl_handle = nullptr;
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;
+ return nullptr;
}
int lflags = gd_mono_convert_dl_flags(p_flags);
@@ -174,7 +176,7 @@ void *try_dlopen(const String &p_so_path, int p_flags) {
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;
+ return nullptr;
}
if (OS::get_singleton()->is_stdout_verbose())
@@ -184,7 +186,7 @@ void *try_dlopen(const String &p_so_path, int p_flags) {
}
void *gd_mono_android_dlopen(const char *p_name, int p_flags, char **r_err, void *p_user_data) {
- if (p_name == NULL) {
+ if (p_name == nullptr) {
// __Internal
if (!mono_dl_handle) {
@@ -209,7 +211,7 @@ void *gd_mono_android_dlopen(const char *p_name, int p_flags, char **r_err, void
return try_dlopen(so_path, p_flags);
}
- return NULL;
+ return nullptr;
}
void *gd_mono_android_dlsym(void *p_handle, const char *p_name, char **r_err, void *p_user_data) {
@@ -230,7 +232,7 @@ void *gd_mono_android_dlsym(void *p_handle, const char *p_name, char **r_err, vo
if (r_err)
*r_err = str_format_new("%s\n", dlerror());
- return NULL;
+ return nullptr;
}
void *gd_mono_android_dlclose(void *p_handle, void *p_user_data) {
@@ -238,9 +240,9 @@ void *gd_mono_android_dlclose(void *p_handle, void *p_user_data) {
// 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;
+ mono_dl_handle = nullptr;
- return NULL;
+ return nullptr;
}
int32_t build_version_sdk_int = 0;
@@ -265,7 +267,7 @@ int32_t get_build_version_sdk_int() {
return build_version_sdk_int;
}
-jobject certStore = NULL; // KeyStore
+jobject certStore = nullptr; // KeyStore
MonoBoolean _gd_mono_init_cert_store() {
// The JNI code is the equivalent of:
@@ -293,7 +295,7 @@ MonoBoolean _gd_mono_init_cert_store() {
if (jni_exception_check(env))
return 0;
- env->CallVoidMethod(certStoreLocal, load, NULL);
+ env->CallVoidMethod(certStoreLocal, load, nullptr);
if (jni_exception_check(env))
return 0;
@@ -317,7 +319,7 @@ MonoArray *_gd_mono_android_cert_store_lookup(MonoString *p_alias) {
if (!mono_error_ok(&mono_error)) {
ERR_PRINT(String() + "Failed to convert MonoString* to UTF-8: '" + mono_error_get_message(&mono_error) + "'.");
mono_error_cleanup(&mono_error);
- return NULL;
+ return nullptr;
}
JNIEnv *env = ThreadAndroid::get_env();
@@ -326,20 +328,20 @@ MonoArray *_gd_mono_android_cert_store_lookup(MonoString *p_alias) {
mono_free(alias_utf8);
ScopedLocalRef<jclass> keyStoreClass(env, env->FindClass("java/security/KeyStore"));
- ERR_FAIL_NULL_V(keyStoreClass, NULL);
+ ERR_FAIL_NULL_V(keyStoreClass, nullptr);
ScopedLocalRef<jclass> certificateClass(env, env->FindClass("java/security/cert/Certificate"));
- ERR_FAIL_NULL_V(certificateClass, NULL);
+ ERR_FAIL_NULL_V(certificateClass, nullptr);
jmethodID getCertificate = env->GetMethodID(keyStoreClass, "getCertificate", "(Ljava/lang/String;)Ljava/security/cert/Certificate;");
- ERR_FAIL_NULL_V(getCertificate, NULL);
+ ERR_FAIL_NULL_V(getCertificate, nullptr);
jmethodID getEncoded = env->GetMethodID(certificateClass, "getEncoded", "()[B");
- ERR_FAIL_NULL_V(getEncoded, NULL);
+ ERR_FAIL_NULL_V(getEncoded, nullptr);
ScopedLocalRef<jobject> certificate(env, env->CallObjectMethod(certStore, getCertificate, js_alias.get()));
if (!certificate)
- return NULL;
+ return nullptr;
ScopedLocalRef<jbyteArray> encoded(env, (jbyteArray)env->CallObjectMethod(certificate, getEncoded));
jsize encodedLength = env->GetArrayLength(encoded);
@@ -352,11 +354,16 @@ MonoArray *_gd_mono_android_cert_store_lookup(MonoString *p_alias) {
return encoded_ret;
}
+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 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);
+ mono_dl_fallback_register(gd_mono_android_dlopen, gd_mono_android_dlsym, gd_mono_android_dlclose, nullptr);
String app_native_lib_dir = get_app_native_lib_dir();
String so_path = path::join(app_native_lib_dir, godot_so_name);
@@ -364,31 +371,28 @@ void initialize() {
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);
+ gd_mono_android_dlclose(mono_dl_handle, nullptr);
if (godot_dl_handle)
- gd_mono_android_dlclose(godot_dl_handle, NULL);
+ gd_mono_android_dlclose(godot_dl_handle, nullptr);
JNIEnv *env = ThreadAndroid::get_env();
if (certStore) {
env->DeleteGlobalRef(certStore);
- certStore = NULL;
+ certStore = nullptr;
}
}
-} // namespace GDMonoAndroid
+} // namespace support
+} // namespace android
+} // namespace gdmono
-using namespace GDMonoAndroid;
+using namespace gdmono::android::support;
// 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
@@ -417,7 +421,7 @@ GD_PINVOKE_EXPORT int32_t monodroid_get_system_property(const char *p_name, char
memcpy(*r_value, prop_value_str, len);
(*r_value)[len] = '\0';
} else {
- *r_value = NULL;
+ *r_value = nullptr;
}
}
@@ -604,7 +608,7 @@ 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;
+ *r_dns_servers_array = nullptr;
char *dns_servers[dns_servers_len];
int dns_servers_count = 0;
@@ -648,23 +652,23 @@ GD_PINVOKE_EXPORT const char *_monodroid_timezone_get_default_id() {
JNIEnv *env = ThreadAndroid::get_env();
ScopedLocalRef<jclass> timeZoneClass(env, env->FindClass("java/util/TimeZone"));
- ERR_FAIL_NULL_V(timeZoneClass, NULL);
+ ERR_FAIL_NULL_V(timeZoneClass, nullptr);
jmethodID getDefault = env->GetStaticMethodID(timeZoneClass, "getDefault", "()Ljava/util/TimeZone;");
- ERR_FAIL_NULL_V(getDefault, NULL);
+ ERR_FAIL_NULL_V(getDefault, nullptr);
jmethodID getID = env->GetMethodID(timeZoneClass, "getID", "()Ljava/lang/String;");
- ERR_FAIL_NULL_V(getID, NULL);
+ ERR_FAIL_NULL_V(getID, nullptr);
ScopedLocalRef<jobject> defaultTimeZone(env, env->CallStaticObjectMethod(timeZoneClass, getDefault));
if (!defaultTimeZone)
- return NULL;
+ return nullptr;
ScopedLocalRef<jstring> defaultTimeZoneID(env, (jstring)env->CallObjectMethod(defaultTimeZone, getID));
if (!defaultTimeZoneID)
- return NULL;
+ return nullptr;
const char *default_time_zone_id = env->GetStringUTFChars(defaultTimeZoneID, 0);
diff --git a/modules/mono/mono_gd/gd_mono_android.h b/modules/mono/mono_gd/support/android_support.h
index 0e04847924..dc2e6c95ed 100644..100755
--- a/modules/mono/mono_gd/gd_mono_android.h
+++ b/modules/mono/mono_gd/support/android_support.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* gd_mono_android.h */
+/* android_support.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,25 +28,28 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef GD_MONO_ANDROID_H
-#define GD_MONO_ANDROID_H
+#ifndef ANDROID_SUPPORT_H
+#define ANDROID_SUPPORT_H
#if defined(ANDROID_ENABLED)
#include "core/ustring.h"
-namespace GDMonoAndroid {
+namespace gdmono {
+namespace android {
+namespace support {
String get_app_native_lib_dir();
void initialize();
+void cleanup();
void register_internal_calls();
-void cleanup();
-
-} // namespace GDMonoAndroid
+} // namespace support
+} // namespace android
+} // namespace gdmono
#endif // ANDROID_ENABLED
-#endif // GD_MONO_ANDROID_H
+#endif // ANDROID_SUPPORT_H
diff --git a/modules/mono/utils/thread_local.cpp b/modules/mono/mono_gd/support/ios_support.h
index 4f10e3fb85..e28af120e3 100644..100755
--- a/modules/mono/utils/thread_local.cpp
+++ b/modules/mono/mono_gd/support/ios_support.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* thread_local.cpp */
+/* ios_support.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,80 +28,24 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "thread_local.h"
+#ifndef IOS_SUPPORT_H
+#define IOS_SUPPORT_H
-#ifdef WINDOWS_ENABLED
-#include <windows.h>
-#else
-#include <pthread.h>
-#endif
+#if defined(IPHONE_ENABLED)
-#include "core/os/memory.h"
-#include "core/print_string.h"
+#include "core/ustring.h"
-struct ThreadLocalStorage::Impl {
+namespace gdmono {
+namespace ios {
+namespace support {
-#ifdef WINDOWS_ENABLED
- DWORD dwFlsIndex;
-#else
- pthread_key_t key;
-#endif
+void initialize();
+void cleanup();
- void *get_value() const {
-#ifdef WINDOWS_ENABLED
- return FlsGetValue(dwFlsIndex);
-#else
- return pthread_getspecific(key);
-#endif
- }
+} // namespace support
+} // namespace ios
+} // namespace gdmono
- void set_value(void *p_value) const {
-#ifdef WINDOWS_ENABLED
- FlsSetValue(dwFlsIndex, p_value);
-#else
- pthread_setspecific(key, p_value);
-#endif
- }
+#endif // IPHONE_ENABLED
-#ifdef WINDOWS_ENABLED
-#define _CALLBACK_FUNC_ __stdcall
-#else
-#define _CALLBACK_FUNC_
-#endif
-
- Impl(void(_CALLBACK_FUNC_ *p_destr_callback_func)(void *)) {
-#ifdef WINDOWS_ENABLED
- dwFlsIndex = FlsAlloc(p_destr_callback_func);
- ERR_FAIL_COND(dwFlsIndex == FLS_OUT_OF_INDEXES);
-#else
- pthread_key_create(&key, p_destr_callback_func);
-#endif
- }
-
- ~Impl() {
-#ifdef WINDOWS_ENABLED
- FlsFree(dwFlsIndex);
-#else
- pthread_key_delete(key);
-#endif
- }
-};
-
-void *ThreadLocalStorage::get_value() const {
- return pimpl->get_value();
-}
-
-void ThreadLocalStorage::set_value(void *p_value) const {
- pimpl->set_value(p_value);
-}
-
-void ThreadLocalStorage::alloc(void(_CALLBACK_FUNC_ *p_destr_callback)(void *)) {
- pimpl = memnew(ThreadLocalStorage::Impl(p_destr_callback));
-}
-
-#undef _CALLBACK_FUNC_
-
-void ThreadLocalStorage::free() {
- memdelete(pimpl);
- pimpl = NULL;
-}
+#endif // IOS_SUPPORT_H
diff --git a/modules/mono/mono_gd/support/ios_support.mm b/modules/mono/mono_gd/support/ios_support.mm
new file mode 100755
index 0000000000..e3d1a647fd
--- /dev/null
+++ b/modules/mono/mono_gd/support/ios_support.mm
@@ -0,0 +1,151 @@
+/*************************************************************************/
+/* ios_support.mm */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 "ios_support.h"
+
+#if defined(IPHONE_ENABLED)
+
+#import <Foundation/Foundation.h>
+#include <os/log.h>
+
+#include "core/ustring.h"
+
+#include "../gd_mono_marshal.h"
+
+// Implemented mostly following: https://github.com/mono/mono/blob/master/sdks/ios/app/runtime.m
+
+// Definition generated by the Godot exporter
+extern "C" void gd_mono_setup_aot();
+
+namespace gdmono {
+namespace ios {
+namespace support {
+
+void ios_mono_log_callback(const char *log_domain, const char *log_level, const char *message, mono_bool fatal, void *user_data) {
+ os_log_info(OS_LOG_DEFAULT, "(%s %s) %s", log_domain, log_level, message);
+ if (fatal) {
+ os_log_info(OS_LOG_DEFAULT, "Exit code: %d.", 1);
+ exit(1);
+ }
+}
+
+void initialize() {
+ mono_dllmap_insert(NULL, "System.Native", NULL, "__Internal", NULL);
+ mono_dllmap_insert(NULL, "System.IO.Compression.Native", NULL, "__Internal", NULL);
+ mono_dllmap_insert(NULL, "System.Security.Cryptography.Native.Apple", NULL, "__Internal", NULL);
+
+#ifdef IOS_DEVICE
+ // This function is defined in an auto-generated source file
+ gd_mono_setup_aot();
+#endif
+
+ mono_set_signal_chaining(true);
+ mono_set_crash_chaining(true);
+}
+
+void cleanup() {
+}
+
+} // namespace support
+} // namespace ios
+} // namespace gdmono
+
+// The following are P/Invoke functions required by the monotouch 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 const char *xamarin_get_locale_country_code() {
+ NSLocale *locale = [NSLocale currentLocale];
+ NSString *countryCode = [locale objectForKey:NSLocaleCountryCode];
+ if (countryCode == NULL) {
+ return strdup("US");
+ }
+ return strdup([countryCode UTF8String]);
+}
+
+GD_PINVOKE_EXPORT void xamarin_log(const uint16_t *p_unicode_message) {
+ int length = 0;
+ const uint16_t *ptr = p_unicode_message;
+ while (*ptr++)
+ length += sizeof(uint16_t);
+ NSString *msg = [[NSString alloc] initWithBytes:p_unicode_message length:length encoding:NSUTF16LittleEndianStringEncoding];
+
+ os_log_info(OS_LOG_DEFAULT, "%{public}@", msg);
+}
+
+GD_PINVOKE_EXPORT const char *xamarin_GetFolderPath(int p_folder) {
+ NSSearchPathDirectory dd = (NSSearchPathDirectory)p_folder;
+ NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:dd inDomains:NSUserDomainMask] lastObject];
+ NSString *path = [url path];
+ return strdup([path UTF8String]);
+}
+
+GD_PINVOKE_EXPORT char *xamarin_timezone_get_local_name() {
+ NSTimeZone *tz = nil;
+ tz = [NSTimeZone localTimeZone];
+ NSString *name = [tz name];
+ return (name != nil) ? strdup([name UTF8String]) : strdup("Local");
+}
+
+GD_PINVOKE_EXPORT char **xamarin_timezone_get_names(uint32_t *p_count) {
+ NSArray *array = [NSTimeZone knownTimeZoneNames];
+ *p_count = array.count;
+ char **result = (char **)malloc(sizeof(char *) * (*p_count));
+ for (uint32_t i = 0; i < *p_count; i++) {
+ NSString *s = [array objectAtIndex:i];
+ result[i] = strdup(s.UTF8String);
+ }
+ return result;
+}
+
+GD_PINVOKE_EXPORT void *xamarin_timezone_get_data(const char *p_name, uint32_t *p_size) { // FIXME: uint32_t since Dec 2019, unsigned long before
+ NSTimeZone *tz = nil;
+ if (p_name) {
+ NSString *n = [[NSString alloc] initWithUTF8String:p_name];
+ tz = [[[NSTimeZone alloc] initWithName:n] autorelease];
+ [n release];
+ } else {
+ tz = [NSTimeZone localTimeZone];
+ }
+ NSData *data = [tz data];
+ *p_size = [data length];
+ void *result = malloc(*p_size);
+ memcpy(result, data.bytes, *p_size);
+ return result;
+}
+
+GD_PINVOKE_EXPORT void xamarin_start_wwan(const char *p_uri) {
+ // FIXME: What's this for? No idea how to implement.
+ os_log_error(OS_LOG_DEFAULT, "Not implemented: 'xamarin_start_wwan'");
+}
+
+#endif // IPHONE_ENABLED
diff --git a/modules/mono/register_types.cpp b/modules/mono/register_types.cpp
index 4823ba3679..94431e7c30 100644
--- a/modules/mono/register_types.cpp
+++ b/modules/mono/register_types.cpp
@@ -34,11 +34,11 @@
#include "csharp_script.h"
-CSharpLanguage *script_language_cs = NULL;
+CSharpLanguage *script_language_cs = nullptr;
Ref<ResourceFormatLoaderCSharpScript> resource_loader_cs;
Ref<ResourceFormatSaverCSharpScript> resource_saver_cs;
-_GodotSharp *_godotsharp = NULL;
+_GodotSharp *_godotsharp = nullptr;
void register_mono_types() {
ClassDB::register_class<CSharpScript>();
diff --git a/modules/mono/signal_awaiter_utils.cpp b/modules/mono/signal_awaiter_utils.cpp
index 718bc2bb93..e77a2e98f2 100644
--- a/modules/mono/signal_awaiter_utils.cpp
+++ b/modules/mono/signal_awaiter_utils.cpp
@@ -36,103 +36,192 @@
#include "mono_gd/gd_mono_marshal.h"
#include "mono_gd/gd_mono_utils.h"
-namespace SignalAwaiterUtils {
-
-Error connect_signal_awaiter(Object *p_source, const String &p_signal, Object *p_target, MonoObject *p_awaiter) {
-
+Error gd_mono_connect_signal_awaiter(Object *p_source, const StringName &p_signal, Object *p_target, MonoObject *p_awaiter) {
ERR_FAIL_NULL_V(p_source, ERR_INVALID_DATA);
ERR_FAIL_NULL_V(p_target, ERR_INVALID_DATA);
- Ref<SignalAwaiterHandle> sa_con = memnew(SignalAwaiterHandle(p_awaiter));
-#ifdef DEBUG_ENABLED
- sa_con->set_connection_target(p_target);
-#endif
+ // TODO: Use pooling for ManagedCallable instances.
+ SignalAwaiterCallable *awaiter_callable = memnew(SignalAwaiterCallable(p_target, p_awaiter, p_signal));
+ Callable callable = Callable(awaiter_callable);
+
+ return p_source->connect(p_signal, callable, Vector<Variant>(), Object::CONNECT_ONESHOT);
+}
+
+bool SignalAwaiterCallable::compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) {
+ const SignalAwaiterCallable *a = static_cast<const SignalAwaiterCallable *>(p_a);
+ const SignalAwaiterCallable *b = static_cast<const SignalAwaiterCallable *>(p_b);
- Vector<Variant> binds;
- binds.push_back(sa_con);
+ if (a->target_id != b->target_id)
+ return false;
+
+ if (a->signal != b->signal)
+ return false;
+
+ return true;
+}
+
+bool SignalAwaiterCallable::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) {
+ if (compare_equal(p_a, p_b))
+ return false;
+ return p_a < p_b;
+}
+
+uint32_t SignalAwaiterCallable::hash() const {
+ uint32_t hash = signal.hash();
+ return hash_djb2_one_64(target_id, hash);
+}
- Error err = p_source->connect_compat(p_signal, sa_con.ptr(),
- CSharpLanguage::get_singleton()->get_string_names()._signal_callback,
- binds, Object::CONNECT_ONESHOT);
+String SignalAwaiterCallable::get_as_text() const {
+ Object *base = ObjectDB::get_instance(target_id);
+ if (base) {
+ String class_name = base->get_class();
+ Ref<Script> script = base->get_script();
+ if (script.is_valid() && script->get_path().is_resource_file()) {
- if (err != OK) {
- // Set it as completed to prevent it from calling the failure callback when released.
- // The awaiter will be aware of the failure by checking the returned error.
- sa_con->set_completed(true);
+ class_name += "(" + script->get_path().get_file() + ")";
+ }
+ return class_name + "::SignalAwaiterMiddleman::" + String(signal);
+ } else {
+ return "null::SignalAwaiterMiddleman::" + String(signal);
}
+}
- return err;
+CallableCustom::CompareEqualFunc SignalAwaiterCallable::get_compare_equal_func() const {
+ return compare_equal_func_ptr;
}
-} // namespace SignalAwaiterUtils
-Variant SignalAwaiterHandle::_signal_callback(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+CallableCustom::CompareLessFunc SignalAwaiterCallable::get_compare_less_func() const {
+ return compare_less_func_ptr;
+}
+
+ObjectID SignalAwaiterCallable::get_object() const {
+ return target_id;
+}
+
+void SignalAwaiterCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
+ r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; // Can't find anything better
+ r_return_value = Variant();
#ifdef DEBUG_ENABLED
- ERR_FAIL_COND_V_MSG(conn_target_id.is_valid() && !ObjectDB::get_instance(conn_target_id), Variant(),
+ ERR_FAIL_COND_MSG(target_id.is_valid() && !ObjectDB::get_instance(target_id),
"Resumed after await, but class instance is gone.");
#endif
- if (p_argcount < 1) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = 1;
- return Variant();
- }
+ MonoArray *signal_args = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), p_argcount);
- Ref<SignalAwaiterHandle> self = *p_args[p_argcount - 1];
-
- if (self.is_null()) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = p_argcount - 1;
- r_error.expected = Variant::OBJECT;
- return Variant();
+ for (int i = 0; i < p_argcount; i++) {
+ MonoObject *boxed = GDMonoMarshal::variant_to_mono_object(*p_arguments[i]);
+ mono_array_setref(signal_args, i, boxed);
}
- set_completed(true);
-
- int signal_argc = p_argcount - 1;
- MonoArray *signal_args = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), signal_argc);
+ MonoObject *awaiter = awaiter_handle.get_target();
- for (int i = 0; i < signal_argc; i++) {
- MonoObject *boxed = GDMonoMarshal::variant_to_mono_object(*p_args[i]);
- mono_array_setref(signal_args, i, boxed);
+ if (!awaiter) {
+ r_call_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
+ return;
}
- MonoException *exc = NULL;
- GD_MONO_BEGIN_RUNTIME_INVOKE;
- CACHED_METHOD_THUNK(SignalAwaiter, SignalCallback).invoke(get_target(), signal_args, &exc);
- GD_MONO_END_RUNTIME_INVOKE;
+ MonoException *exc = nullptr;
+ CACHED_METHOD_THUNK(SignalAwaiter, SignalCallback).invoke(awaiter, signal_args, &exc);
if (exc) {
GDMonoUtils::set_pending_exception(exc);
- ERR_FAIL_V(Variant());
+ ERR_FAIL();
+ } else {
+ r_call_error.error = Callable::CallError::CALL_OK;
}
+}
- return Variant();
+SignalAwaiterCallable::SignalAwaiterCallable(Object *p_target, MonoObject *p_awaiter, const StringName &p_signal) :
+ target_id(p_target->get_instance_id()),
+ awaiter_handle(MonoGCHandleData::new_strong_handle(p_awaiter)),
+ signal(p_signal) {
}
-void SignalAwaiterHandle::_bind_methods() {
+SignalAwaiterCallable::~SignalAwaiterCallable() {
+ awaiter_handle.release();
+}
- ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "_signal_callback", &SignalAwaiterHandle::_signal_callback, MethodInfo("_signal_callback"));
+bool EventSignalCallable::compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) {
+ const EventSignalCallable *a = static_cast<const EventSignalCallable *>(p_a);
+ const EventSignalCallable *b = static_cast<const EventSignalCallable *>(p_b);
+
+ if (a->owner != b->owner)
+ return false;
+
+ if (a->event_signal != b->event_signal)
+ return false;
+
+ return true;
}
-SignalAwaiterHandle::SignalAwaiterHandle(MonoObject *p_managed) :
- MonoGCHandle(MonoGCHandle::new_strong_handle(p_managed), STRONG_HANDLE) {}
+bool EventSignalCallable::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) {
+ if (compare_equal(p_a, p_b))
+ return false;
+ return p_a < p_b;
+}
-SignalAwaiterHandle::~SignalAwaiterHandle() {
+uint32_t EventSignalCallable::hash() const {
+ uint32_t hash = event_signal->field->get_name().hash();
+ return hash_djb2_one_64(owner->get_instance_id(), hash);
+}
- if (!completed) {
- MonoObject *awaiter = get_target();
+String EventSignalCallable::get_as_text() const {
+ String class_name = owner->get_class();
+ Ref<Script> script = owner->get_script();
+ if (script.is_valid() && script->get_path().is_resource_file()) {
+ class_name += "(" + script->get_path().get_file() + ")";
+ }
+ StringName signal = event_signal->field->get_name();
+ return class_name + "::EventSignalMiddleman::" + String(signal);
+}
- if (awaiter) {
- MonoException *exc = NULL;
- GD_MONO_BEGIN_RUNTIME_INVOKE;
- CACHED_METHOD_THUNK(SignalAwaiter, FailureCallback).invoke(awaiter, &exc);
- GD_MONO_END_RUNTIME_INVOKE;
+CallableCustom::CompareEqualFunc EventSignalCallable::get_compare_equal_func() const {
+ return compare_equal_func_ptr;
+}
- if (exc) {
- GDMonoUtils::set_pending_exception(exc);
- ERR_FAIL();
- }
- }
+CallableCustom::CompareLessFunc EventSignalCallable::get_compare_less_func() const {
+ return compare_less_func_ptr;
+}
+
+ObjectID EventSignalCallable::get_object() const {
+ return owner->get_instance_id();
+}
+
+StringName EventSignalCallable::get_signal() const {
+ return event_signal->field->get_name();
+}
+
+void EventSignalCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
+ r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; // Can't find anything better
+ r_return_value = Variant();
+
+ ERR_FAIL_COND(p_argcount < event_signal->invoke_method->get_parameters_count());
+
+ CSharpInstance *csharp_instance = CAST_CSHARP_INSTANCE(owner->get_script_instance());
+ ERR_FAIL_NULL(csharp_instance);
+
+ MonoObject *owner_managed = csharp_instance->get_mono_object();
+ ERR_FAIL_NULL(owner_managed);
+
+ MonoObject *delegate_field_value = event_signal->field->get_value(owner_managed);
+ if (!delegate_field_value) {
+ r_call_error.error = Callable::CallError::CALL_OK;
+ return;
}
+
+ MonoException *exc = nullptr;
+ event_signal->invoke_method->invoke(delegate_field_value, p_arguments, &exc);
+
+ if (exc) {
+ GDMonoUtils::set_pending_exception(exc);
+ ERR_FAIL();
+ } else {
+ r_call_error.error = Callable::CallError::CALL_OK;
+ }
+}
+
+EventSignalCallable::EventSignalCallable(Object *p_owner, const CSharpScript::EventSignal *p_event_signal) :
+ owner(p_owner),
+ event_signal(p_event_signal) {
}
diff --git a/modules/mono/signal_awaiter_utils.h b/modules/mono/signal_awaiter_utils.h
index 012f6e5bb3..c550315a23 100644
--- a/modules/mono/signal_awaiter_utils.h
+++ b/modules/mono/signal_awaiter_utils.h
@@ -32,40 +32,66 @@
#define SIGNAL_AWAITER_UTILS_H
#include "core/reference.h"
+
+#include "csharp_script.h"
#include "mono_gc_handle.h"
-namespace SignalAwaiterUtils {
+Error gd_mono_connect_signal_awaiter(Object *p_source, const StringName &p_signal, Object *p_target, MonoObject *p_awaiter);
+
+class SignalAwaiterCallable : public CallableCustom {
+ ObjectID target_id;
+ MonoGCHandleData awaiter_handle;
+ StringName signal;
+
+public:
+ static bool compare_equal(const CallableCustom *p_a, const CallableCustom *p_b);
+ static bool compare_less(const CallableCustom *p_a, const CallableCustom *p_b);
-Error connect_signal_awaiter(Object *p_source, const String &p_signal, Object *p_target, MonoObject *p_awaiter);
-}
+ static constexpr CompareEqualFunc compare_equal_func_ptr = &SignalAwaiterCallable::compare_equal;
+ static constexpr CompareEqualFunc compare_less_func_ptr = &SignalAwaiterCallable::compare_less;
-class SignalAwaiterHandle : public MonoGCHandle {
+ uint32_t hash() const override;
- GDCLASS(SignalAwaiterHandle, MonoGCHandle);
+ String get_as_text() const override;
- bool completed;
+ CompareEqualFunc get_compare_equal_func() const override;
+ CompareLessFunc get_compare_less_func() const override;
-#ifdef DEBUG_ENABLED
- ObjectID conn_target_id;
-#endif
+ ObjectID get_object() const override;
- Variant _signal_callback(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
+ _FORCE_INLINE_ StringName get_signal() const { return signal; }
+
+ void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
+
+ SignalAwaiterCallable(Object *p_target, MonoObject *p_awaiter, const StringName &p_signal);
+ ~SignalAwaiterCallable();
+};
-protected:
- static void _bind_methods();
+class EventSignalCallable : public CallableCustom {
+ Object *owner;
+ const CSharpScript::EventSignal *event_signal;
public:
- _FORCE_INLINE_ bool is_completed() { return completed; }
- _FORCE_INLINE_ void set_completed(bool p_completed) { completed = p_completed; }
+ static bool compare_equal(const CallableCustom *p_a, const CallableCustom *p_b);
+ static bool compare_less(const CallableCustom *p_a, const CallableCustom *p_b);
+
+ static constexpr CompareEqualFunc compare_equal_func_ptr = &EventSignalCallable::compare_equal;
+ static constexpr CompareEqualFunc compare_less_func_ptr = &EventSignalCallable::compare_less;
+
+ uint32_t hash() const override;
+
+ String get_as_text() const override;
+
+ CompareEqualFunc get_compare_equal_func() const override;
+ CompareLessFunc get_compare_less_func() const override;
+
+ ObjectID get_object() const override;
+
+ StringName get_signal() const;
-#ifdef DEBUG_ENABLED
- _FORCE_INLINE_ void set_connection_target(Object *p_target) {
- conn_target_id = p_target->get_instance_id();
- }
-#endif
+ void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
- SignalAwaiterHandle(MonoObject *p_managed);
- ~SignalAwaiterHandle();
+ EventSignalCallable(Object *p_owner, const CSharpScript::EventSignal *p_event_signal);
};
#endif // SIGNAL_AWAITER_UTILS_H
diff --git a/modules/mono/utils/macros.h b/modules/mono/utils/macros.h
index 754000dc14..8650d6cc09 100644
--- a/modules/mono/utils/macros.h
+++ b/modules/mono/utils/macros.h
@@ -36,38 +36,6 @@
#define _GD_VARNAME_CONCAT_(m_a, m_b, m_c) _GD_VARNAME_CONCAT_A_(m_a, m_b, m_c)
#define GD_UNIQUE_NAME(m_name) _GD_VARNAME_CONCAT_(m_name, _, __COUNTER__)
-// static assert
-// TODO: Get rid of this macro once we upgrade to C++11
-
-#ifdef __cpp_static_assert
-#define GD_STATIC_ASSERT(m_cond) static_assert((m_cond), "Condition '" #m_cond "' failed")
-#else
-#define GD_STATIC_ASSERT(m_cond) typedef int GD_UNIQUE_NAME(godot_static_assert)[((m_cond) ? 1 : -1)]
-#endif
-
-// final
-// TODO: Get rid of this macro once we upgrade to C++11
-
-#if (__cplusplus >= 201103L)
-#define GD_FINAL final
-#else
-#define GD_FINAL
-#endif
-
-// noreturn
-// TODO: Get rid of this macro once we upgrade to C++11
-
-#if (__cplusplus >= 201103L)
-#define GD_NORETURN [[noreturn]]
-#elif defined(__GNUC__)
-#define GD_NORETURN __attribute__((noreturn))
-#elif defined(_MSC_VER)
-#define GD_NORETURN __declspec(noreturn)
-#else
-#define GD_NORETURN
-#pragma message "Macro GD_NORETURN will have no effect"
-#endif
-
// unreachable
#if defined(_MSC_VER)
@@ -81,4 +49,25 @@
} while (true);
#endif
+namespace gdmono {
+
+template <typename F>
+struct ScopeExit {
+ ScopeExit(F p_exit_func) :
+ exit_func(p_exit_func) {}
+ ~ScopeExit() { exit_func(); }
+ F exit_func;
+};
+
+class ScopeExitAux {
+public:
+ template <typename F>
+ ScopeExit<F> operator+(F p_exit_func) { return ScopeExit<F>(p_exit_func); }
+};
+
+} // namespace gdmono
+
+#define SCOPE_EXIT \
+ auto GD_UNIQUE_NAME(gd_scope_exit) = gdmono::ScopeExitAux() + [=]()
+
#endif // UTIL_MACROS_H
diff --git a/modules/mono/utils/mono_reg_utils.cpp b/modules/mono/utils/mono_reg_utils.cpp
index c1cd5f1db4..8f0ad8ba5e 100644
--- a/modules/mono/utils/mono_reg_utils.cpp
+++ b/modules/mono/utils/mono_reg_utils.cpp
@@ -73,13 +73,13 @@ LONG _RegKeyQueryString(HKEY hKey, const String &p_value_name, String &r_value)
buffer.resize(512);
DWORD dwBufferSize = buffer.size();
- LONG res = RegQueryValueExW(hKey, p_value_name.c_str(), 0, NULL, (LPBYTE)buffer.ptr(), &dwBufferSize);
+ LONG res = RegQueryValueExW(hKey, p_value_name.c_str(), 0, nullptr, (LPBYTE)buffer.ptr(), &dwBufferSize);
if (res == ERROR_MORE_DATA) {
// dwBufferSize now contains the actual size
Vector<WCHAR> buffer;
buffer.resize(dwBufferSize);
- res = RegQueryValueExW(hKey, p_value_name.c_str(), 0, NULL, (LPBYTE)buffer.ptr(), &dwBufferSize);
+ res = RegQueryValueExW(hKey, p_value_name.c_str(), 0, nullptr, (LPBYTE)buffer.ptr(), &dwBufferSize);
}
if (res == ERROR_SUCCESS) {
@@ -180,7 +180,7 @@ String find_msbuild_tools_path() {
String output;
int exit_code;
- OS::get_singleton()->execute(vswhere_path, vswhere_args, true, NULL, &output, &exit_code);
+ OS::get_singleton()->execute(vswhere_path, vswhere_args, true, nullptr, &output, &exit_code);
if (exit_code == 0) {
Vector<String> lines = output.split("\n");
diff --git a/modules/mono/utils/osx_utils.cpp b/modules/mono/utils/osx_utils.cpp
index 432b306414..8fadf3c109 100644
--- a/modules/mono/utils/osx_utils.cpp
+++ b/modules/mono/utils/osx_utils.cpp
@@ -39,9 +39,9 @@
bool osx_is_app_bundle_installed(const String &p_bundle_id) {
- CFURLRef app_url = NULL;
- CFStringRef bundle_id = CFStringCreateWithCString(NULL, p_bundle_id.utf8(), kCFStringEncodingUTF8);
- OSStatus result = LSFindApplicationForInfo(kLSUnknownCreator, bundle_id, NULL, NULL, &app_url);
+ CFURLRef app_url = nullptr;
+ CFStringRef bundle_id = CFStringCreateWithCString(nullptr, p_bundle_id.utf8(), kCFStringEncodingUTF8);
+ OSStatus result = LSFindApplicationForInfo(kLSUnknownCreator, bundle_id, nullptr, nullptr, &app_url);
CFRelease(bundle_id);
if (app_url)
diff --git a/modules/mono/utils/path_utils.cpp b/modules/mono/utils/path_utils.cpp
index 545da6c79e..973375a471 100644
--- a/modules/mono/utils/path_utils.cpp
+++ b/modules/mono/utils/path_utils.cpp
@@ -80,7 +80,7 @@ String find_executable(const String &p_name) {
String cwd() {
#ifdef WINDOWS_ENABLED
- const DWORD expected_size = ::GetCurrentDirectoryW(0, NULL);
+ const DWORD expected_size = ::GetCurrentDirectoryW(0, nullptr);
String buffer;
buffer.resize((int)expected_size);
@@ -90,7 +90,7 @@ String cwd() {
return buffer.simplify_path();
#else
char buffer[PATH_MAX];
- if (::getcwd(buffer, sizeof(buffer)) == NULL)
+ if (::getcwd(buffer, sizeof(buffer)) == nullptr)
return ".";
String result;
@@ -114,12 +114,12 @@ String realpath(const String &p_path) {
// Open file without read/write access
HANDLE hFile = ::CreateFileW(p_path.c_str(), 0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
- NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (hFile == INVALID_HANDLE_VALUE)
return p_path;
- const DWORD expected_size = ::GetFinalPathNameByHandleW(hFile, NULL, 0, FILE_NAME_NORMALIZED);
+ const DWORD expected_size = ::GetFinalPathNameByHandleW(hFile, nullptr, 0, FILE_NAME_NORMALIZED);
if (expected_size == 0) {
::CloseHandle(hFile);
@@ -133,7 +133,7 @@ String realpath(const String &p_path) {
::CloseHandle(hFile);
return buffer.simplify_path();
#elif UNIX_ENABLED
- char *resolved_path = ::realpath(p_path.utf8().get_data(), NULL);
+ char *resolved_path = ::realpath(p_path.utf8().get_data(), nullptr);
if (!resolved_path)
return p_path;
diff --git a/modules/mono/utils/string_utils.cpp b/modules/mono/utils/string_utils.cpp
index 49c4fb3f73..907811355f 100644
--- a/modules/mono/utils/string_utils.cpp
+++ b/modules/mono/utils/string_utils.cpp
@@ -212,7 +212,7 @@ String str_format(const char *p_format, ...) {
#define gd_vscprintf(m_format, m_args_copy) _vscprintf(m_format, m_args_copy)
#else
#define gd_vsnprintf(m_buffer, m_count, m_format, m_args_copy) vsnprintf(m_buffer, m_count, m_format, m_args_copy)
-#define gd_vscprintf(m_format, m_args_copy) vsnprintf(NULL, 0, p_format, m_args_copy)
+#define gd_vscprintf(m_format, m_args_copy) vsnprintf(nullptr, 0, p_format, m_args_copy)
#endif
String str_format(const char *p_format, va_list p_list) {
diff --git a/modules/mono/utils/thread_local.h b/modules/mono/utils/thread_local.h
deleted file mode 100644
index b1cc2e37ea..0000000000
--- a/modules/mono/utils/thread_local.h
+++ /dev/null
@@ -1,177 +0,0 @@
-/*************************************************************************/
-/* thread_local.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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 THREAD_LOCAL_H
-#define THREAD_LOCAL_H
-
-#ifdef HAVE_CXX11_THREAD_LOCAL
-#define _THREAD_LOCAL_(m_t) thread_local m_t
-#else
-
-#if !defined(__GNUC__) && !defined(_MSC_VER)
-#error Platform or compiler not supported
-#endif
-
-#if defined(__GNUC__)
-
-#ifdef HAVE_GCC___THREAD
-#define _THREAD_LOCAL_(m_t) __thread m_t
-#else
-#define USE_CUSTOM_THREAD_LOCAL
-#endif
-
-#elif defined(_MSC_VER)
-
-#ifdef HAVE_DECLSPEC_THREAD
-#define _THREAD_LOCAL_(m_t) __declspec(thread) m_t
-#else
-#define USE_CUSTOM_THREAD_LOCAL
-#endif
-
-#endif // __GNUC__ _MSC_VER
-
-#endif // HAVE_CXX11_THREAD_LOCAL
-
-#ifdef USE_CUSTOM_THREAD_LOCAL
-#define _THREAD_LOCAL_(m_t) ThreadLocal<m_t>
-#endif
-
-#include "core/typedefs.h"
-
-#ifdef WINDOWS_ENABLED
-#define _CALLBACK_FUNC_ __stdcall
-#else
-#define _CALLBACK_FUNC_
-#endif
-
-struct ThreadLocalStorage {
-
- void *get_value() const;
- void set_value(void *p_value) const;
-
- void alloc(void(_CALLBACK_FUNC_ *p_destr_callback)(void *));
- void free();
-
-private:
- struct Impl;
- Impl *pimpl;
-};
-
-template <typename T>
-class ThreadLocal {
-
- ThreadLocalStorage storage;
-
- T init_val;
-
- static void _CALLBACK_FUNC_ destr_callback(void *tls_data) {
- memdelete(static_cast<T *>(tls_data));
- }
-
- T *_tls_get_value() const {
- void *tls_data = storage.get_value();
-
- if (tls_data)
- return static_cast<T *>(tls_data);
-
- T *data = memnew(T(init_val));
-
- storage.set_value(data);
-
- return data;
- }
-
- void _initialize(const T &p_init_val) {
- init_val = p_init_val;
- storage.alloc(&destr_callback);
- }
-
-public:
- ThreadLocal() {
- _initialize(T());
- }
-
- ThreadLocal(const T &p_init_val) {
- _initialize(p_init_val);
- }
-
- ThreadLocal(const ThreadLocal &other) {
- _initialize(*other._tls_get_value());
- }
-
- ~ThreadLocal() {
- storage.free();
- }
-
- _FORCE_INLINE_ T *operator&() const {
- return _tls_get_value();
- }
-
- _FORCE_INLINE_ operator T &() const {
- return *_tls_get_value();
- }
-
- _FORCE_INLINE_ ThreadLocal &operator=(const T &val) {
- T *ptr = _tls_get_value();
- *ptr = val;
- return *this;
- }
-};
-
-struct FlagScopeGuard {
-
- FlagScopeGuard(bool &p_flag) :
- flag(p_flag) {
- flag = !flag;
- }
-
- ~FlagScopeGuard() {
- flag = !flag;
- }
-
-private:
- bool &flag;
-};
-
-#undef _CALLBACK_FUNC_
-
-#define _TLS_RECURSION_GUARD_V_(m_ret) \
- static _THREAD_LOCAL_(bool) _recursion_flag_ = false; \
- if (_recursion_flag_) \
- return m_ret; \
- FlagScopeGuard _recursion_guard_(_recursion_flag_);
-
-#define _TLS_RECURSION_GUARD_ \
- static _THREAD_LOCAL_(bool) _recursion_flag_ = false; \
- if (_recursion_flag_) \
- return; \
- FlagScopeGuard _recursion_guard_(_recursion_flag_);
-
-#endif // THREAD_LOCAL_H