summaryrefslogtreecommitdiff
path: root/modules/mono
diff options
context:
space:
mode:
Diffstat (limited to 'modules/mono')
-rw-r--r--modules/mono/SCsub341
-rw-r--r--modules/mono/build_scripts/__init__.py0
-rw-r--r--modules/mono/build_scripts/godotsharptools_build.py263
-rw-r--r--modules/mono/build_scripts/make_cs_compressed_header.py61
-rw-r--r--modules/mono/build_scripts/mono_configure.py414
-rw-r--r--modules/mono/build_scripts/mono_reg_utils.py (renamed from modules/mono/mono_reg_utils.py)14
-rw-r--r--modules/mono/build_scripts/tls_configure.py (renamed from modules/mono/tls_configure.py)0
-rw-r--r--modules/mono/config.py426
-rw-r--r--modules/mono/csharp_script.cpp203
-rw-r--r--modules/mono/csharp_script.h39
-rw-r--r--modules/mono/doc_classes/@C#.xml4
-rw-r--r--modules/mono/doc_classes/CSharpScript.xml4
-rw-r--r--modules/mono/doc_classes/GodotSharp.xml4
-rw-r--r--modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs41
-rw-r--r--modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj2
-rw-r--r--modules/mono/editor/GodotSharpTools/Project/IdentifierUtils.cs199
-rw-r--r--modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs5
-rw-r--r--modules/mono/editor/bindings_generator.cpp1580
-rw-r--r--modules/mono/editor/bindings_generator.h112
-rw-r--r--modules/mono/editor/csharp_project.cpp2
-rw-r--r--modules/mono/editor/godotsharp_builds.cpp65
-rw-r--r--modules/mono/editor/godotsharp_editor.cpp82
-rw-r--r--modules/mono/editor/godotsharp_editor.h8
-rw-r--r--modules/mono/editor/godotsharp_export.cpp6
-rw-r--r--modules/mono/editor/mono_bottom_panel.cpp21
-rw-r--r--modules/mono/editor/mono_bottom_panel.h4
-rw-r--r--modules/mono/editor/script_class_parser.cpp43
-rw-r--r--modules/mono/glue/Managed/Files/Array.cs194
-rw-r--r--modules/mono/glue/Managed/Files/Basis.cs435
-rw-r--r--modules/mono/glue/Managed/Files/Color.cs16
-rw-r--r--modules/mono/glue/Managed/Files/DebuggingUtils.cs6
-rw-r--r--modules/mono/glue/Managed/Files/Dictionary.cs265
-rw-r--r--modules/mono/glue/Managed/Files/DynamicObject.cs213
-rw-r--r--modules/mono/glue/Managed/Files/GD.cs128
-rw-r--r--modules/mono/glue/Managed/Files/GodotTraceListener.cs37
-rw-r--r--modules/mono/glue/Managed/Files/MarshalUtils.cs60
-rw-r--r--modules/mono/glue/Managed/Files/Mathf.cs28
-rw-r--r--modules/mono/glue/Managed/Files/MathfEx.cs5
-rw-r--r--modules/mono/glue/Managed/Files/NodePath.cs14
-rw-r--r--modules/mono/glue/Managed/Files/Object.base.cs44
-rw-r--r--modules/mono/glue/Managed/Files/Plane.cs2
-rw-r--r--modules/mono/glue/Managed/Files/Quat.cs33
-rw-r--r--modules/mono/glue/Managed/Files/RID.cs14
-rw-r--r--modules/mono/glue/Managed/Files/Transform.cs38
-rw-r--r--modules/mono/glue/Managed/Files/Transform2D.cs176
-rw-r--r--modules/mono/glue/Managed/Files/Vector2.cs33
-rw-r--r--modules/mono/glue/Managed/Files/Vector3.cs28
-rw-r--r--modules/mono/glue/Managed/IgnoredFiles/Variant.cs11
-rw-r--r--modules/mono/glue/Managed/Managed.csproj2
-rw-r--r--modules/mono/glue/arguments_vector.h68
-rw-r--r--modules/mono/glue/base_object_glue.cpp76
-rw-r--r--modules/mono/glue/base_object_glue.h14
-rw-r--r--modules/mono/glue/collections_glue.cpp38
-rw-r--r--modules/mono/glue/collections_glue.h24
-rw-r--r--modules/mono/glue/gd_glue.cpp45
-rw-r--r--modules/mono/glue/gd_glue.h22
-rw-r--r--modules/mono/glue/glue_header.h2
-rw-r--r--modules/mono/godotsharp_dirs.cpp25
-rw-r--r--modules/mono/godotsharp_dirs.h5
-rw-r--r--modules/mono/mono_gd/gd_mono.cpp136
-rw-r--r--modules/mono/mono_gd/gd_mono.h2
-rw-r--r--modules/mono/mono_gd/gd_mono_assembly.cpp20
-rw-r--r--modules/mono/mono_gd/gd_mono_assembly.h4
-rw-r--r--modules/mono/mono_gd/gd_mono_class.cpp18
-rw-r--r--modules/mono/mono_gd/gd_mono_class.h5
-rw-r--r--modules/mono/mono_gd/gd_mono_field.cpp34
-rw-r--r--modules/mono/mono_gd/gd_mono_field.h2
-rw-r--r--modules/mono/mono_gd/gd_mono_header.h18
-rw-r--r--modules/mono/mono_gd/gd_mono_internals.cpp49
-rw-r--r--modules/mono/mono_gd/gd_mono_log.cpp56
-rw-r--r--modules/mono/mono_gd/gd_mono_log.h1
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.cpp220
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.h24
-rw-r--r--modules/mono/mono_gd/gd_mono_method.h2
-rw-r--r--modules/mono/mono_gd/gd_mono_property.h2
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.cpp52
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.h40
-rw-r--r--modules/mono/mono_gd/i_mono_class_member.h (renamed from modules/mono/mono_gd/gd_mono_class_member.h)8
-rw-r--r--modules/mono/mono_gd/managed_type.cpp58
-rw-r--r--modules/mono/mono_gd/managed_type.h58
-rw-r--r--modules/mono/signal_awaiter_utils.cpp4
-rw-r--r--modules/mono/utils/macros.h6
-rw-r--r--modules/mono/utils/mono_reg_utils.cpp10
-rw-r--r--modules/mono/utils/string_utils.cpp49
-rw-r--r--modules/mono/utils/string_utils.h13
-rw-r--r--modules/mono/utils/thread_local.h4
86 files changed, 4652 insertions, 2287 deletions
diff --git a/modules/mono/SCsub b/modules/mono/SCsub
index 706949154e..341d57f3e4 100644
--- a/modules/mono/SCsub
+++ b/modules/mono/SCsub
@@ -5,70 +5,6 @@ Import('env_modules')
env_mono = env_modules.Clone()
-# TODO move functions to their own modules
-
-def make_cs_files_header(src, dst, version_dst):
- from compat import byte_to_str
-
- with open(dst, 'w') as header:
- header.write('/* THIS FILE IS GENERATED DO NOT EDIT */\n')
- header.write('#ifndef CS_COMPRESSED_H\n')
- header.write('#define CS_COMPRESSED_H\n\n')
- header.write('#ifdef TOOLS_ENABLED\n\n')
- header.write('#include "core/map.h"\n')
- header.write('#include "core/ustring.h"\n')
- inserted_files = ''
- import os
- latest_mtime = 0
- cs_file_count = 0
- for root, _, files in os.walk(src):
- files = [f for f in files if f.endswith('.cs')]
- for file in files:
- cs_file_count += 1
- filepath = os.path.join(root, file)
- filepath_src_rel = os.path.relpath(filepath, src)
- mtime = os.path.getmtime(filepath)
- latest_mtime = mtime if mtime > latest_mtime else latest_mtime
- with open(filepath, 'rb') as f:
- buf = f.read()
- decomp_size = len(buf)
- import zlib
- buf = zlib.compress(buf)
- name = str(cs_file_count)
- header.write('\n')
- header.write('// ' + filepath_src_rel + '\n')
- header.write('static const int _cs_' + name + '_compressed_size = ' + str(len(buf)) + ';\n')
- header.write('static const int _cs_' + name + '_uncompressed_size = ' + str(decomp_size) + ';\n')
- header.write('static const unsigned char _cs_' + name + '_compressed[] = { ')
- for i, buf_idx in enumerate(range(len(buf))):
- if i > 0:
- header.write(', ')
- header.write(byte_to_str(buf[buf_idx]))
- inserted_files += '\tr_files.insert("' + filepath_src_rel.replace('\\', '\\\\') + '", ' \
- 'CompressedFile(_cs_' + name + '_compressed_size, ' \
- '_cs_' + name + '_uncompressed_size, ' \
- '_cs_' + name + '_compressed));\n'
- header.write(' };\n')
- header.write('\nstruct CompressedFile\n' '{\n'
- '\tint compressed_size;\n' '\tint uncompressed_size;\n' '\tconst unsigned char* data;\n'
- '\n\tCompressedFile(int p_comp_size, int p_uncomp_size, const unsigned char* p_data)\n'
- '\t{\n' '\t\tcompressed_size = p_comp_size;\n' '\t\tuncompressed_size = p_uncomp_size;\n'
- '\t\tdata = p_data;\n' '\t}\n' '\n\tCompressedFile() {}\n' '};\n'
- '\nvoid get_compressed_files(Map<String, CompressedFile>& r_files)\n' '{\n' + inserted_files + '}\n'
- )
- header.write('\n#endif // TOOLS_ENABLED\n')
- header.write('\n#endif // CS_COMPRESSED_H\n')
-
- glue_version = int(latest_mtime) # The latest modified time will do for now
-
- with open(version_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')
-
-
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')
@@ -77,7 +13,12 @@ env_mono.add_source_files(env.modules_sources, 'utils/*.cpp')
if env['tools']:
env_mono.add_source_files(env.modules_sources, 'editor/*.cpp')
# NOTE: It is safe to generate this file here, since this is still executed serially
- make_cs_files_header('glue/Managed/Files', 'glue/cs_compressed.gen.h', 'glue/cs_glue_version.gen.h')
+ import build_scripts.make_cs_compressed_header as make_cs_compressed_header
+ make_cs_compressed_header.generate_header(
+ 'glue/Managed/Files',
+ 'glue/cs_compressed.gen.h',
+ 'glue/cs_glue_version.gen.h'
+ )
vars = Variables()
vars.Add(BoolVariable('mono_glue', 'Build with the mono glue sources', True))
@@ -88,273 +29,29 @@ vars.Update(env_mono)
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('Missing mono glue sources. Did you forget to generate them?')
+
if env_mono['tools'] or env_mono['target'] != 'release':
env_mono.Append(CPPDEFINES=['GD_MONO_HOT_RELOAD'])
-# Configure TLS checks
+# Configure Thread Local Storage
+
+import build_scripts.tls_configure as tls_configure
-import tls_configure
conf = Configure(env_mono)
tls_configure.configure(conf)
env_mono = conf.Finish()
+# Configure Mono
-# Build GodotSharpTools solution
-
-
-import os
-
-
-def find_nuget_unix():
- import os
-
- 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')
- 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
-
- for hint_dir in hint_dirs:
- 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'
-
- for hint_dir in os.environ['PATH'].split(os.pathsep):
- hint_dir = hint_dir.strip('"')
- 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'
-
- return None
-
-
-def find_nuget_windows():
- import os
-
- 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')
- if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
- return hint_path
-
- import mono_reg_utils as monoreg
-
- mono_root = ''
- bits = env['bits']
-
- if bits == '32':
- if os.getenv('MONO32_PREFIX'):
- mono_root = os.getenv('MONO32_PREFIX')
- else:
- mono_root = monoreg.find_mono_root_dir(bits)
- else:
- if os.getenv('MONO64_PREFIX'):
- mono_root = os.getenv('MONO64_PREFIX')
- else:
- mono_root = monoreg.find_mono_root_dir(bits)
-
- if mono_root:
- 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):
- hint_dir = hint_dir.strip('"')
- 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
-
- return None
-
-
-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
-
- 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'
+import build_scripts.mono_configure as mono_configure
- 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'
+mono_configure.configure(env, env_mono)
- return None
+# Build GodotSharpTools
+import build_scripts.godotsharptools_build as godotsharptools_build
-def find_msbuild_windows():
- import mono_reg_utils as monoreg
-
- mono_root = ''
- bits = env['bits']
-
- if bits == '32':
- if os.getenv('MONO32_PREFIX'):
- mono_root = os.getenv('MONO32_PREFIX')
- else:
- mono_root = monoreg.find_mono_root_dir(bits)
- else:
- if os.getenv('MONO64_PREFIX'):
- mono_root = os.getenv('MONO64_PREFIX')
- else:
- mono_root = monoreg.find_mono_root_dir(bits)
-
- if not mono_root:
- raise RuntimeError('Cannot find mono root directory')
-
- framework_path = os.path.join(mono_root, 'lib', 'mono', '4.5')
- mono_bin_dir = os.path.join(mono_root, 'bin')
- msbuild_mono = os.path.join(mono_bin_dir, 'msbuild.bat')
-
- 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')
- }
- return (msbuild_mono, framework_path, mono_msbuild_env)
-
- msbuild_tools_path = monoreg.find_msbuild_tools_path_reg()
-
- if msbuild_tools_path:
- return (os.path.join(msbuild_tools_path, 'MSBuild.exe'), framework_path, {})
-
- return None
-
-
-def mono_build_solution(source, target, env):
- import subprocess
- import mono_reg_utils as monoreg
- from shutil import copyfile
-
- sln_path = os.path.abspath(str(source[0]))
- target_path = os.path.abspath(str(target[0]))
-
- framework_path = ''
- msbuild_env = os.environ.copy()
-
- # Needed when running from Developer Command Prompt for VS
- if 'PLATFORM' in msbuild_env:
- del msbuild_env['PLATFORM']
-
- # Find MSBuild
- if os.name == 'nt':
- msbuild_info = find_msbuild_windows()
- if msbuild_info is None:
- raise RuntimeError('Cannot find MSBuild executable')
- msbuild_path = msbuild_info[0]
- framework_path = msbuild_info[1]
- msbuild_env.update(msbuild_info[2])
- else:
- msbuild_path = find_msbuild_unix('msbuild')
- if msbuild_path is None:
- xbuild_fallback = env['xbuild_fallback']
-
- 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')
-
- msbuild_path = find_msbuild_unix('xbuild')
-
- if msbuild_path is None:
- raise RuntimeError('Cannot find xbuild executable')
- else:
- raise RuntimeError('Cannot find MSBuild executable')
-
- print('MSBuild path: ' + msbuild_path)
-
- # Find NuGet
- nuget_path = find_nuget_windows() if os.name == 'nt' else find_nuget_unix()
- if nuget_path is None:
- raise RuntimeError('Cannot find NuGet executable')
-
- print('NuGet path: ' + nuget_path)
-
- # Do NuGet restore
-
- try:
- subprocess.check_call([nuget_path, 'restore', sln_path])
- except subprocess.CalledProcessError:
- raise RuntimeError('GodotSharpTools: NuGet restore failed')
-
- # Build solution
-
- build_config = 'Release'
-
- msbuild_args = [
- msbuild_path,
- sln_path,
- '/p:Configuration=' + build_config,
- ]
-
- if framework_path:
- msbuild_args += ['/p:FrameworkPathOverride=' + framework_path]
-
- try:
- subprocess.check_call(msbuild_args, env=msbuild_env)
- except subprocess.CalledProcessError:
- raise RuntimeError('GodotSharpTools: Build failed')
-
- # Copy files
-
- src_dir = os.path.abspath(os.path.join(sln_path, os.pardir, 'bin', build_config))
- dst_dir = os.path.abspath(os.path.join(target_path, os.pardir))
- asm_file = 'GodotSharpTools.dll'
-
- if not os.path.isdir(dst_dir):
- if os.path.exists(dst_dir):
- raise RuntimeError('Target directory is a file')
- os.makedirs(dst_dir)
-
- copyfile(os.path.join(src_dir, asm_file), os.path.join(dst_dir, asm_file))
-
- # Dependencies
- copyfile(os.path.join(src_dir, "DotNet.Glob.dll"), os.path.join(dst_dir, "DotNet.Glob.dll"))
-
-if env['tools']:
- output_dir = Dir('#bin').abspath
- editor_tools_dir = os.path.join(output_dir, 'GodotSharp', 'Tools')
-
- mono_sln_builder = Builder(action=mono_build_solution)
- env_mono.Append(BUILDERS={'MonoBuildSolution': mono_sln_builder})
- env_mono.MonoBuildSolution(
- os.path.join(editor_tools_dir, 'GodotSharpTools.dll'),
- 'editor/GodotSharpTools/GodotSharpTools.sln'
- )
+godotsharptools_build.build(env_mono)
diff --git a/modules/mono/build_scripts/__init__.py b/modules/mono/build_scripts/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/modules/mono/build_scripts/__init__.py
diff --git a/modules/mono/build_scripts/godotsharptools_build.py b/modules/mono/build_scripts/godotsharptools_build.py
new file mode 100644
index 0000000000..af3a5cb5c6
--- /dev/null
+++ b/modules/mono/build_scripts/godotsharptools_build.py
@@ -0,0 +1,263 @@
+# Build GodotSharpTools solution
+
+
+import os
+
+from SCons.Script import Builder, Dir
+
+
+def find_nuget_unix():
+ import os
+
+ 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')
+ 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
+
+ for hint_dir in hint_dirs:
+ 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'
+
+ for hint_dir in os.environ['PATH'].split(os.pathsep):
+ hint_dir = hint_dir.strip('"')
+ 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'
+
+ return None
+
+
+def find_nuget_windows(env):
+ import os
+
+ 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')
+ if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
+ return hint_path
+
+ from . import mono_reg_utils as monoreg
+
+ mono_root = ''
+ bits = env['bits']
+
+ if bits == '32':
+ if os.getenv('MONO32_PREFIX'):
+ mono_root = os.getenv('MONO32_PREFIX')
+ else:
+ mono_root = monoreg.find_mono_root_dir(bits)
+ else:
+ if os.getenv('MONO64_PREFIX'):
+ mono_root = os.getenv('MONO64_PREFIX')
+ else:
+ mono_root = monoreg.find_mono_root_dir(bits)
+
+ if mono_root:
+ 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):
+ hint_dir = hint_dir.strip('"')
+ 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
+
+ return None
+
+
+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
+
+ 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'
+
+ 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'
+
+ return None
+
+
+def find_msbuild_windows(env):
+ from . import mono_reg_utils as monoreg
+
+ mono_root = ''
+ bits = env['bits']
+
+ if bits == '32':
+ if os.getenv('MONO32_PREFIX'):
+ mono_root = os.getenv('MONO32_PREFIX')
+ else:
+ mono_root = monoreg.find_mono_root_dir(bits)
+ else:
+ if os.getenv('MONO64_PREFIX'):
+ mono_root = os.getenv('MONO64_PREFIX')
+ else:
+ mono_root = monoreg.find_mono_root_dir(bits)
+
+ if not mono_root:
+ raise RuntimeError('Cannot find mono root directory')
+
+ framework_path = os.path.join(mono_root, 'lib', 'mono', '4.5')
+ mono_bin_dir = os.path.join(mono_root, 'bin')
+ msbuild_mono = os.path.join(mono_bin_dir, 'msbuild.bat')
+
+ 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')
+ }
+ return (msbuild_mono, framework_path, mono_msbuild_env)
+
+ msbuild_tools_path = monoreg.find_msbuild_tools_path_reg()
+
+ if msbuild_tools_path:
+ return (os.path.join(msbuild_tools_path, 'MSBuild.exe'), framework_path, {})
+
+ return None
+
+
+def mono_build_solution(source, target, env):
+ import subprocess
+ from shutil import copyfile
+
+ sln_path = os.path.abspath(str(source[0]))
+ target_path = os.path.abspath(str(target[0]))
+
+ framework_path = ''
+ msbuild_env = os.environ.copy()
+
+ # Needed when running from Developer Command Prompt for VS
+ if 'PLATFORM' in msbuild_env:
+ del msbuild_env['PLATFORM']
+
+ # Find MSBuild
+ if os.name == 'nt':
+ msbuild_info = find_msbuild_windows(env)
+ if msbuild_info is None:
+ raise RuntimeError('Cannot find MSBuild executable')
+ msbuild_path = msbuild_info[0]
+ framework_path = msbuild_info[1]
+ msbuild_env.update(msbuild_info[2])
+ else:
+ msbuild_path = find_msbuild_unix('msbuild')
+ if msbuild_path is None:
+ xbuild_fallback = env['xbuild_fallback']
+
+ 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')
+
+ msbuild_path = find_msbuild_unix('xbuild')
+
+ if msbuild_path is None:
+ raise RuntimeError('Cannot find xbuild executable')
+ else:
+ raise RuntimeError('Cannot find MSBuild executable')
+
+ print('MSBuild path: ' + msbuild_path)
+
+ # Find NuGet
+ 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')
+
+ print('NuGet path: ' + nuget_path)
+
+ # Do NuGet restore
+
+ try:
+ subprocess.check_call([nuget_path, 'restore', sln_path])
+ except subprocess.CalledProcessError:
+ raise RuntimeError('GodotSharpTools: NuGet restore failed')
+
+ # Build solution
+
+ build_config = 'Release'
+
+ msbuild_args = [
+ msbuild_path,
+ sln_path,
+ '/p:Configuration=' + build_config,
+ ]
+
+ if framework_path:
+ msbuild_args += ['/p:FrameworkPathOverride=' + framework_path]
+
+ try:
+ subprocess.check_call(msbuild_args, env=msbuild_env)
+ except subprocess.CalledProcessError:
+ raise RuntimeError('GodotSharpTools: Build failed')
+
+ # Copy files
+
+ src_dir = os.path.abspath(os.path.join(sln_path, os.pardir, 'bin', build_config))
+ dst_dir = os.path.abspath(os.path.join(target_path, os.pardir))
+ asm_file = 'GodotSharpTools.dll'
+
+ if not os.path.isdir(dst_dir):
+ if os.path.exists(dst_dir):
+ raise RuntimeError('Target directory is a file')
+ os.makedirs(dst_dir)
+
+ copyfile(os.path.join(src_dir, asm_file), os.path.join(dst_dir, asm_file))
+
+ # Dependencies
+ copyfile(os.path.join(src_dir, "DotNet.Glob.dll"), os.path.join(dst_dir, "DotNet.Glob.dll"))
+
+def build(env_mono):
+ if not env_mono['tools']:
+ return
+
+ output_dir = Dir('#bin').abspath
+ editor_tools_dir = os.path.join(output_dir, 'GodotSharp', 'Tools')
+
+ mono_sln_builder = Builder(action=mono_build_solution)
+ env_mono.Append(BUILDERS={'MonoBuildSolution': mono_sln_builder})
+ env_mono.MonoBuildSolution(
+ os.path.join(editor_tools_dir, 'GodotSharpTools.dll'),
+ 'editor/GodotSharpTools/GodotSharpTools.sln'
+ )
diff --git a/modules/mono/build_scripts/make_cs_compressed_header.py b/modules/mono/build_scripts/make_cs_compressed_header.py
new file mode 100644
index 0000000000..1f9177cef8
--- /dev/null
+++ b/modules/mono/build_scripts/make_cs_compressed_header.py
@@ -0,0 +1,61 @@
+
+def generate_header(src, dst, version_dst):
+ from compat import byte_to_str
+
+ with open(dst, 'w') as header:
+ header.write('/* THIS FILE IS GENERATED DO NOT EDIT */\n')
+ header.write('#ifndef CS_COMPRESSED_H\n')
+ header.write('#define CS_COMPRESSED_H\n\n')
+ header.write('#ifdef TOOLS_ENABLED\n\n')
+ header.write('#include "core/map.h"\n')
+ header.write('#include "core/ustring.h"\n')
+ inserted_files = ''
+ import os
+ latest_mtime = 0
+ cs_file_count = 0
+ for root, _, files in os.walk(src):
+ files = [f for f in files if f.endswith('.cs')]
+ for file in files:
+ cs_file_count += 1
+ filepath = os.path.join(root, file)
+ filepath_src_rel = os.path.relpath(filepath, src)
+ mtime = os.path.getmtime(filepath)
+ latest_mtime = mtime if mtime > latest_mtime else latest_mtime
+ with open(filepath, 'rb') as f:
+ buf = f.read()
+ decomp_size = len(buf)
+ import zlib
+ buf = zlib.compress(buf)
+ name = str(cs_file_count)
+ header.write('\n')
+ header.write('// ' + filepath_src_rel + '\n')
+ header.write('static const int _cs_' + name + '_compressed_size = ' + str(len(buf)) + ';\n')
+ header.write('static const int _cs_' + name + '_uncompressed_size = ' + str(decomp_size) + ';\n')
+ header.write('static const unsigned char _cs_' + name + '_compressed[] = { ')
+ for i, buf_idx in enumerate(range(len(buf))):
+ if i > 0:
+ header.write(', ')
+ header.write(byte_to_str(buf[buf_idx]))
+ inserted_files += '\tr_files.insert("' + filepath_src_rel.replace('\\', '\\\\') + '", ' \
+ 'CompressedFile(_cs_' + name + '_compressed_size, ' \
+ '_cs_' + name + '_uncompressed_size, ' \
+ '_cs_' + name + '_compressed));\n'
+ header.write(' };\n')
+ header.write('\nstruct CompressedFile\n' '{\n'
+ '\tint compressed_size;\n' '\tint uncompressed_size;\n' '\tconst unsigned char* data;\n'
+ '\n\tCompressedFile(int p_comp_size, int p_uncomp_size, const unsigned char* p_data)\n'
+ '\t{\n' '\t\tcompressed_size = p_comp_size;\n' '\t\tuncompressed_size = p_uncomp_size;\n'
+ '\t\tdata = p_data;\n' '\t}\n' '\n\tCompressedFile() {}\n' '};\n'
+ '\nvoid get_compressed_files(Map<String, CompressedFile>& r_files)\n' '{\n' + inserted_files + '}\n'
+ )
+ header.write('\n#endif // TOOLS_ENABLED\n')
+ header.write('\n#endif // CS_COMPRESSED_H\n')
+
+ glue_version = int(latest_mtime) # The latest modified time will do for now
+
+ with open(version_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/mono_configure.py b/modules/mono/build_scripts/mono_configure.py
new file mode 100644
index 0000000000..4cfa2a5b93
--- /dev/null
+++ b/modules/mono/build_scripts/mono_configure.py
@@ -0,0 +1,414 @@
+import imp
+import os
+import sys
+import subprocess
+
+from distutils.version import LooseVersion
+from SCons.Script import BoolVariable, Dir, Environment, Variables
+
+if os.name == 'nt':
+ from . import mono_reg_utils as monoreg
+
+
+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):
+ from shutil import copyfile
+
+ src_path = os.path.join(src_dir, name)
+ dst_path = os.path.join(dst_dir, name)
+
+ if not os.path.isdir(dst_dir):
+ os.mkdir(dst_dir)
+
+ copyfile(src_path, dst_path)
+
+
+def configure(env, env_mono):
+ envvars = Variables()
+ envvars.Add(BoolVariable('mono_static', 'Statically link mono', False))
+ envvars.Add(BoolVariable('copy_mono_root', 'Make a copy of the mono installation directory to bundle with the editor', False))
+ envvars.Update(env)
+
+ bits = env['bits']
+
+ tools_enabled = env['tools']
+ mono_static = env['mono_static']
+ copy_mono_root = env['copy_mono_root']
+
+ mono_lib_names = ['mono-2.0-sgen', 'monosgen-2.0']
+
+ if env['platform'] == 'windows':
+ mono_root = ''
+
+ if bits == '32':
+ if os.getenv('MONO32_PREFIX'):
+ mono_root = os.getenv('MONO32_PREFIX')
+ elif os.name == 'nt':
+ mono_root = monoreg.find_mono_root_dir(bits)
+ else:
+ if os.getenv('MONO64_PREFIX'):
+ mono_root = os.getenv('MONO64_PREFIX')
+ elif os.name == 'nt':
+ mono_root = monoreg.find_mono_root_dir(bits)
+
+ if not mono_root:
+ raise RuntimeError('Mono installation directory not found')
+
+ print('Found Mono root directory: ' + mono_root)
+
+ mono_version = mono_root_try_find_mono_version(mono_root)
+ configure_for_mono_version(env_mono, mono_version)
+
+ 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'))
+
+ if mono_static:
+ lib_suffix = Environment()['LIBSUFFIX']
+
+ if env.msvc:
+ mono_static_lib_name = 'libmono-static-sgen'
+ else:
+ 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)
+
+ if env.msvc:
+ env.Append(LINKFLAGS=mono_static_lib_name + lib_suffix)
+
+ env.Append(LINKFLAGS='Mincore' + lib_suffix)
+ env.Append(LINKFLAGS='msvcrt' + lib_suffix)
+ env.Append(LINKFLAGS='LIBCMT' + lib_suffix)
+ env.Append(LINKFLAGS='Psapi' + lib_suffix)
+ else:
+ env.Append(LINKFLAGS=os.path.join(mono_lib_path, mono_static_lib_name + lib_suffix))
+
+ env.Append(LIBS='psapi')
+ env.Append(LIBS='version')
+ else:
+ mono_lib_name = find_file_in_dir(mono_lib_path, mono_lib_names, extension='.lib')
+
+ if not mono_lib_name:
+ raise RuntimeError('Could not find mono library in: ' + mono_lib_path)
+
+ if env.msvc:
+ env.Append(LINKFLAGS=mono_lib_name + Environment()['LIBSUFFIX'])
+ else:
+ env.Append(LIBS=mono_lib_name)
+
+ mono_bin_path = os.path.join(mono_root, 'bin')
+
+ mono_dll_name = find_file_in_dir(mono_bin_path, mono_lib_names, extension='.dll')
+
+ if not mono_dll_name:
+ raise RuntimeError('Could not find mono shared library in: ' + mono_bin_path)
+
+ copy_file(mono_bin_path, 'bin', mono_dll_name + '.dll')
+ else:
+ is_apple = (sys.platform == 'darwin' or "osxcross" in env)
+
+ sharedlib_ext = '.dylib' if is_apple else '.so'
+
+ mono_root = ''
+ mono_lib_path = ''
+
+ if bits == '32':
+ if os.getenv('MONO32_PREFIX'):
+ mono_root = os.getenv('MONO32_PREFIX')
+ else:
+ if os.getenv('MONO64_PREFIX'):
+ mono_root = os.getenv('MONO64_PREFIX')
+
+ if not mono_root and is_apple:
+ # Try with some known directories under OSX
+ hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current', '/usr/local/var/homebrew/linked/mono']
+ for hint_dir in hint_dirs:
+ if os.path.isdir(hint_dir):
+ mono_root = hint_dir
+ break
+
+ # We can't use pkg-config to link mono statically,
+ # but we can still use it to find the mono root directory
+ if not mono_root and mono_static:
+ mono_root = pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext)
+ if not mono_root:
+ raise RuntimeError('Building with mono_static=yes, but failed to find the mono prefix with pkg-config. Specify one manually')
+
+ if mono_root:
+ print('Found Mono root directory: ' + mono_root)
+
+ mono_version = mono_root_try_find_mono_version(mono_root)
+ configure_for_mono_version(env_mono, mono_version)
+
+ mono_lib_path = os.path.join(mono_root, 'lib')
+
+ env.Append(LIBPATH=mono_lib_path)
+ env_mono.Prepend(CPPPATH=os.path.join(mono_root, 'include', 'mono-2.0'))
+
+ mono_lib = find_file_in_dir(mono_lib_path, mono_lib_names, prefix='lib', extension='.a')
+
+ if not mono_lib:
+ raise RuntimeError('Could not find mono library in: ' + mono_lib_path)
+
+ env_mono.Append(CPPFLAGS=['-D_REENTRANT'])
+
+ if mono_static:
+ mono_lib_file = os.path.join(mono_lib_path, 'lib' + mono_lib + '.a')
+
+ if is_apple:
+ env.Append(LINKFLAGS=['-Wl,-force_load,' + mono_lib_file])
+ else:
+ env.Append(LINKFLAGS=['-Wl,-whole-archive', mono_lib_file, '-Wl,-no-whole-archive'])
+ else:
+ env.Append(LIBS=[mono_lib])
+
+ if is_apple:
+ env.Append(LIBS=['iconv', 'pthread'])
+ else:
+ 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)
+
+ 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)
+ else:
+ assert not mono_static
+
+ # TODO: Add option to force using pkg-config
+ print('Mono root directory not found. Using pkg-config instead')
+
+ mono_version = pkgconfig_try_find_mono_version()
+ configure_for_mono_version(env_mono, mono_version)
+
+ env.ParseConfig('pkg-config monosgen-2 --libs')
+ env_mono.ParseConfig('pkg-config monosgen-2 --cflags')
+
+ mono_lib_path = ''
+ mono_so_name = ''
+
+ 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:
+ mono_lib_path = hint_dir
+ mono_so_name = name_found
+ break
+
+ if not mono_so_name:
+ raise RuntimeError('Could not find mono shared library in: ' + str(tmpenv['LIBPATH']))
+
+ copy_file(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext)
+
+ env.Append(LINKFLAGS='-rdynamic')
+
+ if not tools_enabled:
+ if not mono_root:
+ mono_root = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip()
+
+ make_template_dir(env, mono_root)
+
+ if copy_mono_root:
+ if not mono_root:
+ mono_root = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip()
+
+ if tools_enabled:
+ copy_mono_root_files(env, mono_root)
+ else:
+ print("Ignoring option: 'copy_mono_root'. Only available for builds with 'tools' enabled.")
+
+
+def make_template_dir(env, mono_root):
+ from shutil import rmtree
+
+ platform = env['platform']
+ target = env['target']
+
+ template_dir_name = ''
+
+ if platform in ['windows', 'osx', 'x11']:
+ template_dir_name = 'data.mono.%s.%s.%s' % (platform, env['bits'], target)
+ else:
+ assert False
+
+ 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')
+
+ if os.path.isdir(template_mono_root_dir):
+ rmtree(template_mono_root_dir) # Clean first
+
+ # Copy etc/mono/
+
+ template_mono_config_dir = os.path.join(template_mono_root_dir, 'etc', 'mono')
+ copy_mono_etc_dir(mono_root, template_mono_config_dir, env['platform'])
+
+ # Copy the required shared libraries
+
+ copy_mono_shared_libs(mono_root, template_mono_root_dir, env['platform'])
+
+
+def copy_mono_root_files(env, mono_root):
+ from glob import glob
+ from shutil import copy
+ from shutil import rmtree
+
+ if not mono_root:
+ raise RuntimeError('Mono installation directory not found')
+
+ output_dir = Dir('#bin').abspath
+ editor_mono_root_dir = os.path.join(output_dir, 'GodotSharp', 'Mono')
+
+ if os.path.isdir(editor_mono_root_dir):
+ rmtree(editor_mono_root_dir) # Clean first
+
+ # Copy etc/mono/
+
+ editor_mono_config_dir = os.path.join(editor_mono_root_dir, 'etc', 'mono')
+ copy_mono_etc_dir(mono_root, editor_mono_config_dir, env['platform'])
+
+ # Copy the required shared libraries
+
+ copy_mono_shared_libs(mono_root, editor_mono_root_dir, env['platform'])
+
+ # 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')
+
+ editor_mono_framework_dir = os.path.join(editor_mono_root_dir, 'lib', 'mono', '4.5')
+ editor_mono_framework_facades_dir = os.path.join(editor_mono_framework_dir, 'Facades')
+
+ if not os.path.isdir(editor_mono_framework_dir):
+ os.makedirs(editor_mono_framework_dir)
+ if not os.path.isdir(editor_mono_framework_facades_dir):
+ os.makedirs(editor_mono_framework_facades_dir)
+
+ for assembly in glob(os.path.join(mono_framework_dir, '*.dll')):
+ copy(assembly, editor_mono_framework_dir)
+ for assembly in glob(os.path.join(mono_framework_facades_dir, '*.dll')):
+ copy(assembly, editor_mono_framework_facades_dir)
+
+
+def copy_mono_etc_dir(mono_root, target_mono_config_dir, platform):
+ from distutils.dir_util import copy_tree
+ from glob import glob
+ from shutil import copy
+
+ if not os.path.isdir(target_mono_config_dir):
+ os.makedirs(target_mono_config_dir)
+
+ mono_etc_dir = os.path.join(mono_root, 'etc', 'mono')
+ if not os.path.isdir(mono_etc_dir):
+ mono_etc_dir = ''
+ etc_hint_dirs = []
+ if platform != 'windows':
+ etc_hint_dirs += ['/etc/mono', '/usr/local/etc/mono']
+ if 'MONO_CFG_DIR' in os.environ:
+ etc_hint_dirs += [os.path.join(os.environ['MONO_CFG_DIR'], 'mono')]
+ for etc_hint_dir in etc_hint_dirs:
+ if os.path.isdir(etc_hint_dir):
+ mono_etc_dir = etc_hint_dir
+ break
+ if not mono_etc_dir:
+ raise RuntimeError('Mono installation etc directory not found')
+
+ copy_tree(os.path.join(mono_etc_dir, '2.0'), os.path.join(target_mono_config_dir, '2.0'))
+ copy_tree(os.path.join(mono_etc_dir, '4.0'), os.path.join(target_mono_config_dir, '4.0'))
+ copy_tree(os.path.join(mono_etc_dir, '4.5'), os.path.join(target_mono_config_dir, '4.5'))
+ copy_tree(os.path.join(mono_etc_dir, 'mconfig'), os.path.join(target_mono_config_dir, 'mconfig'))
+
+ for file in glob(os.path.join(mono_etc_dir, '*')):
+ if os.path.isfile(file):
+ copy(file, target_mono_config_dir)
+
+
+def copy_mono_shared_libs(mono_root, target_mono_root_dir, platform):
+ from shutil import copy
+
+ if platform == 'windows':
+ target_mono_bin_dir = os.path.join(target_mono_root_dir, 'bin')
+
+ if not os.path.isdir(target_mono_bin_dir):
+ os.makedirs(target_mono_bin_dir)
+
+ copy(os.path.join(mono_root, 'bin', 'MonoPosixHelper.dll'), os.path.join(target_mono_bin_dir, 'MonoPosixHelper.dll'))
+ else:
+ target_mono_lib_dir = os.path.join(target_mono_root_dir, 'lib')
+
+ if not os.path.isdir(target_mono_lib_dir):
+ os.makedirs(target_mono_lib_dir)
+
+ if platform == 'osx':
+ copy(os.path.join(mono_root, 'lib', 'libMonoPosixHelper.dylib'), os.path.join(target_mono_lib_dir, 'libMonoPosixHelper.dylib'))
+ elif platform == 'x11':
+ copy(os.path.join(mono_root, 'lib', 'libmono-btls-shared.so'), os.path.join(target_mono_lib_dir, 'libmono-btls-shared.so'))
+ copy(os.path.join(mono_root, 'lib', 'libMonoPosixHelper.so'), os.path.join(target_mono_lib_dir, 'libMonoPosixHelper.so'))
+
+
+def configure_for_mono_version(env, mono_version):
+ if mono_version is None:
+ raise RuntimeError('Mono JIT compiler version not found')
+ print('Found Mono JIT compiler version: ' + str(mono_version))
+ if mono_version >= LooseVersion('5.12.0'):
+ env.Append(CPPFLAGS=['-DHAS_PENDING_EXCEPTIONS'])
+
+
+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 ''
+
+
+def pkgconfig_try_find_mono_version():
+ from compat import decode_utf8
+
+ lines = subprocess.check_output(['pkg-config', 'monosgen-2', '--modversion']).splitlines()
+ greater_version = None
+ for line in lines:
+ try:
+ version = LooseVersion(decode_utf8(line))
+ if greater_version is None or version > greater_version:
+ greater_version = version
+ except ValueError:
+ pass
+ return greater_version
+
+
+def mono_root_try_find_mono_version(mono_root):
+ from compat import decode_utf8
+
+ mono_bin = os.path.join(mono_root, 'bin')
+ if os.path.isfile(os.path.join(mono_bin, 'mono')):
+ mono_binary = os.path.join(mono_bin, 'mono')
+ elif os.path.isfile(os.path.join(mono_bin, 'mono.exe')):
+ mono_binary = os.path.join(mono_bin, 'mono.exe')
+ else:
+ return None
+ output = subprocess.check_output([mono_binary, '--version'])
+ first_line = decode_utf8(output.splitlines()[0])
+ try:
+ return LooseVersion(first_line.split()[len('Mono JIT compiler version'.split())])
+ except (ValueError, IndexError):
+ return None
diff --git a/modules/mono/mono_reg_utils.py b/modules/mono/build_scripts/mono_reg_utils.py
index c8ebb54ded..583708bf07 100644
--- a/modules/mono/mono_reg_utils.py
+++ b/modules/mono/build_scripts/mono_reg_utils.py
@@ -40,7 +40,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, regtype = winreg.QueryValueEx(hKey, 'SdkInstallRoot')
+ value = winreg.QueryValueEx(hKey, 'SdkInstallRoot')[0]
return value
except (WindowsError, OSError):
return None
@@ -49,7 +49,7 @@ 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, regtype = winreg.QueryValueEx(hKey, 'DefaultCLR')
+ default_clr = winreg.QueryValueEx(hKey, 'DefaultCLR')[0]
if default_clr:
return _find_mono_in_reg(subkey + '\\' + default_clr, bits)
return None
@@ -91,7 +91,13 @@ def find_msbuild_tools_path_reg():
if not val:
raise ValueError('Value of `installationPath` entry is empty')
- return os.path.join(val, "MSBuild\\15.0\\Bin")
+ # Since VS2019, the directory is simply named "Current"
+ msbuild_dir = os.path.join(val, 'MSBuild\\Current\\Bin')
+ if os.path.isdir(msbuild_dir):
+ return msbuild_dir
+
+ # Directory name "15.0" is used in VS 2017
+ return os.path.join(val, 'MSBuild\\15.0\\Bin')
raise ValueError('Cannot find `installationPath` entry')
except ValueError as e:
@@ -106,7 +112,7 @@ def find_msbuild_tools_path_reg():
try:
subkey = r'SOFTWARE\Microsoft\MSBuild\ToolsVersions\14.0'
with _reg_open_key(winreg.HKEY_LOCAL_MACHINE, subkey) as hKey:
- value, regtype = winreg.QueryValueEx(hKey, 'MSBuildToolsPath')
+ value = winreg.QueryValueEx(hKey, 'MSBuildToolsPath')[0]
return value
except (WindowsError, OSError):
return ''
diff --git a/modules/mono/tls_configure.py b/modules/mono/build_scripts/tls_configure.py
index 622280b00b..622280b00b 100644
--- a/modules/mono/tls_configure.py
+++ b/modules/mono/build_scripts/tls_configure.py
diff --git a/modules/mono/config.py b/modules/mono/config.py
index a81ecfce70..3b2e96765e 100644
--- a/modules/mono/config.py
+++ b/modules/mono/config.py
@@ -1,25 +1,12 @@
-
-import imp
-import os
-import sys
-import subprocess
-
-from distutils.version import LooseVersion
-from SCons.Script import BoolVariable, Dir, Environment, File, SCons, Variables
-
-
-monoreg = imp.load_source('mono_reg_utils', 'modules/mono/mono_reg_utils.py')
-
-
def can_build(env, platform):
if platform in ['javascript']:
return False # Not yet supported
return True
-def is_enabled():
- # The module is disabled by default. Use module_mono_enabled=yes to enable it.
- return False
+def configure(env):
+ env.use_ptrcall = True
+ env.add_module_version_string('mono')
def get_doc_classes():
@@ -34,407 +21,6 @@ def get_doc_path():
return 'doc_classes'
-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):
- from shutil import copyfile
-
- src_path = os.path.join(src_dir, name)
- dst_path = os.path.join(dst_dir, name)
-
- if not os.path.isdir(dst_dir):
- os.mkdir(dst_dir)
-
- copyfile(src_path, dst_path)
-
-
-def configure(env):
- env.use_ptrcall = True
- env.add_module_version_string('mono')
-
- envvars = Variables()
- envvars.Add(BoolVariable('mono_static', 'Statically link mono', False))
- envvars.Add(BoolVariable('copy_mono_root', 'Make a copy of the mono installation directory to bundle with the editor', False))
- envvars.Update(env)
-
- bits = env['bits']
-
- tools_enabled = env['tools']
- mono_static = env['mono_static']
- copy_mono_root = env['copy_mono_root']
-
- mono_lib_names = ['mono-2.0-sgen', 'monosgen-2.0']
-
- if env['platform'] == 'windows':
- mono_root = ''
-
- if bits == '32':
- if os.getenv('MONO32_PREFIX'):
- mono_root = os.getenv('MONO32_PREFIX')
- elif os.name == 'nt':
- mono_root = monoreg.find_mono_root_dir(bits)
- else:
- if os.getenv('MONO64_PREFIX'):
- mono_root = os.getenv('MONO64_PREFIX')
- elif os.name == 'nt':
- mono_root = monoreg.find_mono_root_dir(bits)
-
- if not mono_root:
- raise RuntimeError('Mono installation directory not found')
-
- print('Found Mono root directory: ' + mono_root)
-
- mono_version = mono_root_try_find_mono_version(mono_root)
- configure_for_mono_version(env, mono_version)
-
- mono_lib_path = os.path.join(mono_root, 'lib')
-
- env.Append(LIBPATH=mono_lib_path)
- env.Append(CPPPATH=os.path.join(mono_root, 'include', 'mono-2.0'))
-
- if mono_static:
- lib_suffix = Environment()['LIBSUFFIX']
-
- if env.msvc:
- mono_static_lib_name = 'libmono-static-sgen'
- else:
- 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)
-
- if env.msvc:
- env.Append(LINKFLAGS=mono_static_lib_name + lib_suffix)
-
- env.Append(LINKFLAGS='Mincore' + lib_suffix)
- env.Append(LINKFLAGS='msvcrt' + lib_suffix)
- env.Append(LINKFLAGS='LIBCMT' + lib_suffix)
- env.Append(LINKFLAGS='Psapi' + lib_suffix)
- else:
- env.Append(LINKFLAGS=os.path.join(mono_lib_path, mono_static_lib_name + lib_suffix))
-
- env.Append(LIBS='psapi')
- env.Append(LIBS='version')
- else:
- mono_lib_name = find_file_in_dir(mono_lib_path, mono_lib_names, extension='.lib')
-
- if not mono_lib_name:
- raise RuntimeError('Could not find mono library in: ' + mono_lib_path)
-
- if env.msvc:
- env.Append(LINKFLAGS=mono_lib_name + Environment()['LIBSUFFIX'])
- else:
- env.Append(LIBS=mono_lib_name)
-
- mono_bin_path = os.path.join(mono_root, 'bin')
-
- mono_dll_name = find_file_in_dir(mono_bin_path, mono_lib_names, extension='.dll')
-
- if not mono_dll_name:
- raise RuntimeError('Could not find mono shared library in: ' + mono_bin_path)
-
- copy_file(mono_bin_path, 'bin', mono_dll_name + '.dll')
- else:
- is_apple = (sys.platform == 'darwin' or "osxcross" in env)
-
- sharedlib_ext = '.dylib' if is_apple else '.so'
-
- mono_root = ''
- mono_lib_path = ''
-
- if bits == '32':
- if os.getenv('MONO32_PREFIX'):
- mono_root = os.getenv('MONO32_PREFIX')
- else:
- if os.getenv('MONO64_PREFIX'):
- mono_root = os.getenv('MONO64_PREFIX')
-
- if not mono_root and is_apple:
- # Try with some known directories under OSX
- hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current', '/usr/local/var/homebrew/linked/mono']
- for hint_dir in hint_dirs:
- if os.path.isdir(hint_dir):
- mono_root = hint_dir
- break
-
- # We can't use pkg-config to link mono statically,
- # but we can still use it to find the mono root directory
- if not mono_root and mono_static:
- mono_root = pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext)
- if not mono_root:
- raise RuntimeError('Building with mono_static=yes, but failed to find the mono prefix with pkg-config. Specify one manually')
-
- if mono_root:
- print('Found Mono root directory: ' + mono_root)
-
- mono_version = mono_root_try_find_mono_version(mono_root)
- configure_for_mono_version(env, mono_version)
-
- mono_lib_path = os.path.join(mono_root, 'lib')
-
- env.Append(LIBPATH=mono_lib_path)
- env.Append(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')
-
- if not mono_lib:
- raise RuntimeError('Could not find mono library in: ' + mono_lib_path)
-
- env.Append(CPPFLAGS=['-D_REENTRANT'])
-
- if mono_static:
- mono_lib_file = os.path.join(mono_lib_path, 'lib' + mono_lib + '.a')
-
- if is_apple:
- env.Append(LINKFLAGS=['-Wl,-force_load,' + mono_lib_file])
- else:
- env.Append(LINKFLAGS=['-Wl,-whole-archive', mono_lib_file, '-Wl,-no-whole-archive'])
- else:
- env.Append(LIBS=[mono_lib])
-
- if is_apple:
- env.Append(LIBS=['iconv', 'pthread'])
- else:
- 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)
-
- 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)
- else:
- assert not mono_static
-
- # TODO: Add option to force using pkg-config
- print('Mono root directory not found. Using pkg-config instead')
-
- mono_version = pkgconfig_try_find_mono_version()
- configure_for_mono_version(env, mono_version)
-
- env.ParseConfig('pkg-config monosgen-2 --cflags --libs')
-
- mono_lib_path = ''
- mono_so_name = ''
-
- 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:
- mono_lib_path = hint_dir
- mono_so_name = name_found
- break
-
- if not mono_so_name:
- raise RuntimeError('Could not find mono shared library in: ' + str(tmpenv['LIBPATH']))
-
- copy_file(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext)
-
- env.Append(LINKFLAGS='-rdynamic')
-
- if not tools_enabled:
- if not mono_root:
- mono_root = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip()
-
- make_template_dir(env, mono_root)
-
- if copy_mono_root:
- if not mono_root:
- mono_root = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip()
-
- if tools_enabled:
- copy_mono_root_files(env, mono_root)
- else:
- print("Ignoring option: 'copy_mono_root'. Only available for builds with 'tools' enabled.")
-
-
-def make_template_dir(env, mono_root):
- from shutil import rmtree
-
- platform = env['platform']
- target = env['target']
-
- template_dir_name = ''
-
- if platform in ['windows', 'osx', 'x11']:
- template_dir_name = 'data.mono.%s.%s.%s' % (platform, env['bits'], target)
- else:
- assert False
-
- 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')
-
- if os.path.isdir(template_mono_root_dir):
- rmtree(template_mono_root_dir) # Clean first
-
- # Copy etc/mono/
-
- template_mono_config_dir = os.path.join(template_mono_root_dir, 'etc', 'mono')
- copy_mono_etc_dir(mono_root, template_mono_config_dir, env['platform'])
-
- # Copy the required shared libraries
-
- copy_mono_shared_libs(mono_root, template_mono_root_dir, env['platform'])
-
-
-def copy_mono_root_files(env, mono_root):
- from glob import glob
- from shutil import copy
- from shutil import rmtree
-
- if not mono_root:
- raise RuntimeError('Mono installation directory not found')
-
- output_dir = Dir('#bin').abspath
- editor_mono_root_dir = os.path.join(output_dir, 'GodotSharp', 'Mono')
-
- if os.path.isdir(editor_mono_root_dir):
- rmtree(editor_mono_root_dir) # Clean first
-
- # Copy etc/mono/
-
- editor_mono_config_dir = os.path.join(editor_mono_root_dir, 'etc', 'mono')
- copy_mono_etc_dir(mono_root, editor_mono_config_dir, env['platform'])
-
- # Copy the required shared libraries
-
- copy_mono_shared_libs(mono_root, editor_mono_root_dir, env['platform'])
-
- # 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')
-
- editor_mono_framework_dir = os.path.join(editor_mono_root_dir, 'lib', 'mono', '4.5')
- editor_mono_framework_facades_dir = os.path.join(editor_mono_framework_dir, 'Facades')
-
- if not os.path.isdir(editor_mono_framework_dir):
- os.makedirs(editor_mono_framework_dir)
- if not os.path.isdir(editor_mono_framework_facades_dir):
- os.makedirs(editor_mono_framework_facades_dir)
-
- for assembly in glob(os.path.join(mono_framework_dir, '*.dll')):
- copy(assembly, editor_mono_framework_dir)
- for assembly in glob(os.path.join(mono_framework_facades_dir, '*.dll')):
- copy(assembly, editor_mono_framework_facades_dir)
-
-
-def copy_mono_etc_dir(mono_root, target_mono_config_dir, platform):
- from distutils.dir_util import copy_tree
- from glob import glob
- from shutil import copy
-
- if not os.path.isdir(target_mono_config_dir):
- os.makedirs(target_mono_config_dir)
-
- mono_etc_dir = os.path.join(mono_root, 'etc', 'mono')
- if not os.path.isdir(mono_etc_dir):
- mono_etc_dir = ''
- etc_hint_dirs = []
- if platform != 'windows':
- etc_hint_dirs += ['/etc/mono', '/usr/local/etc/mono']
- if 'MONO_CFG_DIR' in os.environ:
- etc_hint_dirs += [os.path.join(os.environ['MONO_CFG_DIR'], 'mono')]
- for etc_hint_dir in etc_hint_dirs:
- if os.path.isdir(etc_hint_dir):
- mono_etc_dir = etc_hint_dir
- break
- if not mono_etc_dir:
- raise RuntimeError('Mono installation etc directory not found')
-
- copy_tree(os.path.join(mono_etc_dir, '2.0'), os.path.join(target_mono_config_dir, '2.0'))
- copy_tree(os.path.join(mono_etc_dir, '4.0'), os.path.join(target_mono_config_dir, '4.0'))
- copy_tree(os.path.join(mono_etc_dir, '4.5'), os.path.join(target_mono_config_dir, '4.5'))
- copy_tree(os.path.join(mono_etc_dir, 'mconfig'), os.path.join(target_mono_config_dir, 'mconfig'))
-
- for file in glob(os.path.join(mono_etc_dir, '*')):
- if os.path.isfile(file):
- copy(file, target_mono_config_dir)
-
-
-def copy_mono_shared_libs(mono_root, target_mono_root_dir, platform):
- from shutil import copy
-
- if platform == 'windows':
- target_mono_bin_dir = os.path.join(target_mono_root_dir, 'bin')
-
- if not os.path.isdir(target_mono_bin_dir):
- os.makedirs(target_mono_bin_dir)
-
- copy(os.path.join(mono_root, 'bin', 'MonoPosixHelper.dll'), os.path.join(target_mono_bin_dir, 'MonoPosixHelper.dll'))
- else:
- target_mono_lib_dir = os.path.join(target_mono_root_dir, 'lib')
-
- if not os.path.isdir(target_mono_lib_dir):
- os.makedirs(target_mono_lib_dir)
-
- if platform == 'osx':
- copy(os.path.join(mono_root, 'lib', 'libMonoPosixHelper.dylib'), os.path.join(target_mono_lib_dir, 'libMonoPosixHelper.dylib'))
- elif platform == 'x11':
- copy(os.path.join(mono_root, 'lib', 'libmono-btls-shared.so'), os.path.join(target_mono_lib_dir, 'libmono-btls-shared.so'))
- copy(os.path.join(mono_root, 'lib', 'libMonoPosixHelper.so'), os.path.join(target_mono_lib_dir, 'libMonoPosixHelper.so'))
-
-
-def configure_for_mono_version(env, mono_version):
- if mono_version is None:
- raise RuntimeError('Mono JIT compiler version not found')
- print('Found Mono JIT compiler version: ' + str(mono_version))
- if mono_version >= LooseVersion('5.12.0'):
- env.Append(CPPFLAGS=['-DHAS_PENDING_EXCEPTIONS'])
-
-
-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 ''
-
-
-def pkgconfig_try_find_mono_version():
- from compat import decode_utf8
-
- lines = subprocess.check_output(['pkg-config', 'monosgen-2', '--modversion']).splitlines()
- greater_version = None
- for line in lines:
- try:
- version = LooseVersion(decode_utf8(line))
- if greater_version is None or version > greater_version:
- greater_version = version
- except ValueError:
- pass
- return greater_version
-
-
-def mono_root_try_find_mono_version(mono_root):
- from compat import decode_utf8
-
- mono_bin = os.path.join(mono_root, 'bin')
- if os.path.isfile(os.path.join(mono_bin, 'mono')):
- mono_binary = os.path.join(mono_bin, 'mono')
- elif os.path.isfile(os.path.join(mono_bin, 'mono.exe')):
- mono_binary = os.path.join(mono_bin, 'mono.exe')
- else:
- return None
- output = subprocess.check_output([mono_binary, '--version'])
- first_line = decode_utf8(output.splitlines()[0])
- try:
- return LooseVersion(first_line.split()[len('Mono JIT compiler version'.split())])
- except (ValueError, IndexError):
- return None
+def is_enabled():
+ # The module is disabled by default. Use module_mono_enabled=yes to enable it.
+ return False
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 5da1344595..ef09e76d11 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -131,14 +131,6 @@ void CSharpLanguage::finish() {
finalizing = true;
-#ifdef TOOLS_ENABLED
- // Must be here, to avoid StringName leaks
- if (BindingsGenerator::singleton) {
- memdelete(BindingsGenerator::singleton);
- BindingsGenerator::singleton = NULL;
- }
-#endif
-
// 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();
@@ -449,7 +441,7 @@ static String variant_type_to_managed_name(const String &p_var_type_name) {
Variant::_RID
};
- for (int i = 0; i < sizeof(var_types) / sizeof(Variant::Type); i++) {
+ for (unsigned int i = 0; i < sizeof(var_types) / sizeof(Variant::Type); i++) {
if (p_var_type_name == Variant::get_type_name(var_types[i]))
return p_var_type_name;
}
@@ -499,6 +491,47 @@ String CSharpLanguage::_get_indentation() const {
return "\t";
}
+String CSharpLanguage::debug_get_error() const {
+
+ return _debug_error;
+}
+
+int CSharpLanguage::debug_get_stack_level_count() const {
+
+ if (_debug_parse_err_line >= 0)
+ return 1;
+
+ // TODO: StackTrace
+ return 1;
+}
+
+int CSharpLanguage::debug_get_stack_level_line(int p_level) const {
+
+ if (_debug_parse_err_line >= 0)
+ return _debug_parse_err_line;
+
+ // TODO: StackTrace
+ return 1;
+}
+
+String CSharpLanguage::debug_get_stack_level_function(int p_level) const {
+
+ if (_debug_parse_err_line >= 0)
+ return String();
+
+ // TODO: StackTrace
+ return String();
+}
+
+String CSharpLanguage::debug_get_stack_level_source(int p_level) const {
+
+ if (_debug_parse_err_line >= 0)
+ return _debug_parse_err_file;
+
+ // TODO: StackTrace
+ return String();
+}
+
Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info() {
#ifdef DEBUG_ENABLED
@@ -530,7 +563,7 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObjec
MonoException *exc = NULL;
- MonoArray *frames = invoke_method_thunk(CACHED_METHOD_THUNK(System_Diagnostics_StackTrace, GetFrames), p_stack_trace, (MonoObject **)&exc);
+ MonoArray *frames = invoke_method_thunk(CACHED_METHOD_THUNK(System_Diagnostics_StackTrace, GetFrames), p_stack_trace, &exc);
if (exc) {
GDMonoUtils::debug_print_unhandled_exception(exc);
@@ -554,7 +587,7 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObjec
MonoString *file_name;
int file_line_num;
MonoString *method_decl;
- invoke_method_thunk(get_sf_info, frame, &file_name, &file_line_num, &method_decl, (MonoObject **)&exc);
+ invoke_method_thunk(get_sf_info, frame, &file_name, &file_line_num, &method_decl, &exc);
if (exc) {
GDMonoUtils::debug_print_unhandled_exception(exc);
@@ -584,7 +617,7 @@ void CSharpLanguage::frame() {
if (task_scheduler) {
MonoException *exc = NULL;
- invoke_method_thunk(CACHED_METHOD_THUNK(GodotTaskScheduler, Activate), task_scheduler, (MonoObject **)&exc);
+ invoke_method_thunk(CACHED_METHOD_THUNK(GodotTaskScheduler, Activate), task_scheduler, &exc);
if (exc) {
GDMonoUtils::debug_unhandled_exception(exc);
@@ -719,13 +752,13 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
// Script::instances are deleted during managed object disposal, which happens on domain finalize.
// Only placeholders are kept. Therefore we need to keep a copy before that happens.
- for (Set<Object *>::Element *E = script->instances.front(); E; E = E->next()) {
- script->pending_reload_instances.insert(E->get()->get_instance_id());
+ for (Set<Object *>::Element *F = script->instances.front(); F; F = F->next()) {
+ script->pending_reload_instances.insert(F->get()->get_instance_id());
}
#ifdef TOOLS_ENABLED
- for (Set<PlaceHolderScriptInstance *>::Element *E = script->placeholders.front(); E; E = E->next()) {
- script->pending_reload_instances.insert(E->get()->get_owner()->get_instance_id());
+ for (Set<PlaceHolderScriptInstance *>::Element *F = script->placeholders.front(); F; F = F->next()) {
+ script->pending_reload_instances.insert(F->get()->get_owner()->get_instance_id());
}
#endif
@@ -878,7 +911,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
}
#endif
-void CSharpLanguage::project_assembly_loaded() {
+void CSharpLanguage::_load_scripts_metadata() {
scripts_metadata.clear();
@@ -912,6 +945,7 @@ void CSharpLanguage::project_assembly_loaded() {
}
scripts_metadata = old_dict_var.operator Dictionary();
+ scripts_metadata_invalidated = false;
print_verbose("Successfully loaded scripts metadata");
} else {
@@ -958,12 +992,11 @@ void CSharpLanguage::thread_exit() {
bool CSharpLanguage::debug_break_parse(const String &p_file, int p_line, const String &p_error) {
- // Break because of parse 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()) {
- // TODO
- //_debug_parse_err_line = p_line;
- //_debug_parse_err_file = p_file;
- //_debug_error = p_error;
+ _debug_parse_err_line = p_line;
+ _debug_parse_err_file = p_file;
+ _debug_error = p_error;
ScriptDebugger::get_singleton()->debug(this, false);
return true;
} else {
@@ -974,10 +1007,9 @@ 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()) {
- // TODO
- //_debug_parse_err_line = -1;
- //_debug_parse_err_file = "";
- //_debug_error = p_error;
+ _debug_parse_err_line = -1;
+ _debug_parse_err_file = "";
+ _debug_error = p_error;
ScriptDebugger::get_singleton()->debug(this, p_allow_continue);
return true;
} else {
@@ -985,6 +1017,15 @@ 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.inited = false;
+ }
+
+ scripts_metadata_invalidated = true;
+}
+
void CSharpLanguage::set_language_index(int p_idx) {
ERR_FAIL_COND(lang_idx != -1);
@@ -1040,6 +1081,8 @@ CSharpLanguage::CSharpLanguage() {
#endif
lang_idx = -1;
+
+ scripts_metadata_invalidated = true;
}
CSharpLanguage::~CSharpLanguage() {
@@ -1113,19 +1156,23 @@ bool CSharpLanguage::setup_csharp_script_binding(CSharpScriptBinding &r_script_b
void *CSharpLanguage::alloc_instance_binding_data(Object *p_object) {
+ SCOPED_MUTEX_LOCK(language_bind_mutex);
+
+ Map<Object *, CSharpScriptBinding>::Element *match = script_bindings.find(p_object);
+ if (match)
+ return (void *)match;
+
CSharpScriptBinding script_binding;
if (!setup_csharp_script_binding(script_binding, p_object))
return NULL;
- void *data;
+ return (void *)insert_script_binding(p_object, script_binding);
+}
- {
- SCOPED_MUTEX_LOCK(language_bind_mutex);
- data = (void *)script_bindings.insert(p_object, script_binding);
- }
+Map<Object *, CSharpScriptBinding>::Element *CSharpLanguage::insert_script_binding(Object *p_object, const CSharpScriptBinding &p_script_binding) {
- return data;
+ return script_bindings.insert(p_object, p_script_binding);
}
void CSharpLanguage::free_instance_binding_data(void *p_data) {
@@ -1265,14 +1312,14 @@ bool CSharpInstance::set(const StringName &p_name, const Variant &p_value) {
GDMonoClass *top = script->script_class;
while (top && top != script->native) {
- GDMonoField *field = script->script_class->get_field(p_name);
+ GDMonoField *field = top->get_field(p_name);
if (field) {
field->set_value_from_variant(mono_object, p_value);
return true;
}
- GDMonoProperty *property = script->script_class->get_property(p_name);
+ GDMonoProperty *property = top->get_property(p_name);
if (property) {
property->set_value(mono_object, GDMonoMarshal::variant_to_mono_object(p_value, property->get_type()));
@@ -1374,6 +1421,34 @@ void CSharpInstance::get_property_list(List<PropertyInfo> *p_properties) const {
for (Map<StringName, PropertyInfo>::Element *E = script->member_info.front(); E; E = E->next()) {
p_properties->push_back(E->value());
}
+
+ // Call _get_property_list
+
+ ERR_FAIL_COND(!script.is_valid());
+
+ MonoObject *mono_object = get_mono_object();
+ ERR_FAIL_NULL(mono_object);
+
+ GDMonoClass *top = script->script_class;
+
+ while (top && top != script->native) {
+ GDMonoMethod *method = top->get_method(CACHED_STRING_NAME(_get_property_list), 0);
+
+ if (method) {
+ MonoObject *ret = method->invoke(mono_object);
+
+ if (ret) {
+ Array array = Array(GDMonoMarshal::mono_object_to_variant(ret));
+ for (int i = 0, size = array.size(); i < size; i++)
+ p_properties->push_back(PropertyInfo::from_dict(array.get(i)));
+ return;
+ }
+
+ break;
+ }
+
+ top = top->get_parent_class();
+ }
}
Variant::Type CSharpInstance::get_property_type(const StringName &p_name, bool *r_is_valid) const {
@@ -1524,6 +1599,15 @@ MonoObject *CSharpInstance::_internal_new_managed() {
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);
+ if (ctor == NULL) {
+ ERR_PRINTS("Cannot create script instance because the class does not define a parameterless constructor: " + script->get_path());
+
+ ERR_EXPLAIN("Constructor not found");
+ ERR_FAIL_V(NULL);
+ }
+
CSharpLanguage::get_singleton()->release_script_gchandle(gchandle);
ERR_FAIL_NULL_V(owner, NULL);
@@ -1553,7 +1637,6 @@ MonoObject *CSharpInstance::_internal_new_managed() {
CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, owner);
// Construct
- GDMonoMethod *ctor = script->script_class->get_method(CACHED_STRING_NAME(dotctor), 0);
ctor->invoke_raw(mono_object, NULL);
return mono_object;
@@ -1875,6 +1958,9 @@ void CSharpScript::_update_exports_values(Map<StringName, Variant> &values, List
bool CSharpScript::_update_exports() {
#ifdef TOOLS_ENABLED
+ if (!Engine::get_singleton()->is_editor_hint())
+ return false;
+
placeholder_fallback_enabled = true; // until proven otherwise
if (!valid)
@@ -1896,13 +1982,21 @@ bool CSharpScript::_update_exports() {
MonoObject *tmp_object = mono_object_new(SCRIPTS_DOMAIN, script_class->get_mono_ptr());
if (!tmp_object) {
- ERR_PRINT("Failed to create temporary MonoObject");
+ ERR_PRINT("Failed to allocate temporary MonoObject");
return false;
}
uint32_t tmp_pinned_gchandle = MonoGCHandle::new_strong_handle_pinned(tmp_object); // pin it (not sure if needed)
GDMonoMethod *ctor = script_class->get_method(CACHED_STRING_NAME(dotctor), 0);
+
+ if (ctor == NULL) {
+ ERR_PRINTS("Cannot construct temporary MonoObject because the class does not define a parameterless constructor: " + get_path());
+
+ ERR_EXPLAIN("Constructor not found");
+ ERR_FAIL_V(NULL);
+ }
+
MonoException *ctor_exc = NULL;
ctor->invoke(tmp_object, NULL, &ctor_exc);
@@ -2097,7 +2191,8 @@ bool CSharpScript::_get_member_export(GDMonoClass *p_class, IMonoClassMember *p_
CRASH_NOW();
}
- Variant::Type variant_type = GDMonoMarshal::managed_to_variant_type(type);
+ GDMonoMarshal::ExportInfo export_info;
+ Variant::Type variant_type = GDMonoMarshal::managed_to_variant_type(type, &export_info);
if (!p_member->has_attribute(CACHED_CLASS(ExportAttribute))) {
r_prop_info = PropertyInfo(variant_type, name.operator String(), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_SCRIPT_VARIABLE);
@@ -2122,6 +2217,7 @@ bool CSharpScript::_get_member_export(GDMonoClass *p_class, IMonoClassMember *p_
ERR_PRINTS("Unknown type of exported member: " + p_class->get_full_name() + "." + name.operator String());
return false;
} else if (variant_type == Variant::INT && type.type_encoding == MONO_TYPE_VALUETYPE && mono_class_is_enum(type.type_class->get_mono_ptr())) {
+ // TODO: Move to ExportInfo?
variant_type = Variant::INT;
hint = PROPERTY_HINT_ENUM;
@@ -2168,7 +2264,7 @@ bool CSharpScript::_get_member_export(GDMonoClass *p_class, IMonoClassMember *p_
return false;
}
- if (val != i) {
+ if (val != (unsigned int)i) {
uses_default_values = false;
}
@@ -2183,8 +2279,17 @@ bool CSharpScript::_get_member_export(GDMonoClass *p_class, IMonoClassMember *p_
hint_string = name_only_hint_string;
}
} else if (variant_type == Variant::OBJECT && CACHED_CLASS(GodotReference)->is_assignable_from(type.type_class)) {
+ GDMonoClass *field_native_class = GDMonoUtils::get_class_native_base(type.type_class);
+ CRASH_COND(field_native_class == NULL);
+
hint = PROPERTY_HINT_RESOURCE_TYPE;
- hint_string = NATIVE_GDMONOCLASS_NAME(type.type_class);
+ hint_string = NATIVE_GDMONOCLASS_NAME(field_native_class);
+ } else if (variant_type == Variant::ARRAY && export_info.array.element_type != Variant::NIL) {
+ String elem_type_str = itos(export_info.array.element_type);
+ hint = PROPERTY_HINT_TYPE_STRING;
+ hint_string = elem_type_str + "/" + elem_type_str + ":" + export_info.array.element_native_name;
+ } else if (variant_type == Variant::DICTIONARY && export_info.dictionary.key_type != Variant::NIL && export_info.dictionary.value_type != Variant::NIL) {
+ // TODO: There is no hint for this yet
} else {
hint = PropertyHint(CACHED_FIELD(ExportAttribute, hint)->get_int_value(attr));
hint_string = CACHED_FIELD(ExportAttribute, hintString)->get_string_value(attr);
@@ -2279,17 +2384,18 @@ void CSharpScript::_bind_methods() {
ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "new", &CSharpScript::_new, MethodInfo(Variant::OBJECT, "new"));
}
-Ref<CSharpScript> CSharpScript::create_for_managed_type(GDMonoClass *p_class) {
+Ref<CSharpScript> CSharpScript::create_for_managed_type(GDMonoClass *p_class, GDMonoClass *p_native) {
// This method should not fail
CRASH_COND(!p_class);
+ // TODO: Cache the 'CSharpScript' associated with this 'p_class' instead of allocating a new one every time
Ref<CSharpScript> script = memnew(CSharpScript);
script->name = p_class->get_name();
script->script_class = p_class;
- script->native = GDMonoUtils::get_class_native_base(script->script_class);
+ script->native = p_native;
CRASH_COND(script->native == NULL);
@@ -2382,6 +2488,18 @@ StringName CSharpScript::get_instance_base_type() const {
CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_isref, Variant::CallError &r_error) {
/* STEP 1, CREATE */
+
+ // 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) {
+ if (p_argcount == 0) {
+ ERR_PRINTS("Cannot create script instance because the class does not define a parameterless constructor: " + get_path());
+ }
+
+ ERR_EXPLAIN("Constructor not found");
+ ERR_FAIL_V(NULL);
+ }
+
Ref<Reference> ref;
if (p_isref) {
// Hold it alive. Important if we have to dispose a script instance binding before creating the CSharpInstance.
@@ -2448,7 +2566,6 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg
CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, instance->owner);
// Construct
- GDMonoMethod *ctor = script_class->get_method(CACHED_STRING_NAME(dotctor), p_argcount);
ctor->invoke(mono_object, p_args);
/* STEP 3, PARTY */
@@ -2667,6 +2784,7 @@ Error CSharpScript::reload(bool p_keep_state) {
}
load_script_signals(script_class, native);
+ _update_exports();
}
return OK;
@@ -2935,6 +3053,7 @@ CSharpLanguage::StringNameCache::StringNameCache() {
_signal_callback = StaticCString::create("_signal_callback");
_set = StaticCString::create("_set");
_get = StaticCString::create("_get");
+ _get_property_list = StaticCString::create("_get_property_list");
_notification = StaticCString::create("_notification");
_script_source = StaticCString::create("script/source");
dotctor = StaticCString::create(".ctor");
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index db9d6a73a2..fe4eed2e24 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -135,7 +135,7 @@ class CSharpScript : public Script {
// Do not use unless you know what you are doing
friend void GDMonoInternals::tie_managed_to_unmanaged(MonoObject *, Object *);
- static Ref<CSharpScript> create_for_managed_type(GDMonoClass *p_class);
+ static Ref<CSharpScript> create_for_managed_type(GDMonoClass *p_class, GDMonoClass *p_native);
protected:
static void _bind_methods();
@@ -162,7 +162,7 @@ public:
virtual bool has_script_signal(const StringName &p_signal) const;
virtual void get_script_signal_list(List<MethodInfo> *r_signals) const;
- /* TODO */ virtual bool get_property_default_value(const StringName &p_property, Variant &r_value) const;
+ virtual bool get_property_default_value(const StringName &p_property, Variant &r_value) const;
virtual void get_script_property_list(List<PropertyInfo> *p_list) const;
virtual void update_exports();
@@ -298,6 +298,7 @@ class CSharpLanguage : public ScriptLanguage {
StringName _signal_callback;
StringName _set;
StringName _get;
+ StringName _get_property_list;
StringName _notification;
StringName _script_source;
StringName dotctor; // .ctor
@@ -308,10 +309,23 @@ class CSharpLanguage : public ScriptLanguage {
int lang_idx;
Dictionary scripts_metadata;
+ bool scripts_metadata_invalidated;
+
+ // For debug_break and debug_break_parse
+ int _debug_parse_err_line;
+ String _debug_parse_err_file;
+ String _debug_error;
+
+ void _load_scripts_metadata();
+
+ friend class GDMono;
+ void _on_scripts_domain_unloaded();
public:
StringNameCache string_names;
+ Mutex *get_language_bind_mutex() { return language_bind_mutex; }
+
_FORCE_INLINE_ int get_language_index() { return lang_idx; }
void set_language_index(int p_idx);
@@ -330,9 +344,15 @@ public:
void reload_assemblies(bool p_soft_reload);
#endif
- void project_assembly_loaded();
+ _FORCE_INLINE_ Dictionary get_scripts_metadata_or_nothing() {
+ return scripts_metadata_invalidated ? Dictionary() : scripts_metadata;
+ }
- _FORCE_INLINE_ const Dictionary &get_scripts_metadata() { return scripts_metadata; }
+ _FORCE_INLINE_ const Dictionary &get_scripts_metadata() {
+ if (scripts_metadata_invalidated)
+ _load_scripts_metadata();
+ return scripts_metadata;
+ }
virtual String get_name() const;
@@ -363,11 +383,11 @@ public:
/* TODO */ virtual void add_global_constant(const StringName &p_variable, const Variant &p_value) {}
/* DEBUGGER FUNCTIONS */
- /* TODO */ virtual String debug_get_error() const { return ""; }
- /* TODO */ virtual int debug_get_stack_level_count() const { return 1; }
- /* TODO */ virtual int debug_get_stack_level_line(int p_level) const { return 1; }
- /* TODO */ virtual String debug_get_stack_level_function(int p_level) const { return ""; }
- /* TODO */ virtual String debug_get_stack_level_source(int p_level) const { return ""; }
+ virtual String debug_get_error() const;
+ virtual int debug_get_stack_level_count() const;
+ virtual int debug_get_stack_level_line(int p_level) const;
+ virtual String debug_get_stack_level_function(int p_level) const;
+ virtual String debug_get_stack_level_source(int p_level) const;
/* TODO */ virtual void debug_get_stack_level_locals(int p_level, List<String> *p_locals, List<Variant> *p_values, int p_max_subitems, int p_max_depth) {}
/* TODO */ virtual void debug_get_stack_level_members(int p_level, List<String> *p_members, List<Variant> *p_values, int p_max_subitems, int p_max_depth) {}
/* TODO */ virtual void debug_get_globals(List<String> *p_locals, List<Variant> *p_values, int p_max_subitems, int p_max_depth) {}
@@ -406,6 +426,7 @@ public:
virtual void refcount_incremented_instance_binding(Object *p_object);
virtual bool refcount_decremented_instance_binding(Object *p_object);
+ Map<Object *, CSharpScriptBinding>::Element *insert_script_binding(Object *p_object, const CSharpScriptBinding &p_script_binding);
bool setup_csharp_script_binding(CSharpScriptBinding &r_script_binding, Object *p_object);
#ifdef DEBUG_ENABLED
diff --git a/modules/mono/doc_classes/@C#.xml b/modules/mono/doc_classes/@C#.xml
index 082bc30fd8..826c106d7e 100644
--- a/modules/mono/doc_classes/@C#.xml
+++ b/modules/mono/doc_classes/@C#.xml
@@ -1,13 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="@C#" category="Core" version="3.1">
+<class name="@C#" category="Core" version="3.2">
<brief_description>
</brief_description>
<description>
</description>
<tutorials>
</tutorials>
- <demos>
- </demos>
<methods>
</methods>
<constants>
diff --git a/modules/mono/doc_classes/CSharpScript.xml b/modules/mono/doc_classes/CSharpScript.xml
index a1f7399653..de2e246ea9 100644
--- a/modules/mono/doc_classes/CSharpScript.xml
+++ b/modules/mono/doc_classes/CSharpScript.xml
@@ -1,13 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="CSharpScript" inherits="Script" category="Core" version="3.1">
+<class name="CSharpScript" inherits="Script" category="Core" version="3.2">
<brief_description>
</brief_description>
<description>
</description>
<tutorials>
</tutorials>
- <demos>
- </demos>
<methods>
<method name="new" qualifiers="vararg">
<return type="Object">
diff --git a/modules/mono/doc_classes/GodotSharp.xml b/modules/mono/doc_classes/GodotSharp.xml
index 921c1ca825..18556a84ba 100644
--- a/modules/mono/doc_classes/GodotSharp.xml
+++ b/modules/mono/doc_classes/GodotSharp.xml
@@ -1,13 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="GodotSharp" inherits="Object" category="Core" version="3.1">
+<class name="GodotSharp" inherits="Object" category="Core" version="3.2">
<brief_description>
</brief_description>
<description>
</description>
<tutorials>
</tutorials>
- <demos>
- </demos>
<methods>
<method name="attach_thread">
<return type="void">
diff --git a/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs b/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs
index 4137f5eaef..967e3bcc19 100644
--- a/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs
+++ b/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs
@@ -18,11 +18,11 @@ namespace GodotSharpTools.Build
[MethodImpl(MethodImplOptions.InternalCall)]
private extern static string godot_icall_BuildInstance_get_MSBuildPath();
[MethodImpl(MethodImplOptions.InternalCall)]
- private extern static string godot_icall_BuildInstance_get_FrameworkPath();
- [MethodImpl(MethodImplOptions.InternalCall)]
private extern static string godot_icall_BuildInstance_get_MonoWindowsBinDir();
[MethodImpl(MethodImplOptions.InternalCall)]
private extern static bool godot_icall_BuildInstance_get_UsingMonoMSBuildOnWindows();
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ private extern static bool godot_icall_BuildInstance_get_PrintBuildOutput();
private static string GetMSBuildPath()
{
@@ -34,11 +34,6 @@ namespace GodotSharpTools.Build
return msbuildPath;
}
- private static string GetFrameworkPath()
- {
- return godot_icall_BuildInstance_get_FrameworkPath();
- }
-
private static string MonoWindowsBinDir
{
get
@@ -60,6 +55,14 @@ namespace GodotSharpTools.Build
}
}
+ private static bool PrintBuildOutput
+ {
+ get
+ {
+ return godot_icall_BuildInstance_get_PrintBuildOutput();
+ }
+ }
+
private string solution;
private string config;
@@ -78,23 +81,19 @@ namespace GodotSharpTools.Build
public bool Build(string loggerAssemblyPath, string loggerOutputDir, string[] customProperties = null)
{
- bool debugMSBuild = IsDebugMSBuildRequested();
-
List<string> customPropertiesList = new List<string>();
if (customProperties != null)
customPropertiesList.AddRange(customProperties);
- string frameworkPath = GetFrameworkPath();
-
- if (!string.IsNullOrEmpty(frameworkPath))
- customPropertiesList.Add("FrameworkPathOverride=" + frameworkPath);
-
string compilerArgs = BuildArguments(loggerAssemblyPath, loggerOutputDir, customPropertiesList);
ProcessStartInfo startInfo = new ProcessStartInfo(GetMSBuildPath(), compilerArgs);
- bool redirectOutput = !debugMSBuild;
+ bool redirectOutput = !IsDebugMSBuildRequested() && !PrintBuildOutput;
+
+ if (!redirectOutput) // TODO: or if stdout verbose
+ Console.WriteLine($"Running: \"{startInfo.FileName}\" {startInfo.Arguments}");
startInfo.RedirectStandardOutput = redirectOutput;
startInfo.RedirectStandardError = redirectOutput;
@@ -135,8 +134,6 @@ namespace GodotSharpTools.Build
public bool BuildAsync(string loggerAssemblyPath, string loggerOutputDir, string[] customProperties = null)
{
- bool debugMSBuild = IsDebugMSBuildRequested();
-
if (process != null)
throw new InvalidOperationException("Already in use");
@@ -145,16 +142,14 @@ namespace GodotSharpTools.Build
if (customProperties != null)
customPropertiesList.AddRange(customProperties);
- string frameworkPath = GetFrameworkPath();
-
- if (!string.IsNullOrEmpty(frameworkPath))
- customPropertiesList.Add("FrameworkPathOverride=" + frameworkPath);
-
string compilerArgs = BuildArguments(loggerAssemblyPath, loggerOutputDir, customPropertiesList);
ProcessStartInfo startInfo = new ProcessStartInfo(GetMSBuildPath(), compilerArgs);
- bool redirectOutput = !debugMSBuild;
+ bool redirectOutput = !IsDebugMSBuildRequested() && !PrintBuildOutput;
+
+ if (!redirectOutput) // TODO: or if stdout verbose
+ Console.WriteLine($"Running: \"{startInfo.FileName}\" {startInfo.Arguments}");
startInfo.RedirectStandardOutput = redirectOutput;
startInfo.RedirectStandardError = redirectOutput;
diff --git a/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj b/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj
index f9e9f41977..2871c041f5 100644
--- a/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj
+++ b/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj
@@ -8,6 +8,7 @@
<RootNamespace>GodotSharpTools</RootNamespace>
<AssemblyName>GodotSharpTools</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+ <BaseIntermediateOutputPath>obj</BaseIntermediateOutputPath>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@@ -40,6 +41,7 @@
<Compile Include="Build\BuildSystem.cs" />
<Compile Include="Editor\MonoDevelopInstance.cs" />
<Compile Include="Project\ProjectExtensions.cs" />
+ <Compile Include="Project\IdentifierUtils.cs" />
<Compile Include="Project\ProjectGenerator.cs" />
<Compile Include="Project\ProjectUtils.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
diff --git a/modules/mono/editor/GodotSharpTools/Project/IdentifierUtils.cs b/modules/mono/editor/GodotSharpTools/Project/IdentifierUtils.cs
new file mode 100644
index 0000000000..83e2d2cf8d
--- /dev/null
+++ b/modules/mono/editor/GodotSharpTools/Project/IdentifierUtils.cs
@@ -0,0 +1,199 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Text;
+
+namespace GodotSharpTools.Project
+{
+ public static class IdentifierUtils
+ {
+ public static string SanitizeQualifiedIdentifier(string qualifiedIdentifier, bool allowEmptyIdentifiers)
+ {
+ if (string.IsNullOrEmpty(qualifiedIdentifier))
+ throw new ArgumentException($"{nameof(qualifiedIdentifier)} cannot be empty", nameof(qualifiedIdentifier));
+
+ string[] identifiers = qualifiedIdentifier.Split(new[] { '.' });
+
+ for (int i = 0; i < identifiers.Length; i++)
+ {
+ identifiers[i] = SanitizeIdentifier(identifiers[i], allowEmpty: allowEmptyIdentifiers);
+ }
+
+ return string.Join(".", identifiers);
+ }
+
+ public static string SanitizeIdentifier(string identifier, bool allowEmpty)
+ {
+ if (string.IsNullOrEmpty(identifier))
+ {
+ if (allowEmpty)
+ return "Empty"; // Default value for empty identifiers
+
+ throw new ArgumentException($"{nameof(identifier)} cannot be empty if {nameof(allowEmpty)} is false", nameof(identifier));
+ }
+
+ if (identifier.Length > 511)
+ identifier = identifier.Substring(0, 511);
+
+ var identifierBuilder = new StringBuilder();
+ int startIndex = 0;
+
+ if (identifier[0] == '@')
+ {
+ identifierBuilder.Append('@');
+ startIndex += 1;
+ }
+
+ for (int i = startIndex; i < identifier.Length; i++)
+ {
+ char @char = identifier[i];
+
+ switch (Char.GetUnicodeCategory(@char))
+ {
+ case UnicodeCategory.UppercaseLetter:
+ case UnicodeCategory.LowercaseLetter:
+ case UnicodeCategory.TitlecaseLetter:
+ case UnicodeCategory.ModifierLetter:
+ case UnicodeCategory.LetterNumber:
+ case UnicodeCategory.OtherLetter:
+ identifierBuilder.Append(@char);
+ break;
+ case UnicodeCategory.NonSpacingMark:
+ case UnicodeCategory.SpacingCombiningMark:
+ case UnicodeCategory.ConnectorPunctuation:
+ case UnicodeCategory.DecimalDigitNumber:
+ // Identifiers may start with underscore
+ if (identifierBuilder.Length > startIndex || @char == '_')
+ identifierBuilder.Append(@char);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (identifierBuilder.Length == startIndex)
+ {
+ // All characters were invalid so now it's empty. Fill it with something.
+ identifierBuilder.Append("Empty");
+ }
+
+ identifier = identifierBuilder.ToString();
+
+ if (identifier[0] != '@' && IsKeyword(identifier, anyDoubleUnderscore: true))
+ identifier = '@' + identifier;
+
+ return identifier;
+ }
+
+ static bool IsKeyword(string value, bool anyDoubleUnderscore)
+ {
+ // Identifiers that start with double underscore are meant to be used for reserved keywords.
+ // Only existing keywords are enforced, but it may be useful to forbid any identifier
+ // that begins with double underscore to prevent issues with future C# versions.
+ if (anyDoubleUnderscore)
+ {
+ if (value.Length > 2 && value[0] == '_' && value[1] == '_' && value[2] != '_')
+ return true;
+ }
+ else
+ {
+ if (_doubleUnderscoreKeywords.Contains(value))
+ return true;
+ }
+
+ return _keywords.Contains(value);
+ }
+
+ static HashSet<string> _doubleUnderscoreKeywords = new HashSet<string>
+ {
+ "__arglist",
+ "__makeref",
+ "__reftype",
+ "__refvalue",
+ };
+
+ static HashSet<string> _keywords = new HashSet<string>
+ {
+ "as",
+ "do",
+ "if",
+ "in",
+ "is",
+ "for",
+ "int",
+ "new",
+ "out",
+ "ref",
+ "try",
+ "base",
+ "bool",
+ "byte",
+ "case",
+ "char",
+ "else",
+ "enum",
+ "goto",
+ "lock",
+ "long",
+ "null",
+ "this",
+ "true",
+ "uint",
+ "void",
+ "break",
+ "catch",
+ "class",
+ "const",
+ "event",
+ "false",
+ "fixed",
+ "float",
+ "sbyte",
+ "short",
+ "throw",
+ "ulong",
+ "using",
+ "where",
+ "while",
+ "yield",
+ "double",
+ "extern",
+ "object",
+ "params",
+ "public",
+ "return",
+ "sealed",
+ "sizeof",
+ "static",
+ "string",
+ "struct",
+ "switch",
+ "typeof",
+ "unsafe",
+ "ushort",
+ "checked",
+ "decimal",
+ "default",
+ "finally",
+ "foreach",
+ "partial",
+ "private",
+ "virtual",
+ "abstract",
+ "continue",
+ "delegate",
+ "explicit",
+ "implicit",
+ "internal",
+ "operator",
+ "override",
+ "readonly",
+ "volatile",
+ "interface",
+ "namespace",
+ "protected",
+ "unchecked",
+ "stackalloc",
+ };
+ }
+}
diff --git a/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs b/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs
index 9135006172..89279c69a6 100644
--- a/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs
+++ b/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs
@@ -140,6 +140,9 @@ namespace GodotSharpTools.Project
public static ProjectRootElement CreateLibraryProject(string name, out ProjectPropertyGroupElement mainGroup)
{
+ if (string.IsNullOrEmpty(name))
+ throw new ArgumentException($"{nameof(name)} cannot be empty", nameof(name));
+
var root = ProjectRootElement.Create();
root.DefaultTargets = "Build";
@@ -149,7 +152,7 @@ namespace GodotSharpTools.Project
mainGroup.AddProperty("ProjectGuid", "{" + Guid.NewGuid().ToString().ToUpper() + "}");
mainGroup.AddProperty("OutputType", "Library");
mainGroup.AddProperty("OutputPath", Path.Combine("bin", "$(Configuration)"));
- mainGroup.AddProperty("RootNamespace", name);
+ mainGroup.AddProperty("RootNamespace", IdentifierUtils.SanitizeQualifiedIdentifier(name, allowEmptyIdentifiers: true));
mainGroup.AddProperty("AssemblyName", name);
mainGroup.AddProperty("TargetFrameworkVersion", "v4.5");
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 0b6a6a327c..a408716641 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -97,13 +97,19 @@
#define C_METHOD_MONOARRAY_TO(m_type) C_NS_MONOMARSHAL "::mono_array_to_" #m_type
#define C_METHOD_MONOARRAY_FROM(m_type) C_NS_MONOMARSHAL "::" #m_type "_to_mono_array"
-#define BINDINGS_GENERATOR_VERSION UINT32_C(6)
+#define BINDINGS_GENERATOR_VERSION UINT32_C(9)
-const char *BindingsGenerator::TypeInterface::DEFAULT_VARARG_C_IN = "\t%0 %1_in = %1;\n";
+const char *BindingsGenerator::TypeInterface::DEFAULT_VARARG_C_IN("\t%0 %1_in = %1;\n");
-bool BindingsGenerator::verbose_output = false;
+static String fix_doc_description(const String &p_bbcode) {
-BindingsGenerator *BindingsGenerator::singleton = NULL;
+ // This seems to be the correct way to do this. It's the same EditorHelp does.
+
+ return p_bbcode.dedent()
+ .replace("\t", "")
+ .replace("\r", "")
+ .strip_edges();
+}
static String snake_to_pascal_case(const String &p_identifier, bool p_input_is_upper = false) {
@@ -173,6 +179,457 @@ static String snake_to_camel_case(const String &p_identifier, bool p_input_is_up
return ret;
}
+String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterface *p_itype) {
+
+ // Based on the version in EditorHelp
+
+ if (p_bbcode.empty())
+ return String();
+
+ DocData *doc = EditorHelp::get_doc_data();
+
+ String bbcode = p_bbcode;
+
+ StringBuilder xml_output;
+
+ xml_output.append("<para>");
+
+ List<String> tag_stack;
+ bool code_tag = false;
+
+ int pos = 0;
+ while (pos < bbcode.length()) {
+ int brk_pos = bbcode.find("[", pos);
+
+ if (brk_pos < 0)
+ brk_pos = bbcode.length();
+
+ if (brk_pos > pos) {
+ String text = bbcode.substr(pos, brk_pos - pos);
+ if (code_tag || tag_stack.size() > 0) {
+ xml_output.append(text.xml_escape());
+ } else {
+ Vector<String> lines = text.split("\n");
+ for (int i = 0; i < lines.size(); i++) {
+ if (i != 0)
+ xml_output.append("<para>");
+
+ xml_output.append(lines[i].xml_escape());
+
+ if (i != lines.size() - 1)
+ xml_output.append("</para>\n");
+ }
+ }
+ }
+
+ if (brk_pos == bbcode.length())
+ break; // nothing else to add
+
+ int brk_end = bbcode.find("]", brk_pos + 1);
+
+ if (brk_end == -1) {
+ String text = bbcode.substr(brk_pos, bbcode.length() - brk_pos);
+ if (code_tag || tag_stack.size() > 0) {
+ xml_output.append(text.xml_escape());
+ } else {
+ Vector<String> lines = text.split("\n");
+ for (int i = 0; i < lines.size(); i++) {
+ if (i != 0)
+ xml_output.append("<para>");
+
+ xml_output.append(lines[i].xml_escape());
+
+ if (i != lines.size() - 1)
+ xml_output.append("</para>\n");
+ }
+ }
+
+ break;
+ }
+
+ String tag = bbcode.substr(brk_pos + 1, brk_end - brk_pos - 1);
+
+ if (tag.begins_with("/")) {
+ bool tag_ok = tag_stack.size() && tag_stack.front()->get() == tag.substr(1, tag.length());
+
+ if (!tag_ok) {
+ xml_output.append("[");
+ pos = brk_pos + 1;
+ continue;
+ }
+
+ tag_stack.pop_front();
+ pos = brk_end + 1;
+ code_tag = false;
+
+ if (tag == "/url") {
+ xml_output.append("</a>");
+ } else if (tag == "/code") {
+ xml_output.append("</c>");
+ } else if (tag == "/codeblock") {
+ xml_output.append("</code>");
+ }
+ } else if (code_tag) {
+ xml_output.append("[");
+ pos = brk_pos + 1;
+ } else if (tag.begins_with("method ") || tag.begins_with("member ") || tag.begins_with("signal ") || tag.begins_with("enum ") || tag.begins_with("constant ")) {
+ String link_target = tag.substr(tag.find(" ") + 1, tag.length());
+ String link_tag = tag.substr(0, tag.find(" "));
+
+ Vector<String> link_target_parts = link_target.split(".");
+
+ if (link_target_parts.size() <= 0 || link_target_parts.size() > 2) {
+ ERR_PRINTS("Invalid reference format: " + tag);
+
+ xml_output.append("<c>");
+ xml_output.append(tag);
+ xml_output.append("</c>");
+
+ pos = brk_end + 1;
+ continue;
+ }
+
+ const TypeInterface *target_itype;
+ StringName target_cname;
+
+ if (link_target_parts.size() == 2) {
+ target_itype = _get_type_or_null(TypeReference(link_target_parts[0]));
+ if (!target_itype) {
+ target_itype = _get_type_or_null(TypeReference("_" + link_target_parts[0]));
+ }
+ target_cname = link_target_parts[1];
+ } else {
+ target_itype = p_itype;
+ target_cname = link_target_parts[0];
+ }
+
+ if (link_tag == "method") {
+ if (!target_itype || !target_itype->is_object_type) {
+ if (OS::get_singleton()->is_stdout_verbose()) {
+ if (target_itype) {
+ OS::get_singleton()->print("Cannot resolve method reference for non-Godot.Object type in documentation: %s\n", link_target.utf8().get_data());
+ } else {
+ OS::get_singleton()->print("Cannot resolve type from method reference in documentation: %s\n", link_target.utf8().get_data());
+ }
+ }
+
+ // TODO Map what we can
+ xml_output.append("<c>");
+ xml_output.append(link_target);
+ xml_output.append("</c>");
+ } else {
+ const MethodInterface *target_imethod = target_itype->find_method_by_name(target_cname);
+
+ if (target_imethod) {
+ xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".");
+ xml_output.append(target_itype->proxy_name);
+ xml_output.append(".");
+ xml_output.append(target_imethod->proxy_name);
+ xml_output.append("\"/>");
+ }
+ }
+ } else if (link_tag == "member") {
+ if (!target_itype || !target_itype->is_object_type) {
+ if (OS::get_singleton()->is_stdout_verbose()) {
+ if (target_itype) {
+ OS::get_singleton()->print("Cannot resolve member reference for non-Godot.Object type in documentation: %s\n", link_target.utf8().get_data());
+ } else {
+ OS::get_singleton()->print("Cannot resolve type from member reference in documentation: %s\n", link_target.utf8().get_data());
+ }
+ }
+
+ // TODO Map what we can
+ xml_output.append("<c>");
+ xml_output.append(link_target);
+ xml_output.append("</c>");
+ } else {
+ const PropertyInterface *target_iprop = target_itype->find_property_by_name(target_cname);
+
+ if (target_iprop) {
+ xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".");
+ xml_output.append(target_itype->proxy_name);
+ xml_output.append(".");
+ xml_output.append(target_iprop->proxy_name);
+ xml_output.append("\"/>");
+ }
+ }
+ } else if (link_tag == "signal") {
+ // We do not declare signals in any way in C#, so there is nothing to reference
+ xml_output.append("<c>");
+ xml_output.append(link_target);
+ xml_output.append("</c>");
+ } else if (link_tag == "enum") {
+ StringName search_cname = !target_itype ? target_cname :
+ StringName(target_itype->name + "." + (String)target_cname);
+
+ const Map<StringName, TypeInterface>::Element *enum_match = enum_types.find(search_cname);
+
+ if (!enum_match && search_cname != target_cname) {
+ enum_match = enum_types.find(target_cname);
+ }
+
+ if (enum_match) {
+ const TypeInterface &target_enum_itype = enum_match->value();
+
+ xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".");
+ xml_output.append(target_enum_itype.proxy_name); // Includes nesting class if any
+ xml_output.append("\"/>");
+ } else {
+ ERR_PRINTS("Cannot resolve enum reference in documentation: " + link_target);
+
+ xml_output.append("<c>");
+ xml_output.append(link_target);
+ xml_output.append("</c>");
+ }
+ } else if (link_tag == "const") {
+ if (!target_itype || !target_itype->is_object_type) {
+ if (OS::get_singleton()->is_stdout_verbose()) {
+ if (target_itype) {
+ OS::get_singleton()->print("Cannot resolve constant reference for non-Godot.Object type in documentation: %s\n", link_target.utf8().get_data());
+ } else {
+ OS::get_singleton()->print("Cannot resolve type from constant reference in documentation: %s\n", link_target.utf8().get_data());
+ }
+ }
+
+ // TODO Map what we can
+ xml_output.append("<c>");
+ xml_output.append(link_target);
+ xml_output.append("</c>");
+ } else if (!target_itype && target_cname == name_cache.type_at_GlobalScope) {
+ String target_name = (String)target_cname;
+
+ // Try to find as a global constant
+ const ConstantInterface *target_iconst = find_constant_by_name(target_name, global_constants);
+
+ if (target_iconst) {
+ // Found global constant
+ xml_output.append("<see cref=\"" BINDINGS_NAMESPACE "." BINDINGS_GLOBAL_SCOPE_CLASS ".");
+ xml_output.append(target_iconst->proxy_name);
+ xml_output.append("\"/>");
+ } else {
+ // Try to find as global enum constant
+ const EnumInterface *target_ienum = NULL;
+
+ for (const List<EnumInterface>::Element *E = global_enums.front(); E; E = E->next()) {
+ target_ienum = &E->get();
+ target_iconst = find_constant_by_name(target_name, target_ienum->constants);
+ if (target_iconst)
+ break;
+ }
+
+ if (target_iconst) {
+ xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".");
+ xml_output.append(target_ienum->cname);
+ xml_output.append(".");
+ xml_output.append(target_iconst->proxy_name);
+ xml_output.append("\"/>");
+ } else {
+ ERR_PRINTS("Cannot resolve global constant reference in documentation: " + link_target);
+
+ xml_output.append("<c>");
+ xml_output.append(link_target);
+ xml_output.append("</c>");
+ }
+ }
+ } else {
+ String target_name = (String)target_cname;
+
+ // Try to find the constant in the current class
+ const ConstantInterface *target_iconst = find_constant_by_name(target_name, target_itype->constants);
+
+ if (target_iconst) {
+ // Found constant in current class
+ xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".");
+ xml_output.append(target_itype->proxy_name);
+ xml_output.append(".");
+ xml_output.append(target_iconst->proxy_name);
+ xml_output.append("\"/>");
+ } else {
+ // Try to find as enum constant in the current class
+ const EnumInterface *target_ienum = NULL;
+
+ for (const List<EnumInterface>::Element *E = target_itype->enums.front(); E; E = E->next()) {
+ target_ienum = &E->get();
+ target_iconst = find_constant_by_name(target_name, target_ienum->constants);
+ if (target_iconst)
+ break;
+ }
+
+ if (target_iconst) {
+ xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".");
+ xml_output.append(target_itype->proxy_name);
+ xml_output.append(".");
+ xml_output.append(target_ienum->cname);
+ xml_output.append(".");
+ xml_output.append(target_iconst->proxy_name);
+ xml_output.append("\"/>");
+ } else {
+ ERR_PRINTS("Cannot resolve constant reference in documentation: " + link_target);
+
+ xml_output.append("<c>");
+ xml_output.append(link_target);
+ xml_output.append("</c>");
+ }
+ }
+ }
+ }
+
+ pos = brk_end + 1;
+ } else if (doc->class_list.has(tag)) {
+ if (tag == "Array" || tag == "Dictionary") {
+ xml_output.append("<see cref=\"" BINDINGS_NAMESPACE_COLLECTIONS ".");
+ xml_output.append(tag);
+ xml_output.append("\"/>");
+ } else if (tag == "bool" || tag == "int") {
+ xml_output.append("<see cref=\"");
+ xml_output.append(tag);
+ xml_output.append("\"/>");
+ } else if (tag == "float") {
+ xml_output.append("<see cref=\""
+#ifdef REAL_T_IS_DOUBLE
+ "double"
+#else
+ "float"
+#endif
+ "\"/>");
+ } else if (tag == "Variant") {
+ // We use System.Object for Variant, so there is no Variant type in C#
+ xml_output.append("<c>Variant</c>");
+ } else if (tag == "String") {
+ xml_output.append("<see cref=\"string\"/>");
+ } else if (tag == "Nil") {
+ xml_output.append("<see langword=\"null\"/>");
+ } else if (tag.begins_with("@")) {
+ // @GlobalScope, @GDScript, etc
+ xml_output.append("<c>");
+ xml_output.append(tag);
+ xml_output.append("</c>");
+ } else if (tag == "PoolByteArray") {
+ xml_output.append("<see cref=\"byte\"/>");
+ } else if (tag == "PoolIntArray") {
+ xml_output.append("<see cref=\"int\"/>");
+ } else if (tag == "PoolRealArray") {
+#ifdef REAL_T_IS_DOUBLE
+ xml_output.append("<see cref=\"double\"/>");
+#else
+ xml_output.append("<see cref=\"float\"/>");
+#endif
+ } else if (tag == "PoolStringArray") {
+ xml_output.append("<see cref=\"string\"/>");
+ } else if (tag == "PoolVector2Array") {
+ xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".Vector2\"/>");
+ } else if (tag == "PoolVector3Array") {
+ xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".Vector3\"/>");
+ } else if (tag == "PoolColorArray") {
+ xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".Color\"/>");
+ } else {
+ const TypeInterface *target_itype = _get_type_or_null(TypeReference(tag));
+
+ if (!target_itype) {
+ target_itype = _get_type_or_null(TypeReference("_" + tag));
+ }
+
+ if (target_itype) {
+ xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".");
+ xml_output.append(target_itype->proxy_name);
+ xml_output.append("\"/>");
+ } else {
+ ERR_PRINTS("Cannot resolve type reference in documentation: " + tag);
+
+ xml_output.append("<c>");
+ xml_output.append(tag);
+ xml_output.append("</c>");
+ }
+ }
+
+ pos = brk_end + 1;
+ } else if (tag == "b") {
+ // bold is not supported in xml comments
+ pos = brk_end + 1;
+ tag_stack.push_front(tag);
+ } else if (tag == "i") {
+ // italics is not supported in xml comments
+ pos = brk_end + 1;
+ tag_stack.push_front(tag);
+ } else if (tag == "code") {
+ xml_output.append("<c>");
+
+ code_tag = true;
+ pos = brk_end + 1;
+ tag_stack.push_front(tag);
+ } else if (tag == "codeblock") {
+ xml_output.append("<code>");
+
+ code_tag = true;
+ pos = brk_end + 1;
+ tag_stack.push_front(tag);
+ } else if (tag == "center") {
+ // center is alignment not supported in xml comments
+ pos = brk_end + 1;
+ tag_stack.push_front(tag);
+ } else if (tag == "br") {
+ xml_output.append("\n"); // FIXME: Should use <para> instead. Luckily this tag isn't used for now.
+ pos = brk_end + 1;
+ } else if (tag == "u") {
+ // underline is not supported in xml comments
+ pos = brk_end + 1;
+ tag_stack.push_front(tag);
+ } else if (tag == "s") {
+ // strikethrough is not supported in xml comments
+ pos = brk_end + 1;
+ tag_stack.push_front(tag);
+ } else if (tag == "url") {
+ int end = bbcode.find("[", brk_end);
+ if (end == -1)
+ end = bbcode.length();
+ String url = bbcode.substr(brk_end + 1, end - brk_end - 1);
+ xml_output.append("<a href=\"");
+ xml_output.append(url);
+ xml_output.append("\">");
+ xml_output.append(url);
+
+ pos = brk_end + 1;
+ tag_stack.push_front(tag);
+ } else if (tag.begins_with("url=")) {
+ String url = tag.substr(4, tag.length());
+ xml_output.append("<a href=\"");
+ xml_output.append(url);
+ xml_output.append("\">");
+
+ pos = brk_end + 1;
+ tag_stack.push_front("url");
+ } else if (tag == "img") {
+ int end = bbcode.find("[", brk_end);
+ if (end == -1)
+ end = bbcode.length();
+ String image = bbcode.substr(brk_end + 1, end - brk_end - 1);
+
+ // Not supported. Just append the bbcode.
+ xml_output.append("[img]");
+ xml_output.append(image);
+ xml_output.append("[/img]");
+
+ pos = end;
+ tag_stack.push_front(tag);
+ } else if (tag.begins_with("color=")) {
+ // Not supported.
+ pos = brk_end + 1;
+ tag_stack.push_front("color");
+ } else if (tag.begins_with("font=")) {
+ // Not supported.
+ pos = brk_end + 1;
+ tag_stack.push_front("font");
+ } else {
+ xml_output.append("["); // ignore
+ pos = brk_pos + 1;
+ }
+ }
+
+ xml_output.append("</para>");
+
+ return xml_output.as_string();
+}
+
int BindingsGenerator::_determine_enum_prefix(const EnumInterface &p_ienum) {
CRASH_COND(p_ienum.constants.empty());
@@ -295,44 +752,47 @@ void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) {
}
}
-void BindingsGenerator::_generate_global_constants(List<String> &p_output) {
+void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
// Constants (in partial GD class)
- p_output.push_back("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK);
- p_output.push_back(INDENT1 "public static partial class " BINDINGS_GLOBAL_SCOPE_CLASS "\n" INDENT1 "{");
+ p_output.append("\n#pragma warning disable CS1591 // Disable warning: "
+ "'Missing XML comment for publicly visible type or member'\n");
+
+ p_output.append("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK);
+ p_output.append(INDENT1 "public static partial class " BINDINGS_GLOBAL_SCOPE_CLASS "\n" INDENT1 "{");
for (const List<ConstantInterface>::Element *E = global_constants.front(); E; E = E->next()) {
const ConstantInterface &iconstant = E->get();
if (iconstant.const_doc && iconstant.const_doc->description.size()) {
- p_output.push_back(MEMBER_BEGIN "/// <summary>\n");
+ String xml_summary = bbcode_to_xml(fix_doc_description(iconstant.const_doc->description), NULL);
+ Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
- Vector<String> description_lines = iconstant.const_doc->description.split("\n");
+ if (summary_lines.size()) {
+ p_output.append(MEMBER_BEGIN "/// <summary>\n");
- for (int i = 0; i < description_lines.size(); i++) {
- String description_line = description_lines[i].strip_edges();
- if (description_line.size()) {
- p_output.push_back(INDENT2 "/// ");
- p_output.push_back(description_line.xml_escape());
- p_output.push_back("\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.push_back(INDENT2 "/// </summary>");
+ p_output.append(INDENT2 "/// </summary>");
+ }
}
- p_output.push_back(MEMBER_BEGIN "public const int ");
- p_output.push_back(iconstant.proxy_name);
- p_output.push_back(" = ");
- p_output.push_back(itos(iconstant.value));
- p_output.push_back(";");
+ p_output.append(MEMBER_BEGIN "public const int ");
+ p_output.append(iconstant.proxy_name);
+ p_output.append(" = ");
+ p_output.append(itos(iconstant.value));
+ p_output.append(";");
}
if (!global_constants.empty())
- p_output.push_back("\n");
+ p_output.append("\n");
- p_output.push_back(INDENT1 CLOSE_BLOCK); // end of GD class
+ p_output.append(INDENT1 CLOSE_BLOCK); // end of GD class
// Enums
@@ -352,58 +812,56 @@ void BindingsGenerator::_generate_global_constants(List<String> &p_output) {
CRASH_COND(enum_class_name != "Variant"); // Hard-coded...
- if (verbose_output) {
- WARN_PRINTS("Declaring global enum `" + enum_proxy_name + "` inside static class `" + enum_class_name + "`");
- }
+ _log("Declaring global enum `%s` inside static class `%s`\n", enum_proxy_name.utf8().get_data(), enum_class_name.utf8().get_data());
- p_output.push_back("\n" INDENT1 "public static partial class ");
- p_output.push_back(enum_class_name);
- p_output.push_back("\n" INDENT1 OPEN_BLOCK);
+ p_output.append("\n" INDENT1 "public static partial class ");
+ p_output.append(enum_class_name);
+ p_output.append("\n" INDENT1 OPEN_BLOCK);
}
- p_output.push_back("\n" INDENT1 "public enum ");
- p_output.push_back(enum_proxy_name);
- p_output.push_back("\n" INDENT1 OPEN_BLOCK);
+ p_output.append("\n" INDENT1 "public enum ");
+ p_output.append(enum_proxy_name);
+ p_output.append("\n" INDENT1 OPEN_BLOCK);
- for (const List<ConstantInterface>::Element *E = ienum.constants.front(); E; E = E->next()) {
- const ConstantInterface &iconstant = E->get();
+ for (const List<ConstantInterface>::Element *F = ienum.constants.front(); F; F = F->next()) {
+ const ConstantInterface &iconstant = F->get();
if (iconstant.const_doc && iconstant.const_doc->description.size()) {
- p_output.push_back(INDENT2 "/// <summary>\n");
+ String xml_summary = bbcode_to_xml(fix_doc_description(iconstant.const_doc->description), NULL);
+ Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
- Vector<String> description_lines = iconstant.const_doc->description.split("\n");
+ if (summary_lines.size()) {
+ p_output.append(INDENT2 "/// <summary>\n");
- for (int i = 0; i < description_lines.size(); i++) {
- String description_line = description_lines[i].strip_edges();
- if (description_line.size()) {
- p_output.push_back(INDENT2 "/// ");
- p_output.push_back(description_line.xml_escape());
- p_output.push_back("\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.push_back(INDENT2 "/// </summary>\n");
+ p_output.append(INDENT2 "/// </summary>\n");
+ }
}
- p_output.push_back(INDENT2);
- p_output.push_back(iconstant.proxy_name);
- p_output.push_back(" = ");
- p_output.push_back(itos(iconstant.value));
- p_output.push_back(E != ienum.constants.back() ? ",\n" : "\n");
+ p_output.append(INDENT2);
+ p_output.append(iconstant.proxy_name);
+ p_output.append(" = ");
+ p_output.append(itos(iconstant.value));
+ p_output.append(F != ienum.constants.back() ? ",\n" : "\n");
}
- p_output.push_back(INDENT1 CLOSE_BLOCK);
+ p_output.append(INDENT1 CLOSE_BLOCK);
if (enum_in_static_class)
- p_output.push_back(INDENT1 CLOSE_BLOCK);
+ p_output.append(INDENT1 CLOSE_BLOCK);
}
- p_output.push_back(CLOSE_BLOCK); // end of namespace
-}
+ p_output.append(CLOSE_BLOCK); // end of namespace
-Error BindingsGenerator::generate_cs_core_project(const String &p_solution_dir, DotNetSolution &r_solution, bool p_verbose_output) {
+ p_output.append("\n#pragma warning restore CS1591\n");
+}
- verbose_output = p_verbose_output;
+Error BindingsGenerator::generate_cs_core_project(const String &p_solution_dir, DotNetSolution &r_solution) {
String proj_dir = p_solution_dir.plus_file(CORE_API_ASSEMBLY_NAME);
@@ -426,7 +884,7 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_solution_dir,
// Generate source file for global scope constants and enums
{
- List<String> constants_source;
+ StringBuilder constants_source;
_generate_global_constants(constants_source);
String output_file = path_join(core_dir, BINDINGS_GLOBAL_SCOPE_CLASS "_constants.cs");
Error save_err = _save_file(output_file, constants_source);
@@ -472,8 +930,6 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_solution_dir,
String output_dir = output_file.get_base_dir();
if (!DirAccess::exists(output_dir)) {
- DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
- ERR_FAIL_COND_V(!da, ERR_CANT_CREATE);
Error err = da->make_dir_recursive(ProjectSettings::get_singleton()->globalize_path(output_dir));
ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
}
@@ -486,28 +942,28 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_solution_dir,
compile_items.push_back(output_file);
}
- List<String> cs_icalls_content;
-
- cs_icalls_content.push_back("using System;\n"
- "using System.Runtime.CompilerServices;\n"
- "\n");
- cs_icalls_content.push_back("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK);
- cs_icalls_content.push_back(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS "\n" INDENT1 OPEN_BLOCK);
-
- cs_icalls_content.push_back(MEMBER_BEGIN "internal static ulong godot_api_hash = ");
- cs_icalls_content.push_back(String::num_uint64(GDMono::get_singleton()->get_api_core_hash()) + ";\n");
- cs_icalls_content.push_back(MEMBER_BEGIN "internal static uint bindings_version = ");
- cs_icalls_content.push_back(String::num_uint64(BINDINGS_GENERATOR_VERSION) + ";\n");
- cs_icalls_content.push_back(MEMBER_BEGIN "internal static uint cs_glue_version = ");
- cs_icalls_content.push_back(String::num_uint64(CS_GLUE_VERSION) + ";\n");
-
-#define ADD_INTERNAL_CALL(m_icall) \
- if (!m_icall.editor_only) { \
- cs_icalls_content.push_back(MEMBER_BEGIN "[MethodImpl(MethodImplOptions.InternalCall)]\n"); \
- cs_icalls_content.push_back(INDENT2 "internal extern static "); \
- cs_icalls_content.push_back(m_icall.im_type_out + " "); \
- cs_icalls_content.push_back(m_icall.name + "("); \
- cs_icalls_content.push_back(m_icall.im_sig + ");\n"); \
+ StringBuilder cs_icalls_content;
+
+ cs_icalls_content.append("using System;\n"
+ "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(MEMBER_BEGIN "internal static ulong godot_api_hash = ");
+ cs_icalls_content.append(String::num_uint64(GDMono::get_singleton()->get_api_core_hash()) + ";\n");
+ cs_icalls_content.append(MEMBER_BEGIN "internal static uint bindings_version = ");
+ cs_icalls_content.append(String::num_uint64(BINDINGS_GENERATOR_VERSION) + ";\n");
+ cs_icalls_content.append(MEMBER_BEGIN "internal static uint cs_glue_version = ");
+ cs_icalls_content.append(String::num_uint64(CS_GLUE_VERSION) + ";\n");
+
+#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(m_icall.im_type_out + " "); \
+ cs_icalls_content.append(m_icall.name + "("); \
+ cs_icalls_content.append(m_icall.im_sig + ");\n"); \
}
for (const List<InternalCall>::Element *E = core_custom_icalls.front(); E; E = E->next())
@@ -517,7 +973,7 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_solution_dir,
#undef ADD_INTERNAL_CALL
- cs_icalls_content.push_back(INDENT1 CLOSE_BLOCK CLOSE_BLOCK);
+ cs_icalls_content.append(INDENT1 CLOSE_BLOCK CLOSE_BLOCK);
String internal_methods_file = path_join(core_dir, BINDINGS_CLASS_NATIVECALLS ".cs");
@@ -537,15 +993,12 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_solution_dir,
r_solution.add_new_project(CORE_API_ASSEMBLY_NAME, proj_info);
- if (verbose_output)
- OS::get_singleton()->print("The solution and C# project for the Core API was generated successfully\n");
+ _log("The solution and C# project for the Core API was generated successfully\n");
return OK;
}
-Error BindingsGenerator::generate_cs_editor_project(const String &p_solution_dir, DotNetSolution &r_solution, bool p_verbose_output) {
-
- verbose_output = p_verbose_output;
+Error BindingsGenerator::generate_cs_editor_project(const String &p_solution_dir, DotNetSolution &r_solution) {
String proj_dir = p_solution_dir.plus_file(EDITOR_API_ASSEMBLY_NAME);
@@ -584,29 +1037,29 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_solution_dir
compile_items.push_back(output_file);
}
- List<String> cs_icalls_content;
-
- cs_icalls_content.push_back("using System;\n"
- "using System.Runtime.CompilerServices;\n"
- "\n");
- cs_icalls_content.push_back("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK);
- cs_icalls_content.push_back(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS_EDITOR "\n" INDENT1 OPEN_BLOCK);
-
- cs_icalls_content.push_back(INDENT2 "internal static ulong godot_api_hash = ");
- cs_icalls_content.push_back(String::num_uint64(GDMono::get_singleton()->get_api_editor_hash()) + ";\n");
- cs_icalls_content.push_back(INDENT2 "internal static uint bindings_version = ");
- cs_icalls_content.push_back(String::num_uint64(BINDINGS_GENERATOR_VERSION) + ";\n");
- cs_icalls_content.push_back(INDENT2 "internal static uint cs_glue_version = ");
- cs_icalls_content.push_back(String::num_uint64(CS_GLUE_VERSION) + ";\n");
- cs_icalls_content.push_back("\n");
-
-#define ADD_INTERNAL_CALL(m_icall) \
- if (m_icall.editor_only) { \
- cs_icalls_content.push_back(INDENT2 "[MethodImpl(MethodImplOptions.InternalCall)]\n"); \
- cs_icalls_content.push_back(INDENT2 "internal extern static "); \
- cs_icalls_content.push_back(m_icall.im_type_out + " "); \
- cs_icalls_content.push_back(m_icall.name + "("); \
- cs_icalls_content.push_back(m_icall.im_sig + ");\n"); \
+ StringBuilder cs_icalls_content;
+
+ cs_icalls_content.append("using System;\n"
+ "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_EDITOR "\n" INDENT1 OPEN_BLOCK);
+
+ cs_icalls_content.append(INDENT2 "internal static ulong godot_api_hash = ");
+ cs_icalls_content.append(String::num_uint64(GDMono::get_singleton()->get_api_editor_hash()) + ";\n");
+ cs_icalls_content.append(INDENT2 "internal static uint bindings_version = ");
+ cs_icalls_content.append(String::num_uint64(BINDINGS_GENERATOR_VERSION) + ";\n");
+ cs_icalls_content.append(INDENT2 "internal static uint cs_glue_version = ");
+ cs_icalls_content.append(String::num_uint64(CS_GLUE_VERSION) + ";\n");
+ cs_icalls_content.append("\n");
+
+#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(m_icall.im_type_out + " "); \
+ cs_icalls_content.append(m_icall.name + "("); \
+ cs_icalls_content.append(m_icall.im_sig + ");\n"); \
}
for (const List<InternalCall>::Element *E = editor_custom_icalls.front(); E; E = E->next())
@@ -616,7 +1069,7 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_solution_dir
#undef ADD_INTERNAL_CALL
- cs_icalls_content.push_back(INDENT1 CLOSE_BLOCK CLOSE_BLOCK);
+ cs_icalls_content.append(INDENT1 CLOSE_BLOCK CLOSE_BLOCK);
String internal_methods_file = path_join(core_dir, BINDINGS_CLASS_NATIVECALLS_EDITOR ".cs");
@@ -636,13 +1089,12 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_solution_dir
r_solution.add_new_project(EDITOR_API_ASSEMBLY_NAME, proj_info);
- if (verbose_output)
- OS::get_singleton()->print("The solution and C# project for the Editor API was generated successfully\n");
+ _log("The solution and C# project for the Editor API was generated successfully\n");
return OK;
}
-Error BindingsGenerator::generate_cs_api(const String &p_output_dir, bool p_verbose_output) {
+Error BindingsGenerator::generate_cs_api(const String &p_output_dir) {
DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
ERR_FAIL_COND_V(!da, ERR_CANT_CREATE);
@@ -659,13 +1111,13 @@ Error BindingsGenerator::generate_cs_api(const String &p_output_dir, bool p_verb
Error proj_err;
- proj_err = generate_cs_core_project(p_output_dir, solution, p_verbose_output);
+ proj_err = generate_cs_core_project(p_output_dir, solution);
if (proj_err != OK) {
ERR_PRINT("Generation of the Core API C# project failed");
return proj_err;
}
- proj_err = generate_cs_editor_project(p_output_dir, solution, p_verbose_output);
+ proj_err = generate_cs_editor_project(p_output_dir, solution);
if (proj_err != OK) {
ERR_PRINT("Generation of the Editor API C# project failed");
return proj_err;
@@ -704,62 +1156,64 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
List<InternalCall> &custom_icalls = itype.api_type == ClassDB::API_EDITOR ? editor_custom_icalls : core_custom_icalls;
- if (verbose_output)
- OS::get_singleton()->print(String("Generating " + itype.proxy_name + ".cs...\n").utf8());
+ _log("Generating %s.cs...\n", itype.proxy_name.utf8().get_data());
String ctor_method(ICALL_PREFIX + itype.proxy_name + "_Ctor"); // Used only for derived types
- List<String> output;
+ StringBuilder output;
- output.push_back("using System;\n"); // IntPtr
- output.push_back("using System.Diagnostics;\n"); // DebuggerBrowsable
+ output.append("using System;\n"); // IntPtr
+ output.append("using System.Diagnostics;\n"); // DebuggerBrowsable
- output.push_back("\n#pragma warning disable CS1591 // Disable warning: "
- "'Missing XML comment for publicly visible type or member'\n");
+ output.append("\n"
+ "#pragma warning disable CS1591 // Disable warning: "
+ "'Missing XML comment for publicly visible type or member'\n"
+ "#pragma warning disable CS1573 // Disable warning: "
+ "'Parameter has no matching param tag in the XML comment'\n");
- output.push_back("\nnamespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK);
+ output.append("\nnamespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK);
const DocData::ClassDoc *class_doc = itype.class_doc;
if (class_doc && class_doc->description.size()) {
- output.push_back(INDENT1 "/// <summary>\n");
+ String xml_summary = bbcode_to_xml(fix_doc_description(class_doc->description), &itype);
+ Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
- Vector<String> description_lines = class_doc->description.split("\n");
+ if (summary_lines.size()) {
+ output.append(INDENT1 "/// <summary>\n");
- for (int i = 0; i < description_lines.size(); i++) {
- String description_line = description_lines[i].strip_edges();
- if (description_line.size()) {
- output.push_back(INDENT1 "/// ");
- output.push_back(description_line.xml_escape());
- output.push_back("\n");
+ for (int i = 0; i < summary_lines.size(); i++) {
+ output.append(INDENT1 "/// ");
+ output.append(summary_lines[i]);
+ output.append("\n");
}
- }
- output.push_back(INDENT1 "/// </summary>\n");
+ output.append(INDENT1 "/// </summary>\n");
+ }
}
- output.push_back(INDENT1 "public ");
+ output.append(INDENT1 "public ");
if (itype.is_singleton) {
- output.push_back("static partial class ");
+ output.append("static partial class ");
} else {
- output.push_back(itype.is_instantiable ? "partial class " : "abstract partial class ");
+ output.append(itype.is_instantiable ? "partial class " : "abstract partial class ");
}
- output.push_back(itype.proxy_name);
+ output.append(itype.proxy_name);
if (itype.is_singleton) {
- output.push_back("\n");
+ output.append("\n");
} else if (is_derived_type) {
if (obj_types.has(itype.base_name)) {
- output.push_back(" : ");
- output.push_back(obj_types[itype.base_name].proxy_name);
- output.push_back("\n");
+ output.append(" : ");
+ output.append(obj_types[itype.base_name].proxy_name);
+ output.append("\n");
} else {
ERR_PRINTS("Base type '" + itype.base_name.operator String() + "' does not exist, for class " + itype.name);
return ERR_INVALID_DATA;
}
}
- output.push_back(INDENT1 "{");
+ output.append(INDENT1 "{");
if (class_doc) {
@@ -769,31 +1223,31 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
const ConstantInterface &iconstant = E->get();
if (iconstant.const_doc && iconstant.const_doc->description.size()) {
- output.push_back(MEMBER_BEGIN "/// <summary>\n");
+ String xml_summary = bbcode_to_xml(fix_doc_description(iconstant.const_doc->description), &itype);
+ Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
- Vector<String> description_lines = iconstant.const_doc->description.split("\n");
+ if (summary_lines.size()) {
+ output.append(MEMBER_BEGIN "/// <summary>\n");
- for (int i = 0; i < description_lines.size(); i++) {
- String description_line = description_lines[i].strip_edges();
- if (description_line.size()) {
- output.push_back(INDENT2 "/// ");
- output.push_back(description_line.xml_escape());
- output.push_back("\n");
+ for (int i = 0; i < summary_lines.size(); i++) {
+ output.append(INDENT2 "/// ");
+ output.append(summary_lines[i]);
+ output.append("\n");
}
- }
- output.push_back(INDENT2 "/// </summary>");
+ output.append(INDENT2 "/// </summary>");
+ }
}
- output.push_back(MEMBER_BEGIN "public const int ");
- output.push_back(iconstant.proxy_name);
- output.push_back(" = ");
- output.push_back(itos(iconstant.value));
- output.push_back(";");
+ output.append(MEMBER_BEGIN "public const int ");
+ output.append(iconstant.proxy_name);
+ output.append(" = ");
+ output.append(itos(iconstant.value));
+ output.append(";");
}
if (itype.constants.size())
- output.push_back("\n");
+ output.append("\n");
// Add enums
@@ -802,38 +1256,38 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
ERR_FAIL_COND_V(ienum.constants.empty(), ERR_BUG);
- output.push_back(MEMBER_BEGIN "public enum ");
- output.push_back(ienum.cname.operator String());
- output.push_back(MEMBER_BEGIN OPEN_BLOCK);
+ output.append(MEMBER_BEGIN "public enum ");
+ output.append(ienum.cname.operator String());
+ output.append(MEMBER_BEGIN OPEN_BLOCK);
- for (const List<ConstantInterface>::Element *E = ienum.constants.front(); E; E = E->next()) {
- const ConstantInterface &iconstant = E->get();
+ for (const List<ConstantInterface>::Element *F = ienum.constants.front(); F; F = F->next()) {
+ const ConstantInterface &iconstant = F->get();
if (iconstant.const_doc && iconstant.const_doc->description.size()) {
- output.push_back(INDENT3 "/// <summary>\n");
+ String xml_summary = bbcode_to_xml(fix_doc_description(iconstant.const_doc->description), &itype);
+ Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
- Vector<String> description_lines = iconstant.const_doc->description.split("\n");
+ if (summary_lines.size()) {
+ output.append(INDENT3 "/// <summary>\n");
- for (int i = 0; i < description_lines.size(); i++) {
- String description_line = description_lines[i].strip_edges();
- if (description_line.size()) {
- output.push_back(INDENT3 "/// ");
- output.push_back(description_line.xml_escape());
- output.push_back("\n");
+ for (int i = 0; i < summary_lines.size(); i++) {
+ output.append(INDENT3 "/// ");
+ output.append(summary_lines[i]);
+ output.append("\n");
}
- }
- output.push_back(INDENT3 "/// </summary>\n");
+ output.append(INDENT3 "/// </summary>\n");
+ }
}
- output.push_back(INDENT3);
- output.push_back(iconstant.proxy_name);
- output.push_back(" = ");
- output.push_back(itos(iconstant.value));
- output.push_back(E != ienum.constants.back() ? ",\n" : "\n");
+ output.append(INDENT3);
+ output.append(iconstant.proxy_name);
+ output.append(" = ");
+ output.append(itos(iconstant.value));
+ output.append(F != ienum.constants.back() ? ",\n" : "\n");
}
- output.push_back(INDENT2 CLOSE_BLOCK);
+ output.append(INDENT2 CLOSE_BLOCK);
}
// Add properties
@@ -849,50 +1303,58 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
}
}
+ // TODO: BINDINGS_NATIVE_NAME_FIELD should be StringName, once we support it in C#
+
if (itype.is_singleton) {
// Add the type name and the singleton pointer as static fields
- output.push_back(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \"");
- output.push_back(itype.name);
- output.push_back("\";\n");
-
- output.push_back(INDENT2 "internal static IntPtr " BINDINGS_PTR_FIELD " = ");
- output.push_back(itype.api_type == ClassDB::API_EDITOR ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS);
- output.push_back("." ICALL_PREFIX);
- output.push_back(itype.name);
- output.push_back(SINGLETON_ICALL_SUFFIX "();\n");
+ output.append(MEMBER_BEGIN "private static Godot.Object singleton;\n");
+ output.append(MEMBER_BEGIN "public static Godot.Object Singleton\n" INDENT2 "{\n" INDENT3
+ "get\n" INDENT3 "{\n" INDENT4 "if (singleton == null)\n" INDENT5
+ "singleton = Engine.GetSingleton(" BINDINGS_NATIVE_NAME_FIELD ");\n" INDENT4
+ "return singleton;\n" INDENT3 "}\n" INDENT2 "}\n");
+
+ output.append(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \"");
+ output.append(itype.name);
+ output.append("\";\n");
+
+ output.append(INDENT2 "internal static IntPtr " BINDINGS_PTR_FIELD " = ");
+ output.append(itype.api_type == ClassDB::API_EDITOR ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS);
+ output.append("." ICALL_PREFIX);
+ output.append(itype.name);
+ output.append(SINGLETON_ICALL_SUFFIX "();\n");
} else if (is_derived_type) {
// Add member fields
- output.push_back(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \"");
- output.push_back(itype.name);
- output.push_back("\";\n");
+ output.append(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \"");
+ output.append(itype.name);
+ output.append("\";\n");
// Add default constructor
if (itype.is_instantiable) {
- output.push_back(MEMBER_BEGIN "public ");
- output.push_back(itype.proxy_name);
- output.push_back("() : this(");
- output.push_back(itype.memory_own ? "true" : "false");
+ output.append(MEMBER_BEGIN "public ");
+ output.append(itype.proxy_name);
+ output.append("() : this(");
+ output.append(itype.memory_own ? "true" : "false");
// The default constructor may also be called by the engine when instancing existing native objects
// The engine will initialize the pointer field of the managed side before calling the constructor
// This is why we only allocate a new native object from the constructor if the pointer field is not set
- output.push_back(")\n" OPEN_BLOCK_L2 "if (" BINDINGS_PTR_FIELD " == IntPtr.Zero)\n" INDENT4 BINDINGS_PTR_FIELD " = ");
- output.push_back(itype.api_type == ClassDB::API_EDITOR ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS);
- output.push_back("." + ctor_method);
- output.push_back("(this);\n" CLOSE_BLOCK_L2);
+ output.append(")\n" OPEN_BLOCK_L2 "if (" BINDINGS_PTR_FIELD " == IntPtr.Zero)\n" INDENT4 BINDINGS_PTR_FIELD " = ");
+ output.append(itype.api_type == ClassDB::API_EDITOR ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS);
+ output.append("." + ctor_method);
+ output.append("(this);\n" CLOSE_BLOCK_L2);
} else {
// Hide the constructor
- output.push_back(MEMBER_BEGIN "internal ");
- output.push_back(itype.proxy_name);
- output.push_back("() {}\n");
+ output.append(MEMBER_BEGIN "internal ");
+ output.append(itype.proxy_name);
+ output.append("() {}\n");
}
// Add.. em.. trick constructor. Sort of.
- output.push_back(MEMBER_BEGIN "internal ");
- output.push_back(itype.proxy_name);
- output.push_back("(bool " CS_FIELD_MEMORYOWN ") : base(" CS_FIELD_MEMORYOWN ") {}\n");
+ output.append(MEMBER_BEGIN "internal ");
+ output.append(itype.proxy_name);
+ output.append("(bool " CS_FIELD_MEMORYOWN ") : base(" CS_FIELD_MEMORYOWN ") {}\n");
}
int method_bind_count = 0;
@@ -919,15 +1381,17 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
custom_icalls.push_back(ctor_icall);
}
- output.push_back(INDENT1 CLOSE_BLOCK /* class */
+ output.append(INDENT1 CLOSE_BLOCK /* class */
CLOSE_BLOCK /* namespace */);
- output.push_back("\n#pragma warning restore CS1591\n");
+ output.append("\n"
+ "#pragma warning restore CS1591\n"
+ "#pragma warning restore CS1573\n");
return _save_file(p_output_file, output);
}
-Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInterface &p_itype, const PropertyInterface &p_iprop, List<String> &p_output) {
+Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInterface &p_itype, const PropertyInterface &p_iprop, StringBuilder &p_output) {
const MethodInterface *setter = p_itype.find_method_by_name(p_iprop.setter);
@@ -972,86 +1436,96 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
const TypeInterface *prop_itype = _get_type_or_null(proptype_name);
ERR_FAIL_NULL_V(prop_itype, ERR_BUG); // Property type not found
- String prop_proxy_name = escape_csharp_keyword(snake_to_pascal_case(p_iprop.cname));
-
- // Prevent property and enclosing type from sharing the same name
- if (prop_proxy_name == p_itype.proxy_name) {
- if (verbose_output) {
- WARN_PRINTS("Name of property `" + prop_proxy_name + "` is ambiguous with the name of its class `" +
- p_itype.proxy_name + "`. Renaming property to `" + prop_proxy_name + "_`");
- }
-
- prop_proxy_name += "_";
- }
-
if (p_iprop.prop_doc && p_iprop.prop_doc->description.size()) {
- p_output.push_back(MEMBER_BEGIN "/// <summary>\n");
+ String xml_summary = bbcode_to_xml(fix_doc_description(p_iprop.prop_doc->description), &p_itype);
+ Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
- Vector<String> description_lines = p_iprop.prop_doc->description.split("\n");
+ if (summary_lines.size()) {
+ p_output.append(MEMBER_BEGIN "/// <summary>\n");
- for (int i = 0; i < description_lines.size(); i++) {
- String description_line = description_lines[i].strip_edges();
- if (description_line.size()) {
- p_output.push_back(INDENT2 "/// ");
- p_output.push_back(description_line.xml_escape());
- p_output.push_back("\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.push_back(INDENT2 "/// </summary>");
+ p_output.append(INDENT2 "/// </summary>");
+ }
}
- p_output.push_back(MEMBER_BEGIN "public ");
+ p_output.append(MEMBER_BEGIN "public ");
if (p_itype.is_singleton)
- p_output.push_back("static ");
+ p_output.append("static ");
- p_output.push_back(prop_itype->cs_type);
- p_output.push_back(" ");
- p_output.push_back(prop_proxy_name.replace("/", "__"));
- p_output.push_back("\n" INDENT2 OPEN_BLOCK);
+ p_output.append(prop_itype->cs_type);
+ p_output.append(" ");
+ p_output.append(p_iprop.proxy_name);
+ p_output.append("\n" INDENT2 OPEN_BLOCK);
if (getter) {
- p_output.push_back(INDENT3 "get\n" OPEN_BLOCK_L3);
- p_output.push_back("return ");
- p_output.push_back(getter->proxy_name + "(");
+ p_output.append(INDENT3 "get\n"
+
+ // TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that)
+ "#pragma warning disable CS0618 // Disable warning about obsolete method\n"
+
+ OPEN_BLOCK_L3);
+
+ p_output.append("return ");
+ p_output.append(getter->proxy_name + "(");
if (p_iprop.index != -1) {
const ArgumentInterface &idx_arg = getter->arguments.front()->get();
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);
- p_output.push_back("(" + idx_arg_type->proxy_name + ")" + itos(p_iprop.index));
+ p_output.append("(" + idx_arg_type->proxy_name + ")" + itos(p_iprop.index));
} else {
- p_output.push_back(itos(p_iprop.index));
+ p_output.append(itos(p_iprop.index));
}
}
- p_output.push_back(");\n" CLOSE_BLOCK_L3);
+ p_output.append(");\n"
+
+ CLOSE_BLOCK_L3
+
+ // TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that)
+ "#pragma warning restore CS0618\n");
}
if (setter) {
- p_output.push_back(INDENT3 "set\n" OPEN_BLOCK_L3);
- p_output.push_back(setter->proxy_name + "(");
+ p_output.append(INDENT3 "set\n"
+
+ // TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that)
+ "#pragma warning disable CS0618 // Disable warning about obsolete method\n"
+
+ OPEN_BLOCK_L3);
+
+ p_output.append(setter->proxy_name + "(");
if (p_iprop.index != -1) {
const ArgumentInterface &idx_arg = setter->arguments.front()->get();
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);
- p_output.push_back("(" + idx_arg_type->proxy_name + ")" + itos(p_iprop.index) + ", ");
+ p_output.append("(" + idx_arg_type->proxy_name + ")" + itos(p_iprop.index) + ", ");
} else {
- p_output.push_back(itos(p_iprop.index) + ", ");
+ p_output.append(itos(p_iprop.index) + ", ");
}
}
- p_output.push_back("value);\n" CLOSE_BLOCK_L3);
+ p_output.append("value);\n"
+
+ CLOSE_BLOCK_L3
+
+ // TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that)
+ "#pragma warning restore CS0618\n");
}
- p_output.push_back(CLOSE_BLOCK_L2);
+ p_output.append(CLOSE_BLOCK_L2);
return OK;
}
-Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::MethodInterface &p_imethod, int &p_method_bind_count, List<String> &p_output) {
+Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::MethodInterface &p_imethod, int &p_method_bind_count, StringBuilder &p_output) {
const TypeInterface *return_type = _get_type_or_placeholder(p_imethod.return_type);
@@ -1063,7 +1537,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
String icall_params = method_bind_field + ", ";
icall_params += sformat(p_itype.cs_in, "this");
- List<String> default_args_doc;
+ StringBuilder default_args_doc;
// Retrieve information from the arguments
for (const List<ArgumentInterface>::Element *F = p_imethod.arguments.front(); F; F = F->next()) {
@@ -1129,7 +1603,10 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
icall_params += arg_type->cs_in.empty() ? arg_in : sformat(arg_type->cs_in, arg_in);
- default_args_doc.push_back(INDENT2 "/// <param name=\"" + iarg.name + "\">If the param is null, then the default value is " + def_arg + "</param>\n");
+ // Apparently the name attribute must not include the @
+ String param_tag_name = iarg.name.begins_with("@") ? iarg.name.substr(1, iarg.name.length()) : iarg.name;
+
+ default_args_doc.append(INDENT2 "/// <param name=\"" + param_tag_name + "\">If the parameter is null, then the default value is " + def_arg + "</param>\n");
} else {
icall_params += arg_type->cs_in.empty() ? iarg.name : sformat(arg_type->cs_in, iarg.name);
}
@@ -1138,61 +1615,67 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
// Generate method
{
if (!p_imethod.is_virtual && !p_imethod.requires_object_call) {
- p_output.push_back(MEMBER_BEGIN "[DebuggerBrowsable(DebuggerBrowsableState.Never)]" MEMBER_BEGIN "private static IntPtr ");
- p_output.push_back(method_bind_field + " = Object." ICALL_GET_METHODBIND "(" BINDINGS_NATIVE_NAME_FIELD ", \"");
- p_output.push_back(p_imethod.name);
- p_output.push_back("\");\n");
+ 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(p_imethod.name);
+ p_output.append("\");\n");
}
if (p_imethod.method_doc && p_imethod.method_doc->description.size()) {
- p_output.push_back(MEMBER_BEGIN "/// <summary>\n");
+ String xml_summary = bbcode_to_xml(fix_doc_description(p_imethod.method_doc->description), &p_itype);
+ Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
- Vector<String> description_lines = p_imethod.method_doc->description.split("\n");
+ if (summary_lines.size() || default_args_doc.get_string_length()) {
+ p_output.append(MEMBER_BEGIN "/// <summary>\n");
- for (int i = 0; i < description_lines.size(); i++) {
- String description_line = description_lines[i].strip_edges();
- if (description_line.size()) {
- p_output.push_back(INDENT2 "/// ");
- p_output.push_back(description_line.xml_escape());
- p_output.push_back("\n");
+ for (int i = 0; i < summary_lines.size(); i++) {
+ p_output.append(INDENT2 "/// ");
+ p_output.append(summary_lines[i]);
+ p_output.append("\n");
}
- }
- for (List<String>::Element *E = default_args_doc.front(); E; E = E->next()) {
- p_output.push_back(E->get().xml_escape());
+ p_output.append(default_args_doc.as_string());
+ p_output.append(INDENT2 "/// </summary>");
}
-
- p_output.push_back(INDENT2 "/// </summary>");
}
if (!p_imethod.is_internal) {
- p_output.push_back(MEMBER_BEGIN "[GodotMethod(\"");
- p_output.push_back(p_imethod.name);
- p_output.push_back("\")]");
+ p_output.append(MEMBER_BEGIN "[GodotMethod(\"");
+ p_output.append(p_imethod.name);
+ p_output.append("\")]");
+ }
+
+ if (p_imethod.is_deprecated) {
+ if (p_imethod.deprecation_message.empty())
+ WARN_PRINTS("An empty deprecation message is discouraged. Method: " + p_imethod.proxy_name);
+
+ p_output.append(MEMBER_BEGIN "[Obsolete(\"");
+ p_output.append(p_imethod.deprecation_message);
+ p_output.append("\")]");
}
- p_output.push_back(MEMBER_BEGIN);
- p_output.push_back(p_imethod.is_internal ? "internal " : "public ");
+ p_output.append(MEMBER_BEGIN);
+ p_output.append(p_imethod.is_internal ? "internal " : "public ");
if (p_itype.is_singleton) {
- p_output.push_back("static ");
+ p_output.append("static ");
} else if (p_imethod.is_virtual) {
- p_output.push_back("virtual ");
+ p_output.append("virtual ");
}
- p_output.push_back(return_type->cs_type + " ");
- p_output.push_back(p_imethod.proxy_name + "(");
- p_output.push_back(arguments_sig + ")\n" OPEN_BLOCK_L2);
+ p_output.append(return_type->cs_type + " ");
+ p_output.append(p_imethod.proxy_name + "(");
+ p_output.append(arguments_sig + ")\n" OPEN_BLOCK_L2);
if (p_imethod.is_virtual) {
// Godot virtual method must be overridden, therefore we return a default value by default.
if (return_type->cname == name_cache.type_void) {
- p_output.push_back("return;\n" CLOSE_BLOCK_L2);
+ p_output.append("return;\n" CLOSE_BLOCK_L2);
} else {
- p_output.push_back("return default(");
- p_output.push_back(return_type->cs_type);
- p_output.push_back(");\n" CLOSE_BLOCK_L2);
+ p_output.append("return default(");
+ p_output.append(return_type->cs_type);
+ p_output.append(");\n" CLOSE_BLOCK_L2);
}
return OK; // Won't increment method bind count
@@ -1201,16 +1684,16 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
if (p_imethod.requires_object_call) {
// Fallback to Godot's object.Call(string, params)
- p_output.push_back(CS_METHOD_CALL "(\"");
- p_output.push_back(p_imethod.name);
- p_output.push_back("\"");
+ p_output.append(CS_METHOD_CALL "(\"");
+ p_output.append(p_imethod.name);
+ p_output.append("\"");
for (const List<ArgumentInterface>::Element *F = p_imethod.arguments.front(); F; F = F->next()) {
- p_output.push_back(", ");
- p_output.push_back(F->get().name);
+ p_output.append(", ");
+ p_output.append(F->get().name);
}
- p_output.push_back(");\n" CLOSE_BLOCK_L2);
+ p_output.append(");\n" CLOSE_BLOCK_L2);
return OK; // Won't increment method bind count
}
@@ -1224,37 +1707,36 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
im_call += "." + im_icall->name + "(" + icall_params + ")";
if (p_imethod.arguments.size())
- p_output.push_back(cs_in_statements);
+ p_output.append(cs_in_statements);
if (return_type->cname == name_cache.type_void) {
- p_output.push_back(im_call + ";\n");
+ p_output.append(im_call + ";\n");
} else if (return_type->cs_out.empty()) {
- p_output.push_back("return " + im_call + ";\n");
+ p_output.append("return " + im_call + ";\n");
} else {
- p_output.push_back(sformat(return_type->cs_out, im_call, return_type->cs_type, return_type->im_type_out));
- p_output.push_back("\n");
+ p_output.append(sformat(return_type->cs_out, im_call, return_type->cs_type, return_type->im_type_out));
+ p_output.append("\n");
}
- p_output.push_back(CLOSE_BLOCK_L2);
+ p_output.append(CLOSE_BLOCK_L2);
}
p_method_bind_count++;
+
return OK;
}
Error BindingsGenerator::generate_glue(const String &p_output_dir) {
- verbose_output = true;
-
bool dir_exists = DirAccess::exists(p_output_dir);
ERR_EXPLAIN("The output directory does not exist.");
ERR_FAIL_COND_V(!dir_exists, ERR_FILE_BAD_PATH);
- List<String> output;
+ StringBuilder output;
- output.push_back("/* THIS FILE IS GENERATED DO NOT EDIT */\n");
- output.push_back("#include \"" GLUE_HEADER_FILE "\"\n");
- output.push_back("\n#ifdef MONO_GLUE_ENABLED\n");
+ output.append("/* THIS FILE IS GENERATED DO NOT EDIT */\n");
+ output.append("#include \"" GLUE_HEADER_FILE "\"\n");
+ output.append("\n#ifdef MONO_GLUE_ENABLED\n");
generated_icall_funcs.clear();
@@ -1274,7 +1756,7 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) {
List<InternalCall> &custom_icalls = itype.api_type == ClassDB::API_EDITOR ? editor_custom_icalls : core_custom_icalls;
- OS::get_singleton()->print(String("Generating " + itype.name + "...\n").utf8());
+ OS::get_singleton()->print("Generating %s...\n", itype.name.utf8().get_data());
String ctor_method(ICALL_PREFIX + itype.proxy_name + "_Ctor"); // Used only for derived types
@@ -1294,11 +1776,11 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) {
if (!find_icall_by_name(singleton_icall.name, custom_icalls))
custom_icalls.push_back(singleton_icall);
- output.push_back("Object* ");
- output.push_back(singleton_icall_name);
- output.push_back("() " OPEN_BLOCK "\treturn Engine::get_singleton()->get_singleton_object(\"");
- output.push_back(itype.proxy_name);
- output.push_back("\");\n" CLOSE_BLOCK "\n");
+ output.append("Object* ");
+ output.append(singleton_icall_name);
+ output.append("() " OPEN_BLOCK "\treturn Engine::get_singleton()->get_singleton_object(\"");
+ output.append(itype.proxy_name);
+ output.append("\");\n" CLOSE_BLOCK "\n");
}
if (is_derived_type && itype.is_instantiable) {
@@ -1307,43 +1789,43 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) {
if (!find_icall_by_name(ctor_icall.name, custom_icalls))
custom_icalls.push_back(ctor_icall);
- output.push_back("Object* ");
- output.push_back(ctor_method);
- output.push_back("(MonoObject* obj) " OPEN_BLOCK
- "\t" C_MACRO_OBJECT_CONSTRUCT "(instance, \"");
- output.push_back(itype.name);
- output.push_back("\");\n"
- "\t" C_METHOD_TIE_MANAGED_TO_UNMANAGED "(obj, instance);\n"
- "\treturn instance;\n" CLOSE_BLOCK "\n");
+ output.append("Object* ");
+ output.append(ctor_method);
+ output.append("(MonoObject* obj) " OPEN_BLOCK
+ "\t" C_MACRO_OBJECT_CONSTRUCT "(instance, \"");
+ output.append(itype.name);
+ output.append("\");\n"
+ "\t" C_METHOD_TIE_MANAGED_TO_UNMANAGED "(obj, instance);\n"
+ "\treturn instance;\n" CLOSE_BLOCK "\n");
}
}
- output.push_back("namespace GodotSharpBindings\n" OPEN_BLOCK "\n");
+ output.append("namespace GodotSharpBindings\n" OPEN_BLOCK "\n");
- output.push_back("uint64_t get_core_api_hash() { return ");
- output.push_back(String::num_uint64(GDMono::get_singleton()->get_api_core_hash()) + "U; }\n");
+ output.append("uint64_t get_core_api_hash() { return ");
+ output.append(String::num_uint64(GDMono::get_singleton()->get_api_core_hash()) + "U; }\n");
- output.push_back("#ifdef TOOLS_ENABLED\n"
- "uint64_t get_editor_api_hash() { return ");
- output.push_back(String::num_uint64(GDMono::get_singleton()->get_api_editor_hash()) + "U; }\n");
- output.push_back("#endif // TOOLS_ENABLED\n");
+ output.append("#ifdef TOOLS_ENABLED\n"
+ "uint64_t get_editor_api_hash() { return ");
+ output.append(String::num_uint64(GDMono::get_singleton()->get_api_editor_hash()) + "U; }\n");
+ output.append("#endif // TOOLS_ENABLED\n");
- output.push_back("uint32_t get_bindings_version() { return ");
- output.push_back(String::num_uint64(BINDINGS_GENERATOR_VERSION) + "; }\n");
+ output.append("uint32_t get_bindings_version() { return ");
+ output.append(String::num_uint64(BINDINGS_GENERATOR_VERSION) + "; }\n");
- output.push_back("\nvoid register_generated_icalls() " OPEN_BLOCK);
- output.push_back("\tgodot_register_glue_header_icalls();\n");
+ output.append("\nvoid register_generated_icalls() " OPEN_BLOCK);
+ output.append("\tgodot_register_glue_header_icalls();\n");
-#define ADD_INTERNAL_CALL_REGISTRATION(m_icall) \
- { \
- output.push_back("\tmono_add_internal_call("); \
- output.push_back("\"" BINDINGS_NAMESPACE "."); \
- output.push_back(m_icall.editor_only ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS); \
- output.push_back("::"); \
- output.push_back(m_icall.name); \
- output.push_back("\", (void*)"); \
- output.push_back(m_icall.name); \
- output.push_back(");\n"); \
+#define ADD_INTERNAL_CALL_REGISTRATION(m_icall) \
+ { \
+ output.append("\tmono_add_internal_call("); \
+ output.append("\"" BINDINGS_NAMESPACE "."); \
+ output.append(m_icall.editor_only ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS); \
+ output.append("::"); \
+ output.append(m_icall.name); \
+ output.append("\", (void*)"); \
+ output.append(m_icall.name); \
+ output.append(");\n"); \
}
bool tools_sequence = false;
@@ -1352,11 +1834,11 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) {
if (tools_sequence) {
if (!E->get().editor_only) {
tools_sequence = false;
- output.push_back("#endif\n");
+ output.append("#endif\n");
}
} else {
if (E->get().editor_only) {
- output.push_back("#ifdef TOOLS_ENABLED\n");
+ output.append("#ifdef TOOLS_ENABLED\n");
tools_sequence = true;
}
}
@@ -1366,23 +1848,23 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) {
if (tools_sequence) {
tools_sequence = false;
- output.push_back("#endif\n");
+ output.append("#endif\n");
}
- output.push_back("#ifdef TOOLS_ENABLED\n");
+ output.append("#ifdef TOOLS_ENABLED\n");
for (const List<InternalCall>::Element *E = editor_custom_icalls.front(); E; E = E->next())
ADD_INTERNAL_CALL_REGISTRATION(E->get());
- output.push_back("#endif // TOOLS_ENABLED\n");
+ output.append("#endif // TOOLS_ENABLED\n");
for (const List<InternalCall>::Element *E = method_icalls.front(); E; E = E->next()) {
if (tools_sequence) {
if (!E->get().editor_only) {
tools_sequence = false;
- output.push_back("#endif\n");
+ output.append("#endif\n");
}
} else {
if (E->get().editor_only) {
- output.push_back("#ifdef TOOLS_ENABLED\n");
+ output.append("#ifdef TOOLS_ENABLED\n");
tools_sequence = true;
}
}
@@ -1392,14 +1874,14 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) {
if (tools_sequence) {
tools_sequence = false;
- output.push_back("#endif\n");
+ output.append("#endif\n");
}
#undef ADD_INTERNAL_CALL_REGISTRATION
- output.push_back(CLOSE_BLOCK "\n} // namespace GodotSharpBindings\n");
+ output.append(CLOSE_BLOCK "\n} // namespace GodotSharpBindings\n");
- output.push_back("\n#endif // MONO_GLUE_ENABLED\n");
+ output.append("\n#endif // MONO_GLUE_ENABLED\n");
Error save_err = _save_file(path_join(p_output_dir, "mono_glue.gen.cpp"), output);
if (save_err != OK)
@@ -1414,23 +1896,20 @@ uint32_t BindingsGenerator::get_version() {
return BINDINGS_GENERATOR_VERSION;
}
-Error BindingsGenerator::_save_file(const String &p_path, const List<String> &p_content) {
+Error BindingsGenerator::_save_file(const String &p_path, const StringBuilder &p_content) {
FileAccessRef file = FileAccess::open(p_path, FileAccess::WRITE);
ERR_EXPLAIN("Cannot open file: " + p_path);
ERR_FAIL_COND_V(!file, ERR_FILE_CANT_WRITE);
- for (const List<String>::Element *E = p_content.front(); E; E = E->next()) {
- file->store_string(E->get());
- }
-
+ file->store_string(p_content.as_string());
file->close();
return OK;
}
-Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::MethodInterface &p_imethod, List<String> &p_output) {
+Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::MethodInterface &p_imethod, StringBuilder &p_output) {
if (p_imethod.is_virtual)
return OK; // Ignore
@@ -1486,15 +1965,15 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte
generated_icall_funcs.push_back(im_icall);
if (im_icall->editor_only)
- p_output.push_back("#ifdef TOOLS_ENABLED\n");
+ p_output.append("#ifdef TOOLS_ENABLED\n");
// Generate icall function
- p_output.push_back(ret_void ? "void " : return_type->c_type_out + " ");
- p_output.push_back(icall_method);
- p_output.push_back("(");
- p_output.push_back(c_func_sig);
- p_output.push_back(") " OPEN_BLOCK);
+ p_output.append(ret_void ? "void " : return_type->c_type_out + " ");
+ p_output.append(icall_method);
+ p_output.append("(");
+ p_output.append(c_func_sig);
+ p_output.append(") " OPEN_BLOCK);
String fail_ret = ret_void ? "" : ", " + (return_type->c_type_out.ends_with("*") ? "NULL" : return_type->c_type_out + "()");
@@ -1508,7 +1987,7 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte
// the Variant alive until the method returns. Otherwise, if the returned Variant holds a RefPtr,
// it could be deleted too early. This is the case with GDScript.new() which returns OBJECT.
// Alternatively, we could just return Variant, but that would result in a worse API.
- p_output.push_back("\tVariant " C_LOCAL_VARARG_RET ";\n");
+ p_output.append("\tVariant " C_LOCAL_VARARG_RET ";\n");
}
if (return_type->is_object_type) {
@@ -1518,90 +1997,82 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte
ptrcall_return_type = return_type->c_type;
}
- p_output.push_back("\t" + ptrcall_return_type);
- p_output.push_back(" " C_LOCAL_RET);
- p_output.push_back(initialization + ";\n");
- p_output.push_back("\tERR_FAIL_NULL_V(" CS_PARAM_INSTANCE);
- p_output.push_back(fail_ret);
- p_output.push_back(");\n");
+ p_output.append("\t" + ptrcall_return_type);
+ p_output.append(" " C_LOCAL_RET);
+ p_output.append(initialization + ";\n");
+ p_output.append("\tERR_FAIL_NULL_V(" CS_PARAM_INSTANCE);
+ p_output.append(fail_ret);
+ p_output.append(");\n");
} else {
- p_output.push_back("\tERR_FAIL_NULL(" CS_PARAM_INSTANCE ");\n");
+ p_output.append("\tERR_FAIL_NULL(" CS_PARAM_INSTANCE ");\n");
}
if (p_imethod.arguments.size()) {
if (p_imethod.is_vararg) {
- String err_fail_macro = ret_void ? "ERR_FAIL_COND" : "ERR_FAIL_COND_V";
String vararg_arg = "arg" + argc_str;
String real_argc_str = itos(p_imethod.arguments.size() - 1); // Arguments count without vararg
- p_output.push_back("\tVector<Variant> varargs;\n"
- "\tint vararg_length = mono_array_length(");
- p_output.push_back(vararg_arg);
- p_output.push_back(");\n\tint total_length = ");
- p_output.push_back(real_argc_str);
- p_output.push_back(" + vararg_length;\n\t");
- p_output.push_back(err_fail_macro);
- p_output.push_back("(varargs.resize(vararg_length) != OK");
- p_output.push_back(fail_ret);
- p_output.push_back(");\n\tVector<Variant*> " C_LOCAL_PTRCALL_ARGS ";\n\t");
- p_output.push_back(err_fail_macro);
- p_output.push_back("(call_args.resize(total_length) != OK");
- p_output.push_back(fail_ret);
- p_output.push_back(");\n");
- p_output.push_back(c_in_statements);
- p_output.push_back("\tfor (int i = 0; i < vararg_length; i++) " OPEN_BLOCK
- "\t\tMonoObject* elem = mono_array_get(");
- p_output.push_back(vararg_arg);
- p_output.push_back(", MonoObject*, i);\n"
- "\t\tvarargs.set(i, GDMonoMarshal::mono_object_to_variant(elem));\n"
- "\t\t" C_LOCAL_PTRCALL_ARGS ".set(");
- p_output.push_back(real_argc_str);
- p_output.push_back(" + i, &varargs.write[i]);\n\t" CLOSE_BLOCK);
+ p_output.append("\tint vararg_length = mono_array_length(");
+ p_output.append(vararg_arg);
+ p_output.append(");\n\tint total_length = ");
+ p_output.append(real_argc_str);
+ p_output.append(" + vararg_length;\n"
+ "\tArgumentsVector<Variant> varargs(vararg_length);\n"
+ "\tArgumentsVector<const Variant *> " C_LOCAL_PTRCALL_ARGS "(total_length);\n");
+ p_output.append(c_in_statements);
+ p_output.append("\tfor (int i = 0; i < vararg_length; i++) " OPEN_BLOCK
+ "\t\tMonoObject* elem = mono_array_get(");
+ p_output.append(vararg_arg);
+ p_output.append(", MonoObject*, i);\n"
+ "\t\tvarargs.set(i, GDMonoMarshal::mono_object_to_variant(elem));\n"
+ "\t\t" C_LOCAL_PTRCALL_ARGS ".set(");
+ p_output.append(real_argc_str);
+ p_output.append(" + i, &varargs.get(i));\n\t" CLOSE_BLOCK);
} else {
- p_output.push_back(c_in_statements);
- p_output.push_back("\tconst void* " C_LOCAL_PTRCALL_ARGS "[");
- p_output.push_back(argc_str + "] = { ");
- p_output.push_back(c_args_var_content + " };\n");
+ p_output.append(c_in_statements);
+ p_output.append("\tconst void* " C_LOCAL_PTRCALL_ARGS "[");
+ p_output.append(argc_str + "] = { ");
+ p_output.append(c_args_var_content + " };\n");
}
}
if (p_imethod.is_vararg) {
- p_output.push_back("\tVariant::CallError vcall_error;\n\t");
+ p_output.append("\tVariant::CallError vcall_error;\n\t");
if (!ret_void) {
// See the comment on the C_LOCAL_VARARG_RET declaration
if (return_type->cname != name_cache.type_Variant) {
- p_output.push_back(C_LOCAL_VARARG_RET " = ");
+ p_output.append(C_LOCAL_VARARG_RET " = ");
} else {
- p_output.push_back(C_LOCAL_RET " = ");
+ p_output.append(C_LOCAL_RET " = ");
}
}
- p_output.push_back(CS_PARAM_METHODBIND "->call(" CS_PARAM_INSTANCE ", ");
- p_output.push_back(p_imethod.arguments.size() ? "(const Variant**)" C_LOCAL_PTRCALL_ARGS ".ptr()" : "NULL");
- p_output.push_back(", total_length, vcall_error);\n");
+ 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(", total_length, vcall_error);\n");
// See the comment on the C_LOCAL_VARARG_RET declaration
if (return_type->cname != name_cache.type_Variant) {
- p_output.push_back("\t" C_LOCAL_RET " = " C_LOCAL_VARARG_RET ";\n");
+ p_output.append("\t" C_LOCAL_RET " = " C_LOCAL_VARARG_RET ";\n");
}
} else {
- p_output.push_back("\t" CS_PARAM_METHODBIND "->ptrcall(" CS_PARAM_INSTANCE ", ");
- p_output.push_back(p_imethod.arguments.size() ? C_LOCAL_PTRCALL_ARGS ", " : "NULL, ");
- p_output.push_back(!ret_void ? "&" C_LOCAL_RET ");\n" : "NULL);\n");
+ 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");
}
if (!ret_void) {
if (return_type->c_out.empty())
- p_output.push_back("\treturn " C_LOCAL_RET ";\n");
+ p_output.append("\treturn " C_LOCAL_RET ";\n");
else
- p_output.push_back(sformat(return_type->c_out, return_type->c_type_out, C_LOCAL_RET, return_type->name));
+ p_output.append(sformat(return_type->c_out, return_type->c_type_out, C_LOCAL_RET, return_type->name));
}
- p_output.push_back(CLOSE_BLOCK "\n");
+ p_output.append(CLOSE_BLOCK "\n");
if (im_icall->editor_only)
- p_output.push_back("#endif // TOOLS_ENABLED\n");
+ p_output.append("#endif // TOOLS_ENABLED\n");
}
return OK;
@@ -1654,6 +2125,58 @@ const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_placehol
return &placeholder_types.insert(placeholder.cname, placeholder)->get();
}
+StringName BindingsGenerator::_get_int_type_name_from_meta(GodotTypeInfo::Metadata p_meta) {
+
+ switch (p_meta) {
+ case GodotTypeInfo::METADATA_INT_IS_INT8:
+ return "sbyte";
+ break;
+ case GodotTypeInfo::METADATA_INT_IS_INT16:
+ return "short";
+ break;
+ case GodotTypeInfo::METADATA_INT_IS_INT32:
+ return "int";
+ break;
+ case GodotTypeInfo::METADATA_INT_IS_INT64:
+ return "long";
+ break;
+ case GodotTypeInfo::METADATA_INT_IS_UINT8:
+ return "byte";
+ break;
+ case GodotTypeInfo::METADATA_INT_IS_UINT16:
+ return "ushort";
+ break;
+ case GodotTypeInfo::METADATA_INT_IS_UINT32:
+ return "uint";
+ break;
+ case GodotTypeInfo::METADATA_INT_IS_UINT64:
+ return "ulong";
+ break;
+ default:
+ // Assume INT32
+ return "int";
+ }
+}
+
+StringName BindingsGenerator::_get_float_type_name_from_meta(GodotTypeInfo::Metadata p_meta) {
+
+ switch (p_meta) {
+ case GodotTypeInfo::METADATA_REAL_IS_FLOAT:
+ return "float";
+ break;
+ case GodotTypeInfo::METADATA_REAL_IS_DOUBLE:
+ return "double";
+ break;
+ default:
+ // Assume real_t (float or double depending of REAL_T_IS_DOUBLE)
+#ifdef REAL_T_IS_DOUBLE
+ return "double";
+#else
+ return "float";
+#endif
+ }
+}
+
void BindingsGenerator::_populate_object_type_interfaces() {
obj_types.clear();
@@ -1673,15 +2196,13 @@ void BindingsGenerator::_populate_object_type_interfaces() {
}
if (!ClassDB::is_class_exposed(type_cname)) {
- if (verbose_output)
- WARN_PRINTS("Ignoring type " + type_cname.operator String() + " because it's not exposed");
+ _log("Ignoring type `%s` because it's not exposed\n", String(type_cname).utf8().get_data());
class_list.pop_front();
continue;
}
if (!ClassDB::is_class_enabled(type_cname)) {
- if (verbose_output)
- WARN_PRINTS("Ignoring type " + type_cname.operator String() + " because it's not enabled");
+ _log("Ignoring type `%s` because it's not enabled\n", String(type_cname).utf8().get_data());
class_list.pop_front();
continue;
}
@@ -1709,10 +2230,12 @@ void BindingsGenerator::_populate_object_type_interfaces() {
itype.im_type_in = "IntPtr";
itype.im_type_out = itype.proxy_name;
+ // Populate properties
+
List<PropertyInfo> property_list;
ClassDB::get_property_list(type_cname, &property_list, true);
- // Populate properties
+ Map<StringName, StringName> accessor_methods;
for (const List<PropertyInfo>::Element *E = property_list.front(); E; E = E->next()) {
const PropertyInfo &property = E->get();
@@ -1722,24 +2245,30 @@ void BindingsGenerator::_populate_object_type_interfaces() {
PropertyInterface iprop;
iprop.cname = property.name;
- iprop.proxy_name = escape_csharp_keyword(snake_to_pascal_case(iprop.cname));
iprop.setter = ClassDB::get_property_setter(type_cname, iprop.cname);
iprop.getter = ClassDB::get_property_getter(type_cname, iprop.cname);
+ if (iprop.setter != StringName())
+ accessor_methods[iprop.setter] = iprop.cname;
+ if (iprop.getter != StringName())
+ accessor_methods[iprop.getter] = iprop.cname;
+
bool valid = false;
iprop.index = ClassDB::get_property_index(type_cname, iprop.cname, &valid);
ERR_FAIL_COND(!valid);
- // Prevent property and enclosing type from sharing the same name
+ iprop.proxy_name = escape_csharp_keyword(snake_to_pascal_case(iprop.cname));
+
+ // Prevent the property and its enclosing type from sharing the same name
if (iprop.proxy_name == itype.proxy_name) {
- if (verbose_output) {
- WARN_PRINTS("Name of property `" + iprop.proxy_name + "` is ambiguous with the name of its class `" +
- itype.proxy_name + "`. Renaming property to `" + iprop.proxy_name + "_`");
- }
+ _log("Name of property `%s` is ambiguous with the name of its enclosing class `%s`. Renaming property to `%s_`\n",
+ iprop.proxy_name.utf8().get_data(), itype.proxy_name.utf8().get_data(), iprop.proxy_name.utf8().get_data());
iprop.proxy_name += "_";
}
+ iprop.proxy_name = iprop.proxy_name.replace("/", "__"); // Some members have a slash...
+
iprop.prop_doc = NULL;
for (int i = 0; i < itype.class_doc->properties.size(); i++) {
@@ -1800,14 +2329,13 @@ void BindingsGenerator::_populate_object_type_interfaces() {
// which could actually will return something different.
// Let's put this to notify us if that ever happens.
if (itype.cname != name_cache.type_Object || imethod.name != "free") {
- if (verbose_output) {
- WARN_PRINTS("Notification: New unexpected virtual non-overridable method found.\n"
- "We only expected Object.free, but found " +
- itype.name + "." + imethod.name);
- }
+ ERR_PRINTS("Notification: New unexpected virtual non-overridable method found.\n"
+ "We only expected Object.free, but found " +
+ itype.name + "." + imethod.name);
}
} else {
- ERR_PRINTS("Missing MethodBind for non-virtual method: " + itype.name + "." + imethod.name);
+ ERR_EXPLAIN("Missing MethodBind for non-virtual method: " + itype.name + "." + imethod.name);
+ ERR_FAIL();
}
} else if (return_info.type == Variant::INT && return_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
imethod.return_type.cname = return_info.class_name;
@@ -1821,7 +2349,13 @@ void BindingsGenerator::_populate_object_type_interfaces() {
} else if (return_info.type == Variant::NIL) {
imethod.return_type.cname = name_cache.type_void;
} else {
- imethod.return_type.cname = Variant::get_type_name(return_info.type);
+ if (return_info.type == Variant::INT) {
+ imethod.return_type.cname = _get_int_type_name_from_meta(m ? m->get_argument_meta(-1) : GodotTypeInfo::METADATA_NONE);
+ } else if (return_info.type == Variant::REAL) {
+ imethod.return_type.cname = _get_float_type_name_from_meta(m ? m->get_argument_meta(-1) : GodotTypeInfo::METADATA_NONE);
+ } else {
+ imethod.return_type.cname = Variant::get_type_name(return_info.type);
+ }
}
for (int i = 0; i < argc; i++) {
@@ -1840,7 +2374,13 @@ void BindingsGenerator::_populate_object_type_interfaces() {
} else if (arginfo.type == Variant::NIL) {
iarg.type.cname = name_cache.type_Variant;
} else {
- iarg.type.cname = Variant::get_type_name(arginfo.type);
+ if (arginfo.type == Variant::INT) {
+ iarg.type.cname = _get_int_type_name_from_meta(m ? m->get_argument_meta(i) : GodotTypeInfo::METADATA_NONE);
+ } else if (arginfo.type == Variant::REAL) {
+ iarg.type.cname = _get_float_type_name_from_meta(m ? m->get_argument_meta(i) : GodotTypeInfo::METADATA_NONE);
+ } else {
+ iarg.type.cname = Variant::get_type_name(arginfo.type);
+ }
}
iarg.name = escape_csharp_keyword(snake_to_camel_case(iarg.name));
@@ -1861,16 +2401,24 @@ void BindingsGenerator::_populate_object_type_interfaces() {
imethod.proxy_name = escape_csharp_keyword(snake_to_pascal_case(imethod.name));
- // Prevent naming the property and its enclosing type from sharing the same name
+ // Prevent the method and its enclosing type from sharing the same name
if (imethod.proxy_name == itype.proxy_name) {
- if (verbose_output) {
- WARN_PRINTS("Name of method `" + imethod.proxy_name + "` is ambiguous with the name of its class `" +
- itype.proxy_name + "`. Renaming method to `" + imethod.proxy_name + "_`");
- }
+ _log("Name of method `%s` is ambiguous with the name of its enclosing class `%s`. Renaming method to `%s_`\n",
+ imethod.proxy_name.utf8().get_data(), itype.proxy_name.utf8().get_data(), imethod.proxy_name.utf8().get_data());
imethod.proxy_name += "_";
}
+ Map<StringName, StringName>::Element *accessor = accessor_methods.find(imethod.cname);
+ if (accessor) {
+ const PropertyInterface *accessor_property = itype.find_property_by_name(accessor->value());
+
+ // We only deprecate an accessor method if it's in the same class as the property. It's easier this way, but also
+ // we don't know if an accessor method in a different class could have other purposes, so better leave those untouched.
+ imethod.is_deprecated = true;
+ imethod.deprecation_message = imethod.proxy_name + " is deprecated. Use the " + accessor_property->proxy_name + " property instead.";
+ }
+
if (itype.class_doc) {
for (int i = 0; i < itype.class_doc->methods.size(); i++) {
if (itype.class_doc->methods[i].name == imethod.name) {
@@ -1881,8 +2429,8 @@ void BindingsGenerator::_populate_object_type_interfaces() {
}
if (!imethod.is_virtual && imethod.name[0] == '_') {
- for (const List<PropertyInterface>::Element *E = itype.properties.front(); E; E = E->next()) {
- const PropertyInterface &iprop = E->get();
+ for (const List<PropertyInterface>::Element *F = itype.properties.front(); F; F = F->next()) {
+ const PropertyInterface &iprop = F->get();
if (iprop.setter == imethod.name || iprop.getter == imethod.name) {
imethod.is_internal = true;
@@ -1897,8 +2445,8 @@ void BindingsGenerator::_populate_object_type_interfaces() {
// Populate enums and constants
- List<String> constant_list;
- ClassDB::get_integer_constant_list(type_cname, &constant_list, true);
+ 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;
@@ -1913,13 +2461,13 @@ void BindingsGenerator::_populate_object_type_interfaces() {
enum_proxy_cname = StringName(enum_proxy_name);
}
EnumInterface ienum(enum_proxy_cname);
- const List<StringName> &constants = enum_map.get(*k);
- for (const List<StringName>::Element *E = constants.front(); E; E = E->next()) {
+ const List<StringName> &enum_constants = enum_map.get(*k);
+ for (const List<StringName>::Element *E = enum_constants.front(); E; E = E->next()) {
const StringName &constant_cname = E->get();
String constant_name = constant_cname.operator String();
int *value = class_info->constant_map.getptr(constant_cname);
ERR_FAIL_NULL(value);
- constant_list.erase(constant_name);
+ constants.erase(constant_name);
ConstantInterface iconstant(constant_name, snake_to_pascal_case(constant_name, true), *value);
@@ -1951,7 +2499,7 @@ void BindingsGenerator::_populate_object_type_interfaces() {
enum_types.insert(enum_itype.cname, enum_itype);
}
- for (const List<String>::Element *E = constant_list.front(); E; E = E->next()) {
+ for (const List<String>::Element *E = constants.front(); E; E = E->next()) {
const String &constant_name = E->get();
int *value = class_info->constant_map.getptr(StringName(E->get()));
ERR_FAIL_NULL(value);
@@ -2032,6 +2580,7 @@ void BindingsGenerator::_default_argument_from_variant(const Variant &p_val, Arg
r_iarg.default_argument = "null";
break;
}
+ FALLTHROUGH;
case Variant::DICTIONARY:
case Variant::_RID:
r_iarg.default_argument = "new %s()";
@@ -2054,7 +2603,8 @@ void BindingsGenerator::_default_argument_from_variant(const Variant &p_val, Arg
r_iarg.default_argument = Variant::get_type_name(p_val.get_type()) + ".Identity";
r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
break;
- default: {}
+ default: {
+ }
}
if (r_iarg.def_param_mode == ArgumentInterface::CONSTANT && r_iarg.type.cname == name_cache.type_Variant && r_iarg.default_argument != "null")
@@ -2096,7 +2646,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
// bool
itype = TypeInterface::create_value_type(String("bool"));
-
{
// MonoBoolean <---> bool
itype.c_in = "\t%0 %1_in = (%0)%1;\n";
@@ -2110,45 +2659,73 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.im_type_out = itype.name;
builtin_types.insert(itype.cname, itype);
- // int
- // C interface is the same as that of enums. Remember to apply any
- // changes done here to TypeInterface::postsetup_enum_type as well
- itype = TypeInterface::create_value_type(String("int"));
- itype.c_arg_in = "&%s_in";
+ // Integer types
{
- // The expected types for parameters and return value in ptrcall are 'int64_t' or 'uint64_t'.
- itype.c_in = "\t%0 %1_in = (%0)%1;\n";
- itype.c_out = "\treturn (%0)%1;\n";
- itype.c_type = "int64_t";
+ // C interface for 'uint32_t' is the same as that of enums. Remember to apply
+ // any of the changes done here to 'TypeInterface::postsetup_enum_type' as well.
+#define INSERT_INT_TYPE(m_name, m_c_type_in_out, m_c_type) \
+ { \
+ itype = TypeInterface::create_value_type(String(m_name)); \
+ { \
+ itype.c_in = "\t%0 %1_in = (%0)%1;\n"; \
+ itype.c_out = "\treturn (%0)%1;\n"; \
+ itype.c_type = #m_c_type; \
+ itype.c_arg_in = "&%s_in"; \
+ } \
+ itype.c_type_in = #m_c_type_in_out; \
+ itype.c_type_out = itype.c_type_in; \
+ itype.im_type_in = itype.name; \
+ itype.im_type_out = itype.name; \
+ builtin_types.insert(itype.cname, itype); \
}
- itype.c_type_in = "int32_t";
- itype.c_type_out = itype.c_type_in;
- itype.im_type_in = itype.name;
- itype.im_type_out = itype.name;
- builtin_types.insert(itype.cname, itype);
- // real_t
- itype = TypeInterface();
- itype.name = "float"; // The name is always "float" in Variant, even with REAL_T_IS_DOUBLE.
- itype.cname = itype.name;
-#ifdef REAL_T_IS_DOUBLE
- itype.proxy_name = "double";
-#else
- itype.proxy_name = "float";
-#endif
+ // The expected type for all integers in ptrcall is 'int64_t', so that's what we use for 'c_type'
+
+ INSERT_INT_TYPE("sbyte", int8_t, int64_t);
+ INSERT_INT_TYPE("short", int16_t, int64_t);
+ INSERT_INT_TYPE("int", int32_t, int64_t);
+ INSERT_INT_TYPE("long", int64_t, int64_t);
+ INSERT_INT_TYPE("byte", uint8_t, int64_t);
+ INSERT_INT_TYPE("ushort", uint16_t, int64_t);
+ INSERT_INT_TYPE("uint", uint32_t, int64_t);
+ INSERT_INT_TYPE("ulong", uint64_t, int64_t);
+ }
+
+ // Floating point types
{
- // The expected type for parameters and return value in ptrcall is 'double'.
- itype.c_in = "\t%0 %1_in = (%0)%1;\n";
- itype.c_out = "\treturn (%0)%1;\n";
+ // float
+ itype = TypeInterface();
+ itype.name = "float";
+ itype.cname = itype.name;
+ itype.proxy_name = "float";
+ {
+ // The expected type for 'float' in ptrcall is 'double'
+ itype.c_in = "\t%0 %1_in = (%0)%1;\n";
+ itype.c_out = "\treturn (%0)%1;\n";
+ itype.c_type = "double";
+ itype.c_type_in = "float";
+ itype.c_type_out = "float";
+ itype.c_arg_in = "&%s_in";
+ }
+ itype.cs_type = itype.proxy_name;
+ itype.im_type_in = itype.proxy_name;
+ itype.im_type_out = itype.proxy_name;
+ builtin_types.insert(itype.cname, itype);
+
+ // double
+ itype = TypeInterface();
+ itype.name = "double";
+ itype.cname = itype.name;
+ itype.proxy_name = "double";
itype.c_type = "double";
- itype.c_type_in = "real_t";
- itype.c_type_out = "real_t";
- itype.c_arg_in = "&%s_in";
+ itype.c_type_in = "double";
+ itype.c_type_out = "double";
+ itype.c_arg_in = "&%s";
+ itype.cs_type = itype.proxy_name;
+ itype.im_type_in = itype.proxy_name;
+ itype.im_type_out = itype.proxy_name;
+ builtin_types.insert(itype.cname, itype);
}
- itype.cs_type = itype.proxy_name;
- itype.im_type_in = itype.proxy_name;
- itype.im_type_out = itype.proxy_name;
- builtin_types.insert(itype.cname, itype);
// String
itype = TypeInterface();
@@ -2344,9 +2921,9 @@ void BindingsGenerator::_populate_global_constants() {
if (enum_name != StringName()) {
EnumInterface ienum(enum_name);
- List<EnumInterface>::Element *match = global_enums.find(ienum);
- if (match) {
- match->get().constants.push_back(iconstant);
+ List<EnumInterface>::Element *enum_match = global_enums.find(ienum);
+ if (enum_match) {
+ enum_match->get().constants.push_back(iconstant);
} else {
ienum.constants.push_back(iconstant);
global_enums.push_back(ienum);
@@ -2398,7 +2975,18 @@ void BindingsGenerator::_populate_global_constants() {
}
}
-void BindingsGenerator::initialize() {
+void BindingsGenerator::_log(const char *p_format, ...) {
+
+ if (log_print_enabled) {
+ va_list list;
+
+ va_start(list, p_format);
+ OS::get_singleton()->print("%s", str_format(p_format, list).utf8().get_data());
+ va_end(list);
+ }
+}
+
+void BindingsGenerator::_initialize() {
EditorHelp::generate_doc();
@@ -2421,38 +3009,33 @@ void BindingsGenerator::initialize() {
void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args) {
const int NUM_OPTIONS = 2;
- int options_left = NUM_OPTIONS;
-
String mono_glue_option = "--generate-mono-glue";
String cs_api_option = "--generate-cs-api";
- verbose_output = true;
+ String mono_glue_path;
+ String cs_api_path;
+
+ int options_left = NUM_OPTIONS;
const List<String>::Element *elem = p_cmdline_args.front();
while (elem && options_left) {
-
if (elem->get() == mono_glue_option) {
-
const List<String>::Element *path_elem = elem->next();
if (path_elem) {
- if (get_singleton()->generate_glue(path_elem->get()) != OK)
- ERR_PRINTS(mono_glue_option + ": Failed to generate mono glue");
+ mono_glue_path = path_elem->get();
elem = elem->next();
} else {
ERR_PRINTS(mono_glue_option + ": No output directory specified");
}
--options_left;
-
} else if (elem->get() == cs_api_option) {
-
const List<String>::Element *path_elem = elem->next();
if (path_elem) {
- if (get_singleton()->generate_cs_api(path_elem->get()) != OK)
- ERR_PRINTS(cs_api_option + ": Failed to generate the C# API");
+ cs_api_path = path_elem->get();
elem = elem->next();
} else {
ERR_PRINTS(cs_api_option + ": No output directory specified");
@@ -2464,10 +3047,23 @@ void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args)
elem = elem->next();
}
- verbose_output = false;
+ if (mono_glue_path.length() || cs_api_path.length()) {
+ BindingsGenerator bindings_generator;
+ bindings_generator.set_log_print_enabled(true);
+
+ if (mono_glue_path.length()) {
+ if (bindings_generator.generate_glue(mono_glue_path) != OK)
+ ERR_PRINTS(mono_glue_option + ": Failed to generate mono glue");
+ }
- if (options_left != NUM_OPTIONS)
+ if (cs_api_path.length()) {
+ if (bindings_generator.generate_cs_api(cs_api_path) != OK)
+ ERR_PRINTS(cs_api_option + ": Failed to generate the C# API");
+ }
+
+ // Exit once done
::exit(0);
+ }
}
#endif
diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h
index 8a1c942f6b..bdba28c267 100644
--- a/modules/mono/editor/bindings_generator.h
+++ b/modules/mono/editor/bindings_generator.h
@@ -32,6 +32,7 @@
#define BINDINGS_GENERATOR_H
#include "core/class_db.h"
+#include "core/string_builder.h"
#include "dotnet_solution.h"
#include "editor/doc/doc_data.h"
#include "editor/editor_help.h"
@@ -87,8 +88,13 @@ class BindingsGenerator {
StringName cname;
bool is_enum;
- TypeReference() {
- is_enum = false;
+ TypeReference() :
+ is_enum(false) {
+ }
+
+ TypeReference(const StringName &p_cname) :
+ cname(p_cname),
+ is_enum(false) {
}
};
@@ -153,17 +159,20 @@ class BindingsGenerator {
const DocData::MethodDoc *method_doc;
+ bool is_deprecated;
+ String deprecation_message;
+
void add_argument(const ArgumentInterface &argument) {
arguments.push_back(argument);
}
MethodInterface() {
- return_type.cname = BindingsGenerator::get_singleton()->name_cache.type_void;
is_vararg = false;
is_virtual = false;
requires_object_call = false;
is_internal = false;
method_doc = NULL;
+ is_deprecated = false;
}
};
@@ -321,6 +330,15 @@ class BindingsGenerator {
return NULL;
}
+ const PropertyInterface *find_property_by_name(const StringName &p_cname) const {
+ for (const List<PropertyInterface>::Element *E = properties.front(); E; E = E->next()) {
+ if (E->get().cname == p_cname)
+ return &E->get();
+ }
+
+ return NULL;
+ }
+
const PropertyInterface *find_property_by_proxy_name(const String &p_proxy_name) const {
for (const List<PropertyInterface>::Element *E = properties.front(); E; E = E->next()) {
if (E->get().proxy_name == p_proxy_name)
@@ -385,8 +403,8 @@ class BindingsGenerator {
}
static void postsetup_enum_type(TypeInterface &r_enum_itype) {
- // C interface is the same as that of 'int'. Remember to apply any
- // changes done here to the 'int' type interface as well
+ // C interface for enums is the same as that of 'uint32_t'. Remember to apply
+ // any of the changes done here to the 'uint32_t' type interface as well.
r_enum_itype.c_arg_in = "&%s_in";
{
@@ -454,7 +472,7 @@ class BindingsGenerator {
}
};
- static bool verbose_output;
+ bool log_print_enabled;
OrderedHashMap<StringName, TypeInterface> obj_types;
@@ -475,27 +493,52 @@ class BindingsGenerator {
struct NameCache {
StringName type_void;
- StringName type_int;
StringName type_Array;
StringName type_Dictionary;
StringName type_Variant;
StringName type_VarArg;
StringName type_Object;
StringName type_Reference;
+ StringName type_String;
+ StringName type_at_GlobalScope;
StringName enum_Error;
+ StringName type_sbyte;
+ StringName type_short;
+ StringName type_int;
+ StringName type_long;
+ StringName type_byte;
+ StringName type_ushort;
+ StringName type_uint;
+ StringName type_ulong;
+ StringName type_float;
+ StringName type_double;
+
NameCache() {
type_void = StaticCString::create("void");
- type_int = StaticCString::create("int");
type_Array = StaticCString::create("Array");
type_Dictionary = StaticCString::create("Dictionary");
type_Variant = StaticCString::create("Variant");
type_VarArg = StaticCString::create("VarArg");
type_Object = StaticCString::create("Object");
type_Reference = StaticCString::create("Reference");
+ type_String = StaticCString::create("String");
+ type_at_GlobalScope = StaticCString::create("@GlobalScope");
enum_Error = StaticCString::create("Error");
+
+ type_sbyte = StaticCString::create("sbyte");
+ type_short = StaticCString::create("short");
+ type_int = StaticCString::create("int");
+ type_long = StaticCString::create("long");
+ type_byte = StaticCString::create("byte");
+ type_ushort = StaticCString::create("ushort");
+ type_uint = StaticCString::create("uint");
+ type_ulong = StaticCString::create("ulong");
+ type_float = StaticCString::create("float");
+ type_double = StaticCString::create("double");
}
+ private:
NameCache(const NameCache &);
NameCache &operator=(const NameCache &);
};
@@ -511,6 +554,15 @@ class BindingsGenerator {
return NULL;
}
+ const ConstantInterface *find_constant_by_name(const String &p_name, const List<ConstantInterface> &p_constants) const {
+ for (const List<ConstantInterface>::Element *E = p_constants.front(); E; E = E->next()) {
+ if (E->get().name == p_name)
+ return &E->get();
+ }
+
+ return NULL;
+ }
+
inline String get_unique_sig(const TypeInterface &p_type) {
if (p_type.is_reference)
return "Ref";
@@ -522,6 +574,8 @@ class BindingsGenerator {
return p_type.name;
}
+ String bbcode_to_xml(const String &p_bbcode, const TypeInterface *p_itype);
+
int _determine_enum_prefix(const EnumInterface &p_ienum);
void _apply_prefix_to_enum_constants(EnumInterface &p_ienum, int p_prefix_length);
@@ -530,6 +584,9 @@ class BindingsGenerator {
const TypeInterface *_get_type_or_null(const TypeReference &p_typeref);
const TypeInterface *_get_type_or_placeholder(const TypeReference &p_typeref);
+ StringName _get_int_type_name_from_meta(GodotTypeInfo::Metadata p_meta);
+ StringName _get_float_type_name_from_meta(GodotTypeInfo::Metadata p_meta);
+
void _default_argument_from_variant(const Variant &p_val, ArgumentInterface &r_iarg);
void _populate_object_type_interfaces();
@@ -539,42 +596,35 @@ class BindingsGenerator {
Error _generate_cs_type(const TypeInterface &itype, const String &p_output_file);
- Error _generate_cs_property(const TypeInterface &p_itype, const PropertyInterface &p_iprop, List<String> &p_output);
- Error _generate_cs_method(const TypeInterface &p_itype, const MethodInterface &p_imethod, int &p_method_bind_count, List<String> &p_output);
+ 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);
- void _generate_global_constants(List<String> &p_output);
+ void _generate_global_constants(StringBuilder &p_output);
- Error _generate_glue_method(const TypeInterface &p_itype, const MethodInterface &p_imethod, List<String> &p_output);
+ Error _generate_glue_method(const TypeInterface &p_itype, const MethodInterface &p_imethod, StringBuilder &p_output);
- Error _save_file(const String &p_path, const List<String> &p_content);
+ Error _save_file(const String &p_path, const StringBuilder &p_content);
- BindingsGenerator() {}
+ void _log(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3;
- BindingsGenerator(const BindingsGenerator &);
- BindingsGenerator &operator=(const BindingsGenerator &);
-
- friend class CSharpLanguage;
- static BindingsGenerator *singleton;
+ void _initialize();
public:
- Error generate_cs_core_project(const String &p_solution_dir, DotNetSolution &r_solution, bool p_verbose_output = true);
- Error generate_cs_editor_project(const String &p_solution_dir, DotNetSolution &r_solution, bool p_verbose_output = true);
- Error generate_cs_api(const String &p_output_dir, bool p_verbose_output = true);
+ Error generate_cs_core_project(const String &p_solution_dir, DotNetSolution &r_solution);
+ Error generate_cs_editor_project(const String &p_solution_dir, DotNetSolution &r_solution);
+ Error generate_cs_api(const String &p_output_dir);
Error generate_glue(const String &p_output_dir);
+ void set_log_print_enabled(bool p_enabled) { log_print_enabled = p_enabled; }
+
static uint32_t get_version();
- void initialize();
+ static void handle_cmdline_args(const List<String> &p_cmdline_args);
- _FORCE_INLINE_ static BindingsGenerator *get_singleton() {
- if (!singleton) {
- singleton = memnew(BindingsGenerator);
- singleton->initialize();
- }
- return singleton;
+ BindingsGenerator() :
+ log_print_enabled(true) {
+ _initialize();
}
-
- static void handle_cmdline_args(const List<String> &p_cmdline_args);
};
#endif
diff --git a/modules/mono/editor/csharp_project.cpp b/modules/mono/editor/csharp_project.cpp
index beeff51bc2..fe79286556 100644
--- a/modules/mono/editor/csharp_project.cpp
+++ b/modules/mono/editor/csharp_project.cpp
@@ -158,7 +158,7 @@ Error generate_scripts_metadata(const String &p_project_path, const String &p_ou
PoolStringArray project_files = GDMonoMarshal::mono_array_to_PoolStringArray(ret);
PoolStringArray::Read r = project_files.read();
- Dictionary old_dict = CSharpLanguage::get_singleton()->get_scripts_metadata();
+ Dictionary old_dict = CSharpLanguage::get_singleton()->get_scripts_metadata_or_nothing();
Dictionary new_dict;
for (int i = 0; i < project_files.size(); i++) {
diff --git a/modules/mono/editor/godotsharp_builds.cpp b/modules/mono/editor/godotsharp_builds.cpp
index a040e4a344..de3fd91223 100644
--- a/modules/mono/editor/godotsharp_builds.cpp
+++ b/modules/mono/editor/godotsharp_builds.cpp
@@ -100,22 +100,24 @@ MonoString *godot_icall_BuildInstance_get_MSBuildPath() {
if (msbuild_tools_path.empty() || !FileAccess::exists(msbuild_tools_path)) {
// Try to search it again if it wasn't found last time or if it was removed from its location
msbuild_tools_path = MonoRegUtils::find_msbuild_tools_path();
- }
-
- if (msbuild_tools_path.length()) {
- if (!msbuild_tools_path.ends_with("\\"))
- msbuild_tools_path += "\\";
- return GDMonoMarshal::mono_string_from_godot(msbuild_tools_path + "MSBuild.exe");
+ if (msbuild_tools_path.empty()) {
+ ERR_PRINTS("Cannot find executable for '" PROP_NAME_MSBUILD_VS "'. Tried with path: " + msbuild_tools_path);
+ return NULL;
+ }
}
- print_verbose("Cannot find executable for '" PROP_NAME_MSBUILD_VS "'. Trying with '" PROP_NAME_MSBUILD_MONO "'...");
- } // FALL THROUGH
+ if (!msbuild_tools_path.ends_with("\\"))
+ msbuild_tools_path += "\\";
+
+ return GDMonoMarshal::mono_string_from_godot(msbuild_tools_path + "MSBuild.exe");
+ } break;
case GodotSharpBuilds::MSBUILD_MONO: {
String msbuild_path = GDMono::get_singleton()->get_mono_reg_info().bin_dir.plus_file("msbuild.bat");
if (!FileAccess::exists(msbuild_path)) {
- WARN_PRINTS("Cannot find executable for '" PROP_NAME_MSBUILD_MONO "'. Tried with path: " + msbuild_path);
+ ERR_PRINTS("Cannot find executable for '" PROP_NAME_MSBUILD_MONO "'. Tried with path: " + msbuild_path);
+ return NULL;
}
return GDMonoMarshal::mono_string_from_godot(msbuild_path);
@@ -124,7 +126,8 @@ MonoString *godot_icall_BuildInstance_get_MSBuildPath() {
String xbuild_path = GDMono::get_singleton()->get_mono_reg_info().bin_dir.plus_file("xbuild.bat");
if (!FileAccess::exists(xbuild_path)) {
- WARN_PRINTS("Cannot find executable for '" PROP_NAME_XBUILD "'. Tried with path: " + xbuild_path);
+ ERR_PRINTS("Cannot find executable for '" PROP_NAME_XBUILD "'. Tried with path: " + xbuild_path);
+ return NULL;
}
return GDMonoMarshal::mono_string_from_godot(xbuild_path);
@@ -144,7 +147,7 @@ MonoString *godot_icall_BuildInstance_get_MSBuildPath() {
}
if (xbuild_path.empty()) {
- WARN_PRINT("Cannot find binary for '" PROP_NAME_XBUILD "'");
+ ERR_PRINT("Cannot find binary for '" PROP_NAME_XBUILD "'");
return NULL;
}
} else {
@@ -154,7 +157,7 @@ MonoString *godot_icall_BuildInstance_get_MSBuildPath() {
}
if (msbuild_path.empty()) {
- WARN_PRINT("Cannot find binary for '" PROP_NAME_MSBUILD_MONO "'");
+ ERR_PRINT("Cannot find binary for '" PROP_NAME_MSBUILD_MONO "'");
return NULL;
}
}
@@ -168,22 +171,6 @@ MonoString *godot_icall_BuildInstance_get_MSBuildPath() {
#endif
}
-MonoString *godot_icall_BuildInstance_get_FrameworkPath() {
-
-#if defined(WINDOWS_ENABLED)
- const MonoRegInfo &mono_reg_info = GDMono::get_singleton()->get_mono_reg_info();
- if (mono_reg_info.assembly_dir.length()) {
- String framework_path = path_join(mono_reg_info.assembly_dir, "mono", "4.5");
- return GDMonoMarshal::mono_string_from_godot(framework_path);
- }
-
- ERR_EXPLAIN("Cannot find Mono's assemblies directory in the registry");
- ERR_FAIL_V(NULL);
-#else
- return NULL;
-#endif
-}
-
MonoString *godot_icall_BuildInstance_get_MonoWindowsBinDir() {
#if defined(WINDOWS_ENABLED)
@@ -208,6 +195,11 @@ MonoBoolean godot_icall_BuildInstance_get_UsingMonoMSBuildOnWindows() {
#endif
}
+MonoBoolean godot_icall_BuildInstance_get_PrintBuildOutput() {
+
+ return (bool)EDITOR_GET("mono/builds/print_build_output");
+}
+
void GodotSharpBuilds::register_internal_calls() {
static bool registered = false;
@@ -216,9 +208,9 @@ void GodotSharpBuilds::register_internal_calls() {
mono_add_internal_call("GodotSharpTools.Build.BuildSystem::godot_icall_BuildInstance_ExitCallback", (void *)godot_icall_BuildInstance_ExitCallback);
mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_MSBuildPath", (void *)godot_icall_BuildInstance_get_MSBuildPath);
- mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_FrameworkPath", (void *)godot_icall_BuildInstance_get_FrameworkPath);
mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_MonoWindowsBinDir", (void *)godot_icall_BuildInstance_get_MonoWindowsBinDir);
mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_UsingMonoMSBuildOnWindows", (void *)godot_icall_BuildInstance_get_UsingMonoMSBuildOnWindows);
+ mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_PrintBuildOutput", (void *)godot_icall_BuildInstance_get_PrintBuildOutput);
}
void GodotSharpBuilds::show_build_error_dialog(const String &p_message) {
@@ -331,10 +323,13 @@ bool GodotSharpBuilds::make_api_assembly(APIAssembly::Type p_api_type) {
String api_sln_file = api_sln_dir.plus_file(API_SOLUTION_NAME ".sln");
if (!DirAccess::exists(api_sln_dir) || !FileAccess::exists(api_sln_file)) {
- BindingsGenerator *gen = BindingsGenerator::get_singleton();
- bool gen_verbose = OS::get_singleton()->is_stdout_verbose();
+ BindingsGenerator bindings_generator;
- Error err = gen->generate_cs_api(api_sln_dir, gen_verbose);
+ if (!OS::get_singleton()->is_stdout_verbose()) {
+ bindings_generator.set_log_print_enabled(false);
+ }
+
+ Error err = bindings_generator.generate_cs_api(api_sln_dir);
if (err != OK) {
show_build_error_dialog("Failed to generate " API_SOLUTION_NAME " solution. Error: " + itos(err));
return false;
@@ -459,7 +454,11 @@ GodotSharpBuilds::GodotSharpBuilds() {
// Build tool settings
EditorSettings *ed_settings = EditorSettings::get_singleton();
+#ifdef WINDOWS_ENABLED
+ EDITOR_DEF("mono/builds/build_tool", MSBUILD_VS);
+#else
EDITOR_DEF("mono/builds/build_tool", MSBUILD_MONO);
+#endif
ed_settings->add_property_hint(PropertyInfo(Variant::INT, "mono/builds/build_tool", PROPERTY_HINT_ENUM,
PROP_NAME_MSBUILD_MONO
@@ -467,6 +466,8 @@ GodotSharpBuilds::GodotSharpBuilds() {
"," PROP_NAME_MSBUILD_VS
#endif
"," PROP_NAME_XBUILD));
+
+ EDITOR_DEF("mono/builds/print_build_output", false);
}
GodotSharpBuilds::~GodotSharpBuilds() {
diff --git a/modules/mono/editor/godotsharp_editor.cpp b/modules/mono/editor/godotsharp_editor.cpp
index ee93229700..9d42528927 100644
--- a/modules/mono/editor/godotsharp_editor.cpp
+++ b/modules/mono/editor/godotsharp_editor.cpp
@@ -167,9 +167,6 @@ void GodotSharpEditor::_remove_create_sln_menu_option() {
menu_popup->remove_item(menu_popup->get_item_index(MENU_CREATE_SLN));
- if (menu_popup->get_item_count() == 0)
- menu_button->hide();
-
bottom_panel_btn->show();
}
@@ -188,6 +185,16 @@ void GodotSharpEditor::_toggle_about_dialog_on_start(bool p_enabled) {
}
}
+void GodotSharpEditor::_build_solution_pressed() {
+
+ if (!FileAccess::exists(GodotSharpDirs::get_project_sln_path())) {
+ if (!_create_project_solution())
+ return; // Failed to create solution
+ }
+
+ MonoBottomPanel::get_singleton()->call("_build_project_pressed");
+}
+
void GodotSharpEditor::_menu_option_pressed(int p_id) {
switch (p_id) {
@@ -223,6 +230,7 @@ void GodotSharpEditor::_notification(int p_notification) {
void GodotSharpEditor::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_build_solution_pressed"), &GodotSharpEditor::_build_solution_pressed);
ClassDB::bind_method(D_METHOD("_create_project_solution"), &GodotSharpEditor::_create_project_solution);
ClassDB::bind_method(D_METHOD("_make_api_solutions_if_needed"), &GodotSharpEditor::_make_api_solutions_if_needed);
ClassDB::bind_method(D_METHOD("_remove_create_sln_menu_option"), &GodotSharpEditor::_remove_create_sln_menu_option);
@@ -272,19 +280,30 @@ Error GodotSharpEditor::open_in_external_editor(const Ref<Script> &p_script, int
static String vscode_path;
if (vscode_path.empty() || !FileAccess::exists(vscode_path)) {
- static List<String> vscode_name;
- vscode_name.push_back("code");
- vscode_name.push_back("code-oss");
- vscode_name.push_back("vscode");
- vscode_name.push_back("vscode-oss");
- vscode_name.push_back("visual-studio-code");
- vscode_name.push_back("visual-studio-code-oss");
// Try to search it again if it wasn't found last time or if it was removed from its location
- for (int i = 0; i < vscode_name.size(); i++) {
- vscode_path = path_which(vscode_name[i]);
- if (!vscode_path.empty() || FileAccess::exists(vscode_path))
+ bool found = false;
+
+ // TODO: Use initializer lists once C++11 is allowed
+
+ static Vector<String> vscode_names;
+ if (vscode_names.empty()) {
+ vscode_names.push_back("code");
+ vscode_names.push_back("code-oss");
+ vscode_names.push_back("vscode");
+ vscode_names.push_back("vscode-oss");
+ vscode_names.push_back("visual-studio-code");
+ vscode_names.push_back("visual-studio-code-oss");
+ }
+ for (int i = 0; i < vscode_names.size(); i++) {
+ vscode_path = path_which(vscode_names[i]);
+ if (!vscode_path.empty()) {
+ found = true;
break;
+ }
}
+
+ if (!found)
+ vscode_path.clear(); // Not found, clear so next time the empty() check is enough
}
List<String> args;
@@ -402,9 +421,12 @@ GodotSharpEditor::GodotSharpEditor(EditorNode *p_editor) {
editor->add_child(memnew(MonoReloadNode));
- menu_button = memnew(MenuButton);
- menu_button->set_text(TTR("Mono"));
- menu_popup = menu_button->get_popup();
+ menu_popup = memnew(PopupMenu);
+ menu_popup->hide();
+ menu_popup->set_as_toplevel(true);
+ menu_popup->set_pass_on_modal_close_click(false);
+
+ editor->add_tool_submenu_item("Mono", menu_popup);
// TODO: Remove or edit this info dialog once Mono support is no longer in alpha
{
@@ -435,12 +457,12 @@ GodotSharpEditor::GodotSharpEditor(EditorNode *p_editor) {
about_label->set_v_size_flags(Control::SIZE_EXPAND_FILL);
about_label->set_autowrap(true);
String about_text =
- String("C# support in Godot Engine is a brand new feature and a work in progress.\n") +
- "It is currently in an alpha stage and is not suitable for use in production.\n\n" +
- "As of Godot 3.1, C# support is not feature-complete and may crash in some situations. " +
- "Bugs and usability issues will be addressed gradually over future 3.x releases, " +
- "including compatibility breaking changes as new features are implemented for a better overall C# experience.\n\n" +
- "If you experience issues with this Mono build, please report them on Godot's issue tracker with details about your system, Mono version, IDE, etc:\n\n" +
+ String("C# support in Godot Engine is in late alpha stage and, while already usable, ") +
+ "it is not meant for use in production.\n\n" +
+ "Projects can be exported to Linux, macOS and Windows, but not yet to mobile or web platforms. " +
+ "Bugs and usability issues will be addressed gradually over future releases, " +
+ "potentially including compatibility breaking changes as new features are implemented for a better overall C# experience.\n\n" +
+ "If you experience issues with this Mono build, please report them on Godot's issue tracker with details about your system, MSBuild version, IDE, etc.:\n\n" +
" https://github.com/godotengine/godot/issues\n\n" +
"Your critical feedback at this stage will play a great role in shaping the C# support in future releases, so thank you!";
about_label->set_text(about_text);
@@ -467,10 +489,12 @@ GodotSharpEditor::GodotSharpEditor(EditorNode *p_editor) {
menu_popup->connect("id_pressed", this, "_menu_option_pressed");
- if (menu_popup->get_item_count() == 0)
- menu_button->hide();
-
- editor->get_menu_hb()->add_child(menu_button);
+ ToolButton *build_button = memnew(ToolButton);
+ build_button->set_text("Build");
+ build_button->set_tooltip("Build solution");
+ build_button->set_focus_mode(Control::FOCUS_NONE);
+ build_button->connect("pressed", this, "_build_solution_pressed");
+ editor->get_menu_hb()->add_child(build_button);
// External editor settings
EditorSettings *ed_settings = EditorSettings::get_singleton();
@@ -478,11 +502,11 @@ GodotSharpEditor::GodotSharpEditor(EditorNode *p_editor) {
String settings_hint_str = "Disabled";
-#ifdef WINDOWS_ENABLED
+#if defined(WINDOWS_ENABLED)
settings_hint_str += ",MonoDevelop,Visual Studio Code";
-#elif OSX_ENABLED
+#elif defined(OSX_ENABLED)
settings_hint_str += ",Visual Studio,MonoDevelop,Visual Studio Code";
-#elif UNIX_ENABLED
+#elif defined(UNIX_ENABLED)
settings_hint_str += ",MonoDevelop,Visual Studio Code";
#endif
diff --git a/modules/mono/editor/godotsharp_editor.h b/modules/mono/editor/godotsharp_editor.h
index c9744a9eed..d9523c384c 100644
--- a/modules/mono/editor/godotsharp_editor.h
+++ b/modules/mono/editor/godotsharp_editor.h
@@ -65,6 +65,8 @@ class GodotSharpEditor : public Node {
void _menu_option_pressed(int p_id);
+ void _build_solution_pressed();
+
static GodotSharpEditor *singleton;
protected:
@@ -79,15 +81,15 @@ public:
enum ExternalEditor {
EDITOR_NONE,
-#ifdef WINDOWS_ENABLED
+#if defined(WINDOWS_ENABLED)
//EDITOR_VISUALSTUDIO, // TODO
EDITOR_MONODEVELOP,
EDITOR_VSCODE
-#elif OSX_ENABLED
+#elif defined(OSX_ENABLED)
EDITOR_VISUALSTUDIO_MAC,
EDITOR_MONODEVELOP,
EDITOR_VSCODE
-#elif UNIX_ENABLED
+#elif defined(UNIX_ENABLED)
EDITOR_MONODEVELOP,
EDITOR_VSCODE
#endif
diff --git a/modules/mono/editor/godotsharp_export.cpp b/modules/mono/editor/godotsharp_export.cpp
index 47341e3555..ee5fed1a0c 100644
--- a/modules/mono/editor/godotsharp_export.cpp
+++ b/modules/mono/editor/godotsharp_export.cpp
@@ -124,7 +124,7 @@ void GodotSharpExport::_export_begin(const Set<String> &p_features, bool p_debug
ERR_FAIL_COND(!load_success);
Vector<String> search_dirs;
- GDMonoAssembly::fill_search_dirs(search_dirs);
+ GDMonoAssembly::fill_search_dirs(search_dirs, build_config);
Error depend_error = _get_assembly_dependencies(scripts_assembly, search_dirs, dependencies);
ERR_FAIL_COND(depend_error != OK);
}
@@ -194,8 +194,8 @@ Error GodotSharpExport::_get_assembly_dependencies(GDMonoAssembly *p_assembly, c
String path;
bool has_extension = ref_name.ends_with(".dll") || ref_name.ends_with(".exe");
- for (int i = 0; i < p_search_dirs.size(); i++) {
- const String &search_dir = p_search_dirs[i];
+ for (int j = 0; j < p_search_dirs.size(); j++) {
+ const String &search_dir = p_search_dirs[j];
if (has_extension) {
path = search_dir.plus_file(ref_name);
diff --git a/modules/mono/editor/mono_bottom_panel.cpp b/modules/mono/editor/mono_bottom_panel.cpp
index d3efa288e9..21ce9ca5c4 100644
--- a/modules/mono/editor/mono_bottom_panel.cpp
+++ b/modules/mono/editor/mono_bottom_panel.cpp
@@ -125,9 +125,14 @@ void MonoBottomPanel::_build_tabs_item_selected(int p_idx) {
void MonoBottomPanel::_build_tabs_nothing_selected() {
- if (build_tabs->get_tab_count() != 0) // just in case
+ if (build_tabs->get_tab_count() != 0) { // just in case
build_tabs->set_visible(false);
+ // This callback is called when clicking on the empty space of the list.
+ // ItemList won't deselect the items automatically, so we must do it ourselves.
+ build_tabs_list->unselect_all();
+ }
+
warnings_btn->set_visible(false);
errors_btn->set_visible(false);
view_log_btn->set_visible(false);
@@ -156,10 +161,20 @@ void MonoBottomPanel::_build_project_pressed() {
if (!FileAccess::exists(GodotSharpDirs::get_project_sln_path()))
return; // No solution to build
- String scripts_metadata_path = GodotSharpDirs::get_res_metadata_dir().plus_file("scripts_metadata.editor");
- Error metadata_err = CSharpProject::generate_scripts_metadata(GodotSharpDirs::get_project_csproj_path(), scripts_metadata_path);
+ String scripts_metadata_path_editor = GodotSharpDirs::get_res_metadata_dir().plus_file("scripts_metadata.editor");
+ String scripts_metadata_path_player = GodotSharpDirs::get_res_metadata_dir().plus_file("scripts_metadata.editor_player");
+
+ Error metadata_err = CSharpProject::generate_scripts_metadata(GodotSharpDirs::get_project_csproj_path(), scripts_metadata_path_editor);
ERR_FAIL_COND(metadata_err != OK);
+ if (FileAccess::exists(scripts_metadata_path_editor)) {
+ DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+ Error copy_err = da->copy(scripts_metadata_path_editor, scripts_metadata_path_player);
+
+ ERR_EXPLAIN("Failed to copy scripts metadata file");
+ ERR_FAIL_COND(copy_err != OK);
+ }
+
bool build_success = GodotSharpBuilds::get_singleton()->build_project_blocking("Tools");
if (build_success) {
diff --git a/modules/mono/editor/mono_bottom_panel.h b/modules/mono/editor/mono_bottom_panel.h
index d3109592a9..406e46f7ce 100644
--- a/modules/mono/editor/mono_bottom_panel.h
+++ b/modules/mono/editor/mono_bottom_panel.h
@@ -51,8 +51,8 @@ class MonoBottomPanel : public VBoxContainer {
ItemList *build_tabs_list;
TabContainer *build_tabs;
- Button *warnings_btn;
- Button *errors_btn;
+ ToolButton *warnings_btn;
+ ToolButton *errors_btn;
Button *view_log_btn;
void _update_build_tabs_list();
diff --git a/modules/mono/editor/script_class_parser.cpp b/modules/mono/editor/script_class_parser.cpp
index 1281ed669f..dfb652a7aa 100644
--- a/modules/mono/editor/script_class_parser.cpp
+++ b/modules/mono/editor/script_class_parser.cpp
@@ -266,6 +266,20 @@ Error ScriptClassParser::_skip_generic_type_params() {
if (tk == TK_IDENTIFIER) {
tk = get_token();
+ // Type specifications can end with "?" to denote nullable types, such as IList<int?>
+ if (tk == TK_SYMBOL) {
+ tk = get_token();
+ if (value.operator String() != "?") {
+ error_str = "Expected " + get_token_name(TK_IDENTIFIER) + ", found unexpected symbol '" + value + "'";
+ error = true;
+ return ERR_PARSE_ERROR;
+ }
+ if (tk != TK_OP_GREATER && tk != TK_COMMA) {
+ error_str = "Nullable type symbol '?' is only allowed after an identifier, but found " + get_token_name(tk) + " next.";
+ error = true;
+ return ERR_PARSE_ERROR;
+ }
+ }
if (tk == TK_PERIOD) {
while (true) {
@@ -322,6 +336,15 @@ Error ScriptClassParser::_parse_type_full_name(String &r_full_name) {
r_full_name += String(value);
+ if (code[idx] == '<') {
+ idx++;
+
+ // We don't mind if the base is generic, but we skip it any ways since this information is not needed
+ Error err = _skip_generic_type_params();
+ if (err)
+ return err;
+ }
+
if (code[idx] != '.') // We only want to take the next token if it's a period
return OK;
@@ -344,22 +367,12 @@ Error ScriptClassParser::_parse_class_base(Vector<String> &r_base) {
Token tk = get_token();
- bool generic = false;
- if (tk == TK_OP_LESS) {
- Error err = _skip_generic_type_params();
- if (err)
- return err;
- // We don't add it to the base list if it's generic
- generic = true;
- tk = get_token();
- }
-
if (tk == TK_COMMA) {
- Error err = _parse_class_base(r_base);
+ err = _parse_class_base(r_base);
if (err)
return err;
} else if (tk == TK_IDENTIFIER && String(value) == "where") {
- Error err = _parse_type_constraints();
+ err = _parse_type_constraints();
if (err) {
return err;
}
@@ -373,9 +386,7 @@ Error ScriptClassParser::_parse_class_base(Vector<String> &r_base) {
return ERR_PARSE_ERROR;
}
- if (!generic) {
- r_base.push_back(name);
- }
+ r_base.push_back(name);
return OK;
}
@@ -567,7 +578,7 @@ Error ScriptClassParser::parse(const String &p_code) {
if (full_name.length())
full_name += ".";
full_name += class_decl.name;
- OS::get_singleton()->print(String("Ignoring generic class declaration: " + class_decl.name).utf8());
+ OS::get_singleton()->print("Ignoring generic class declaration: %s\n", class_decl.name.utf8().get_data());
}
}
} else if (tk == TK_IDENTIFIER && String(value) == "struct") {
diff --git a/modules/mono/glue/Managed/Files/Array.cs b/modules/mono/glue/Managed/Files/Array.cs
index d5a35d7ae0..0e7b0362e0 100644
--- a/modules/mono/glue/Managed/Files/Array.cs
+++ b/modules/mono/glue/Managed/Files/Array.cs
@@ -28,7 +28,7 @@ namespace Godot.Collections
}
}
- public class Array : IList<object>, ICollection<object>, IEnumerable<object>, IDisposable
+ public class Array : IList, IDisposable
{
ArraySafeHandle safeHandle;
bool disposed = false;
@@ -38,6 +38,14 @@ namespace Godot.Collections
safeHandle = new ArraySafeHandle(godot_icall_Array_Ctor());
}
+ public Array(IEnumerable collection) : this()
+ {
+ if (collection == null)
+ throw new NullReferenceException($"Parameter '{nameof(collection)} cannot be null.'");
+
+ MarshalUtils.EnumerableToArray(collection, GetPtr());
+ }
+
internal Array(ArraySafeHandle handle)
{
safeHandle = handle;
@@ -50,9 +58,19 @@ namespace Godot.Collections
internal IntPtr GetPtr()
{
+ if (disposed)
+ throw new ObjectDisposedException(GetType().FullName);
+
return safeHandle.DangerousGetHandle();
}
+ public Error Resize(int newSize)
+ {
+ return godot_icall_Array_Resize(GetPtr(), newSize);
+ }
+
+ // IDisposable
+
public void Dispose()
{
if (disposed)
@@ -67,62 +85,55 @@ namespace Godot.Collections
disposed = true;
}
+ // IList
+
+ public bool IsReadOnly => false;
+
+ public bool IsFixedSize => false;
+
public object this[int index]
{
- get
- {
- return godot_icall_Array_At(GetPtr(), index);
- }
- set
- {
- godot_icall_Array_SetAt(GetPtr(), index, value);
- }
+ get => godot_icall_Array_At(GetPtr(), index);
+ set => godot_icall_Array_SetAt(GetPtr(), index, value);
}
- public int Count
- {
- get
- {
- return godot_icall_Array_Count(GetPtr());
- }
- }
+ public int Add(object value) => godot_icall_Array_Add(GetPtr(), value);
- public bool IsReadOnly
- {
- get
- {
- return false;
- }
- }
+ public bool Contains(object value) => godot_icall_Array_Contains(GetPtr(), value);
- public void Add(object item)
- {
- godot_icall_Array_Add(GetPtr(), item);
- }
+ public void Clear() => godot_icall_Array_Clear(GetPtr());
- public void Clear()
- {
- godot_icall_Array_Clear(GetPtr());
- }
+ public int IndexOf(object value) => godot_icall_Array_IndexOf(GetPtr(), value);
- public bool Contains(object item)
- {
- return godot_icall_Array_Contains(GetPtr(), item);
- }
+ public void Insert(int index, object value) => godot_icall_Array_Insert(GetPtr(), index, value);
+
+ public void Remove(object value) => godot_icall_Array_Remove(GetPtr(), value);
- public void CopyTo(object[] array, int arrayIndex)
+ public void RemoveAt(int index) => godot_icall_Array_RemoveAt(GetPtr(), index);
+
+ // ICollection
+
+ public int Count => godot_icall_Array_Count(GetPtr());
+
+ public object SyncRoot => this;
+
+ public bool IsSynchronized => false;
+
+ public void CopyTo(System.Array array, int index)
{
if (array == null)
throw new ArgumentNullException(nameof(array), "Value cannot be null.");
- if (arrayIndex < 0)
- throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Number was less than the array's lower bound in the first dimension.");
+ if (index < 0)
+ throw new ArgumentOutOfRangeException(nameof(index), "Number was less than the array's lower bound in the first dimension.");
// Internal call may throw ArgumentException
- godot_icall_Array_CopyTo(GetPtr(), array, arrayIndex);
+ godot_icall_Array_CopyTo(GetPtr(), array, index);
}
- public IEnumerator<object> GetEnumerator()
+ // IEnumerable
+
+ public IEnumerator GetEnumerator()
{
int count = Count;
@@ -132,29 +143,9 @@ namespace Godot.Collections
}
}
- public int IndexOf(object item)
+ public override string ToString()
{
- return godot_icall_Array_IndexOf(GetPtr(), item);
- }
-
- public void Insert(int index, object item)
- {
- godot_icall_Array_Insert(GetPtr(), index, item);
- }
-
- public bool Remove(object item)
- {
- return godot_icall_Array_Remove(GetPtr(), item);
- }
-
- public void RemoveAt(int index)
- {
- godot_icall_Array_RemoveAt(GetPtr(), index);
- }
-
- IEnumerator IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
+ return godot_icall_Array_ToString(GetPtr());
}
[MethodImpl(MethodImplOptions.InternalCall)]
@@ -176,7 +167,7 @@ namespace Godot.Collections
internal extern static int godot_icall_Array_Count(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_Array_Add(IntPtr ptr, object item);
+ internal extern static int godot_icall_Array_Add(IntPtr ptr, object item);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static void godot_icall_Array_Clear(IntPtr ptr);
@@ -185,7 +176,7 @@ namespace Godot.Collections
internal extern static bool godot_icall_Array_Contains(IntPtr ptr, object item);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_Array_CopyTo(IntPtr ptr, object[] array, int arrayIndex);
+ internal extern static void godot_icall_Array_CopyTo(IntPtr ptr, System.Array array, int arrayIndex);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static int godot_icall_Array_IndexOf(IntPtr ptr, object item);
@@ -200,7 +191,13 @@ namespace Godot.Collections
internal extern static void godot_icall_Array_RemoveAt(IntPtr ptr, int index);
[MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static Error godot_icall_Array_Resize(IntPtr ptr, int newSize);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
internal extern static void godot_icall_Array_Generic_GetElementTypeInfo(Type elemType, out int elemTypeEncoding, out IntPtr elemTypeClass);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static string godot_icall_Array_ToString(IntPtr ptr);
}
public class Array<T> : IList<T>, ICollection<T>, IEnumerable<T>
@@ -220,6 +217,14 @@ namespace Godot.Collections
objectArray = new Array();
}
+ public Array(IEnumerable<T> collection)
+ {
+ if (collection == null)
+ throw new NullReferenceException($"Parameter '{nameof(collection)} cannot be null.'");
+
+ objectArray = new Array(collection);
+ }
+
public Array(Array array)
{
objectArray = array;
@@ -235,11 +240,23 @@ namespace Godot.Collections
objectArray = new Array(handle);
}
+ internal IntPtr GetPtr()
+ {
+ return objectArray.GetPtr();
+ }
+
public static explicit operator Array(Array<T> from)
{
return from.objectArray;
}
+ public Error Resize(int newSize)
+ {
+ return objectArray.Resize(newSize);
+ }
+
+ // IList<T>
+
public T this[int index]
{
get
@@ -252,6 +269,23 @@ namespace Godot.Collections
}
}
+ public int IndexOf(T item)
+ {
+ return objectArray.IndexOf(item);
+ }
+
+ public void Insert(int index, T item)
+ {
+ objectArray.Insert(index, item);
+ }
+
+ public void RemoveAt(int index)
+ {
+ objectArray.RemoveAt(index);
+ }
+
+ // ICollection<T>
+
public int Count
{
get
@@ -306,6 +340,13 @@ namespace Godot.Collections
}
}
+ public bool Remove(T item)
+ {
+ return Array.godot_icall_Array_Remove(GetPtr(), item);
+ }
+
+ // IEnumerable<T>
+
public IEnumerator<T> GetEnumerator()
{
int count = objectArray.Count;
@@ -316,34 +357,11 @@ namespace Godot.Collections
}
}
- public int IndexOf(T item)
- {
- return objectArray.IndexOf(item);
- }
-
- public void Insert(int index, T item)
- {
- objectArray.Insert(index, item);
- }
-
- public bool Remove(T item)
- {
- return objectArray.Remove(item);
- }
-
- public void RemoveAt(int index)
- {
- objectArray.RemoveAt(index);
- }
-
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
- internal IntPtr GetPtr()
- {
- return objectArray.GetPtr();
- }
+ public override string ToString() => objectArray.ToString();
}
}
diff --git a/modules/mono/glue/Managed/Files/Basis.cs b/modules/mono/glue/Managed/Files/Basis.cs
index b318d96bb9..ac9576cebd 100644
--- a/modules/mono/glue/Managed/Files/Basis.cs
+++ b/modules/mono/glue/Managed/Files/Basis.cs
@@ -45,74 +45,119 @@ namespace Godot
new Basis(0f, -1f, 0f, 0f, 0f, -1f, 1f, 0f, 0f)
};
+ // NOTE: x, y and z are public-only. Use Column0, Column1 and Column2 internally.
+
+ /// <summary>
+ /// Returns the basis matrix’s x vector.
+ /// This is equivalent to <see cref="Column0"/>.
+ /// </summary>
public Vector3 x
{
- get { return GetAxis(0); }
- set { SetAxis(0, value); }
+ get => Column0;
+ set => Column0 = value;
}
+ /// <summary>
+ /// Returns the basis matrix’s y vector.
+ /// This is equivalent to <see cref="Column1"/>.
+ /// </summary>
public Vector3 y
{
- get { return GetAxis(1); }
- set { SetAxis(1, value); }
+
+ get => Column1;
+ set => Column1 = value;
}
+ /// <summary>
+ /// Returns the basis matrix’s z vector.
+ /// This is equivalent to <see cref="Column2"/>.
+ /// </summary>
public Vector3 z
{
- get { return GetAxis(2); }
- set { SetAxis(2, value); }
+
+ get => Column2;
+ set => Column2 = value;
}
- private Vector3 _x;
- private Vector3 _y;
- private Vector3 _z;
+ public Vector3 Row0;
+ public Vector3 Row1;
+ public Vector3 Row2;
- public static Basis Identity
+ public Vector3 Column0
+ {
+ get => new Vector3(Row0.x, Row1.x, Row2.x);
+ set
+ {
+ this.Row0.x = value.x;
+ this.Row1.x = value.y;
+ this.Row2.x = value.z;
+ }
+ }
+ public Vector3 Column1
{
- get { return identity; }
+ get => new Vector3(Row0.y, Row1.y, Row2.y);
+ set
+ {
+ this.Row0.y = value.x;
+ this.Row1.y = value.y;
+ this.Row2.y = value.z;
+ }
+ }
+ public Vector3 Column2
+ {
+ get => new Vector3(Row0.z, Row1.z, Row2.z);
+ set
+ {
+ this.Row0.z = value.x;
+ this.Row1.z = value.y;
+ this.Row2.z = value.z;
+ }
}
+ public static Basis Identity => identity;
+
public Vector3 Scale
{
get
{
- return new Vector3
+ real_t detSign = Mathf.Sign(Determinant());
+ return detSign * new Vector3
(
- new Vector3(this[0, 0], this[1, 0], this[2, 0]).Length(),
- new Vector3(this[0, 1], this[1, 1], this[2, 1]).Length(),
- new Vector3(this[0, 2], this[1, 2], this[2, 2]).Length()
+ new Vector3(this.Row0[0], this.Row1[0], this.Row2[0]).Length(),
+ new Vector3(this.Row0[1], this.Row1[1], this.Row2[1]).Length(),
+ new Vector3(this.Row0[2], this.Row1[2], this.Row2[2]).Length()
);
}
}
- public Vector3 this[int index]
+ public Vector3 this[int columnIndex]
{
get
{
- switch (index)
+ switch (columnIndex)
{
case 0:
- return _x;
+ return Column0;
case 1:
- return _y;
+ return Column1;
case 2:
- return _z;
+ return Column2;
default:
throw new IndexOutOfRangeException();
}
}
set
{
- switch (index)
+ switch (columnIndex)
{
case 0:
- _x = value;
+ Column0 = value;
return;
case 1:
- _y = value;
+ Column1 = value;
return;
case 2:
- _z = value;
+ Column2 = value;
return;
default:
throw new IndexOutOfRangeException();
@@ -120,51 +165,53 @@ namespace Godot
}
}
- public real_t this[int index, int axis]
+ public real_t this[int columnIndex, int rowIndex]
{
get
{
- switch (index)
+ switch (columnIndex)
{
case 0:
- return _x[axis];
+ return Column0[rowIndex];
case 1:
- return _y[axis];
+ return Column1[rowIndex];
case 2:
- return _z[axis];
+ return Column2[rowIndex];
default:
throw new IndexOutOfRangeException();
}
}
set
{
- switch (index)
+ switch (columnIndex)
{
case 0:
- _x[axis] = value;
+ {
+ var column0 = Column0;
+ column0[rowIndex] = value;
+ Column0 = column0;
return;
+ }
case 1:
- _y[axis] = value;
+ {
+ var column1 = Column1;
+ column1[rowIndex] = value;
+ Column1 = column1;
return;
+ }
case 2:
- _z[axis] = value;
+ {
+ var column2 = Column2;
+ column2[rowIndex] = value;
+ Column2 = column2;
return;
+ }
default:
throw new IndexOutOfRangeException();
}
}
}
- internal static Basis CreateFromAxes(Vector3 xAxis, Vector3 yAxis, Vector3 zAxis)
- {
- return new Basis
- (
- xAxis.x, yAxis.x, zAxis.x,
- xAxis.y, yAxis.y, zAxis.y,
- xAxis.z, yAxis.z, zAxis.z
- );
- }
-
internal Quat RotationQuat()
{
Basis orthonormalizedBasis = Orthonormalized();
@@ -191,29 +238,19 @@ namespace Godot
private void SetDiagonal(Vector3 diagonal)
{
- _x = new Vector3(diagonal.x, 0, 0);
- _y = new Vector3(0, diagonal.y, 0);
- _z = new Vector3(0, 0, diagonal.z);
+ Row0 = new Vector3(diagonal.x, 0, 0);
+ Row1 = new Vector3(0, diagonal.y, 0);
+ Row2 = new Vector3(0, 0, diagonal.z);
}
public real_t Determinant()
{
- return this[0, 0] * (this[1, 1] * this[2, 2] - this[2, 1] * this[1, 2]) -
- this[1, 0] * (this[0, 1] * this[2, 2] - this[2, 1] * this[0, 2]) +
- this[2, 0] * (this[0, 1] * this[1, 2] - this[1, 1] * this[0, 2]);
- }
+ real_t cofac00 = Row1[1] * Row2[2] - Row1[2] * Row2[1];
+ real_t cofac10 = Row1[2] * Row2[0] - Row1[0] * Row2[2];
+ real_t cofac20 = Row1[0] * Row2[1] - Row1[1] * Row2[0];
- public Vector3 GetAxis(int axis)
- {
- return new Vector3(this[0, axis], this[1, axis], this[2, axis]);
- }
-
- public void SetAxis(int axis, Vector3 value)
- {
- this[0, axis] = value.x;
- this[1, axis] = value.y;
- this[2, axis] = value.z;
+ return Row0[0] * cofac00 + Row0[1] * cofac10 + Row0[2] * cofac20;
}
public Vector3 GetEuler()
@@ -223,32 +260,80 @@ namespace Godot
Vector3 euler;
euler.z = 0.0f;
- real_t mxy = m[1, 2];
-
+ real_t mxy = m.Row1[2];
if (mxy < 1.0f)
{
if (mxy > -1.0f)
{
euler.x = Mathf.Asin(-mxy);
- euler.y = Mathf.Atan2(m[0, 2], m[2, 2]);
- euler.z = Mathf.Atan2(m[1, 0], m[1, 1]);
+ euler.y = Mathf.Atan2(m.Row0[2], m.Row2[2]);
+ euler.z = Mathf.Atan2(m.Row1[0], m.Row1[1]);
}
else
{
euler.x = Mathf.Pi * 0.5f;
- euler.y = -Mathf.Atan2(-m[0, 1], m[0, 0]);
+ euler.y = -Mathf.Atan2(-m.Row0[1], m.Row0[0]);
}
}
else
{
euler.x = -Mathf.Pi * 0.5f;
- euler.y = -Mathf.Atan2(-m[0, 1], m[0, 0]);
+ euler.y = -Mathf.Atan2(-m.Row0[1], m.Row0[0]);
}
return euler;
}
+ public Vector3 GetRow(int index)
+ {
+ switch (index)
+ {
+ case 0:
+ return Row0;
+ case 1:
+ return Row1;
+ case 2:
+ return Row2;
+ default:
+ throw new IndexOutOfRangeException();
+ }
+ }
+
+ public void SetRow(int index, Vector3 value)
+ {
+ switch (index)
+ {
+ case 0:
+ Row0 = value;
+ return;
+ case 1:
+ Row1 = value;
+ return;
+ case 2:
+ Row2 = value;
+ return;
+ default:
+ throw new IndexOutOfRangeException();
+ }
+ }
+
+ public Vector3 GetColumn(int index)
+ {
+ return this[index];
+ }
+
+ public void SetColumn(int index, Vector3 value)
+ {
+ this[index] = value;
+ }
+
+ [Obsolete("GetAxis is deprecated. Use GetColumn instead.")]
+ public Vector3 GetAxis(int axis)
+ {
+ return new Vector3(this.Row0[axis], this.Row1[axis], this.Row2[axis]);
+ }
+
public int GetOrthogonalIndex()
{
var orth = this;
@@ -257,7 +342,9 @@ namespace Godot
{
for (int j = 0; j < 3; j++)
{
- real_t v = orth[i, j];
+ var row = orth.GetRow(i);
+
+ real_t v = row[j];
if (v > 0.5f)
v = 1.0f;
@@ -266,7 +353,9 @@ namespace Godot
else
v = 0f;
- orth[i, j] = v;
+ row[j] = v;
+
+ orth.SetRow(i, row);
}
}
@@ -281,57 +370,45 @@ namespace Godot
public Basis Inverse()
{
- var inv = this;
-
- real_t[] co = {
- inv[1, 1] * inv[2, 2] - inv[1, 2] * inv[2, 1],
- inv[1, 2] * inv[2, 0] - inv[1, 0] * inv[2, 2],
- inv[1, 0] * inv[2, 1] - inv[1, 1] * inv[2, 0]
- };
+ real_t cofac00 = Row1[1] * Row2[2] - Row1[2] * Row2[1];
+ real_t cofac10 = Row1[2] * Row2[0] - Row1[0] * Row2[2];
+ real_t cofac20 = Row1[0] * Row2[1] - Row1[1] * Row2[0];
- real_t det = inv[0, 0] * co[0] + inv[0, 1] * co[1] + inv[0, 2] * co[2];
+ real_t det = Row0[0] * cofac00 + Row0[1] * cofac10 + Row0[2] * cofac20;
if (det == 0)
- {
- return new Basis
- (
- real_t.NaN, real_t.NaN, real_t.NaN,
- real_t.NaN, real_t.NaN, real_t.NaN,
- real_t.NaN, real_t.NaN, real_t.NaN
- );
- }
+ throw new InvalidOperationException("Matrix determinant is zero and cannot be inverted.");
- real_t s = 1.0f / det;
+ real_t detInv = 1.0f / det;
- inv = new Basis
+ real_t cofac01 = Row0[2] * Row2[1] - Row0[1] * Row2[2];
+ real_t cofac02 = Row0[1] * Row1[2] - Row0[2] * Row1[1];
+ real_t cofac11 = Row0[0] * Row2[2] - Row0[2] * Row2[0];
+ real_t cofac12 = Row0[2] * Row1[0] - Row0[0] * Row1[2];
+ real_t cofac21 = Row0[1] * Row2[0] - Row0[0] * Row2[1];
+ real_t cofac22 = Row0[0] * Row1[1] - Row0[1] * Row1[0];
+
+ return new Basis
(
- co[0] * s,
- inv[0, 2] * inv[2, 1] - inv[0, 1] * inv[2, 2] * s,
- inv[0, 1] * inv[1, 2] - inv[0, 2] * inv[1, 1] * s,
- co[1] * s,
- inv[0, 0] * inv[2, 2] - inv[0, 2] * inv[2, 0] * s,
- inv[0, 2] * inv[1, 0] - inv[0, 0] * inv[1, 2] * s,
- co[2] * s,
- inv[0, 1] * inv[2, 0] - inv[0, 0] * inv[2, 1] * s,
- inv[0, 0] * inv[1, 1] - inv[0, 1] * inv[1, 0] * s
+ cofac00 * detInv, cofac01 * detInv, cofac02 * detInv,
+ cofac10 * detInv, cofac11 * detInv, cofac12 * detInv,
+ cofac20 * detInv, cofac21 * detInv, cofac22 * detInv
);
-
- return inv;
}
public Basis Orthonormalized()
{
- Vector3 xAxis = GetAxis(0);
- Vector3 yAxis = GetAxis(1);
- Vector3 zAxis = GetAxis(2);
+ Vector3 column0 = GetColumn(0);
+ Vector3 column1 = GetColumn(1);
+ Vector3 column2 = GetColumn(2);
- xAxis.Normalize();
- yAxis = yAxis - xAxis * xAxis.Dot(yAxis);
- yAxis.Normalize();
- zAxis = zAxis - xAxis * xAxis.Dot(zAxis) - yAxis * yAxis.Dot(zAxis);
- zAxis.Normalize();
+ column0.Normalize();
+ column1 = column1 - column0 * column0.Dot(column1);
+ column1.Normalize();
+ column2 = column2 - column0 * column0.Dot(column2) - column1 * column1.Dot(column2);
+ column2.Normalize();
- return CreateFromAxes(xAxis, yAxis, zAxis);
+ return new Basis(column0, column1, column2);
}
public Basis Rotated(Vector3 axis, real_t phi)
@@ -343,49 +420,49 @@ namespace Godot
{
var m = this;
- m[0, 0] *= scale.x;
- m[0, 1] *= scale.x;
- m[0, 2] *= scale.x;
- m[1, 0] *= scale.y;
- m[1, 1] *= scale.y;
- m[1, 2] *= scale.y;
- m[2, 0] *= scale.z;
- m[2, 1] *= scale.z;
- m[2, 2] *= scale.z;
+ m.Row0[0] *= scale.x;
+ m.Row0[1] *= scale.x;
+ m.Row0[2] *= scale.x;
+ m.Row1[0] *= scale.y;
+ m.Row1[1] *= scale.y;
+ m.Row1[2] *= scale.y;
+ m.Row2[0] *= scale.z;
+ m.Row2[1] *= scale.z;
+ m.Row2[2] *= scale.z;
return m;
}
public real_t Tdotx(Vector3 with)
{
- return this[0, 0] * with[0] + this[1, 0] * with[1] + this[2, 0] * with[2];
+ return this.Row0[0] * with[0] + this.Row1[0] * with[1] + this.Row2[0] * with[2];
}
public real_t Tdoty(Vector3 with)
{
- return this[0, 1] * with[0] + this[1, 1] * with[1] + this[2, 1] * with[2];
+ return this.Row0[1] * with[0] + this.Row1[1] * with[1] + this.Row2[1] * with[2];
}
public real_t Tdotz(Vector3 with)
{
- return this[0, 2] * with[0] + this[1, 2] * with[1] + this[2, 2] * with[2];
+ return this.Row0[2] * with[0] + this.Row1[2] * with[1] + this.Row2[2] * with[2];
}
public Basis Transposed()
{
var tr = this;
- real_t temp = tr[0, 1];
- tr[0, 1] = tr[1, 0];
- tr[1, 0] = temp;
+ real_t temp = tr.Row0[1];
+ tr.Row0[1] = tr.Row1[0];
+ tr.Row1[0] = temp;
- temp = tr[0, 2];
- tr[0, 2] = tr[2, 0];
- tr[2, 0] = temp;
+ temp = tr.Row0[2];
+ tr.Row0[2] = tr.Row2[0];
+ tr.Row2[0] = temp;
- temp = tr[1, 2];
- tr[1, 2] = tr[2, 1];
- tr[2, 1] = temp;
+ temp = tr.Row1[2];
+ tr.Row1[2] = tr.Row2[1];
+ tr.Row2[1] = temp;
return tr;
}
@@ -394,9 +471,9 @@ namespace Godot
{
return new Vector3
(
- this[0].Dot(v),
- this[1].Dot(v),
- this[2].Dot(v)
+ this.Row0.Dot(v),
+ this.Row1.Dot(v),
+ this.Row2.Dot(v)
);
}
@@ -404,60 +481,60 @@ namespace Godot
{
return new Vector3
(
- this[0, 0] * v.x + this[1, 0] * v.y + this[2, 0] * v.z,
- this[0, 1] * v.x + this[1, 1] * v.y + this[2, 1] * v.z,
- this[0, 2] * v.x + this[1, 2] * v.y + this[2, 2] * v.z
+ this.Row0[0] * v.x + this.Row1[0] * v.y + this.Row2[0] * v.z,
+ this.Row0[1] * v.x + this.Row1[1] * v.y + this.Row2[1] * v.z,
+ this.Row0[2] * v.x + this.Row1[2] * v.y + this.Row2[2] * v.z
);
}
public Quat Quat()
{
- real_t trace = _x[0] + _y[1] + _z[2];
+ real_t trace = Row0[0] + Row1[1] + Row2[2];
if (trace > 0.0f)
{
real_t s = Mathf.Sqrt(trace + 1.0f) * 2f;
real_t inv_s = 1f / s;
return new Quat(
- (_z[1] - _y[2]) * inv_s,
- (_x[2] - _z[0]) * inv_s,
- (_y[0] - _x[1]) * inv_s,
+ (Row2[1] - Row1[2]) * inv_s,
+ (Row0[2] - Row2[0]) * inv_s,
+ (Row1[0] - Row0[1]) * inv_s,
s * 0.25f
);
}
- if (_x[0] > _y[1] && _x[0] > _z[2])
+ if (Row0[0] > Row1[1] && Row0[0] > Row2[2])
{
- real_t s = Mathf.Sqrt(_x[0] - _y[1] - _z[2] + 1.0f) * 2f;
+ real_t s = Mathf.Sqrt(Row0[0] - Row1[1] - Row2[2] + 1.0f) * 2f;
real_t inv_s = 1f / s;
return new Quat(
s * 0.25f,
- (_x[1] + _y[0]) * inv_s,
- (_x[2] + _z[0]) * inv_s,
- (_z[1] - _y[2]) * inv_s
+ (Row0[1] + Row1[0]) * inv_s,
+ (Row0[2] + Row2[0]) * inv_s,
+ (Row2[1] - Row1[2]) * inv_s
);
}
- if (_y[1] > _z[2])
+ if (Row1[1] > Row2[2])
{
- real_t s = Mathf.Sqrt(-_x[0] + _y[1] - _z[2] + 1.0f) * 2f;
+ real_t s = Mathf.Sqrt(-Row0[0] + Row1[1] - Row2[2] + 1.0f) * 2f;
real_t inv_s = 1f / s;
return new Quat(
- (_x[1] + _y[0]) * inv_s,
+ (Row0[1] + Row1[0]) * inv_s,
s * 0.25f,
- (_y[2] + _z[1]) * inv_s,
- (_x[2] - _z[0]) * inv_s
+ (Row1[2] + Row2[1]) * inv_s,
+ (Row0[2] - Row2[0]) * inv_s
);
}
else
{
- real_t s = Mathf.Sqrt(-_x[0] - _y[1] + _z[2] + 1.0f) * 2f;
+ real_t s = Mathf.Sqrt(-Row0[0] - Row1[1] + Row2[2] + 1.0f) * 2f;
real_t inv_s = 1f / s;
return new Quat(
- (_x[2] + _z[0]) * inv_s,
- (_y[2] + _z[1]) * inv_s,
+ (Row0[2] + Row2[0]) * inv_s,
+ (Row1[2] + Row2[1]) * inv_s,
s * 0.25f,
- (_y[0] - _x[1]) * inv_s
+ (Row1[0] - Row0[1]) * inv_s
);
}
}
@@ -479,9 +556,9 @@ namespace Godot
real_t yz = quat.y * zs;
real_t zz = quat.z * zs;
- _x = new Vector3(1.0f - (yy + zz), xy - wz, xz + wy);
- _y = new Vector3(xy + wz, 1.0f - (xx + zz), yz - wx);
- _z = new Vector3(xz - wy, yz + wx, 1.0f - (xx + yy));
+ Row0 = new Vector3(1.0f - (yy + zz), xy - wz, xz + wy);
+ Row1 = new Vector3(xy + wz, 1.0f - (xx + zz), yz - wx);
+ Row2 = new Vector3(xz - wy, yz + wx, 1.0f - (xx + yy));
}
public Basis(Vector3 euler)
@@ -511,21 +588,21 @@ namespace Godot
real_t cosine = Mathf.Cos(phi);
real_t sine = Mathf.Sin(phi);
- _x = new Vector3
+ Row0 = new Vector3
(
axis_sq.x + cosine * (1.0f - axis_sq.x),
axis.x * axis.y * (1.0f - cosine) - axis.z * sine,
axis.z * axis.x * (1.0f - cosine) + axis.y * sine
);
- _y = new Vector3
+ Row1 = new Vector3
(
axis.x * axis.y * (1.0f - cosine) + axis.z * sine,
axis_sq.y + cosine * (1.0f - axis_sq.y),
axis.y * axis.z * (1.0f - cosine) - axis.x * sine
);
- _z = new Vector3
+ Row2 = new Vector3
(
axis.z * axis.x * (1.0f - cosine) - axis.y * sine,
axis.y * axis.z * (1.0f - cosine) + axis.x * sine,
@@ -533,32 +610,32 @@ namespace Godot
);
}
- public Basis(Vector3 xAxis, Vector3 yAxis, Vector3 zAxis)
+ public Basis(Vector3 column0, Vector3 column1, Vector3 column2)
{
- _x = new Vector3(xAxis.x, yAxis.x, zAxis.x);
- _y = new Vector3(xAxis.y, yAxis.y, zAxis.y);
- _z = new Vector3(xAxis.z, yAxis.z, zAxis.z);
+ Row0 = new Vector3(column0.x, column1.x, column2.x);
+ Row1 = new Vector3(column0.y, column1.y, column2.y);
+ Row2 = new Vector3(column0.z, column1.z, column2.z);
// Same as:
- // SetAxis(0, xAxis);
- // SetAxis(1, yAxis);
- // SetAxis(2, zAxis);
- // We need to assign the struct fields so we can't do that...
+ // Column0 = column0;
+ // Column1 = column1;
+ // Column2 = column2;
+ // We need to assign the struct fields here first so we can't do it that way...
}
internal Basis(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz)
{
- _x = new Vector3(xx, xy, xz);
- _y = new Vector3(yx, yy, yz);
- _z = new Vector3(zx, zy, zz);
+ Row0 = new Vector3(xx, xy, xz);
+ Row1 = new Vector3(yx, yy, yz);
+ Row2 = new Vector3(zx, zy, zz);
}
public static Basis operator *(Basis left, Basis right)
{
return new Basis
(
- right.Tdotx(left[0]), right.Tdoty(left[0]), right.Tdotz(left[0]),
- right.Tdotx(left[1]), right.Tdoty(left[1]), right.Tdotz(left[1]),
- right.Tdotx(left[2]), right.Tdoty(left[2]), right.Tdotz(left[2])
+ right.Tdotx(left.Row0), right.Tdoty(left.Row0), right.Tdotz(left.Row0),
+ right.Tdotx(left.Row1), right.Tdoty(left.Row1), right.Tdotz(left.Row1),
+ right.Tdotx(left.Row2), right.Tdoty(left.Row2), right.Tdotz(left.Row2)
);
}
@@ -584,21 +661,21 @@ namespace Godot
public bool Equals(Basis other)
{
- return _x.Equals(other[0]) && _y.Equals(other[1]) && _z.Equals(other[2]);
+ return Row0.Equals(other.Row0) && Row1.Equals(other.Row1) && Row2.Equals(other.Row2);
}
public override int GetHashCode()
{
- return _x.GetHashCode() ^ _y.GetHashCode() ^ _z.GetHashCode();
+ return Row0.GetHashCode() ^ Row1.GetHashCode() ^ Row2.GetHashCode();
}
public override string ToString()
{
return String.Format("({0}, {1}, {2})", new object[]
{
- _x.ToString(),
- _y.ToString(),
- _z.ToString()
+ Row0.ToString(),
+ Row1.ToString(),
+ Row2.ToString()
});
}
@@ -606,9 +683,9 @@ namespace Godot
{
return String.Format("({0}, {1}, {2})", new object[]
{
- _x.ToString(format),
- _y.ToString(format),
- _z.ToString(format)
+ Row0.ToString(format),
+ Row1.ToString(format),
+ Row2.ToString(format)
});
}
}
diff --git a/modules/mono/glue/Managed/Files/Color.cs b/modules/mono/glue/Managed/Files/Color.cs
index 88fa3323c2..84ff19fc54 100644
--- a/modules/mono/glue/Managed/Files/Color.cs
+++ b/modules/mono/glue/Managed/Files/Color.cs
@@ -168,7 +168,7 @@ namespace Godot
int max = Mathf.Max(color.r8, Mathf.Max(color.g8, color.b8));
int min = Mathf.Min(color.r8, Mathf.Min(color.g8, color.b8));
- float delta = max - min;
+ int delta = max - min;
if (delta == 0)
{
@@ -591,11 +591,11 @@ namespace Godot
public static bool operator <(Color left, Color right)
{
- if (left.r == right.r)
+ if (Mathf.IsEqualApprox(left.r, right.r))
{
- if (left.g == right.g)
+ if (Mathf.IsEqualApprox(left.g, right.g))
{
- if (left.b == right.b)
+ if (Mathf.IsEqualApprox(left.b, right.b))
return left.a < right.a;
return left.b < right.b;
}
@@ -608,11 +608,11 @@ namespace Godot
public static bool operator >(Color left, Color right)
{
- if (left.r == right.r)
+ if (Mathf.IsEqualApprox(left.r, right.r))
{
- if (left.g == right.g)
+ if (Mathf.IsEqualApprox(left.g, right.g))
{
- if (left.b == right.b)
+ if (Mathf.IsEqualApprox(left.b, right.b))
return left.a > right.a;
return left.b > right.b;
}
@@ -635,7 +635,7 @@ namespace Godot
public bool Equals(Color other)
{
- return r == other.r && g == other.g && b == other.b && a == other.a;
+ return Mathf.IsEqualApprox(r, other.r) && Mathf.IsEqualApprox(g, other.g) && Mathf.IsEqualApprox(b, other.b) && Mathf.IsEqualApprox(a, other.a);
}
public override int GetHashCode()
diff --git a/modules/mono/glue/Managed/Files/DebuggingUtils.cs b/modules/mono/glue/Managed/Files/DebuggingUtils.cs
index b27816084e..edfe3464ec 100644
--- a/modules/mono/glue/Managed/Files/DebuggingUtils.cs
+++ b/modules/mono/glue/Managed/Files/DebuggingUtils.cs
@@ -19,6 +19,12 @@ namespace Godot
sb.Append(" ");
}
+ public static void InstallTraceListener()
+ {
+ Trace.Listeners.Clear();
+ Trace.Listeners.Add(new GodotTraceListener());
+ }
+
public static void GetStackFrameInfo(StackFrame frame, out string fileName, out int fileLineNumber, out string methodDecl)
{
fileName = frame.GetFileName();
diff --git a/modules/mono/glue/Managed/Files/Dictionary.cs b/modules/mono/glue/Managed/Files/Dictionary.cs
index 7695f03cd6..6ab8549a01 100644
--- a/modules/mono/glue/Managed/Files/Dictionary.cs
+++ b/modules/mono/glue/Managed/Files/Dictionary.cs
@@ -29,9 +29,7 @@ namespace Godot.Collections
}
public class Dictionary :
- IDictionary<object, object>,
- ICollection<KeyValuePair<object, object>>,
- IEnumerable<KeyValuePair<object, object>>,
+ IDictionary,
IDisposable
{
DictionarySafeHandle safeHandle;
@@ -42,6 +40,14 @@ namespace Godot.Collections
safeHandle = new DictionarySafeHandle(godot_icall_Dictionary_Ctor());
}
+ public Dictionary(IDictionary dictionary) : this()
+ {
+ if (dictionary == null)
+ throw new NullReferenceException($"Parameter '{nameof(dictionary)} cannot be null.'");
+
+ MarshalUtils.IDictionaryToDictionary(dictionary, GetPtr());
+ }
+
internal Dictionary(DictionarySafeHandle handle)
{
safeHandle = handle;
@@ -54,6 +60,9 @@ namespace Godot.Collections
internal IntPtr GetPtr()
{
+ if (disposed)
+ throw new ObjectDisposedException(GetType().FullName);
+
return safeHandle.DangerousGetHandle();
}
@@ -71,19 +80,9 @@ namespace Godot.Collections
disposed = true;
}
- public object this[object key]
- {
- get
- {
- return godot_icall_Dictionary_GetValue(GetPtr(), key);
- }
- set
- {
- godot_icall_Dictionary_SetValue(GetPtr(), key, value);
- }
- }
+ // IDictionary
- public ICollection<object> Keys
+ public ICollection Keys
{
get
{
@@ -92,7 +91,7 @@ namespace Godot.Collections
}
}
- public ICollection<object> Values
+ public ICollection Values
{
get
{
@@ -101,97 +100,102 @@ namespace Godot.Collections
}
}
- public int Count
- {
- get
- {
- return godot_icall_Dictionary_Count(GetPtr());
- }
- }
+ public bool IsFixedSize => false;
- public bool IsReadOnly
- {
- get
- {
- return false;
- }
- }
+ public bool IsReadOnly => false;
- public void Add(object key, object value)
+ public object this[object key]
{
- godot_icall_Dictionary_Add(GetPtr(), key, value);
+ get => godot_icall_Dictionary_GetValue(GetPtr(), key);
+ set => godot_icall_Dictionary_SetValue(GetPtr(), key, value);
}
- public void Add(KeyValuePair<object, object> item)
- {
- Add(item.Key, item.Value);
- }
+ public void Add(object key, object value) => godot_icall_Dictionary_Add(GetPtr(), key, value);
- public void Clear()
- {
- godot_icall_Dictionary_Clear(GetPtr());
- }
+ public void Clear() => godot_icall_Dictionary_Clear(GetPtr());
- public bool Contains(KeyValuePair<object, object> item)
- {
- return godot_icall_Dictionary_Contains(GetPtr(), item.Key, item.Value);
- }
+ public bool Contains(object key) => godot_icall_Dictionary_ContainsKey(GetPtr(), key);
- public bool ContainsKey(object key)
- {
- return godot_icall_Dictionary_ContainsKey(GetPtr(), key);
- }
+ public IDictionaryEnumerator GetEnumerator() => new DictionaryEnumerator(this);
+
+ public void Remove(object key) => godot_icall_Dictionary_RemoveKey(GetPtr(), key);
- public void CopyTo(KeyValuePair<object, object>[] array, int arrayIndex)
+ // ICollection
+
+ public object SyncRoot => this;
+
+ public bool IsSynchronized => false;
+
+ public int Count => godot_icall_Dictionary_Count(GetPtr());
+
+ public void CopyTo(System.Array array, int index)
{
- // TODO 3 internal calls, can reduce to 1
+ // TODO Can be done with single internal call
+
+ if (array == null)
+ throw new ArgumentNullException(nameof(array), "Value cannot be null.");
+
+ if (index < 0)
+ throw new ArgumentOutOfRangeException(nameof(index), "Number was less than the array's lower bound in the first dimension.");
+
Array keys = (Array)Keys;
Array values = (Array)Values;
int count = Count;
+ if (array.Length < (index + count))
+ throw new ArgumentException("Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
+
for (int i = 0; i < count; i++)
{
- // TODO 2 internal calls, can reduce to 1
- array[arrayIndex] = new KeyValuePair<object, object>(keys[i], values[i]);
- arrayIndex++;
+ array.SetValue(new DictionaryEntry(keys[i], values[i]), index);
+ index++;
}
}
- public IEnumerator<KeyValuePair<object, object>> GetEnumerator()
+ // IEnumerable
+
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+
+ private class DictionaryEnumerator : IDictionaryEnumerator
{
- // TODO 3 internal calls, can reduce to 1
- Array keys = (Array)Keys;
- Array values = (Array)Values;
- int count = Count;
+ Array keys;
+ Array values;
+ int count;
+ int index = -1;
- for (int i = 0; i < count; i++)
+ public DictionaryEnumerator(Dictionary dictionary)
{
- // TODO 2 internal calls, can reduce to 1
- yield return new KeyValuePair<object, object>(keys[i], values[i]);
+ // TODO 3 internal calls, can reduce to 1
+ keys = (Array)dictionary.Keys;
+ values = (Array)dictionary.Values;
+ count = dictionary.Count;
}
- }
- public bool Remove(object key)
- {
- return godot_icall_Dictionary_RemoveKey(GetPtr(), key);
- }
+ public object Current => Entry;
- public bool Remove(KeyValuePair<object, object> item)
- {
- return godot_icall_Dictionary_Remove(GetPtr(), item.Key, item.Value);
- }
+ public DictionaryEntry Entry =>
+ // TODO 2 internal calls, can reduce to 1
+ new DictionaryEntry(keys[index], values[index]);
- public bool TryGetValue(object key, out object value)
- {
- object retValue;
- bool found = godot_icall_Dictionary_TryGetValue(GetPtr(), key, out retValue);
- value = found ? retValue : default(object);
- return found;
+ public object Key => Entry.Key;
+
+ public object Value => Entry.Value;
+
+ public bool MoveNext()
+ {
+ index++;
+ return index < count;
+ }
+
+ public void Reset()
+ {
+ index = -1;
+ }
}
- IEnumerator IEnumerable.GetEnumerator()
+ public override string ToString()
{
- return GetEnumerator();
+ return godot_icall_Dictionary_ToString(GetPtr());
}
[MethodImpl(MethodImplOptions.InternalCall)]
@@ -244,12 +248,13 @@ namespace Godot.Collections
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static void godot_icall_Dictionary_Generic_GetValueTypeInfo(Type valueType, out int valTypeEncoding, out IntPtr valTypeClass);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static string godot_icall_Dictionary_ToString(IntPtr ptr);
}
public class Dictionary<TKey, TValue> :
- IDictionary<TKey, TValue>,
- ICollection<KeyValuePair<TKey, TValue>>,
- IEnumerable<KeyValuePair<TKey, TValue>>
+ IDictionary<TKey, TValue>
{
Dictionary objectDict;
@@ -266,6 +271,23 @@ namespace Godot.Collections
objectDict = new Dictionary();
}
+ public Dictionary(IDictionary<TKey, TValue> dictionary)
+ {
+ objectDict = new Dictionary();
+
+ if (dictionary == null)
+ throw new NullReferenceException($"Parameter '{nameof(dictionary)} cannot be null.'");
+
+ // TODO: Can be optimized
+
+ IntPtr godotDictionaryPtr = GetPtr();
+
+ foreach (KeyValuePair<TKey, TValue> entry in dictionary)
+ {
+ Dictionary.godot_icall_Dictionary_Add(godotDictionaryPtr, entry.Key, entry.Value);
+ }
+ }
+
public Dictionary(Dictionary dictionary)
{
objectDict = dictionary;
@@ -286,6 +308,13 @@ namespace Godot.Collections
return from.objectDict;
}
+ internal IntPtr GetPtr()
+ {
+ return objectDict.GetPtr();
+ }
+
+ // IDictionary<TKey, TValue>
+
public TValue this[TKey key]
{
get
@@ -316,6 +345,31 @@ namespace Godot.Collections
}
}
+ public void Add(TKey key, TValue value)
+ {
+ objectDict.Add(key, value);
+ }
+
+ public bool ContainsKey(TKey key)
+ {
+ return objectDict.Contains(key);
+ }
+
+ public bool Remove(TKey key)
+ {
+ return Dictionary.godot_icall_Dictionary_RemoveKey(GetPtr(), key);
+ }
+
+ public bool TryGetValue(TKey key, out TValue value)
+ {
+ object retValue;
+ bool found = Dictionary.godot_icall_Dictionary_TryGetValue_Generic(GetPtr(), key, out retValue, valTypeEncoding, valTypeClass);
+ value = found ? (TValue)retValue : default(TValue);
+ return found;
+ }
+
+ // ICollection<KeyValuePair<TKey, TValue>>
+
public int Count
{
get
@@ -332,11 +386,6 @@ namespace Godot.Collections
}
}
- public void Add(TKey key, TValue value)
- {
- objectDict.Add(key, value);
- }
-
public void Add(KeyValuePair<TKey, TValue> item)
{
objectDict.Add(item.Key, item.Value);
@@ -352,18 +401,22 @@ namespace Godot.Collections
return objectDict.Contains(new KeyValuePair<object, object>(item.Key, item.Value));
}
- public bool ContainsKey(TKey key)
- {
- return objectDict.ContainsKey(key);
- }
-
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
+ if (array == null)
+ throw new ArgumentNullException(nameof(array), "Value cannot be null.");
+
+ if (arrayIndex < 0)
+ throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Number was less than the array's lower bound in the first dimension.");
+
// TODO 3 internal calls, can reduce to 1
Array<TKey> keys = (Array<TKey>)Keys;
Array<TValue> values = (Array<TValue>)Values;
int count = Count;
+ if (array.Length < (arrayIndex + count))
+ throw new ArgumentException("Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
+
for (int i = 0; i < count; i++)
{
// TODO 2 internal calls, can reduce to 1
@@ -372,6 +425,13 @@ namespace Godot.Collections
}
}
+ public bool Remove(KeyValuePair<TKey, TValue> item)
+ {
+ return Dictionary.godot_icall_Dictionary_Remove(GetPtr(), item.Key, item.Value); ;
+ }
+
+ // IEnumerable<KeyValuePair<TKey, TValue>>
+
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
// TODO 3 internal calls, can reduce to 1
@@ -386,32 +446,11 @@ namespace Godot.Collections
}
}
- public bool Remove(TKey key)
- {
- return objectDict.Remove(key);
- }
-
- public bool Remove(KeyValuePair<TKey, TValue> item)
- {
- return objectDict.Remove(new KeyValuePair<object, object>(item.Key, item.Value));
- }
-
- public bool TryGetValue(TKey key, out TValue value)
- {
- object retValue;
- bool found = Dictionary.godot_icall_Dictionary_TryGetValue_Generic(GetPtr(), key, out retValue, valTypeEncoding, valTypeClass);
- value = found ? (TValue)retValue : default(TValue);
- return found;
- }
-
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
- internal IntPtr GetPtr()
- {
- return objectDict.GetPtr();
- }
+ public override string ToString() => objectDict.ToString();
}
}
diff --git a/modules/mono/glue/Managed/Files/DynamicObject.cs b/modules/mono/glue/Managed/Files/DynamicObject.cs
new file mode 100644
index 0000000000..9860feafdd
--- /dev/null
+++ b/modules/mono/glue/Managed/Files/DynamicObject.cs
@@ -0,0 +1,213 @@
+
+using System;
+using System.Collections.Generic;
+using System.Dynamic;
+using System.Linq.Expressions;
+using System.Runtime.CompilerServices;
+
+namespace Godot
+{
+ /// <summary>
+ /// Represents an <see cref="Godot.Object"/> whose members can be dynamically accessed at runtime through the Variant API.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// The <see cref="Godot.DynamicGodotObject"/> class enables access to the Variant
+ /// members of a <see cref="Godot.Object"/> instance at runtime.
+ /// </para>
+ /// <para>
+ /// This allows accessing the class members using their original names in the engine as well as the members from the
+ /// script attached to the <see cref="Godot.Object"/>, regardless of the scripting language it was written in.
+ /// </para>
+ /// </remarks>
+ /// <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;
+ /// sprite.add_child(this);
+ ///
+ /// if ((sprite.hframes * sprite.vframes) &gt; 0)
+ /// sprite.frame = 0;
+ /// </code>
+ /// </example>
+ /// <example>
+ /// This sample shows how to use <see cref="Godot.DynamicGodotObject"/> to dynamically access the members of the script attached to a <see cref="Godot.Object"/>.
+ /// <code>
+ /// dynamic childNode = GetNode("ChildNode").DynamicGodotObject;
+ ///
+ /// if (childNode.print_allowed)
+ /// {
+ /// childNode.message = "Hello from C#";
+ /// childNode.print_message(3);
+ /// }
+ /// </code>
+ /// The <c>ChildNode</c> node has the following GDScript script attached:
+ /// <code>
+ /// // # ChildNode.gd
+ /// // var print_allowed = true
+ /// // var message = ""
+ /// //
+ /// // func print_message(times):
+ /// // for i in times:
+ /// // print(message)
+ /// </code>
+ /// </example>
+ public class DynamicGodotObject : DynamicObject
+ {
+ /// <summary>
+ /// Gets the <see cref="Godot.Object"/> associated with this <see cref="Godot.DynamicGodotObject"/>.
+ /// </summary>
+ public Object Value { get; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Godot.DynamicGodotObject"/> class.
+ /// </summary>
+ /// <param name="godotObject">
+ /// The <see cref="Godot.Object"/> that will be associated with this <see cref="Godot.DynamicGodotObject"/>.
+ /// </param>
+ /// <exception cref="System.ArgumentNullException">
+ /// Thrown when the <paramref name="godotObject"/> parameter is null.
+ /// </exception>
+ public DynamicGodotObject(Object godotObject)
+ {
+ if (godotObject == null)
+ throw new ArgumentNullException(nameof(godotObject));
+
+ this.Value = godotObject;
+ }
+
+ public override IEnumerable<string> GetDynamicMemberNames()
+ {
+ return godot_icall_DynamicGodotObject_SetMemberList(Object.GetPtr(Value));
+ }
+
+ public override bool TryBinaryOperation(BinaryOperationBinder binder, object arg, out object result)
+ {
+ switch (binder.Operation)
+ {
+ case ExpressionType.Equal:
+ case ExpressionType.NotEqual:
+ if (binder.ReturnType == typeof(bool) || binder.ReturnType.IsAssignableFrom(typeof(bool)))
+ {
+ if (arg == null)
+ {
+ bool boolResult = Object.IsInstanceValid(Value);
+
+ if (binder.Operation == ExpressionType.Equal)
+ boolResult = !boolResult;
+
+ result = boolResult;
+ return true;
+ }
+
+ if (arg is Object other)
+ {
+ bool boolResult = (Value == other);
+
+ if (binder.Operation == ExpressionType.NotEqual)
+ boolResult = !boolResult;
+
+ result = boolResult;
+ return true;
+ }
+ }
+
+ break;
+ default:
+ // We're not implementing operators <, <=, >, and >= (LessThan, LessThanOrEqual, GreaterThan, GreaterThanOrEqual).
+ // These are used on the actual pointers in variant_op.cpp. It's better to let the user do that explicitly.
+ break;
+ }
+
+ return base.TryBinaryOperation(binder, arg, out result);
+ }
+
+ public override bool TryConvert(ConvertBinder binder, out object result)
+ {
+ if (binder.Type == typeof(Object))
+ {
+ result = Value;
+ return true;
+ }
+
+ if (typeof(Object).IsAssignableFrom(binder.Type))
+ {
+ // Throws InvalidCastException when the cast fails
+ result = Convert.ChangeType(Value, binder.Type);
+ return true;
+ }
+
+ return base.TryConvert(binder, out result);
+ }
+
+ public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
+ {
+ if (indexes.Length == 1)
+ {
+ if (indexes[0] is string name)
+ {
+ return godot_icall_DynamicGodotObject_GetMember(Object.GetPtr(Value), name, out result);
+ }
+ }
+
+ return base.TryGetIndex(binder, indexes, out result);
+ }
+
+ public override bool TryGetMember(GetMemberBinder binder, out object result)
+ {
+ return godot_icall_DynamicGodotObject_GetMember(Object.GetPtr(Value), binder.Name, out result);
+ }
+
+ public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
+ {
+ return godot_icall_DynamicGodotObject_InvokeMember(Object.GetPtr(Value), binder.Name, args, out result);
+ }
+
+ public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value)
+ {
+ if (indexes.Length == 1)
+ {
+ if (indexes[0] is string name)
+ {
+ return godot_icall_DynamicGodotObject_SetMember(Object.GetPtr(Value), name, value);
+ }
+ }
+
+ return base.TrySetIndex(binder, indexes, value);
+ }
+
+ public override bool TrySetMember(SetMemberBinder binder, object value)
+ {
+ return godot_icall_DynamicGodotObject_SetMember(Object.GetPtr(Value), binder.Name, value);
+ }
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static string[] godot_icall_DynamicGodotObject_SetMemberList(IntPtr godotObject);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static bool godot_icall_DynamicGodotObject_InvokeMember(IntPtr godotObject, string name, object[] args, out object result);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static bool godot_icall_DynamicGodotObject_GetMember(IntPtr godotObject, string name, out object result);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static bool godot_icall_DynamicGodotObject_SetMember(IntPtr godotObject, string name, object value);
+
+ #region We don't override these methods
+
+ // Looks like this is not usable from C#
+ //public override bool TryCreateInstance(CreateInstanceBinder binder, object[] args, out object result);
+
+ // Object members cannot be deleted
+ //public override bool TryDeleteIndex(DeleteIndexBinder binder, object[] indexes);
+ //public override bool TryDeleteMember(DeleteMemberBinder binder);
+
+ // Invokation on the object itself, e.g.: obj(param)
+ //public override bool TryInvoke(InvokeBinder binder, object[] args, out object result);
+
+ // No unnary operations to handle
+ //public override bool TryUnaryOperation(UnaryOperationBinder binder, out object result);
+
+ #endregion
+ }
+}
diff --git a/modules/mono/glue/Managed/Files/GD.cs b/modules/mono/glue/Managed/Files/GD.cs
index 75a35a9eea..2068099ac6 100644
--- a/modules/mono/glue/Managed/Files/GD.cs
+++ b/modules/mono/glue/Managed/Files/GD.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Runtime.CompilerServices;
#if REAL_T_IS_DOUBLE
using real_t = System.Double;
@@ -12,12 +13,12 @@ namespace Godot
{
public static partial class GD
{
- public static object Bytes2Var(byte[] bytes)
+ public static object Bytes2Var(byte[] bytes, bool allow_objects = false)
{
- return godot_icall_GD_bytes2var(bytes);
+ return godot_icall_GD_bytes2var(bytes, allow_objects);
}
- public static object Convert(object what, int type)
+ public static object Convert(object what, Variant.Type type)
{
return godot_icall_GD_convert(what, type);
}
@@ -50,7 +51,7 @@ namespace Godot
return godot_icall_GD_hash(var);
}
- public static Object InstanceFromId(int instanceId)
+ public static Object InstanceFromId(ulong instanceId)
{
return godot_icall_GD_instance_from_id(instanceId);
}
@@ -82,7 +83,7 @@ namespace Godot
public static void Print(params object[] what)
{
- godot_icall_GD_print(what);
+ godot_icall_GD_print(Array.ConvertAll(what, x => x.ToString()));
}
public static void PrintStack()
@@ -92,89 +93,80 @@ namespace Godot
public static void PrintErr(params object[] what)
{
- godot_icall_GD_printerr(what);
+ godot_icall_GD_printerr(Array.ConvertAll(what, x => x.ToString()));
}
public static void PrintRaw(params object[] what)
{
- godot_icall_GD_printraw(what);
+ godot_icall_GD_printraw(Array.ConvertAll(what, x => x.ToString()));
}
public static void PrintS(params object[] what)
{
- godot_icall_GD_prints(what);
+ godot_icall_GD_prints(Array.ConvertAll(what, x => x.ToString()));
}
public static void PrintT(params object[] what)
{
- godot_icall_GD_printt(what);
+ godot_icall_GD_printt(Array.ConvertAll(what, x => x.ToString()));
}
- public static int[] Range(int length)
+ public static float Randf()
{
- var ret = new int[length];
-
- for (int i = 0; i < length; i++)
- {
- ret[i] = i;
- }
-
- return ret;
+ return godot_icall_GD_randf();
}
- public static int[] Range(int from, int to)
+ public static uint Randi()
{
- if (to < from)
- return new int[0];
+ return godot_icall_GD_randi();
+ }
- var ret = new int[to - from];
+ public static void Randomize()
+ {
+ godot_icall_GD_randomize();
+ }
- for (int i = from; i < to; i++)
- {
- ret[i - from] = i;
- }
+ public static double RandRange(double from, double to)
+ {
+ return godot_icall_GD_rand_range(from, to);
+ }
- return ret;
+ public static uint RandSeed(ulong seed, out ulong newSeed)
+ {
+ return godot_icall_GD_rand_seed(seed, out newSeed);
}
- public static int[] Range(int from, int to, int increment)
+ public static IEnumerable<int> Range(int end)
{
- if (to < from && increment > 0)
- return new int[0];
- if (to > from && increment < 0)
- return new int[0];
+ return Range(0, end, 1);
+ }
- // Calculate count
- int count;
+ public static IEnumerable<int> Range(int start, int end)
+ {
+ return Range(start, end, 1);
+ }
- if (increment > 0)
- count = (to - from - 1) / increment + 1;
- else
- count = (from - to - 1) / -increment + 1;
+ public static IEnumerable<int> Range(int start, int end, int step)
+ {
+ if (end < start && step > 0)
+ yield break;
- var ret = new int[count];
+ if (end > start && step < 0)
+ yield break;
- if (increment > 0)
+ if (step > 0)
{
- int idx = 0;
- for (int i = from; i < to; i += increment)
- {
- ret[idx++] = i;
- }
+ for (int i = start; i < end; i += step)
+ yield return i;
}
else
{
- int idx = 0;
- for (int i = from; i > to; i += increment)
- {
- ret[idx++] = i;
- }
+ for (int i = start; i > end; i += step)
+ yield return i;
}
-
- return ret;
}
- public static void Seed(int seed)
+ public static void Seed(ulong seed)
{
godot_icall_GD_seed(seed);
}
@@ -194,9 +186,9 @@ namespace Godot
return godot_icall_GD_type_exists(type);
}
- public static byte[] Var2Bytes(object var)
+ public static byte[] Var2Bytes(object var, bool full_objects = false)
{
- return godot_icall_GD_var2bytes(var);
+ return godot_icall_GD_var2bytes(var, full_objects);
}
public static string Var2Str(object var)
@@ -205,16 +197,16 @@ namespace Godot
}
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static object godot_icall_GD_bytes2var(byte[] bytes);
+ internal extern static object godot_icall_GD_bytes2var(byte[] bytes, bool allow_objects);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static object godot_icall_GD_convert(object what, int type);
+ internal extern static object godot_icall_GD_convert(object what, Variant.Type type);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static int godot_icall_GD_hash(object var);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static Object godot_icall_GD_instance_from_id(int instance_id);
+ internal extern static Object godot_icall_GD_instance_from_id(ulong instance_id);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static void godot_icall_GD_print(object[] what);
@@ -232,7 +224,23 @@ namespace Godot
internal extern static void godot_icall_GD_printt(object[] what);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_GD_seed(int seed);
+ internal extern static float godot_icall_GD_randf();
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static uint godot_icall_GD_randi();
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void godot_icall_GD_randomize();
+
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static double godot_icall_GD_rand_range(double from, double to);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static uint godot_icall_GD_rand_seed(ulong seed, out ulong newSeed);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void godot_icall_GD_seed(ulong seed);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static string godot_icall_GD_str(object[] what);
@@ -244,7 +252,7 @@ namespace Godot
internal extern static bool godot_icall_GD_type_exists(string type);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static byte[] godot_icall_GD_var2bytes(object what);
+ internal extern static byte[] godot_icall_GD_var2bytes(object what, bool full_objects);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static string godot_icall_GD_var2str(object var);
diff --git a/modules/mono/glue/Managed/Files/GodotTraceListener.cs b/modules/mono/glue/Managed/Files/GodotTraceListener.cs
new file mode 100644
index 0000000000..f1a00ae0fa
--- /dev/null
+++ b/modules/mono/glue/Managed/Files/GodotTraceListener.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Diagnostics;
+
+namespace Godot
+{
+ internal class GodotTraceListener : TraceListener
+ {
+ public override void Write(string message)
+ {
+ GD.PrintRaw(message);
+ }
+
+ public override void WriteLine(string message)
+ {
+ GD.Print(message);
+ }
+
+ public override void Fail(string message, string detailMessage)
+ {
+ GD.PrintErr("Assertion failed: ", message);
+ if (detailMessage != null)
+ {
+ GD.PrintErr(" Details: ", detailMessage);
+ }
+
+ try
+ {
+ var stackTrace = new StackTrace(true).ToString();
+ GD.PrintErr(stackTrace);
+ }
+ catch
+ {
+ // ignored
+ }
+ }
+ }
+}
diff --git a/modules/mono/glue/Managed/Files/MarshalUtils.cs b/modules/mono/glue/Managed/Files/MarshalUtils.cs
index f7699a15bf..7e72b0edb5 100644
--- a/modules/mono/glue/Managed/Files/MarshalUtils.cs
+++ b/modules/mono/glue/Managed/Files/MarshalUtils.cs
@@ -1,18 +1,68 @@
using System;
-using Godot.Collections;
+using System.Collections;
namespace Godot
{
+ using Array = Godot.Collections.Array;
+ using Dictionary = Godot.Collections.Dictionary;
+
static class MarshalUtils
{
- static bool IsArrayGenericType(Type type)
+ static bool TypeIsGenericArray(Type type)
{
- return type.GetGenericTypeDefinition() == typeof(Array<>);
+ return type.GetGenericTypeDefinition() == typeof(Godot.Collections.Array<>);
+ }
+
+ static bool TypeIsGenericDictionary(Type type)
+ {
+ return type.GetGenericTypeDefinition() == typeof(Godot.Collections.Dictionary<,>);
+ }
+
+ static void ArrayGetElementType(Type type, out Type elementType)
+ {
+ elementType = type.GetGenericArguments()[0];
+ }
+
+ static void DictionaryGetKeyValueTypes(Type type, out Type keyType, out Type valueType)
+ {
+ var genericArgs = type.GetGenericArguments();
+
+ keyType = genericArgs[0];
+ valueType = genericArgs[1];
+ }
+
+ // TODO Add support for IEnumerable<T> and IDictionary<TKey, TValue>
+ // TODO: EnumerableToArray and IDictionaryToDictionary can be optimized
+
+ internal static void EnumerableToArray(IEnumerable enumerable, IntPtr godotArrayPtr)
+ {
+ if (enumerable is ICollection collection)
+ {
+ int count = collection.Count;
+
+ object[] tempArray = new object[count];
+ collection.CopyTo(tempArray, 0);
+
+ for (int i = 0; i < count; i++)
+ {
+ Array.godot_icall_Array_Add(godotArrayPtr, tempArray[i]);
+ }
+ }
+ else
+ {
+ foreach (object element in enumerable)
+ {
+ Array.godot_icall_Array_Add(godotArrayPtr, element);
+ }
+ }
}
- static bool IsDictionaryGenericType(Type type)
+ internal static void IDictionaryToDictionary(IDictionary dictionary, IntPtr godotDictionaryPtr)
{
- return type.GetGenericTypeDefinition() == typeof(Dictionary<, >);
+ foreach (DictionaryEntry entry in dictionary)
+ {
+ Dictionary.godot_icall_Dictionary_Add(godotDictionaryPtr, entry.Key, entry.Value);
+ }
}
}
}
diff --git a/modules/mono/glue/Managed/Files/Mathf.cs b/modules/mono/glue/Managed/Files/Mathf.cs
index dcab3c1ffc..947fbb6665 100644
--- a/modules/mono/glue/Managed/Files/Mathf.cs
+++ b/modules/mono/glue/Managed/Files/Mathf.cs
@@ -143,6 +143,15 @@ namespace Godot
return (weight - from) / (to - from);
}
+ public static bool IsEqualApprox(real_t a, real_t b)
+ {
+ real_t tolerance = Epsilon * Abs(a);
+ if (tolerance < Epsilon) {
+ tolerance = Epsilon;
+ }
+ return Abs(a - b) < tolerance;
+ }
+
public static bool IsInf(real_t s)
{
return real_t.IsInfinity(s);
@@ -153,6 +162,11 @@ namespace Godot
return real_t.IsNaN(s);
}
+ public static bool IsZeroApprox(real_t s)
+ {
+ return Abs(s) < Epsilon;
+ }
+
public static real_t Lerp(real_t from, real_t to, real_t weight)
{
return from + (to - from) * weight;
@@ -261,6 +275,16 @@ namespace Godot
return (real_t)Math.Sinh(s);
}
+ public static real_t SmoothStep(real_t from, real_t to, real_t weight)
+ {
+ if (IsEqualApprox(from, to))
+ {
+ return from;
+ }
+ real_t x = Clamp((weight - from) / (to - from), (real_t)0.0, (real_t)1.0);
+ return x * x * (3 - 2 * x);
+ }
+
public static real_t Sqrt(real_t s)
{
return (real_t)Math.Sqrt(s);
@@ -289,13 +313,13 @@ namespace Godot
public static int Wrap(int value, int min, int max)
{
int rng = max - min;
- return min + ((value - min) % rng + rng) % rng;
+ return rng != 0 ? min + ((value - min) % rng + rng) % rng : min;
}
public static real_t Wrap(real_t value, real_t min, real_t max)
{
real_t rng = max - min;
- return min + ((value - min) % rng + rng) % rng;
+ return !IsEqualApprox(rng, default(real_t)) ? min + ((value - min) % rng + rng) % rng : min;
}
}
}
diff --git a/modules/mono/glue/Managed/Files/MathfEx.cs b/modules/mono/glue/Managed/Files/MathfEx.cs
index 2ef02cc288..d6eb65b097 100644
--- a/modules/mono/glue/Managed/Files/MathfEx.cs
+++ b/modules/mono/glue/Managed/Files/MathfEx.cs
@@ -35,5 +35,10 @@ namespace Godot
{
return (int)Math.Round(s);
}
+
+ public static bool IsEqualApprox(real_t a, real_t b, real_t tolerance)
+ {
+ return Abs(a - b) < tolerance;
+ }
}
} \ No newline at end of file
diff --git a/modules/mono/glue/Managed/Files/NodePath.cs b/modules/mono/glue/Managed/Files/NodePath.cs
index 2c89bec87f..94a4ed1de9 100644
--- a/modules/mono/glue/Managed/Files/NodePath.cs
+++ b/modules/mono/glue/Managed/Files/NodePath.cs
@@ -3,7 +3,7 @@ using System.Runtime.CompilerServices;
namespace Godot
{
- public partial class NodePath : IDisposable
+ public sealed partial class NodePath : IDisposable
{
private bool disposed = false;
@@ -11,7 +11,13 @@ namespace Godot
internal static IntPtr GetPtr(NodePath instance)
{
- return instance == null ? IntPtr.Zero : instance.ptr;
+ if (instance == null)
+ return IntPtr.Zero;
+
+ if (instance.disposed)
+ throw new ObjectDisposedException(instance.GetType().FullName);
+
+ return instance.ptr;
}
~NodePath()
@@ -25,7 +31,7 @@ namespace Godot
GC.SuppressFinalize(this);
}
- protected virtual void Dispose(bool disposing)
+ private void Dispose(bool disposing)
{
if (disposed)
return;
@@ -49,7 +55,7 @@ namespace Godot
get { return ptr; }
}
- public NodePath() : this(string.Empty) {}
+ public NodePath() : this(string.Empty) { }
public NodePath(string path)
{
diff --git a/modules/mono/glue/Managed/Files/Object.base.cs b/modules/mono/glue/Managed/Files/Object.base.cs
index 30490a715f..de80f7fddc 100644
--- a/modules/mono/glue/Managed/Files/Object.base.cs
+++ b/modules/mono/glue/Managed/Files/Object.base.cs
@@ -30,7 +30,13 @@ namespace Godot
internal static IntPtr GetPtr(Object instance)
{
- return instance == null ? IntPtr.Zero : instance.ptr;
+ if (instance == null)
+ return IntPtr.Zero;
+
+ if (instance.disposed)
+ throw new ObjectDisposedException(instance.GetType().FullName);
+
+ return instance.ptr;
}
~Object()
@@ -67,11 +73,44 @@ namespace Godot
disposed = true;
}
+ public override string ToString()
+ {
+ return godot_icall_Object_ToString(GetPtr(this));
+ }
+
+ /// <summary>
+ /// Returns a new <see cref="Godot.SignalAwaiter"/> awaiter configured to complete when the instance
+ /// <paramref name="source"/> emits the signal specified by the <paramref name="signal"/> parameter.
+ /// </summary>
+ /// <param name="source">
+ /// The instance the awaiter will be listening to.
+ /// </param>
+ /// <param name="signal">
+ /// The signal the awaiter will be waiting for.
+ /// </param>
+ /// <example>
+ /// This sample prints a message once every frame up to 100 times.
+ /// <code>
+ /// public override void _Ready()
+ /// {
+ /// for (int i = 0; i &lt; 100; i++)
+ /// {
+ /// await ToSignal(GetTree(), "idle_frame");
+ /// GD.Print($"Frame {i}");
+ /// }
+ /// }
+ /// </code>
+ /// </example>
public SignalAwaiter ToSignal(Object source, string signal)
{
return new SignalAwaiter(source, signal, this);
}
+ /// <summary>
+ /// Gets a new <see cref="Godot.DynamicGodotObject"/> associated with this instance.
+ /// </summary>
+ public dynamic DynamicObject => new DynamicGodotObject(this);
+
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static IntPtr godot_icall_Object_Ctor(Object obj);
@@ -81,6 +120,9 @@ namespace Godot
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static void godot_icall_Reference_Disposed(Object obj, IntPtr ptr, bool isFinalizer);
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static 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);
diff --git a/modules/mono/glue/Managed/Files/Plane.cs b/modules/mono/glue/Managed/Files/Plane.cs
index f11cd490a9..e16d4315be 100644
--- a/modules/mono/glue/Managed/Files/Plane.cs
+++ b/modules/mono/glue/Managed/Files/Plane.cs
@@ -200,7 +200,7 @@ namespace Godot
public bool Equals(Plane other)
{
- return _normal == other._normal && D == other.D;
+ return _normal == other._normal && Mathf.IsEqualApprox(D, other.D);
}
public override int GetHashCode()
diff --git a/modules/mono/glue/Managed/Files/Quat.cs b/modules/mono/glue/Managed/Files/Quat.cs
index fd1ac01083..0d4349084a 100644
--- a/modules/mono/glue/Managed/Files/Quat.cs
+++ b/modules/mono/glue/Managed/Files/Quat.cs
@@ -123,22 +123,23 @@ namespace Godot
// Calculate cosine
real_t cosom = x * b.x + y * b.y + z * b.z + w * b.w;
- var to1 = new real_t[4];
+ var to1 = new Quat();
// Adjust signs if necessary
if (cosom < 0.0)
{
- cosom = -cosom; to1[0] = -b.x;
- to1[1] = -b.y;
- to1[2] = -b.z;
- to1[3] = -b.w;
+ cosom = -cosom;
+ to1.x = -b.x;
+ to1.y = -b.y;
+ to1.z = -b.z;
+ to1.w = -b.w;
}
else
{
- to1[0] = b.x;
- to1[1] = b.y;
- to1[2] = b.z;
- to1[3] = b.w;
+ to1.x = b.x;
+ to1.y = b.y;
+ to1.z = b.z;
+ to1.w = b.w;
}
real_t sinom, scale0, scale1;
@@ -162,10 +163,10 @@ namespace Godot
// Calculate final values
return new Quat
(
- scale0 * x + scale1 * to1[0],
- scale0 * y + scale1 * to1[1],
- scale0 * z + scale1 * to1[2],
- scale0 * w + scale1 * to1[3]
+ scale0 * x + scale1 * to1.x,
+ scale0 * y + scale1 * to1.y,
+ scale0 * z + scale1 * to1.z,
+ scale0 * w + scale1 * to1.w
);
}
@@ -347,9 +348,9 @@ namespace Godot
public override bool Equals(object obj)
{
- if (obj is Vector2)
+ if (obj is Quat)
{
- return Equals((Vector2)obj);
+ return Equals((Quat)obj);
}
return false;
@@ -357,7 +358,7 @@ namespace Godot
public bool Equals(Quat other)
{
- return x == other.x && y == other.y && z == other.z && w == other.w;
+ return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y) && Mathf.IsEqualApprox(z, other.z) && Mathf.IsEqualApprox(w, other.w);
}
public override int GetHashCode()
diff --git a/modules/mono/glue/Managed/Files/RID.cs b/modules/mono/glue/Managed/Files/RID.cs
index b862b8cac0..12064beca2 100644
--- a/modules/mono/glue/Managed/Files/RID.cs
+++ b/modules/mono/glue/Managed/Files/RID.cs
@@ -3,7 +3,7 @@ using System.Runtime.CompilerServices;
namespace Godot
{
- public partial class RID : IDisposable
+ public sealed partial class RID : IDisposable
{
private bool disposed = false;
@@ -11,7 +11,13 @@ namespace Godot
internal static IntPtr GetPtr(RID instance)
{
- return instance == null ? IntPtr.Zero : instance.ptr;
+ if (instance == null)
+ return IntPtr.Zero;
+
+ if (instance.disposed)
+ throw new ObjectDisposedException(instance.GetType().FullName);
+
+ return instance.ptr;
}
~RID()
@@ -25,7 +31,7 @@ namespace Godot
GC.SuppressFinalize(this);
}
- protected virtual void Dispose(bool disposing)
+ private void Dispose(bool disposing)
{
if (disposed)
return;
@@ -64,6 +70,8 @@ namespace Godot
return godot_icall_RID_get_id(RID.GetPtr(this));
}
+ public override string ToString() => "[RID]";
+
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static IntPtr godot_icall_RID_Ctor(IntPtr from);
diff --git a/modules/mono/glue/Managed/Files/Transform.cs b/modules/mono/glue/Managed/Files/Transform.cs
index fa85855edd..bd79144873 100644
--- a/modules/mono/glue/Managed/Files/Transform.cs
+++ b/modules/mono/glue/Managed/Files/Transform.cs
@@ -71,21 +71,21 @@ namespace Godot
{
// Make rotation matrix
// Z vector
- Vector3 zAxis = eye - target;
+ Vector3 column2 = eye - target;
- zAxis.Normalize();
+ column2.Normalize();
- Vector3 yAxis = up;
+ Vector3 column1 = up;
- Vector3 xAxis = yAxis.Cross(zAxis);
+ Vector3 column0 = column1.Cross(column2);
// Recompute Y = Z cross X
- yAxis = zAxis.Cross(xAxis);
+ column1 = column2.Cross(column0);
- xAxis.Normalize();
- yAxis.Normalize();
+ column0.Normalize();
+ column1.Normalize();
- basis = Basis.CreateFromAxes(xAxis, yAxis, zAxis);
+ basis = new Basis(column0, column1, column2);
origin = eye;
}
@@ -94,9 +94,9 @@ namespace Godot
{
return new Transform(basis, new Vector3
(
- origin[0] += basis[0].Dot(ofs),
- origin[1] += basis[1].Dot(ofs),
- origin[2] += basis[2].Dot(ofs)
+ origin[0] += basis.Row0.Dot(ofs),
+ origin[1] += basis.Row1.Dot(ofs),
+ origin[2] += basis.Row2.Dot(ofs)
));
}
@@ -104,9 +104,9 @@ namespace Godot
{
return new Vector3
(
- basis[0].Dot(v) + origin.x,
- basis[1].Dot(v) + origin.y,
- basis[2].Dot(v) + origin.z
+ basis.Row0.Dot(v) + origin.x,
+ basis.Row1.Dot(v) + origin.y,
+ basis.Row2.Dot(v) + origin.z
);
}
@@ -116,9 +116,9 @@ namespace Godot
return new Vector3
(
- basis[0, 0] * vInv.x + basis[1, 0] * vInv.y + basis[2, 0] * vInv.z,
- basis[0, 1] * vInv.x + basis[1, 1] * vInv.y + basis[2, 1] * vInv.z,
- basis[0, 2] * vInv.x + basis[1, 2] * vInv.y + basis[2, 2] * vInv.z
+ basis.Row0[0] * vInv.x + basis.Row1[0] * vInv.y + basis.Row2[0] * vInv.z,
+ basis.Row0[1] * vInv.x + basis.Row1[1] * vInv.y + basis.Row2[1] * vInv.z,
+ basis.Row0[2] * vInv.x + basis.Row1[2] * vInv.y + basis.Row2[2] * vInv.z
);
}
@@ -134,9 +134,9 @@ namespace Godot
public static Transform FlipZ { get { return _flipZ; } }
// Constructors
- public Transform(Vector3 xAxis, Vector3 yAxis, Vector3 zAxis, Vector3 origin)
+ public Transform(Vector3 column0, Vector3 column1, Vector3 column2, Vector3 origin)
{
- basis = Basis.CreateFromAxes(xAxis, yAxis, zAxis);
+ basis = new Basis(column0, column1, column2);
this.origin = origin;
}
diff --git a/modules/mono/glue/Managed/Files/Transform2D.cs b/modules/mono/glue/Managed/Files/Transform2D.cs
index c9e5b560b2..f7bb41d523 100644
--- a/modules/mono/glue/Managed/Files/Transform2D.cs
+++ b/modules/mono/glue/Managed/Files/Transform2D.cs
@@ -13,42 +13,65 @@ namespace Godot
{
public Vector2 x;
public Vector2 y;
- public Vector2 o;
-
- public Vector2 Origin
- {
- get { return o; }
- }
+ public Vector2 origin;
public real_t Rotation
{
- get { return Mathf.Atan2(y.x, o.y); }
+ get
+ {
+ real_t det = BasisDeterminant();
+ Transform2D t = Orthonormalized();
+ if (det < 0)
+ {
+ t.ScaleBasis(new Vector2(1, -1));
+ }
+ return Mathf.Atan2(t.x.y, t.x.x);
+ }
+ set
+ {
+ Vector2 scale = Scale;
+ x.x = y.y = Mathf.Cos(value);
+ x.y = y.x = Mathf.Sin(value);
+ y.x *= -1;
+ Scale = scale;
+ }
}
public Vector2 Scale
{
- get { return new Vector2(x.Length(), y.Length()); }
+ get
+ {
+ real_t detSign = Mathf.Sign(BasisDeterminant());
+ return new Vector2(x.Length(), detSign * y.Length());
+ }
+ set
+ {
+ x = x.Normalized();
+ y = y.Normalized();
+ x *= value.x;
+ y *= value.y;
+ }
}
- public Vector2 this[int index]
+ public Vector2 this[int rowIndex]
{
get
{
- switch (index)
+ switch (rowIndex)
{
case 0:
return x;
case 1:
return y;
case 2:
- return o;
+ return origin;
default:
throw new IndexOutOfRangeException();
}
}
set
{
- switch (index)
+ switch (rowIndex)
{
case 0:
x = value;
@@ -57,7 +80,7 @@ namespace Godot
y = value;
return;
case 2:
- o = value;
+ origin = value;
return;
default:
throw new IndexOutOfRangeException();
@@ -65,30 +88,29 @@ namespace Godot
}
}
-
- public real_t this[int index, int axis]
+ public real_t this[int rowIndex, int columnIndex]
{
get
{
- switch (index)
+ switch (rowIndex)
{
case 0:
- return x[axis];
+ return x[columnIndex];
case 1:
- return y[axis];
+ return y[columnIndex];
default:
throw new IndexOutOfRangeException();
}
}
set
{
- switch (index)
+ switch (rowIndex)
{
case 0:
- x[axis] = value;
+ x[columnIndex] = value;
return;
case 1:
- y[axis] = value;
+ y[columnIndex] = value;
return;
default:
throw new IndexOutOfRangeException();
@@ -98,34 +120,32 @@ namespace Godot
public Transform2D AffineInverse()
{
- var inv = this;
-
- real_t det = this[0, 0] * this[1, 1] - this[1, 0] * this[0, 1];
+ real_t det = BasisDeterminant();
if (det == 0)
- {
- return new Transform2D
- (
- float.NaN, float.NaN,
- float.NaN, float.NaN,
- float.NaN, float.NaN
- );
- }
+ throw new InvalidOperationException("Matrix determinant is zero and cannot be inverted.");
- real_t idet = 1.0f / det;
+ var inv = this;
+
+ real_t temp = inv[0, 0];
+ inv[0, 0] = inv[1, 1];
+ inv[1, 1] = temp;
- real_t temp = this[0, 0];
- this[0, 0] = this[1, 1];
- this[1, 1] = temp;
+ real_t detInv = 1.0f / det;
- this[0] *= new Vector2(idet, -idet);
- this[1] *= new Vector2(-idet, idet);
+ inv[0] *= new Vector2(detInv, -detInv);
+ inv[1] *= new Vector2(-detInv, detInv);
- this[2] = BasisXform(-this[2]);
+ inv[2] = BasisXform(-inv[2]);
return inv;
}
+ private real_t BasisDeterminant()
+ {
+ return x.x * y.y - x.y * y.x;
+ }
+
public Vector2 BasisXform(Vector2 v)
{
return new Vector2(Tdotx(v), Tdoty(v));
@@ -168,8 +188,8 @@ namespace Godot
}
// Extract parameters
- Vector2 p1 = Origin;
- Vector2 p2 = m.Origin;
+ Vector2 p1 = origin;
+ Vector2 p2 = m.origin;
// Construct matrix
var res = new Transform2D(Mathf.Atan2(v.y, v.x), p1.LinearInterpolate(p2, c));
@@ -189,7 +209,7 @@ namespace Godot
inv.x.y = inv.y.x;
inv.y.x = temp;
- inv.o = inv.BasisXform(-inv.o);
+ inv.origin = inv.BasisXform(-inv.origin);
return inv;
}
@@ -221,10 +241,18 @@ namespace Godot
var copy = this;
copy.x *= scale;
copy.y *= scale;
- copy.o *= scale;
+ copy.origin *= scale;
return copy;
}
+ private void ScaleBasis(Vector2 scale)
+ {
+ x.x *= scale.x;
+ x.y *= scale.y;
+ y.x *= scale.x;
+ y.y *= scale.y;
+ }
+
private real_t Tdotx(Vector2 with)
{
return this[0, 0] * with[0] + this[1, 0] * with[1];
@@ -238,66 +266,61 @@ namespace Godot
public Transform2D Translated(Vector2 offset)
{
var copy = this;
- copy.o += copy.BasisXform(offset);
+ copy.origin += copy.BasisXform(offset);
return copy;
}
public Vector2 Xform(Vector2 v)
{
- return new Vector2(Tdotx(v), Tdoty(v)) + o;
+ return new Vector2(Tdotx(v), Tdoty(v)) + origin;
}
public Vector2 XformInv(Vector2 v)
{
- Vector2 vInv = v - o;
+ Vector2 vInv = v - origin;
return new Vector2(x.Dot(vInv), y.Dot(vInv));
}
// Constants
- private static readonly Transform2D _identity = new Transform2D(new Vector2(1f, 0f), new Vector2(0f, 1f), Vector2.Zero);
- private static readonly Transform2D _flipX = new Transform2D(new Vector2(-1f, 0f), new Vector2(0f, 1f), Vector2.Zero);
- private static readonly Transform2D _flipY = new Transform2D(new Vector2(1f, 0f), new Vector2(0f, -1f), Vector2.Zero);
+ private static readonly Transform2D _identity = new Transform2D(1, 0, 0, 1, 0, 0);
+ private static readonly Transform2D _flipX = new Transform2D(-1, 0, 0, 1, 0, 0);
+ private static readonly Transform2D _flipY = new Transform2D(1, 0, 0, -1, 0, 0);
- public static Transform2D Identity { get { return _identity; } }
- public static Transform2D FlipX { get { return _flipX; } }
- public static Transform2D FlipY { get { return _flipY; } }
+ public static Transform2D Identity => _identity;
+ public static Transform2D FlipX => _flipX;
+ public static Transform2D FlipY => _flipY;
// Constructors
- public Transform2D(Vector2 xAxis, Vector2 yAxis, Vector2 origin)
+ public Transform2D(Vector2 xAxis, Vector2 yAxis, Vector2 originPos)
{
x = xAxis;
y = yAxis;
- o = origin;
+ origin = originPos;
}
public Transform2D(real_t xx, real_t xy, real_t yx, real_t yy, real_t ox, real_t oy)
{
x = new Vector2(xx, xy);
y = new Vector2(yx, yy);
- o = new Vector2(ox, oy);
+ origin = new Vector2(ox, oy);
}
public Transform2D(real_t rot, Vector2 pos)
{
- real_t cr = Mathf.Cos(rot);
- real_t sr = Mathf.Sin(rot);
- x.x = cr;
- y.y = cr;
- x.y = -sr;
- y.x = sr;
- o = pos;
+ x.x = y.y = Mathf.Cos(rot);
+ x.y = y.x = Mathf.Sin(rot);
+ y.x *= -1;
+ origin = pos;
}
public static Transform2D operator *(Transform2D left, Transform2D right)
{
- left.o = left.Xform(right.o);
+ left.origin = left.Xform(right.origin);
- real_t x0, x1, y0, y1;
-
- x0 = left.Tdotx(right.x);
- x1 = left.Tdoty(right.x);
- y0 = left.Tdotx(right.y);
- y1 = left.Tdoty(right.y);
+ real_t x0 = left.Tdotx(right.x);
+ real_t x1 = left.Tdoty(right.x);
+ real_t y0 = left.Tdotx(right.y);
+ real_t y1 = left.Tdoty(right.y);
left.x.x = x0;
left.x.y = x1;
@@ -319,22 +342,17 @@ namespace Godot
public override bool Equals(object obj)
{
- if (obj is Transform2D)
- {
- return Equals((Transform2D)obj);
- }
-
- return false;
+ return obj is Transform2D transform2D && Equals(transform2D);
}
public bool Equals(Transform2D other)
{
- return x.Equals(other.x) && y.Equals(other.y) && o.Equals(other.o);
+ return x.Equals(other.x) && y.Equals(other.y) && origin.Equals(other.origin);
}
public override int GetHashCode()
{
- return x.GetHashCode() ^ y.GetHashCode() ^ o.GetHashCode();
+ return x.GetHashCode() ^ y.GetHashCode() ^ origin.GetHashCode();
}
public override string ToString()
@@ -343,7 +361,7 @@ namespace Godot
{
x.ToString(),
y.ToString(),
- o.ToString()
+ origin.ToString()
});
}
@@ -353,7 +371,7 @@ namespace Godot
{
x.ToString(format),
y.ToString(format),
- o.ToString(format)
+ origin.ToString(format)
});
}
}
diff --git a/modules/mono/glue/Managed/Files/Vector2.cs b/modules/mono/glue/Managed/Files/Vector2.cs
index ce41886bfc..bb1950e1a8 100644
--- a/modules/mono/glue/Managed/Files/Vector2.cs
+++ b/modules/mono/glue/Managed/Files/Vector2.cs
@@ -52,11 +52,15 @@ namespace Godot
internal void Normalize()
{
- real_t length = x * x + y * y;
+ real_t lengthsq = LengthSquared();
- if (length != 0f)
+ if (lengthsq == 0)
{
- length = Mathf.Sqrt(length);
+ x = y = 0f;
+ }
+ else
+ {
+ real_t length = Mathf.Sqrt(lengthsq);
x /= length;
y /= length;
}
@@ -84,7 +88,7 @@ namespace Godot
public real_t AngleToPoint(Vector2 to)
{
- return Mathf.Atan2(x - to.x, y - to.y);
+ return Mathf.Atan2(y - to.y, x - to.x);
}
public real_t Aspect()
@@ -132,6 +136,11 @@ namespace Godot
(-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3);
}
+ public Vector2 DirectionTo(Vector2 b)
+ {
+ return new Vector2(b.x - x, b.y - y).Normalized();
+ }
+
public real_t DistanceSquaredTo(Vector2 to)
{
return (x - to.x) * (x - to.x) + (y - to.y) * (y - to.y);
@@ -179,9 +188,9 @@ namespace Godot
public Vector2 Normalized()
{
- var result = this;
- result.Normalize();
- return result;
+ var v = this;
+ v.Normalize();
+ return v;
}
public Vector2 Project(Vector2 onNormal)
@@ -338,7 +347,7 @@ namespace Godot
public static bool operator <(Vector2 left, Vector2 right)
{
- if (left.x.Equals(right.x))
+ if (Mathf.IsEqualApprox(left.x, right.x))
{
return left.y < right.y;
}
@@ -348,7 +357,7 @@ namespace Godot
public static bool operator >(Vector2 left, Vector2 right)
{
- if (left.x.Equals(right.x))
+ if (Mathf.IsEqualApprox(left.x, right.x))
{
return left.y > right.y;
}
@@ -358,7 +367,7 @@ namespace Godot
public static bool operator <=(Vector2 left, Vector2 right)
{
- if (left.x.Equals(right.x))
+ if (Mathf.IsEqualApprox(left.x, right.x))
{
return left.y <= right.y;
}
@@ -368,7 +377,7 @@ namespace Godot
public static bool operator >=(Vector2 left, Vector2 right)
{
- if (left.x.Equals(right.x))
+ if (Mathf.IsEqualApprox(left.x, right.x))
{
return left.y >= right.y;
}
@@ -388,7 +397,7 @@ namespace Godot
public bool Equals(Vector2 other)
{
- return x == other.x && y == other.y;
+ return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y);
}
public override int GetHashCode()
diff --git a/modules/mono/glue/Managed/Files/Vector3.cs b/modules/mono/glue/Managed/Files/Vector3.cs
index f6ff27989d..283cb6341a 100644
--- a/modules/mono/glue/Managed/Files/Vector3.cs
+++ b/modules/mono/glue/Managed/Files/Vector3.cs
@@ -65,14 +65,15 @@ namespace Godot
internal void Normalize()
{
- real_t length = Length();
+ real_t lengthsq = LengthSquared();
- if (length == 0f)
+ if (lengthsq == 0)
{
x = y = z = 0f;
}
else
{
+ real_t length = Mathf.Sqrt(lengthsq);
x /= length;
y /= length;
z /= length;
@@ -126,6 +127,11 @@ namespace Godot
);
}
+ public Vector3 DirectionTo(Vector3 b)
+ {
+ return new Vector3(b.x - x, b.y - y, b.z - z).Normalized();
+ }
+
public real_t DistanceSquaredTo(Vector3 b)
{
return (b - this).LengthSquared();
@@ -392,9 +398,9 @@ namespace Godot
public static bool operator <(Vector3 left, Vector3 right)
{
- if (left.x == right.x)
+ if (Mathf.IsEqualApprox(left.x, right.x))
{
- if (left.y == right.y)
+ if (Mathf.IsEqualApprox(left.y, right.y))
return left.z < right.z;
return left.y < right.y;
}
@@ -404,9 +410,9 @@ namespace Godot
public static bool operator >(Vector3 left, Vector3 right)
{
- if (left.x == right.x)
+ if (Mathf.IsEqualApprox(left.x, right.x))
{
- if (left.y == right.y)
+ if (Mathf.IsEqualApprox(left.y, right.y))
return left.z > right.z;
return left.y > right.y;
}
@@ -416,9 +422,9 @@ namespace Godot
public static bool operator <=(Vector3 left, Vector3 right)
{
- if (left.x == right.x)
+ if (Mathf.IsEqualApprox(left.x, right.x))
{
- if (left.y == right.y)
+ if (Mathf.IsEqualApprox(left.y, right.y))
return left.z <= right.z;
return left.y < right.y;
}
@@ -428,9 +434,9 @@ namespace Godot
public static bool operator >=(Vector3 left, Vector3 right)
{
- if (left.x == right.x)
+ if (Mathf.IsEqualApprox(left.x, right.x))
{
- if (left.y == right.y)
+ if (Mathf.IsEqualApprox(left.y, right.y))
return left.z >= right.z;
return left.y > right.y;
}
@@ -450,7 +456,7 @@ namespace Godot
public bool Equals(Vector3 other)
{
- return x == other.x && y == other.y && z == other.z;
+ return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y) && Mathf.IsEqualApprox(z, other.z);
}
public override int GetHashCode()
diff --git a/modules/mono/glue/Managed/IgnoredFiles/Variant.cs b/modules/mono/glue/Managed/IgnoredFiles/Variant.cs
new file mode 100644
index 0000000000..802140b062
--- /dev/null
+++ b/modules/mono/glue/Managed/IgnoredFiles/Variant.cs
@@ -0,0 +1,11 @@
+
+namespace Godot
+{
+ public static class Variant
+ {
+ public enum Type
+ {
+
+ }
+ }
+}
diff --git a/modules/mono/glue/Managed/Managed.csproj b/modules/mono/glue/Managed/Managed.csproj
index 1f82dde5e7..61f738922b 100644
--- a/modules/mono/glue/Managed/Managed.csproj
+++ b/modules/mono/glue/Managed/Managed.csproj
@@ -11,7 +11,7 @@
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<DebugSymbols>true</DebugSymbols>
- <DebugType>full</DebugType>
+ <DebugType>portable</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG;</DefineConstants>
diff --git a/modules/mono/glue/arguments_vector.h b/modules/mono/glue/arguments_vector.h
new file mode 100644
index 0000000000..8c0f308c15
--- /dev/null
+++ b/modules/mono/glue/arguments_vector.h
@@ -0,0 +1,68 @@
+/*************************************************************************/
+/* arguments_vector.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef ARGUMENTS_VECTOR_H
+#define ARGUMENTS_VECTOR_H
+
+#include "core/os/memory.h"
+
+template <typename T, int POOL_SIZE = 5>
+struct ArgumentsVector {
+
+private:
+ T pool[POOL_SIZE];
+ T *_ptr;
+ int size;
+
+ ArgumentsVector();
+ ArgumentsVector(const ArgumentsVector &);
+
+public:
+ T *ptr() { return _ptr; }
+ T &get(int p_idx) { return _ptr[p_idx]; }
+ void set(int p_idx, const T &p_value) { _ptr[p_idx] = p_value; }
+
+ explicit ArgumentsVector(int p_size) :
+ size(p_size) {
+ if (p_size <= POOL_SIZE) {
+ _ptr = pool;
+ } else {
+ _ptr = memnew_arr(T, p_size);
+ }
+ }
+
+ ~ArgumentsVector() {
+ if (size > POOL_SIZE) {
+ memdelete_arr(_ptr);
+ }
+ }
+};
+
+#endif // ARGUMENTS_VECTOR_H
diff --git a/modules/mono/glue/base_object_glue.cpp b/modules/mono/glue/base_object_glue.cpp
index 2963619b79..7385014a53 100644
--- a/modules/mono/glue/base_object_glue.cpp
+++ b/modules/mono/glue/base_object_glue.cpp
@@ -33,12 +33,14 @@
#ifdef MONO_GLUE_ENABLED
#include "core/reference.h"
-#include "core/string_db.h"
+#include "core/string_name.h"
#include "../csharp_script.h"
+#include "../mono_gd/gd_mono_class.h"
#include "../mono_gd/gd_mono_internals.h"
#include "../mono_gd/gd_mono_utils.h"
#include "../signal_awaiter_utils.h"
+#include "arguments_vector.h"
Object *godot_icall_Object_Ctor(MonoObject *p_obj) {
Object *instance = memnew(Object);
@@ -75,7 +77,7 @@ void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr) {
}
}
-void godot_icall_Reference_Disposed(MonoObject *p_obj, Object *p_ptr, bool p_is_finalizer) {
+void godot_icall_Reference_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoolean p_is_finalizer) {
#ifdef DEBUG_ENABLED
CRASH_COND(p_ptr == NULL);
// This is only called with Reference derived classes
@@ -155,13 +157,83 @@ Error godot_icall_SignalAwaiter_connect(Object *p_source, MonoString *p_signal,
return SignalAwaiterUtils::connect_signal_awaiter(p_source, signal, p_target, p_awaiter);
}
+MonoArray *godot_icall_DynamicGodotObject_SetMemberList(Object *p_ptr) {
+ List<PropertyInfo> property_list;
+ p_ptr->get_property_list(&property_list);
+
+ MonoArray *result = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(String), property_list.size());
+
+ int i = 0;
+ for (List<PropertyInfo>::Element *E = property_list.front(); E; E = E->next()) {
+ MonoString *boxed = GDMonoMarshal::mono_string_from_godot(E->get().name);
+ mono_array_set(result, MonoString *, i, boxed);
+ i++;
+ }
+
+ return result;
+}
+
+MonoBoolean godot_icall_DynamicGodotObject_InvokeMember(Object *p_ptr, MonoString *p_name, MonoArray *p_args, MonoObject **r_result) {
+ String name = GDMonoMarshal::mono_string_to_godot(p_name);
+
+ int argc = mono_array_length(p_args);
+
+ ArgumentsVector<Variant> arg_store(argc);
+ ArgumentsVector<const Variant *> args(argc);
+
+ for (int i = 0; i < argc; i++) {
+ MonoObject *elem = mono_array_get(p_args, MonoObject *, i);
+ arg_store.set(i, GDMonoMarshal::mono_object_to_variant(elem));
+ args.set(i, &arg_store.get(i));
+ }
+
+ Variant::CallError error;
+ Variant result = p_ptr->call(StringName(name), args.ptr(), argc, error);
+
+ *r_result = GDMonoMarshal::variant_to_mono_object(result);
+
+ return error.error == Variant::CallError::CALL_OK;
+}
+
+MonoBoolean godot_icall_DynamicGodotObject_GetMember(Object *p_ptr, MonoString *p_name, MonoObject **r_result) {
+ String name = GDMonoMarshal::mono_string_to_godot(p_name);
+
+ bool valid;
+ Variant value = p_ptr->get(StringName(name), &valid);
+
+ if (valid) {
+ *r_result = GDMonoMarshal::variant_to_mono_object(value);
+ }
+
+ return valid;
+}
+
+MonoBoolean godot_icall_DynamicGodotObject_SetMember(Object *p_ptr, MonoString *p_name, MonoObject *p_value) {
+ String name = GDMonoMarshal::mono_string_to_godot(p_name);
+ Variant value = GDMonoMarshal::mono_object_to_variant(p_value);
+
+ bool valid;
+ p_ptr->set(StringName(name), value, &valid);
+
+ return valid;
+}
+
+MonoString *godot_icall_Object_ToString(Object *p_ptr) {
+ return GDMonoMarshal::mono_string_from_godot(Variant(p_ptr).operator String());
+}
+
void godot_register_object_icalls() {
mono_add_internal_call("Godot.Object::godot_icall_Object_Ctor", (void *)godot_icall_Object_Ctor);
mono_add_internal_call("Godot.Object::godot_icall_Object_Disposed", (void *)godot_icall_Object_Disposed);
mono_add_internal_call("Godot.Object::godot_icall_Reference_Disposed", (void *)godot_icall_Reference_Disposed);
mono_add_internal_call("Godot.Object::godot_icall_Object_ClassDB_get_method", (void *)godot_icall_Object_ClassDB_get_method);
+ mono_add_internal_call("Godot.Object::godot_icall_Object_ToString", (void *)godot_icall_Object_ToString);
mono_add_internal_call("Godot.Object::godot_icall_Object_weakref", (void *)godot_icall_Object_weakref);
mono_add_internal_call("Godot.SignalAwaiter::godot_icall_SignalAwaiter_connect", (void *)godot_icall_SignalAwaiter_connect);
+ mono_add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_SetMemberList", (void *)godot_icall_DynamicGodotObject_SetMemberList);
+ mono_add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_InvokeMember", (void *)godot_icall_DynamicGodotObject_InvokeMember);
+ mono_add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_GetMember", (void *)godot_icall_DynamicGodotObject_GetMember);
+ mono_add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_SetMember", (void *)godot_icall_DynamicGodotObject_SetMember);
}
#endif // MONO_GLUE_ENABLED
diff --git a/modules/mono/glue/base_object_glue.h b/modules/mono/glue/base_object_glue.h
index e126fac6ca..e4ac9fb99d 100644
--- a/modules/mono/glue/base_object_glue.h
+++ b/modules/mono/glue/base_object_glue.h
@@ -42,7 +42,7 @@ Object *godot_icall_Object_Ctor(MonoObject *p_obj);
void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr);
-void godot_icall_Reference_Disposed(MonoObject *p_obj, Object *p_ptr, bool p_is_finalizer);
+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);
@@ -50,6 +50,18 @@ MonoObject *godot_icall_Object_weakref(Object *p_obj);
Error godot_icall_SignalAwaiter_connect(Object *p_source, MonoString *p_signal, Object *p_target, MonoObject *p_awaiter);
+// DynamicGodotObject
+
+MonoArray *godot_icall_DynamicGodotObject_SetMemberList(Object *p_ptr);
+
+MonoBoolean godot_icall_DynamicGodotObject_InvokeMember(Object *p_ptr, MonoString *p_name, MonoArray *p_args, MonoObject **r_result);
+
+MonoBoolean godot_icall_DynamicGodotObject_GetMember(Object *p_ptr, MonoString *p_name, MonoObject **r_result);
+
+MonoBoolean godot_icall_DynamicGodotObject_SetMember(Object *p_ptr, MonoString *p_name, MonoObject *p_value);
+
+MonoString *godot_icall_Object_ToString(Object *p_ptr);
+
// Register internal calls
void godot_register_object_icalls();
diff --git a/modules/mono/glue/collections_glue.cpp b/modules/mono/glue/collections_glue.cpp
index 0e5747a014..4aef5684fd 100644
--- a/modules/mono/glue/collections_glue.cpp
+++ b/modules/mono/glue/collections_glue.cpp
@@ -73,20 +73,21 @@ int godot_icall_Array_Count(Array *ptr) {
return ptr->size();
}
-void godot_icall_Array_Add(Array *ptr, MonoObject *item) {
+int godot_icall_Array_Add(Array *ptr, MonoObject *item) {
ptr->append(GDMonoMarshal::mono_object_to_variant(item));
+ return ptr->size();
}
void godot_icall_Array_Clear(Array *ptr) {
ptr->clear();
}
-bool godot_icall_Array_Contains(Array *ptr, MonoObject *item) {
+MonoBoolean godot_icall_Array_Contains(Array *ptr, MonoObject *item) {
return ptr->find(GDMonoMarshal::mono_object_to_variant(item)) != -1;
}
void godot_icall_Array_CopyTo(Array *ptr, MonoArray *array, int array_index) {
- int count = ptr->size();
+ unsigned int count = ptr->size();
if (mono_array_length(array) < (array_index + count)) {
MonoException *exc = mono_get_exception_argument("", "Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
@@ -94,7 +95,7 @@ void godot_icall_Array_CopyTo(Array *ptr, MonoArray *array, int array_index) {
return;
}
- for (int i = 0; i < count; i++) {
+ for (unsigned int i = 0; i < count; i++) {
MonoObject *boxed = GDMonoMarshal::variant_to_mono_object(ptr->operator[](i));
mono_array_setref(array, array_index, boxed);
array_index++;
@@ -113,7 +114,7 @@ void godot_icall_Array_Insert(Array *ptr, int index, MonoObject *item) {
ptr->insert(index, GDMonoMarshal::mono_object_to_variant(item));
}
-bool godot_icall_Array_Remove(Array *ptr, MonoObject *item) {
+MonoBoolean godot_icall_Array_Remove(Array *ptr, MonoObject *item) {
int idx = ptr->find(GDMonoMarshal::mono_object_to_variant(item));
if (idx >= 0) {
ptr->remove(idx);
@@ -130,6 +131,10 @@ void godot_icall_Array_RemoveAt(Array *ptr, int index) {
ptr->remove(index);
}
+Error godot_icall_Array_Resize(Array *ptr, int new_size) {
+ return ptr->resize(new_size);
+}
+
void godot_icall_Array_Generic_GetElementTypeInfo(MonoReflectionType *refltype, uint32_t *type_encoding, GDMonoClass **type_class) {
MonoType *elem_type = mono_reflection_type_get_type(refltype);
@@ -138,6 +143,10 @@ void godot_icall_Array_Generic_GetElementTypeInfo(MonoReflectionType *refltype,
*type_class = GDMono::get_singleton()->get_class(type_class_raw);
}
+MonoString *godot_icall_Array_ToString(Array *ptr) {
+ return GDMonoMarshal::mono_string_from_godot(Variant(*ptr).operator String());
+}
+
Dictionary *godot_icall_Dictionary_Ctor() {
return memnew(Dictionary);
}
@@ -204,21 +213,21 @@ void godot_icall_Dictionary_Clear(Dictionary *ptr) {
ptr->clear();
}
-bool godot_icall_Dictionary_Contains(Dictionary *ptr, MonoObject *key, MonoObject *value) {
+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);
}
-bool godot_icall_Dictionary_ContainsKey(Dictionary *ptr, MonoObject *key) {
+MonoBoolean godot_icall_Dictionary_ContainsKey(Dictionary *ptr, MonoObject *key) {
return ptr->has(GDMonoMarshal::mono_object_to_variant(key));
}
-bool godot_icall_Dictionary_RemoveKey(Dictionary *ptr, MonoObject *key) {
+MonoBoolean godot_icall_Dictionary_RemoveKey(Dictionary *ptr, MonoObject *key) {
return ptr->erase(GDMonoMarshal::mono_object_to_variant(key));
}
-bool godot_icall_Dictionary_Remove(Dictionary *ptr, MonoObject *key, MonoObject *value) {
+MonoBoolean godot_icall_Dictionary_Remove(Dictionary *ptr, MonoObject *key, MonoObject *value) {
Variant varKey = GDMonoMarshal::mono_object_to_variant(key);
// no dupes
@@ -231,7 +240,7 @@ bool godot_icall_Dictionary_Remove(Dictionary *ptr, MonoObject *key, MonoObject
return false;
}
-bool godot_icall_Dictionary_TryGetValue(Dictionary *ptr, MonoObject *key, MonoObject **value) {
+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;
@@ -241,7 +250,7 @@ bool godot_icall_Dictionary_TryGetValue(Dictionary *ptr, MonoObject *key, MonoOb
return true;
}
-bool godot_icall_Dictionary_TryGetValue_Generic(Dictionary *ptr, MonoObject *key, MonoObject **value, uint32_t type_encoding, GDMonoClass *type_class) {
+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;
@@ -259,6 +268,10 @@ void godot_icall_Dictionary_Generic_GetValueTypeInfo(MonoReflectionType *refltyp
*type_class = GDMono::get_singleton()->get_class(type_class_raw);
}
+MonoString *godot_icall_Dictionary_ToString(Dictionary *ptr) {
+ return GDMonoMarshal::mono_string_from_godot(Variant(*ptr).operator String());
+}
+
void godot_register_collections_icalls() {
mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Ctor", (void *)godot_icall_Array_Ctor);
mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Dtor", (void *)godot_icall_Array_Dtor);
@@ -274,7 +287,9 @@ void godot_register_collections_icalls() {
mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Insert", (void *)godot_icall_Array_Insert);
mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Remove", (void *)godot_icall_Array_Remove);
mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_RemoveAt", (void *)godot_icall_Array_RemoveAt);
+ mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Resize", (void *)godot_icall_Array_Resize);
mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Generic_GetElementTypeInfo", (void *)godot_icall_Array_Generic_GetElementTypeInfo);
+ mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_ToString", (void *)godot_icall_Array_ToString);
mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Ctor", (void *)godot_icall_Dictionary_Ctor);
mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Dtor", (void *)godot_icall_Dictionary_Dtor);
@@ -293,6 +308,7 @@ void godot_register_collections_icalls() {
mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_TryGetValue", (void *)godot_icall_Dictionary_TryGetValue);
mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_TryGetValue_Generic", (void *)godot_icall_Dictionary_TryGetValue_Generic);
mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Generic_GetValueTypeInfo", (void *)godot_icall_Dictionary_Generic_GetValueTypeInfo);
+ mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_ToString", (void *)godot_icall_Dictionary_ToString);
}
#endif // MONO_GLUE_ENABLED
diff --git a/modules/mono/glue/collections_glue.h b/modules/mono/glue/collections_glue.h
index 52ca98b7f9..54ed42c3b6 100644
--- a/modules/mono/glue/collections_glue.h
+++ b/modules/mono/glue/collections_glue.h
@@ -51,11 +51,11 @@ void godot_icall_Array_SetAt(Array *ptr, int index, MonoObject *value);
int godot_icall_Array_Count(Array *ptr);
-void godot_icall_Array_Add(Array *ptr, MonoObject *item);
+int godot_icall_Array_Add(Array *ptr, MonoObject *item);
void godot_icall_Array_Clear(Array *ptr);
-bool godot_icall_Array_Contains(Array *ptr, MonoObject *item);
+MonoBoolean godot_icall_Array_Contains(Array *ptr, MonoObject *item);
void godot_icall_Array_CopyTo(Array *ptr, MonoArray *array, int array_index);
@@ -63,12 +63,16 @@ int godot_icall_Array_IndexOf(Array *ptr, MonoObject *item);
void godot_icall_Array_Insert(Array *ptr, int index, MonoObject *item);
-bool godot_icall_Array_Remove(Array *ptr, MonoObject *item);
+MonoBoolean godot_icall_Array_Remove(Array *ptr, MonoObject *item);
void godot_icall_Array_RemoveAt(Array *ptr, int index);
+Error godot_icall_Array_Resize(Array *ptr, int new_size);
+
void godot_icall_Array_Generic_GetElementTypeInfo(MonoReflectionType *refltype, uint32_t *type_encoding, GDMonoClass **type_class);
+MonoString *godot_icall_Array_ToString(Array *ptr);
+
// Dictionary
Dictionary *godot_icall_Dictionary_Ctor();
@@ -91,20 +95,22 @@ void godot_icall_Dictionary_Add(Dictionary *ptr, MonoObject *key, MonoObject *va
void godot_icall_Dictionary_Clear(Dictionary *ptr);
-bool godot_icall_Dictionary_Contains(Dictionary *ptr, MonoObject *key, MonoObject *value);
+MonoBoolean godot_icall_Dictionary_Contains(Dictionary *ptr, MonoObject *key, MonoObject *value);
-bool godot_icall_Dictionary_ContainsKey(Dictionary *ptr, MonoObject *key);
+MonoBoolean godot_icall_Dictionary_ContainsKey(Dictionary *ptr, MonoObject *key);
-bool godot_icall_Dictionary_RemoveKey(Dictionary *ptr, MonoObject *key);
+MonoBoolean godot_icall_Dictionary_RemoveKey(Dictionary *ptr, MonoObject *key);
-bool godot_icall_Dictionary_Remove(Dictionary *ptr, MonoObject *key, MonoObject *value);
+MonoBoolean godot_icall_Dictionary_Remove(Dictionary *ptr, MonoObject *key, MonoObject *value);
-bool godot_icall_Dictionary_TryGetValue(Dictionary *ptr, MonoObject *key, MonoObject **value);
+MonoBoolean godot_icall_Dictionary_TryGetValue(Dictionary *ptr, MonoObject *key, MonoObject **value);
-bool godot_icall_Dictionary_TryGetValue_Generic(Dictionary *ptr, MonoObject *key, MonoObject **value, uint32_t type_encoding, GDMonoClass *type_class);
+MonoBoolean godot_icall_Dictionary_TryGetValue_Generic(Dictionary *ptr, MonoObject *key, MonoObject **value, uint32_t type_encoding, GDMonoClass *type_class);
void godot_icall_Dictionary_Generic_GetValueTypeInfo(MonoReflectionType *refltype, uint32_t *type_encoding, GDMonoClass **type_class);
+MonoString *godot_icall_Dictionary_ToString(Dictionary *ptr);
+
// Register internal calls
void godot_register_collections_icalls();
diff --git a/modules/mono/glue/gd_glue.cpp b/modules/mono/glue/gd_glue.cpp
index e49ed876e7..7c30092855 100644
--- a/modules/mono/glue/gd_glue.cpp
+++ b/modules/mono/glue/gd_glue.cpp
@@ -41,18 +41,18 @@
#include "../mono_gd/gd_mono_utils.h"
-MonoObject *godot_icall_GD_bytes2var(MonoArray *p_bytes) {
+MonoObject *godot_icall_GD_bytes2var(MonoArray *p_bytes, MonoBoolean p_allow_objects) {
Variant ret;
PoolByteArray varr = GDMonoMarshal::mono_array_to_PoolByteArray(p_bytes);
PoolByteArray::Read r = varr.read();
- Error err = decode_variant(ret, r.ptr(), varr.size(), NULL);
+ Error err = decode_variant(ret, r.ptr(), varr.size(), NULL, p_allow_objects);
if (err != OK) {
ret = RTR("Not enough bytes for decoding bytes, or invalid format.");
}
return GDMonoMarshal::variant_to_mono_object(ret);
}
-MonoObject *godot_icall_GD_convert(MonoObject *p_what, int p_type) {
+MonoObject *godot_icall_GD_convert(MonoObject *p_what, int32_t p_type) {
Variant what = GDMonoMarshal::mono_object_to_variant(p_what);
const Variant *args[1] = { &what };
Variant::CallError ce;
@@ -65,7 +65,7 @@ int godot_icall_GD_hash(MonoObject *p_var) {
return GDMonoMarshal::mono_object_to_variant(p_var).hash();
}
-MonoObject *godot_icall_GD_instance_from_id(int p_instance_id) {
+MonoObject *godot_icall_GD_instance_from_id(uint64_t p_instance_id) {
return GDMonoUtils::unmanaged_get_managed(ObjectDB::get_instance(p_instance_id));
}
@@ -115,7 +115,29 @@ void godot_icall_GD_printt(MonoArray *p_what) {
print_line(str);
}
-void godot_icall_GD_seed(int p_seed) {
+float godot_icall_GD_randf() {
+ return Math::randf();
+}
+
+uint32_t godot_icall_GD_randi() {
+ return Math::rand();
+}
+
+void godot_icall_GD_randomize() {
+ Math::randomize();
+}
+
+double godot_icall_GD_rand_range(double from, double to) {
+ return Math::random(from, to);
+}
+
+uint32_t godot_icall_GD_rand_seed(uint64_t seed, uint64_t *newSeed) {
+ int ret = Math::rand_from_seed(&seed);
+ *newSeed = seed;
+ return ret;
+}
+
+void godot_icall_GD_seed(uint64_t p_seed) {
Math::seed(p_seed);
}
@@ -153,7 +175,7 @@ MonoObject *godot_icall_GD_str2var(MonoString *p_str) {
return GDMonoMarshal::variant_to_mono_object(ret);
}
-bool godot_icall_GD_type_exists(MonoString *p_type) {
+MonoBoolean godot_icall_GD_type_exists(MonoString *p_type) {
return ClassDB::class_exists(GDMonoMarshal::mono_string_to_godot(p_type));
}
@@ -165,19 +187,19 @@ void godot_icall_GD_pushwarning(MonoString *p_str) {
WARN_PRINTS(GDMonoMarshal::mono_string_to_godot(p_str));
}
-MonoArray *godot_icall_GD_var2bytes(MonoObject *p_var) {
+MonoArray *godot_icall_GD_var2bytes(MonoObject *p_var, MonoBoolean p_full_objects) {
Variant var = GDMonoMarshal::mono_object_to_variant(p_var);
PoolByteArray barr;
int len;
- Error err = encode_variant(var, NULL, len);
+ Error err = encode_variant(var, NULL, len, p_full_objects);
ERR_EXPLAIN("Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID).");
ERR_FAIL_COND_V(err != OK, NULL);
barr.resize(len);
{
PoolByteArray::Write w = barr.write();
- encode_variant(var, w.ptr(), len);
+ encode_variant(var, w.ptr(), len, p_full_objects);
}
return GDMonoMarshal::PoolByteArray_to_mono_array(barr);
@@ -201,6 +223,11 @@ void godot_register_gd_icalls() {
mono_add_internal_call("Godot.GD::godot_icall_GD_printraw", (void *)godot_icall_GD_printraw);
mono_add_internal_call("Godot.GD::godot_icall_GD_prints", (void *)godot_icall_GD_prints);
mono_add_internal_call("Godot.GD::godot_icall_GD_printt", (void *)godot_icall_GD_printt);
+ mono_add_internal_call("Godot.GD::godot_icall_GD_randf", (void *)godot_icall_GD_randf);
+ mono_add_internal_call("Godot.GD::godot_icall_GD_randi", (void *)godot_icall_GD_randi);
+ mono_add_internal_call("Godot.GD::godot_icall_GD_randomize", (void *)godot_icall_GD_randomize);
+ mono_add_internal_call("Godot.GD::godot_icall_GD_rand_range", (void *)godot_icall_GD_rand_range);
+ mono_add_internal_call("Godot.GD::godot_icall_GD_rand_seed", (void *)godot_icall_GD_rand_seed);
mono_add_internal_call("Godot.GD::godot_icall_GD_seed", (void *)godot_icall_GD_seed);
mono_add_internal_call("Godot.GD::godot_icall_GD_str", (void *)godot_icall_GD_str);
mono_add_internal_call("Godot.GD::godot_icall_GD_str2var", (void *)godot_icall_GD_str2var);
diff --git a/modules/mono/glue/gd_glue.h b/modules/mono/glue/gd_glue.h
index 00611fef12..d4e20e2887 100644
--- a/modules/mono/glue/gd_glue.h
+++ b/modules/mono/glue/gd_glue.h
@@ -35,13 +35,13 @@
#include "../mono_gd/gd_mono_marshal.h"
-MonoObject *godot_icall_GD_bytes2var(MonoArray *p_bytes);
+MonoObject *godot_icall_GD_bytes2var(MonoArray *p_bytes, MonoBoolean p_allow_objects);
-MonoObject *godot_icall_GD_convert(MonoObject *p_what, int p_type);
+MonoObject *godot_icall_GD_convert(MonoObject *p_what, int32_t p_type);
int godot_icall_GD_hash(MonoObject *p_var);
-MonoObject *godot_icall_GD_instance_from_id(int p_instance_id);
+MonoObject *godot_icall_GD_instance_from_id(uint64_t p_instance_id);
void godot_icall_GD_print(MonoArray *p_what);
@@ -53,15 +53,25 @@ void godot_icall_GD_prints(MonoArray *p_what);
void godot_icall_GD_printt(MonoArray *p_what);
-void godot_icall_GD_seed(int p_seed);
+float godot_icall_GD_randf();
+
+uint32_t godot_icall_GD_randi();
+
+void godot_icall_GD_randomize();
+
+double godot_icall_GD_rand_range(double from, double to);
+
+uint32_t godot_icall_GD_rand_seed(uint64_t seed, uint64_t *newSeed);
+
+void godot_icall_GD_seed(uint64_t p_seed);
MonoString *godot_icall_GD_str(MonoArray *p_what);
MonoObject *godot_icall_GD_str2var(MonoString *p_str);
-bool godot_icall_GD_type_exists(MonoString *p_type);
+MonoBoolean godot_icall_GD_type_exists(MonoString *p_type);
-MonoArray *godot_icall_GD_var2bytes(MonoObject *p_var);
+MonoArray *godot_icall_GD_var2bytes(MonoObject *p_var, MonoBoolean p_full_objects);
MonoString *godot_icall_GD_var2str(MonoObject *p_var);
diff --git a/modules/mono/glue/glue_header.h b/modules/mono/glue/glue_header.h
index b6e8ac6909..1836130b76 100644
--- a/modules/mono/glue/glue_header.h
+++ b/modules/mono/glue/glue_header.h
@@ -74,4 +74,6 @@ void godot_register_glue_header_icalls() {
} \
Object *m_instance = ci->creation_func();
+#include "arguments_vector.h"
+
#endif // MONO_GLUE_ENABLED
diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp
index 3943c0c7d7..09a1fc6fbc 100644
--- a/modules/mono/godotsharp_dirs.cpp
+++ b/modules/mono/godotsharp_dirs.cpp
@@ -99,7 +99,6 @@ public:
String sln_filepath;
String csproj_filepath;
- String data_mono_bin_dir;
String data_editor_tools_dir;
String data_editor_prebuilt_api_dir;
#endif
@@ -107,6 +106,10 @@ public:
String data_mono_etc_dir;
String data_mono_lib_dir;
+#ifdef WINDOWS_ENABLED
+ String data_mono_bin_dir;
+#endif
+
private:
_GodotSharpDirs() {
res_data_dir = "res://.mono";
@@ -146,10 +149,13 @@ private:
data_editor_prebuilt_api_dir = data_dir_root.plus_file("Api");
String data_mono_root_dir = data_dir_root.plus_file("Mono");
- data_mono_bin_dir = data_mono_root_dir.plus_file("bin");
data_mono_etc_dir = data_mono_root_dir.plus_file("etc");
data_mono_lib_dir = data_mono_root_dir.plus_file("lib");
+#ifdef WINDOWS_ENABLED
+ data_mono_bin_dir = data_mono_root_dir.plus_file("bin");
+#endif
+
#ifdef OSX_ENABLED
if (!DirAccess::exists(data_editor_tools_dir)) {
data_editor_tools_dir = exe_dir.plus_file("../Frameworks/GodotSharp/Tools");
@@ -160,7 +166,6 @@ private:
}
if (!DirAccess::exists(data_mono_root_dir)) {
- data_mono_bin_dir = exe_dir.plus_file("../Frameworks/GodotSharp/Mono/bin");
data_mono_etc_dir = exe_dir.plus_file("../Resources/GodotSharp/Mono/etc");
data_mono_lib_dir = exe_dir.plus_file("../Frameworks/GodotSharp/Mono/lib");
}
@@ -178,6 +183,10 @@ private:
data_mono_etc_dir = data_mono_root_dir.plus_file("etc");
data_mono_lib_dir = data_mono_root_dir.plus_file("lib");
+#ifdef WINDOWS_ENABLED
+ data_mono_bin_dir = data_mono_root_dir.plus_file("bin");
+#endif
+
#ifdef OSX_ENABLED
if (!DirAccess::exists(data_mono_root_dir)) {
data_mono_etc_dir = exe_dir.plus_file("../Resources/GodotSharp/Mono/etc");
@@ -251,10 +260,6 @@ String get_project_csproj_path() {
return _GodotSharpDirs::get_singleton().csproj_filepath;
}
-String get_data_mono_bin_dir() {
- return _GodotSharpDirs::get_singleton().data_mono_bin_dir;
-}
-
String get_data_editor_tools_dir() {
return _GodotSharpDirs::get_singleton().data_editor_tools_dir;
}
@@ -272,4 +277,10 @@ String get_data_mono_lib_dir() {
return _GodotSharpDirs::get_singleton().data_mono_lib_dir;
}
+#ifdef WINDOWS_ENABLED
+String get_data_mono_bin_dir() {
+ return _GodotSharpDirs::get_singleton().data_mono_bin_dir;
+}
+#endif
+
} // namespace GodotSharpDirs
diff --git a/modules/mono/godotsharp_dirs.h b/modules/mono/godotsharp_dirs.h
index a038e6486c..556df959e2 100644
--- a/modules/mono/godotsharp_dirs.h
+++ b/modules/mono/godotsharp_dirs.h
@@ -53,7 +53,6 @@ String get_build_logs_dir();
String get_project_sln_path();
String get_project_csproj_path();
-String get_data_mono_bin_dir();
String get_data_editor_tools_dir();
String get_data_editor_prebuilt_api_dir();
#endif
@@ -61,6 +60,10 @@ String get_data_editor_prebuilt_api_dir();
String get_data_mono_etc_dir();
String get_data_mono_lib_dir();
+#ifdef WINDOWS_ENABLED
+String get_data_mono_bin_dir();
+#endif
+
} // namespace GodotSharpDirs
#endif // GODOTSHARP_DIRS_H
diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp
index ca1ff9f9ff..19e49d29f9 100644
--- a/modules/mono/mono_gd/gd_mono.cpp
+++ b/modules/mono/mono_gd/gd_mono.cpp
@@ -30,10 +30,12 @@
#include "gd_mono.h"
+#include <mono/metadata/environment.h>
#include <mono/metadata/exception.h>
#include <mono/metadata/mono-config.h>
#include <mono/metadata/mono-debug.h>
#include <mono/metadata/mono-gc.h>
+#include <mono/metadata/profiler.h>
#include "core/os/dir_access.h"
#include "core/os/file_access.h"
@@ -54,6 +56,10 @@
#include "main/main.h"
#endif
+#define OUT_OF_SYNC_ERR_MESSAGE(m_assembly_name) "The assembly '" m_assembly_name "' is out of sync. " \
+ "This error is expected if you just upgraded to a newer Godot version. " \
+ "Building the project will update the assembly to the correct version."
+
GDMono *GDMono::singleton = NULL;
namespace {
@@ -79,6 +85,14 @@ void setup_runtime_main_args() {
mono_runtime_set_main_args(main_args.size(), main_args.ptrw());
}
+void gdmono_profiler_init() {
+ String profiler_args = GLOBAL_DEF("mono/profiler/args", "log:calls,alloc,sample,output=output.mlpd");
+ bool profiler_enabled = GLOBAL_DEF("mono/profiler/enabled", false);
+ if (profiler_enabled) {
+ mono_profiler_load(profiler_args.utf8());
+ }
+}
+
#ifdef DEBUG_ENABLED
static bool _wait_for_debugger_msecs(uint32_t p_msecs) {
@@ -91,7 +105,7 @@ static bool _wait_for_debugger_msecs(uint32_t p_msecs) {
OS::get_singleton()->delay_usec((p_msecs < 25 ? p_msecs : 25) * 1000);
- int tdiff = OS::get_singleton()->get_ticks_msec() - last_tick;
+ uint32_t tdiff = OS::get_singleton()->get_ticks_msec() - last_tick;
if (tdiff > p_msecs) {
p_msecs = 0;
@@ -157,7 +171,17 @@ void GDMono::add_mono_shared_libs_dir_to_path() {
path_value += ';';
String bundled_bin_dir = GodotSharpDirs::get_data_mono_bin_dir();
- path_value += DirAccess::exists(bundled_bin_dir) ? bundled_bin_dir : mono_reg_info.bin_dir;
+#ifdef TOOLS_ENABLED
+ if (DirAccess::exists(bundled_bin_dir)) {
+ path_value += bundled_bin_dir;
+ } else {
+ path_value += mono_reg_info.bin_dir;
+ }
+#else
+ if (DirAccess::exists(bundled_bin_dir))
+ path_value += bundled_bin_dir;
+#endif // TOOLS_ENABLED
+
#else
path_value += ':';
@@ -167,10 +191,10 @@ void GDMono::add_mono_shared_libs_dir_to_path() {
} else {
// TODO: Do we need to add the lib dir when using the system installed Mono on Unix platforms?
}
-#endif
+#endif // WINDOWS_ENABLED
OS::get_singleton()->set_environment(path_var, path_value);
-#endif
+#endif // WINDOWS_ENABLED || UNIX_ENABLED
}
void GDMono::initialize() {
@@ -189,7 +213,7 @@ void GDMono::initialize() {
String config_dir;
#ifdef TOOLS_ENABLED
-#ifdef WINDOWS_ENABLED
+#if defined(WINDOWS_ENABLED)
mono_reg_info = MonoRegUtils::find_mono();
if (mono_reg_info.assembly_dir.length() && DirAccess::exists(mono_reg_info.assembly_dir)) {
@@ -199,7 +223,7 @@ void GDMono::initialize() {
if (mono_reg_info.config_dir.length() && DirAccess::exists(mono_reg_info.config_dir)) {
config_dir = mono_reg_info.config_dir;
}
-#elif OSX_ENABLED
+#elif defined(OSX_ENABLED)
const char *c_assembly_rootdir = mono_assembly_getrootdir();
const char *c_config_dir = mono_get_config_dir();
@@ -231,11 +255,21 @@ void GDMono::initialize() {
assembly_rootdir = bundled_assembly_rootdir;
config_dir = bundled_config_dir;
}
+
+#ifdef WINDOWS_ENABLED
+ if (assembly_rootdir.empty() || config_dir.empty()) {
+ // Assertion: if they are not set, then they weren't found in the registry
+ CRASH_COND(mono_reg_info.assembly_dir.length() > 0 || mono_reg_info.config_dir.length() > 0);
+
+ ERR_PRINT("Cannot find Mono in the registry");
+ }
+#endif // WINDOWS_ENABLED
+
#else
// These are always the directories in export templates
assembly_rootdir = bundled_assembly_rootdir;
config_dir = bundled_config_dir;
-#endif
+#endif // TOOLS_ENABLED
// Leak if we call mono_set_dirs more than once
mono_set_dirs(assembly_rootdir.length() ? assembly_rootdir.utf8().get_data() : NULL,
@@ -245,6 +279,8 @@ void GDMono::initialize() {
GDMonoAssembly::initialize();
+ gdmono_profiler_init();
+
#ifdef DEBUG_ENABLED
gdmono_debug_init();
#endif
@@ -253,6 +289,29 @@ void GDMono::initialize() {
mono_install_unhandled_exception_hook(&unhandled_exception_hook, NULL);
+#ifndef TOOLS_ENABLED
+ if (!DirAccess::exists("res://.mono")) {
+ // 'res://.mono/' is missing so there is nothing to load. We don't need to initialize mono, but
+ // we still do so unless mscorlib is missing (which is the case for projects that don't use C#).
+
+ String mscorlib_fname("mscorlib.dll");
+
+ Vector<String> search_dirs;
+ GDMonoAssembly::fill_search_dirs(search_dirs);
+
+ bool found = false;
+ for (int i = 0; i < search_dirs.size(); i++) {
+ if (FileAccess::exists(search_dirs[i].plus_file(mscorlib_fname))) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ return; // mscorlib is missing, do not initialize mono
+ }
+#endif
+
root_domain = mono_jit_init_version("GodotEngine.RootDomain", "v4.0.30319");
ERR_EXPLAIN("Mono: Failed to initialize runtime");
@@ -310,15 +369,15 @@ void GDMono::initialize() {
// metadata, so we invalidate the version in the metadata and unload the script domain.
if (core_api_assembly_out_of_sync) {
- ERR_PRINT("The loaded Core API assembly is out of sync");
+ ERR_PRINT(OUT_OF_SYNC_ERR_MESSAGE(CORE_API_ASSEMBLY_NAME));
metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true);
} else if (!GDMonoUtils::mono_cache.godot_api_cache_updated) {
- ERR_PRINT("The loaded Core API assembly is in sync, but the cache update failed");
+ ERR_PRINT("The loaded assembly '" CORE_API_ASSEMBLY_NAME "' is in sync, but the cache update failed");
metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true);
}
if (editor_api_assembly_out_of_sync) {
- ERR_PRINT("The loaded Editor API assembly is out of sync");
+ ERR_PRINT(OUT_OF_SYNC_ERR_MESSAGE(EDITOR_API_ASSEMBLY_NAME));
metadata_set_api_assembly_invalidated(APIAssembly::API_EDITOR, true);
}
@@ -529,6 +588,8 @@ bool GDMono::_load_core_api_assembly() {
CS_GLUE_VERSION != api_assembly_ver.cs_glue_version;
if (!core_api_assembly_out_of_sync) {
GDMonoUtils::update_godot_api_cache();
+
+ _install_trace_listener();
}
#else
GDMonoUtils::update_godot_api_cache();
@@ -597,8 +658,6 @@ bool GDMono::_load_project_assembly() {
if (success) {
mono_assembly_set_main(project_assembly->get_assembly());
-
- CSharpLanguage::get_singleton()->project_assembly_loaded();
} else {
if (OS::get_singleton()->is_stdout_verbose())
print_error("Mono: Failed to load project assembly");
@@ -632,6 +691,23 @@ bool GDMono::_load_api_assemblies() {
return true;
}
+void GDMono::_install_trace_listener() {
+
+#ifdef DEBUG_ENABLED
+ // Install the trace listener now before the project assembly is loaded
+ typedef void (*DebuggingUtils_InstallTraceListener)(MonoObject **);
+ MonoException *exc = NULL;
+ GDMonoClass *debug_utils = core_api_assembly->get_class(BINDINGS_NAMESPACE, "DebuggingUtils");
+ DebuggingUtils_InstallTraceListener install_func =
+ (DebuggingUtils_InstallTraceListener)debug_utils->get_method_thunk("InstallTraceListener");
+ install_func((MonoObject **)&exc);
+ if (exc) {
+ ERR_PRINT("Failed to install System.Diagnostics.Trace listener");
+ GDMonoUtils::debug_print_unhandled_exception(exc);
+ }
+#endif
+}
+
#ifdef TOOLS_ENABLED
String GDMono::_get_api_assembly_metadata_path() {
@@ -721,8 +797,6 @@ Error GDMono::_unload_scripts_domain() {
if (mono_domain_get() != root_domain)
mono_domain_set(root_domain, true);
- mono_gc_collect(mono_gc_max_generation());
-
finalizing_scripts_domain = true;
if (!mono_domain_finalize(scripts_domain, 2000)) {
@@ -790,6 +864,8 @@ Error GDMono::reload_scripts_domain() {
}
}
+ CSharpLanguage::get_singleton()->_on_scripts_domain_unloaded();
+
Error err = _load_scripts_domain();
if (err != OK) {
ERR_PRINT("Mono: Failed to load scripts domain");
@@ -809,7 +885,7 @@ Error GDMono::reload_scripts_domain() {
// metadata, so we invalidate the version in the metadata and unload the script domain.
if (core_api_assembly_out_of_sync) {
- ERR_PRINT("The loaded Core API assembly is out of sync");
+ ERR_PRINT(OUT_OF_SYNC_ERR_MESSAGE(CORE_API_ASSEMBLY_NAME));
metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true);
} else if (!GDMonoUtils::mono_cache.godot_api_cache_updated) {
ERR_PRINT("The loaded Core API assembly is in sync, but the cache update failed");
@@ -817,11 +893,11 @@ Error GDMono::reload_scripts_domain() {
}
if (editor_api_assembly_out_of_sync) {
- ERR_PRINT("The loaded Editor API assembly is out of sync");
+ ERR_PRINT(OUT_OF_SYNC_ERR_MESSAGE(EDITOR_API_ASSEMBLY_NAME));
metadata_set_api_assembly_invalidated(APIAssembly::API_EDITOR, true);
}
- Error err = _unload_scripts_domain();
+ err = _unload_scripts_domain();
if (err != OK) {
WARN_PRINT("Mono: Failed to unload scripts domain");
}
@@ -858,14 +934,20 @@ Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) {
if (mono_domain_get() == p_domain)
mono_domain_set(root_domain, true);
- mono_gc_collect(mono_gc_max_generation());
if (!mono_domain_finalize(p_domain, 2000)) {
ERR_PRINT("Mono: Domain finalization timeout");
}
+
mono_gc_collect(mono_gc_max_generation());
_domain_assemblies_cleanup(mono_domain_get_id(p_domain));
+#ifdef TOOLS_ENABLED
+ if (p_domain == tools_domain) {
+ editor_tools_assembly = NULL;
+ }
+#endif
+
MonoException *exc = NULL;
mono_domain_try_unload(p_domain, (MonoObject **)&exc);
@@ -925,7 +1007,9 @@ void GDMono::unhandled_exception_hook(MonoObject *p_exc, void *) {
if (ScriptDebugger::get_singleton())
ScriptDebugger::get_singleton()->idle_poll();
#endif
- abort();
+
+ exit(mono_environment_exitcode_get());
+
GD_UNREACHABLE();
}
@@ -967,11 +1051,19 @@ GDMono::~GDMono() {
if (is_runtime_initialized()) {
- if (scripts_domain) {
+#ifdef TOOLS_ENABLED
+ if (tools_domain) {
+ Error err = finalize_and_unload_domain(tools_domain);
+ if (err != OK) {
+ ERR_PRINT("Mono: Failed to unload tools domain");
+ }
+ }
+#endif
+ if (scripts_domain) {
Error err = _unload_scripts_domain();
if (err != OK) {
- WARN_PRINT("Mono: Failed to unload scripts domain");
+ ERR_PRINT("Mono: Failed to unload scripts domain");
}
}
@@ -992,6 +1084,8 @@ GDMono::~GDMono() {
mono_jit_cleanup(root_domain);
+ print_verbose("Mono: Finalized");
+
runtime_initialized = false;
}
diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h
index 785b65ce13..216c96a612 100644
--- a/modules/mono/mono_gd/gd_mono.h
+++ b/modules/mono/mono_gd/gd_mono.h
@@ -125,6 +125,8 @@ class GDMono {
String _get_api_assembly_metadata_path();
#endif
+ void _install_trace_listener();
+
void _register_internal_calls();
Error _load_scripts_domain();
diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp
index 85273bfdb4..8fec28b186 100644
--- a/modules/mono/mono_gd/gd_mono_assembly.cpp
+++ b/modules/mono/mono_gd/gd_mono_assembly.cpp
@@ -50,7 +50,7 @@ void GDMonoAssembly::fill_search_dirs(Vector<String> &r_search_dirs, const Strin
const char *rootdir = mono_assembly_getrootdir();
if (rootdir) {
- String framework_dir = String(rootdir).plus_file("mono").plus_file("4.5");
+ String framework_dir = String::utf8(rootdir).plus_file("mono").plus_file("4.5");
r_search_dirs.push_back(framework_dir);
r_search_dirs.push_back(framework_dir.plus_file("Facades"));
}
@@ -277,6 +277,7 @@ Error GDMonoAssembly::load(bool p_refonly) {
ERR_FAIL_NULL_V(image, ERR_FILE_CANT_OPEN);
#ifdef DEBUG_ENABLED
+ Vector<uint8_t> pdb_data;
String pdb_path(path + ".pdb");
if (!FileAccess::exists(pdb_path)) {
@@ -286,8 +287,9 @@ Error GDMonoAssembly::load(bool p_refonly) {
goto no_pdb;
}
- pdb_data.clear();
pdb_data = FileAccess::get_file_as_array(pdb_path);
+
+ // mono_debug_close_image doesn't seem to be needed
mono_debug_open_image_from_memory(image, &pdb_data[0], pdb_data.size());
no_pdb:
@@ -306,6 +308,9 @@ no_pdb:
ERR_FAIL_COND_V(status != MONO_IMAGE_OK || assembly == NULL, ERR_FILE_CANT_OPEN);
+ // 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;
@@ -321,8 +326,6 @@ Error GDMonoAssembly::wrapper_for_image(MonoImage *p_image) {
image = p_image;
- mono_image_addref(image);
-
loaded = true;
return OK;
@@ -332,13 +335,6 @@ void GDMonoAssembly::unload() {
ERR_FAIL_COND(!loaded);
-#ifdef DEBUG_ENABLED
- if (pdb_data.size()) {
- mono_debug_close_image(image);
- pdb_data.clear();
- }
-#endif
-
for (Map<MonoClass *, GDMonoClass *>::Element *E = cached_raw.front(); E; E = E->next()) {
memdelete(E->value());
}
@@ -346,8 +342,6 @@ void GDMonoAssembly::unload() {
cached_classes.clear();
cached_raw.clear();
- mono_image_close(image);
-
assembly = NULL;
image = NULL;
loaded = false;
diff --git a/modules/mono/mono_gd/gd_mono_assembly.h b/modules/mono/mono_gd/gd_mono_assembly.h
index 8f47ee26f8..32432af37d 100644
--- a/modules/mono/mono_gd/gd_mono_assembly.h
+++ b/modules/mono/mono_gd/gd_mono_assembly.h
@@ -84,10 +84,6 @@ class GDMonoAssembly {
bool gdobject_class_cache_updated;
Map<StringName, GDMonoClass *> gdobject_class_cache;
-#ifdef DEBUG_ENABLED
- Vector<uint8_t> pdb_data;
-#endif
-
static bool no_search;
static bool in_preload;
static Vector<String> search_dirs;
diff --git a/modules/mono/mono_gd/gd_mono_class.cpp b/modules/mono/mono_gd/gd_mono_class.cpp
index bf2a84c708..4342f46109 100644
--- a/modules/mono/mono_gd/gd_mono_class.cpp
+++ b/modules/mono/mono_gd/gd_mono_class.cpp
@@ -55,12 +55,21 @@ String GDMonoClass::get_full_name() const {
}
MonoType *GDMonoClass::get_mono_type() {
- // Care, you cannot compare MonoType pointers
+ // Careful, you cannot compare two MonoType*.
+ // There is mono_metadata_type_equal, how is this different from comparing two MonoClass*?
return get_mono_type(mono_class);
}
-bool GDMonoClass::is_assignable_from(GDMonoClass *p_from) const {
+uint32_t GDMonoClass::get_flags() const {
+ return mono_class_get_flags(mono_class);
+}
+
+bool GDMonoClass::is_static() const {
+ uint32_t static_class_flags = MONO_TYPE_ATTR_ABSTRACT | MONO_TYPE_ATTR_SEALED;
+ return (get_flags() & static_class_flags) == static_class_flags;
+}
+bool GDMonoClass::is_assignable_from(GDMonoClass *p_from) const {
return mono_class_is_assignable_from(mono_class, p_from->mono_class);
}
@@ -252,6 +261,11 @@ bool GDMonoClass::has_fetched_method_unknown_params(const StringName &p_name) {
return get_fetched_method_unknown_params(p_name) != NULL;
}
+bool GDMonoClass::implements_interface(GDMonoClass *p_interface) {
+
+ return mono_class_implements_interface(mono_class, p_interface->get_mono_ptr());
+}
+
GDMonoMethod *GDMonoClass::get_method(const StringName &p_name, int p_params_count) {
MethodKey key = MethodKey(p_name, p_params_count);
diff --git a/modules/mono/mono_gd/gd_mono_class.h b/modules/mono/mono_gd/gd_mono_class.h
index 08718ec1a4..249422b844 100644
--- a/modules/mono/mono_gd/gd_mono_class.h
+++ b/modules/mono/mono_gd/gd_mono_class.h
@@ -109,6 +109,9 @@ public:
String get_full_name() const;
MonoType *get_mono_type();
+ uint32_t get_flags() const;
+ bool is_static() const;
+
bool is_assignable_from(GDMonoClass *p_from) const;
_FORCE_INLINE_ StringName get_namespace() const { return namespace_name; }
@@ -132,6 +135,8 @@ public:
void fetch_attributes();
void fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base);
+ bool implements_interface(GDMonoClass *p_interface);
+
GDMonoMethod *get_method(const StringName &p_name, int p_params_count = 0);
GDMonoMethod *get_method(MonoMethod *p_raw_method);
GDMonoMethod *get_method(MonoMethod *p_raw_method, const StringName &p_name);
diff --git a/modules/mono/mono_gd/gd_mono_field.cpp b/modules/mono/mono_gd/gd_mono_field.cpp
index 48fa380456..9779797d1a 100644
--- a/modules/mono/mono_gd/gd_mono_field.cpp
+++ b/modules/mono/mono_gd/gd_mono_field.cpp
@@ -49,7 +49,7 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
#define SET_FROM_ARRAY(m_type) \
{ \
MonoArray *managed = GDMonoMarshal::m_type##_to_mono_array(p_value.operator ::m_type()); \
- mono_field_set_value(p_object, mono_field, &managed); \
+ mono_field_set_value(p_object, mono_field, managed); \
}
switch (type.type_encoding) {
@@ -313,6 +313,18 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
break;
}
+ if (type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) {
+ MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary));
+ mono_field_set_value(p_object, mono_field, managed);
+ break;
+ }
+
+ if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
+ MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array));
+ mono_field_set_value(p_object, mono_field, managed);
+ break;
+ }
+
ERR_EXPLAIN(String() + "Attempted to set the value of a field of unmarshallable type: " + type_class->get_name());
ERR_FAIL();
} break;
@@ -422,8 +434,8 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
MonoException *exc = NULL;
- GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType);
- MonoBoolean is_dict = invoke_method_thunk(type_is_dict, (MonoObject *)reftype, (MonoObject **)&exc);
+ GDMonoUtils::TypeIsGenericDictionary type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary);
+ MonoBoolean is_dict = invoke_method_thunk(type_is_dict, reftype, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
if (is_dict) {
@@ -434,8 +446,8 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
exc = NULL;
- GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType);
- MonoBoolean is_array = invoke_method_thunk(type_is_array, (MonoObject *)reftype, (MonoObject **)&exc);
+ GDMonoUtils::TypeIsGenericArray type_is_array = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray);
+ MonoBoolean is_array = invoke_method_thunk(type_is_array, reftype, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
if (is_array) {
@@ -443,6 +455,18 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
mono_field_set_value(p_object, mono_field, managed);
break;
}
+
+ if (type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) {
+ MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary));
+ mono_field_set_value(p_object, mono_field, managed);
+ break;
+ }
+
+ if (type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
+ MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array));
+ mono_field_set_value(p_object, mono_field, managed);
+ break;
+ }
} break;
default: {
diff --git a/modules/mono/mono_gd/gd_mono_field.h b/modules/mono/mono_gd/gd_mono_field.h
index a3244101d8..e348583370 100644
--- a/modules/mono/mono_gd/gd_mono_field.h
+++ b/modules/mono/mono_gd/gd_mono_field.h
@@ -32,8 +32,8 @@
#define GDMONOFIELD_H
#include "gd_mono.h"
-#include "gd_mono_class_member.h"
#include "gd_mono_header.h"
+#include "i_mono_class_member.h"
class GDMonoField : public IMonoClassMember {
diff --git a/modules/mono/mono_gd/gd_mono_header.h b/modules/mono/mono_gd/gd_mono_header.h
index dd8c047386..d7962eac8b 100644
--- a/modules/mono/mono_gd/gd_mono_header.h
+++ b/modules/mono/mono_gd/gd_mono_header.h
@@ -35,24 +35,12 @@
class GDMonoAssembly;
class GDMonoClass;
-class IMonoClassMember;
class GDMonoField;
-class GDMonoProperty;
class GDMonoMethod;
+class GDMonoProperty;
-struct ManagedType {
- int type_encoding;
- GDMonoClass *type_class;
-
- ManagedType() :
- type_encoding(0),
- type_class(NULL) {
- }
+class IMonoClassMember;
- ManagedType(int p_type_encoding, GDMonoClass *p_type_class) :
- type_encoding(p_type_encoding),
- type_class(p_type_class) {
- }
-};
+#include "managed_type.h"
#endif // GD_MONO_HEADER_H
diff --git a/modules/mono/mono_gd/gd_mono_internals.cpp b/modules/mono/mono_gd/gd_mono_internals.cpp
index 0daa394e63..63bcfe053c 100644
--- a/modules/mono/mono_gd/gd_mono_internals.cpp
+++ b/modules/mono/mono_gd/gd_mono_internals.cpp
@@ -34,6 +34,8 @@
#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 <mono/metadata/exception.h>
@@ -55,10 +57,48 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {
CRASH_COND(!klass);
- Ref<MonoGCHandle> gchandle = ref ? MonoGCHandle::create_weak(managed) :
- MonoGCHandle::create_strong(managed);
+ GDMonoClass *native = GDMonoUtils::get_class_native_base(klass);
- Ref<CSharpScript> script = CSharpScript::create_for_managed_type(klass);
+ CRASH_COND(native == NULL);
+
+ if (native == klass) {
+ // If it's just a wrapper Godot class and not a custom inheriting class, then attach a
+ // script binding instead. One of the advantages of this is that if a script is attached
+ // later and it's not a C# script, then the managed object won't have to be disposed.
+ // Another reason for doing this is that this instance could outlive CSharpLanguage, which would
+ // be problematic when using a script. See: https://github.com/godotengine/godot/issues/25621
+
+ CSharpScriptBinding script_binding;
+
+ script_binding.inited = true;
+ script_binding.type_name = NATIVE_GDMONOCLASS_NAME(klass);
+ script_binding.wrapper_class = klass;
+ script_binding.gchandle = MonoGCHandle::create_strong(managed);
+
+ Reference *kref = Object::cast_to<Reference>(unmanaged);
+ if (kref) {
+ // Unsafe refcount increment. The managed instance also counts as a reference.
+ // This way if the unmanaged world has no references to our owner
+ // but the managed instance is alive, the refcount will be 1 instead of 0.
+ // See: godot_icall_Reference_Dtor(MonoObject *p_obj, Object *p_ptr)
+
+ kref->reference();
+ }
+
+ // The object was just created, no script instance binding should have been attached
+ CRASH_COND(unmanaged->has_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index()));
+
+ void *data = (void *)CSharpLanguage::get_singleton()->insert_script_binding(unmanaged, script_binding);
+
+ // Should be thread safe because the object was just created and nothing else should be referencing it
+ unmanaged->set_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index(), data);
+
+ return;
+ }
+
+ Ref<MonoGCHandle> gchandle = ref ? MonoGCHandle::create_weak(managed) : MonoGCHandle::create_strong(managed);
+
+ Ref<CSharpScript> script = CSharpScript::create_for_managed_type(klass, native);
CRASH_COND(script.is_null());
@@ -71,7 +111,8 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {
void unhandled_exception(MonoException *p_exc) {
mono_unhandled_exception((MonoObject *)p_exc); // prints the exception as well
- abort();
+ // Too bad 'mono_invoke_unhandled_exception_hook' is not exposed to embedders
+ GDMono::unhandled_exception_hook((MonoObject *)p_exc, NULL);
GD_UNREACHABLE();
}
diff --git a/modules/mono/mono_gd/gd_mono_log.cpp b/modules/mono/mono_gd/gd_mono_log.cpp
index ec89df1959..a6e04e561d 100644
--- a/modules/mono/mono_gd/gd_mono_log.cpp
+++ b/modules/mono/mono_gd/gd_mono_log.cpp
@@ -37,6 +37,7 @@
#include "core/os/os.h"
#include "../godotsharp_dirs.h"
+#include "../utils/string_utils.h"
static int log_level_get_id(const char *p_log_level) {
@@ -72,8 +73,11 @@ static void mono_log_callback(const char *log_domain, const char *log_level, con
if (fatal) {
ERR_PRINTS("Mono: FATAL ERROR, ABORTING! Logfile: " + GDMonoLog::get_singleton()->get_log_file_path() + "\n");
- // If we were to abort without flushing, the log wouldn't get written.
+ // Make sure to flush before aborting
f->flush();
+ f->close();
+ memdelete(f);
+
abort();
}
}
@@ -93,17 +97,9 @@ bool GDMonoLog::_try_create_logs_dir(const String &p_logs_dir) {
return true;
}
-void GDMonoLog::_open_log_file(const String &p_file_path) {
-
- log_file = FileAccess::open(p_file_path, FileAccess::WRITE);
-
- ERR_EXPLAIN("Failed to create log file");
- ERR_FAIL_COND(!log_file);
-}
-
void GDMonoLog::_delete_old_log_files(const String &p_logs_dir) {
- static const uint64_t MAX_SECS = 5 * 86400;
+ static const uint64_t MAX_SECS = 5 * 86400; // 5 days
DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
ERR_FAIL_COND(!da);
@@ -120,10 +116,9 @@ void GDMonoLog::_delete_old_log_files(const String &p_logs_dir) {
if (!current.ends_with(".txt"))
continue;
- String name = current.get_basename();
- uint64_t unixtime = (uint64_t)name.to_int64();
+ uint64_t modified_time = FileAccess::get_modified_time(da->get_current_dir().plus_file(current));
- if (OS::get_singleton()->get_unix_time() - unixtime > MAX_SECS) {
+ if (OS::get_singleton()->get_unix_time() - modified_time > MAX_SECS) {
da->remove(current);
}
}
@@ -133,26 +128,47 @@ void GDMonoLog::_delete_old_log_files(const String &p_logs_dir) {
void GDMonoLog::initialize() {
+ CharString log_level = OS::get_singleton()->get_environment("GODOT_MONO_LOG_LEVEL").utf8();
+
+ if (log_level.length() != 0 && log_level_get_id(log_level.get_data()) == -1) {
+ ERR_PRINTS(String() + "Mono: Ignoring invalid log level (GODOT_MONO_LOG_LEVEL): " + log_level.get_data());
+ log_level = CharString();
+ }
+
+ if (log_level.length() == 0) {
#ifdef DEBUG_ENABLED
- const char *log_level = "debug";
+ log_level = String("info").utf8();
#else
- const char *log_level = "warning";
+ log_level = String("warning").utf8();
#endif
+ }
String logs_dir = GodotSharpDirs::get_mono_logs_dir();
if (_try_create_logs_dir(logs_dir)) {
_delete_old_log_files(logs_dir);
- log_file_path = logs_dir.plus_file(String::num_int64(OS::get_singleton()->get_unix_time()) + ".txt");
- _open_log_file(log_file_path);
+ OS::Date date_now = OS::get_singleton()->get_date();
+ OS::Time time_now = OS::get_singleton()->get_time();
+ int pid = OS::get_singleton()->get_process_id();
+
+ String log_file_name = str_format("%d_%02d_%02d %02d.%02d.%02d (%d).txt",
+ date_now.year, date_now.month, date_now.day,
+ time_now.hour, time_now.min, time_now.sec, pid);
+
+ log_file_path = logs_dir.plus_file(log_file_name);
+
+ log_file = FileAccess::open(log_file_path, FileAccess::WRITE);
+ if (!log_file) {
+ ERR_PRINT("Mono: Cannot create log file");
+ }
}
- mono_trace_set_level_string(log_level);
- log_level_id = log_level_get_id(log_level);
+ mono_trace_set_level_string(log_level.get_data());
+ log_level_id = log_level_get_id(log_level.get_data());
if (log_file) {
- print_verbose("Mono: Logfile is " + log_file_path);
+ OS::get_singleton()->print("Mono: Logfile is: %s\n", log_file_path.utf8().get_data());
mono_trace_set_log_handler(mono_log_callback, this);
} else {
OS::get_singleton()->printerr("Mono: No log file, using default log handler\n");
diff --git a/modules/mono/mono_gd/gd_mono_log.h b/modules/mono/mono_gd/gd_mono_log.h
index a477e5edee..91d0557aa3 100644
--- a/modules/mono/mono_gd/gd_mono_log.h
+++ b/modules/mono/mono_gd/gd_mono_log.h
@@ -41,7 +41,6 @@ class GDMonoLog {
String log_file_path;
bool _try_create_logs_dir(const String &p_logs_dir);
- void _open_log_file(const String &p_file_path);
void _delete_old_log_files(const String &p_logs_dir);
static GDMonoLog *singleton;
diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp
index 74395de41c..d586b73cf9 100644
--- a/modules/mono/mono_gd/gd_mono_marshal.cpp
+++ b/modules/mono/mono_gd/gd_mono_marshal.cpp
@@ -35,7 +35,7 @@
namespace GDMonoMarshal {
-Variant::Type managed_to_variant_type(const ManagedType &p_type) {
+Variant::Type managed_to_variant_type(const ManagedType &p_type, ExportInfo *r_export_info) {
switch (p_type.type_encoding) {
case MONO_TYPE_BOOLEAN:
return Variant::BOOL;
@@ -68,39 +68,39 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
} break;
case MONO_TYPE_VALUETYPE: {
- GDMonoClass *tclass = p_type.type_class;
+ GDMonoClass *vtclass = p_type.type_class;
- if (tclass == CACHED_CLASS(Vector2))
+ if (vtclass == CACHED_CLASS(Vector2))
return Variant::VECTOR2;
- if (tclass == CACHED_CLASS(Rect2))
+ if (vtclass == CACHED_CLASS(Rect2))
return Variant::RECT2;
- if (tclass == CACHED_CLASS(Transform2D))
+ if (vtclass == CACHED_CLASS(Transform2D))
return Variant::TRANSFORM2D;
- if (tclass == CACHED_CLASS(Vector3))
+ if (vtclass == CACHED_CLASS(Vector3))
return Variant::VECTOR3;
- if (tclass == CACHED_CLASS(Basis))
+ if (vtclass == CACHED_CLASS(Basis))
return Variant::BASIS;
- if (tclass == CACHED_CLASS(Quat))
+ if (vtclass == CACHED_CLASS(Quat))
return Variant::QUAT;
- if (tclass == CACHED_CLASS(Transform))
+ if (vtclass == CACHED_CLASS(Transform))
return Variant::TRANSFORM;
- if (tclass == CACHED_CLASS(AABB))
+ if (vtclass == CACHED_CLASS(AABB))
return Variant::AABB;
- if (tclass == CACHED_CLASS(Color))
+ if (vtclass == CACHED_CLASS(Color))
return Variant::COLOR;
- if (tclass == CACHED_CLASS(Plane))
+ if (vtclass == CACHED_CLASS(Plane))
return Variant::PLANE;
- if (mono_class_is_enum(tclass->get_mono_ptr()))
+ if (mono_class_is_enum(vtclass->get_mono_ptr()))
return Variant::INT;
} break;
@@ -156,26 +156,74 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
if (CACHED_CLASS(Array) == type_class) {
return Variant::ARRAY;
}
+
+ if (type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) {
+ return Variant::DICTIONARY;
+ }
+
+ if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
+ return Variant::ARRAY;
+ }
} break;
case MONO_TYPE_GENERICINST: {
MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_type.type_class->get_mono_type());
MonoException *exc = NULL;
- GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType);
- MonoBoolean is_dict = invoke_method_thunk(type_is_dict, (MonoObject *)reftype, (MonoObject **)&exc);
+ GDMonoUtils::TypeIsGenericDictionary type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary);
+ MonoBoolean is_dict = invoke_method_thunk(type_is_dict, reftype, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
if (is_dict) {
+ if (r_export_info) {
+ MonoReflectionType *key_reftype;
+ MonoReflectionType *value_reftype;
+
+ exc = NULL;
+ invoke_method_thunk(CACHED_METHOD_THUNK(MarshalUtils, DictionaryGetKeyValueTypes),
+ reftype, &key_reftype, &value_reftype, &exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+
+ ManagedType key_type = ManagedType::from_reftype(key_reftype);
+ ManagedType value_type = ManagedType::from_reftype(value_reftype);
+
+ r_export_info->dictionary.key_type = managed_to_variant_type(key_type);
+ r_export_info->dictionary.key_native_name = NATIVE_GDMONOCLASS_NAME(key_type.type_class);
+ r_export_info->dictionary.value_type = managed_to_variant_type(value_type);
+ r_export_info->dictionary.value_native_name = NATIVE_GDMONOCLASS_NAME(value_type.type_class);
+ }
+
return Variant::DICTIONARY;
}
exc = NULL;
- GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType);
- MonoBoolean is_array = invoke_method_thunk(type_is_array, (MonoObject *)reftype, (MonoObject **)&exc);
+ GDMonoUtils::TypeIsGenericArray type_is_array = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray);
+ MonoBoolean is_array = invoke_method_thunk(type_is_array, reftype, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
if (is_array) {
+ if (r_export_info) {
+ MonoReflectionType *elem_reftype;
+
+ exc = NULL;
+ invoke_method_thunk(CACHED_METHOD_THUNK(MarshalUtils, ArrayGetElementType),
+ reftype, &elem_reftype, &exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+
+ ManagedType elem_type = ManagedType::from_reftype(elem_reftype);
+
+ r_export_info->array.element_type = managed_to_variant_type(elem_type);
+ r_export_info->array.element_native_name = NATIVE_GDMONOCLASS_NAME(elem_type.type_class);
+ }
+
+ return Variant::ARRAY;
+ }
+
+ if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) {
+ return Variant::DICTIONARY;
+ }
+
+ if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
return Variant::ARRAY;
}
} break;
@@ -294,60 +342,60 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
} break;
case MONO_TYPE_VALUETYPE: {
- GDMonoClass *tclass = p_type.type_class;
+ GDMonoClass *vtclass = p_type.type_class;
- if (tclass == CACHED_CLASS(Vector2)) {
+ if (vtclass == CACHED_CLASS(Vector2)) {
GDMonoMarshal::M_Vector2 from = MARSHALLED_OUT(Vector2, p_var->operator ::Vector2());
return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector2), &from);
}
- if (tclass == CACHED_CLASS(Rect2)) {
+ 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 (tclass == CACHED_CLASS(Transform2D)) {
+ 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);
}
- if (tclass == CACHED_CLASS(Vector3)) {
+ if (vtclass == CACHED_CLASS(Vector3)) {
GDMonoMarshal::M_Vector3 from = MARSHALLED_OUT(Vector3, p_var->operator ::Vector3());
return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector3), &from);
}
- if (tclass == CACHED_CLASS(Basis)) {
+ 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);
}
- if (tclass == CACHED_CLASS(Quat)) {
+ if (vtclass == CACHED_CLASS(Quat)) {
GDMonoMarshal::M_Quat from = MARSHALLED_OUT(Quat, p_var->operator ::Quat());
return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Quat), &from);
}
- if (tclass == CACHED_CLASS(Transform)) {
+ if (vtclass == CACHED_CLASS(Transform)) {
GDMonoMarshal::M_Transform from = MARSHALLED_OUT(Transform, p_var->operator ::Transform());
return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform), &from);
}
- if (tclass == CACHED_CLASS(AABB)) {
+ if (vtclass == CACHED_CLASS(AABB)) {
GDMonoMarshal::M_AABB from = MARSHALLED_OUT(AABB, p_var->operator ::AABB());
return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(AABB), &from);
}
- if (tclass == CACHED_CLASS(Color)) {
+ if (vtclass == CACHED_CLASS(Color)) {
GDMonoMarshal::M_Color from = MARSHALLED_OUT(Color, p_var->operator ::Color());
return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Color), &from);
}
- if (tclass == CACHED_CLASS(Plane)) {
+ if (vtclass == CACHED_CLASS(Plane)) {
GDMonoMarshal::M_Plane from = MARSHALLED_OUT(Plane, p_var->operator ::Plane());
return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Plane), &from);
}
- if (mono_class_is_enum(tclass->get_mono_ptr())) {
- MonoType *enum_basetype = mono_class_enum_basetype(tclass->get_mono_ptr());
+ 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);
switch (mono_type_get_type(enum_basetype)) {
case MONO_TYPE_BOOLEAN: {
@@ -453,6 +501,14 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
if (CACHED_CLASS(Array) == type_class) {
return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array));
}
+
+ if (type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) {
+ return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary));
+ }
+
+ if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
+ return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array));
+ }
} break;
case MONO_TYPE_OBJECT: {
// Variant
@@ -548,8 +604,8 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_type.type_class->get_mono_type());
MonoException *exc = NULL;
- GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType);
- MonoBoolean is_dict = invoke_method_thunk(type_is_dict, (MonoObject *)reftype, (MonoObject **)&exc);
+ GDMonoUtils::TypeIsGenericDictionary type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary);
+ MonoBoolean is_dict = invoke_method_thunk(type_is_dict, reftype, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
if (is_dict) {
@@ -557,13 +613,21 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
}
exc = NULL;
- GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType);
- MonoBoolean is_array = invoke_method_thunk(type_is_array, (MonoObject *)reftype, (MonoObject **)&exc);
+ GDMonoUtils::TypeIsGenericArray type_is_array = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray);
+ MonoBoolean is_array = invoke_method_thunk(type_is_array, reftype, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
if (is_array) {
return GDMonoUtils::create_managed_from(p_var->operator Array(), p_type.type_class);
}
+
+ if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) {
+ return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary));
+ }
+
+ if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
+ return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array));
+ }
} break;
} break;
}
@@ -577,15 +641,9 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
if (!p_obj)
return Variant();
- GDMonoClass *tclass = GDMono::get_singleton()->get_class(mono_object_get_class(p_obj));
- ERR_FAIL_COND_V(!tclass, Variant());
+ ManagedType type = ManagedType::from_class(mono_object_get_class(p_obj));
- MonoType *raw_type = tclass->get_mono_type();
-
- ManagedType type;
-
- type.type_encoding = mono_type_get_type(raw_type);
- type.type_class = tclass;
+ ERR_FAIL_COND_V(!type.type_class, Variant());
switch (type.type_encoding) {
case MONO_TYPE_BOOLEAN:
@@ -624,39 +682,39 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
} break;
case MONO_TYPE_VALUETYPE: {
- GDMonoClass *tclass = type.type_class;
+ GDMonoClass *vtclass = type.type_class;
- if (tclass == CACHED_CLASS(Vector2))
+ if (vtclass == CACHED_CLASS(Vector2))
return MARSHALLED_IN(Vector2, (GDMonoMarshal::M_Vector2 *)mono_object_unbox(p_obj));
- if (tclass == CACHED_CLASS(Rect2))
+ if (vtclass == CACHED_CLASS(Rect2))
return MARSHALLED_IN(Rect2, (GDMonoMarshal::M_Rect2 *)mono_object_unbox(p_obj));
- if (tclass == CACHED_CLASS(Transform2D))
+ if (vtclass == CACHED_CLASS(Transform2D))
return MARSHALLED_IN(Transform2D, (GDMonoMarshal::M_Transform2D *)mono_object_unbox(p_obj));
- if (tclass == CACHED_CLASS(Vector3))
+ if (vtclass == CACHED_CLASS(Vector3))
return MARSHALLED_IN(Vector3, (GDMonoMarshal::M_Vector3 *)mono_object_unbox(p_obj));
- if (tclass == CACHED_CLASS(Basis))
+ if (vtclass == CACHED_CLASS(Basis))
return MARSHALLED_IN(Basis, (GDMonoMarshal::M_Basis *)mono_object_unbox(p_obj));
- if (tclass == CACHED_CLASS(Quat))
+ if (vtclass == CACHED_CLASS(Quat))
return MARSHALLED_IN(Quat, (GDMonoMarshal::M_Quat *)mono_object_unbox(p_obj));
- if (tclass == CACHED_CLASS(Transform))
+ if (vtclass == CACHED_CLASS(Transform))
return MARSHALLED_IN(Transform, (GDMonoMarshal::M_Transform *)mono_object_unbox(p_obj));
- if (tclass == CACHED_CLASS(AABB))
+ if (vtclass == CACHED_CLASS(AABB))
return MARSHALLED_IN(AABB, (GDMonoMarshal::M_AABB *)mono_object_unbox(p_obj));
- if (tclass == CACHED_CLASS(Color))
+ if (vtclass == CACHED_CLASS(Color))
return MARSHALLED_IN(Color, (GDMonoMarshal::M_Color *)mono_object_unbox(p_obj));
- if (tclass == CACHED_CLASS(Plane))
+ if (vtclass == CACHED_CLASS(Plane))
return MARSHALLED_IN(Plane, (GDMonoMarshal::M_Plane *)mono_object_unbox(p_obj));
- if (mono_class_is_enum(tclass->get_mono_ptr()))
+ if (mono_class_is_enum(vtclass->get_mono_ptr()))
return unbox<int32_t>(p_obj);
} break;
@@ -698,7 +756,11 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
// GodotObject
if (CACHED_CLASS(GodotObject)->is_assignable_from(type_class)) {
Object *ptr = unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(p_obj));
- return ptr ? Variant(ptr) : Variant();
+ if (ptr != NULL) {
+ Reference *ref = Object::cast_to<Reference>(ptr);
+ return ref ? Variant(Ref<Reference>(ref)) : Variant(ptr);
+ }
+ return Variant();
}
if (CACHED_CLASS(NodePath) == type_class) {
@@ -713,17 +775,33 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
if (CACHED_CLASS(Array) == type_class) {
MonoException *exc = NULL;
- Array *ptr = invoke_method_thunk(CACHED_METHOD_THUNK(Array, GetPtr), p_obj, (MonoObject **)&exc);
+ Array *ptr = invoke_method_thunk(CACHED_METHOD_THUNK(Array, GetPtr), p_obj, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
return ptr ? Variant(*ptr) : Variant();
}
if (CACHED_CLASS(Dictionary) == type_class) {
MonoException *exc = NULL;
- Dictionary *ptr = invoke_method_thunk(CACHED_METHOD_THUNK(Dictionary, GetPtr), p_obj, (MonoObject **)&exc);
+ Dictionary *ptr = invoke_method_thunk(CACHED_METHOD_THUNK(Dictionary, GetPtr), p_obj, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
return ptr ? Variant(*ptr) : Variant();
}
+
+ if (type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) {
+ Dictionary dict;
+ MonoException *exc = NULL;
+ invoke_method_thunk(CACHED_METHOD_THUNK(MarshalUtils, IDictionaryToDictionary), p_obj, &dict, &exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+ return dict;
+ }
+
+ if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
+ Array array;
+ MonoException *exc = NULL;
+ invoke_method_thunk(CACHED_METHOD_THUNK(MarshalUtils, EnumerableToArray), p_obj, &array, &exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+ return array;
+ }
} break;
case MONO_TYPE_GENERICINST: {
@@ -731,12 +809,12 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
MonoException *exc = NULL;
- GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType);
- MonoBoolean is_dict = invoke_method_thunk(type_is_dict, (MonoObject *)reftype, (MonoObject **)&exc);
+ GDMonoUtils::TypeIsGenericDictionary type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary);
+ MonoBoolean is_dict = invoke_method_thunk(type_is_dict, reftype, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
if (is_dict) {
- MonoException *exc = NULL;
+ exc = NULL;
MonoObject *ret = type.type_class->get_method("GetPtr")->invoke(p_obj, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
return *unbox<Dictionary *>(ret);
@@ -744,16 +822,32 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
exc = NULL;
- GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType);
- MonoBoolean is_array = invoke_method_thunk(type_is_array, (MonoObject *)reftype, (MonoObject **)&exc);
+ GDMonoUtils::TypeIsGenericArray type_is_array = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray);
+ MonoBoolean is_array = invoke_method_thunk(type_is_array, reftype, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
if (is_array) {
- MonoException *exc = NULL;
+ exc = NULL;
MonoObject *ret = type.type_class->get_method("GetPtr")->invoke(p_obj, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
return *unbox<Array *>(ret);
}
+
+ if (type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) {
+ Dictionary dict;
+ exc = NULL;
+ invoke_method_thunk(CACHED_METHOD_THUNK(MarshalUtils, IDictionaryToDictionary), p_obj, &dict, &exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+ return dict;
+ }
+
+ if (type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
+ Array array;
+ exc = NULL;
+ invoke_method_thunk(CACHED_METHOD_THUNK(MarshalUtils, EnumerableToArray), p_obj, &array, &exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+ return array;
+ }
} break;
}
diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h
index 4f86e02f87..8d3fd4b349 100644
--- a/modules/mono/mono_gd/gd_mono_marshal.h
+++ b/modules/mono/mono_gd/gd_mono_marshal.h
@@ -32,6 +32,7 @@
#define GDMONOMARSHAL_H
#include "core/variant.h"
+
#include "gd_mono.h"
#include "gd_mono_utils.h"
@@ -56,7 +57,28 @@ T unbox(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);
+// FIXME: Made this struct in a hurry. It could be done differently.
+struct ExportInfo {
+ struct ArrayInfo {
+ Variant::Type element_type;
+ String element_native_name;
+
+ ArrayInfo() :
+ element_type(Variant::NIL) {}
+ } array;
+ struct DictionaryInfo {
+ Variant::Type key_type;
+ String key_native_name;
+ Variant::Type value_type;
+ String value_native_name;
+
+ DictionaryInfo() :
+ key_type(Variant::NIL),
+ value_type(Variant::NIL) {}
+ } dictionary;
+};
+
+Variant::Type managed_to_variant_type(const ManagedType &p_type, ExportInfo *r_export_info = NULL);
// String
diff --git a/modules/mono/mono_gd/gd_mono_method.h b/modules/mono/mono_gd/gd_mono_method.h
index fc035d387c..f74cef438d 100644
--- a/modules/mono/mono_gd/gd_mono_method.h
+++ b/modules/mono/mono_gd/gd_mono_method.h
@@ -32,8 +32,8 @@
#define GD_MONO_METHOD_H
#include "gd_mono.h"
-#include "gd_mono_class_member.h"
#include "gd_mono_header.h"
+#include "i_mono_class_member.h"
class GDMonoMethod : public IMonoClassMember {
diff --git a/modules/mono/mono_gd/gd_mono_property.h b/modules/mono/mono_gd/gd_mono_property.h
index d8a9f69ffb..2700c460b0 100644
--- a/modules/mono/mono_gd/gd_mono_property.h
+++ b/modules/mono/mono_gd/gd_mono_property.h
@@ -32,8 +32,8 @@
#define GD_MONO_PROPERTY_H
#include "gd_mono.h"
-#include "gd_mono_class_member.h"
#include "gd_mono_header.h"
+#include "i_mono_class_member.h"
class GDMonoProperty : public IMonoClassMember {
diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp
index ac6ccac3a7..bcf5712d16 100644
--- a/modules/mono/mono_gd/gd_mono_utils.cpp
+++ b/modules/mono/mono_gd/gd_mono_utils.cpp
@@ -39,6 +39,7 @@
#include "../csharp_script.h"
#include "../utils/macros.h"
+#include "../utils/mutex_utils.h"
#include "gd_mono.h"
#include "gd_mono_class.h"
#include "gd_mono_marshal.h"
@@ -62,6 +63,7 @@ MonoCache mono_cache;
#define CACHE_FIELD_AND_CHECK(m_class, m_field, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.field_##m_class##_##m_field, m_val)
#define CACHE_METHOD_AND_CHECK(m_class, m_method, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.method_##m_class##_##m_method, m_val)
#define CACHE_METHOD_THUNK_AND_CHECK(m_class, m_method, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.methodthunk_##m_class##_##m_method, m_val)
+#define CACHE_PROPERTY_AND_CHECK(m_class, m_property, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.property_##m_class##_##m_property, m_val)
void MonoCache::clear_members() {
@@ -80,6 +82,9 @@ void MonoCache::clear_members() {
class_String = NULL;
class_IntPtr = NULL;
+ class_System_Collections_IEnumerable = NULL;
+ class_System_Collections_IDictionary = NULL;
+
#ifdef DEBUG_ENABLED
class_System_Diagnostics_StackTrace = NULL;
methodthunk_System_Diagnostics_StackTrace_GetFrames = NULL;
@@ -142,12 +147,17 @@ void MonoCache::clear_members() {
methodthunk_GodotObject_Dispose = NULL;
methodthunk_Array_GetPtr = NULL;
methodthunk_Dictionary_GetPtr = NULL;
- methodthunk_MarshalUtils_IsArrayGenericType = NULL;
- methodthunk_MarshalUtils_IsDictionaryGenericType = NULL;
methodthunk_SignalAwaiter_SignalCallback = NULL;
methodthunk_SignalAwaiter_FailureCallback = NULL;
methodthunk_GodotTaskScheduler_Activate = NULL;
+ methodthunk_MarshalUtils_TypeIsGenericArray = NULL;
+ methodthunk_MarshalUtils_TypeIsGenericDictionary = NULL;
+ methodthunk_MarshalUtils_ArrayGetElementType = NULL;
+ methodthunk_MarshalUtils_DictionaryGetKeyValueTypes = NULL;
+ methodthunk_MarshalUtils_EnumerableToArray = NULL;
+ methodthunk_MarshalUtils_IDictionaryToDictionary = NULL;
+
task_scheduler_handle = Ref<MonoGCHandle>();
}
@@ -177,6 +187,9 @@ void update_corlib_cache() {
CACHE_CLASS_AND_CHECK(String, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_string_class()));
CACHE_CLASS_AND_CHECK(IntPtr, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_intptr_class()));
+ CACHE_CLASS_AND_CHECK(System_Collections_IEnumerable, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections", "IEnumerable"));
+ CACHE_CLASS_AND_CHECK(System_Collections_IDictionary, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections", "IDictionary"));
+
#ifdef DEBUG_ENABLED
CACHE_CLASS_AND_CHECK(System_Diagnostics_StackTrace, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Diagnostics", "StackTrace"));
CACHE_METHOD_THUNK_AND_CHECK(System_Diagnostics_StackTrace, GetFrames, (StackTrace_GetFrames)CACHED_CLASS(System_Diagnostics_StackTrace)->get_method_thunk("GetFrames"));
@@ -241,12 +254,17 @@ void update_godot_api_cache() {
CACHE_METHOD_THUNK_AND_CHECK(GodotObject, Dispose, (GodotObject_Dispose)CACHED_CLASS(GodotObject)->get_method_thunk("Dispose", 0));
CACHE_METHOD_THUNK_AND_CHECK(Array, GetPtr, (Array_GetPtr)GODOT_API_NS_CLAS(BINDINGS_NAMESPACE_COLLECTIONS, Array)->get_method_thunk("GetPtr", 0));
CACHE_METHOD_THUNK_AND_CHECK(Dictionary, GetPtr, (Dictionary_GetPtr)GODOT_API_NS_CLAS(BINDINGS_NAMESPACE_COLLECTIONS, Dictionary)->get_method_thunk("GetPtr", 0));
- CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, IsArrayGenericType, (IsArrayGenericType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("IsArrayGenericType", 1));
- CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, IsDictionaryGenericType, (IsDictionaryGenericType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("IsDictionaryGenericType", 1));
CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, SignalCallback, (SignalAwaiter_SignalCallback)GODOT_API_CLASS(SignalAwaiter)->get_method_thunk("SignalCallback", 1));
CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, FailureCallback, (SignalAwaiter_FailureCallback)GODOT_API_CLASS(SignalAwaiter)->get_method_thunk("FailureCallback", 0));
CACHE_METHOD_THUNK_AND_CHECK(GodotTaskScheduler, Activate, (GodotTaskScheduler_Activate)GODOT_API_CLASS(GodotTaskScheduler)->get_method_thunk("Activate", 0));
+ CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericArray, (TypeIsGenericArray)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("TypeIsGenericArray", 1));
+ CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericDictionary, (TypeIsGenericDictionary)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("TypeIsGenericDictionary", 1));
+ CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, ArrayGetElementType, (ArrayGetElementType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("ArrayGetElementType", 2));
+ CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, DictionaryGetKeyValueTypes, (DictionaryGetKeyValueTypes)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("DictionaryGetKeyValueTypes", 3));
+ CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, EnumerableToArray, (EnumerableToArray)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("EnumerableToArray", 2));
+ CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, IDictionaryToDictionary, (IDictionaryToDictionary)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("IDictionaryToDictionary", 2));
+
#ifdef DEBUG_ENABLED
CACHE_METHOD_THUNK_AND_CHECK(DebuggingUtils, GetStackFrameInfo, (DebugUtils_StackFrameInfo)GODOT_API_CLASS(DebuggingUtils)->get_method_thunk("GetStackFrameInfo", 4));
#endif
@@ -281,17 +299,19 @@ MonoObject *unmanaged_get_managed(Object *unmanaged) {
void *data = unmanaged->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index());
- if (!data)
- return NULL;
+ ERR_FAIL_NULL_V(data, NULL);
CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->value();
if (!script_binding.inited) {
- // Already had a binding that needs to be setup
- CSharpLanguage::get_singleton()->setup_csharp_script_binding(script_binding, unmanaged);
+ SCOPED_MUTEX_LOCK(CSharpLanguage::get_singleton()->get_language_bind_mutex());
+
+ if (!script_binding.inited) { // Other thread may have set it up
+ // Already had a binding that needs to be setup
+ CSharpLanguage::get_singleton()->setup_csharp_script_binding(script_binding, unmanaged);
- if (!script_binding.inited)
- return NULL;
+ ERR_FAIL_COND_V(!script_binding.inited, NULL);
+ }
}
Ref<MonoGCHandle> &gchandle = script_binding.gchandle;
@@ -371,6 +391,11 @@ GDMonoClass *type_get_proxy_class(const StringName &p_type) {
GDMonoClass *klass = GDMono::get_singleton()->get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, class_name);
+ if (klass && klass->is_static()) {
+ // A static class means this is a Godot singleton class. If an instance is needed we use Godot.Object.
+ return mono_cache.class_GodotObject;
+ }
+
#ifdef TOOLS_ENABLED
if (!klass) {
return GDMono::get_singleton()->get_editor_api_assembly()->get_class(BINDINGS_NAMESPACE, class_name);
@@ -607,11 +632,6 @@ void debug_send_unhandled_exception_error(MonoException *p_exc) {
}
void debug_unhandled_exception(MonoException *p_exc) {
-#ifdef DEBUG_ENABLED
- GDMonoUtils::debug_send_unhandled_exception_error(p_exc);
- if (ScriptDebugger::get_singleton())
- ScriptDebugger::get_singleton()->idle_poll();
-#endif
GDMonoInternals::unhandled_exception(p_exc); // prints the exception as well
GD_UNREACHABLE();
}
@@ -704,7 +724,7 @@ uint64_t unbox_enum_value(MonoObject *p_boxed, MonoType *p_enum_basetype, bool &
}
void dispose(MonoObject *p_mono_object, MonoException **r_exc) {
- invoke_method_thunk(CACHED_METHOD_THUNK(GodotObject, Dispose), p_mono_object, (MonoObject **)r_exc);
+ invoke_method_thunk(CACHED_METHOD_THUNK(GodotObject, Dispose), p_mono_object, r_exc);
}
} // namespace GDMonoUtils
diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h
index e88bf1ced9..87610e286c 100644
--- a/modules/mono/mono_gd/gd_mono_utils.h
+++ b/modules/mono/mono_gd/gd_mono_utils.h
@@ -49,18 +49,21 @@
namespace GDMonoUtils {
-typedef void (*GodotObject_Dispose)(MonoObject *, MonoObject **);
-typedef Array *(*Array_GetPtr)(MonoObject *, MonoObject **);
-typedef Dictionary *(*Dictionary_GetPtr)(MonoObject *, MonoObject **);
-typedef MonoObject *(*SignalAwaiter_SignalCallback)(MonoObject *, MonoArray *, MonoObject **);
-typedef MonoObject *(*SignalAwaiter_FailureCallback)(MonoObject *, MonoObject **);
-typedef MonoObject *(*GodotTaskScheduler_Activate)(MonoObject *, MonoObject **);
-typedef MonoArray *(*StackTrace_GetFrames)(MonoObject *, MonoObject **);
-typedef MonoBoolean (*IsArrayGenericType)(MonoObject *, MonoObject **);
-typedef MonoBoolean (*IsDictionaryGenericType)(MonoObject *, MonoObject **);
-typedef MonoBoolean (*IsArrayGenericType)(MonoObject *, MonoObject **);
-typedef MonoBoolean (*IsDictionaryGenericType)(MonoObject *, MonoObject **);
-typedef void (*DebugUtils_StackFrameInfo)(MonoObject *, MonoString **, int *, MonoString **, MonoObject **);
+typedef void (*GodotObject_Dispose)(MonoObject *, MonoException **);
+typedef Array *(*Array_GetPtr)(MonoObject *, MonoException **);
+typedef Dictionary *(*Dictionary_GetPtr)(MonoObject *, MonoException **);
+typedef MonoObject *(*SignalAwaiter_SignalCallback)(MonoObject *, MonoArray *, MonoException **);
+typedef MonoObject *(*SignalAwaiter_FailureCallback)(MonoObject *, MonoException **);
+typedef MonoObject *(*GodotTaskScheduler_Activate)(MonoObject *, MonoException **);
+typedef MonoArray *(*StackTrace_GetFrames)(MonoObject *, MonoException **);
+typedef void (*DebugUtils_StackFrameInfo)(MonoObject *, MonoString **, int *, MonoString **, MonoException **);
+
+typedef MonoBoolean (*TypeIsGenericArray)(MonoReflectionType *, MonoException **);
+typedef MonoBoolean (*TypeIsGenericDictionary)(MonoReflectionType *, MonoException **);
+typedef MonoBoolean (*ArrayGetElementType)(MonoReflectionType *, MonoReflectionType **, MonoException **);
+typedef MonoBoolean (*DictionaryGetKeyValueTypes)(MonoReflectionType *, MonoReflectionType **, MonoReflectionType **, MonoException **);
+typedef void (*EnumerableToArray)(MonoObject *, Array *, MonoException **);
+typedef void (*IDictionaryToDictionary)(MonoObject *, Dictionary *, MonoException **);
struct MonoCache {
@@ -83,6 +86,9 @@ struct MonoCache {
GDMonoClass *class_String;
GDMonoClass *class_IntPtr;
+ GDMonoClass *class_System_Collections_IEnumerable;
+ GDMonoClass *class_System_Collections_IDictionary;
+
#ifdef DEBUG_ENABLED
GDMonoClass *class_System_Diagnostics_StackTrace;
StackTrace_GetFrames methodthunk_System_Diagnostics_StackTrace_GetFrames;
@@ -146,12 +152,17 @@ struct MonoCache {
GodotObject_Dispose methodthunk_GodotObject_Dispose;
Array_GetPtr methodthunk_Array_GetPtr;
Dictionary_GetPtr methodthunk_Dictionary_GetPtr;
- IsArrayGenericType methodthunk_MarshalUtils_IsArrayGenericType;
- IsDictionaryGenericType methodthunk_MarshalUtils_IsDictionaryGenericType;
SignalAwaiter_SignalCallback methodthunk_SignalAwaiter_SignalCallback;
SignalAwaiter_FailureCallback methodthunk_SignalAwaiter_FailureCallback;
GodotTaskScheduler_Activate methodthunk_GodotTaskScheduler_Activate;
+ TypeIsGenericArray methodthunk_MarshalUtils_TypeIsGenericArray;
+ TypeIsGenericDictionary methodthunk_MarshalUtils_TypeIsGenericDictionary;
+ ArrayGetElementType methodthunk_MarshalUtils_ArrayGetElementType;
+ DictionaryGetKeyValueTypes methodthunk_MarshalUtils_DictionaryGetKeyValueTypes;
+ EnumerableToArray methodthunk_MarshalUtils_EnumerableToArray;
+ IDictionaryToDictionary methodthunk_MarshalUtils_IDictionaryToDictionary;
+
Ref<MonoGCHandle> task_scheduler_handle;
bool corlib_cache_updated;
@@ -255,6 +266,7 @@ void dispose(MonoObject *p_mono_object, MonoException **r_exc);
#define CACHED_FIELD(m_class, m_field) (GDMonoUtils::mono_cache.field_##m_class##_##m_field)
#define CACHED_METHOD(m_class, m_method) (GDMonoUtils::mono_cache.method_##m_class##_##m_method)
#define CACHED_METHOD_THUNK(m_class, m_method) (GDMonoUtils::mono_cache.methodthunk_##m_class##_##m_method)
+#define CACHED_PROPERTY(m_class, m_property) (GDMonoUtils::mono_cache.property_##m_class##_##m_property)
#ifdef REAL_T_IS_DOUBLE
#define REAL_T_MONOCLASS CACHED_CLASS_RAW(double)
diff --git a/modules/mono/mono_gd/gd_mono_class_member.h b/modules/mono/mono_gd/i_mono_class_member.h
index 3058b18c05..553d9edc72 100644
--- a/modules/mono/mono_gd/gd_mono_class_member.h
+++ b/modules/mono/mono_gd/i_mono_class_member.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* gd_mono_class_member.h */
+/* i_mono_class_member.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef GD_MONO_CLASS_MEMBER_H
-#define GD_MONO_CLASS_MEMBER_H
+#ifndef I_MONO_CLASS_MEMBER_H
+#define I_MONO_CLASS_MEMBER_H
#include "gd_mono_header.h"
@@ -65,4 +65,4 @@ public:
virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) = 0;
};
-#endif // GD_MONO_CLASS_MEMBER_H
+#endif // I_MONO_CLASS_MEMBER_H
diff --git a/modules/mono/mono_gd/managed_type.cpp b/modules/mono/mono_gd/managed_type.cpp
new file mode 100644
index 0000000000..9f736b71cd
--- /dev/null
+++ b/modules/mono/mono_gd/managed_type.cpp
@@ -0,0 +1,58 @@
+/*************************************************************************/
+/* managed_type.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "managed_type.h"
+
+#include "gd_mono.h"
+#include "gd_mono_class.h"
+
+ManagedType ManagedType::from_class(GDMonoClass *p_class) {
+ return ManagedType(mono_type_get_type(p_class->get_mono_type()), p_class);
+}
+
+ManagedType ManagedType::from_class(MonoClass *p_mono_class) {
+ GDMonoClass *tclass = GDMono::get_singleton()->get_class(p_mono_class);
+ ERR_FAIL_COND_V(!tclass, ManagedType());
+
+ return ManagedType(mono_type_get_type(tclass->get_mono_type()), tclass);
+}
+
+ManagedType ManagedType::from_type(MonoType *p_mono_type) {
+ MonoClass *mono_class = mono_class_from_mono_type(p_mono_type);
+ GDMonoClass *tclass = GDMono::get_singleton()->get_class(mono_class);
+ ERR_FAIL_COND_V(!tclass, ManagedType());
+
+ return ManagedType(mono_type_get_type(p_mono_type), tclass);
+}
+
+ManagedType ManagedType::from_reftype(MonoReflectionType *p_mono_reftype) {
+ MonoType *mono_type = mono_reflection_type_get_type(p_mono_reftype);
+ return from_type(mono_type);
+}
diff --git a/modules/mono/mono_gd/managed_type.h b/modules/mono/mono_gd/managed_type.h
new file mode 100644
index 0000000000..a537e56aea
--- /dev/null
+++ b/modules/mono/mono_gd/managed_type.h
@@ -0,0 +1,58 @@
+/*************************************************************************/
+/* managed_type.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef MANAGED_TYPE_H
+#define MANAGED_TYPE_H
+
+#include <mono/metadata/object.h>
+
+#include "gd_mono_header.h"
+
+struct ManagedType {
+ int type_encoding;
+ GDMonoClass *type_class;
+
+ static ManagedType from_class(GDMonoClass *p_class);
+ static ManagedType from_class(MonoClass *p_mono_class);
+ static ManagedType from_type(MonoType *p_mono_type);
+ static ManagedType from_reftype(MonoReflectionType *p_mono_reftype);
+
+ ManagedType() :
+ type_encoding(0),
+ type_class(NULL) {
+ }
+
+ ManagedType(int p_type_encoding, GDMonoClass *p_type_class) :
+ type_encoding(p_type_encoding),
+ type_class(p_type_class) {
+ }
+};
+
+#endif // MANAGED_TYPE_H
diff --git a/modules/mono/signal_awaiter_utils.cpp b/modules/mono/signal_awaiter_utils.cpp
index 5051d83694..5d37e8212f 100644
--- a/modules/mono/signal_awaiter_utils.cpp
+++ b/modules/mono/signal_awaiter_utils.cpp
@@ -100,7 +100,7 @@ Variant SignalAwaiterHandle::_signal_callback(const Variant **p_args, int p_argc
MonoException *exc = NULL;
GD_MONO_BEGIN_RUNTIME_INVOKE;
- invoke_method_thunk(CACHED_METHOD_THUNK(SignalAwaiter, SignalCallback), get_target(), signal_args, (MonoObject **)&exc);
+ invoke_method_thunk(CACHED_METHOD_THUNK(SignalAwaiter, SignalCallback), get_target(), signal_args, &exc);
GD_MONO_END_RUNTIME_INVOKE;
if (exc) {
@@ -132,7 +132,7 @@ SignalAwaiterHandle::~SignalAwaiterHandle() {
if (awaiter) {
MonoException *exc = NULL;
GD_MONO_BEGIN_RUNTIME_INVOKE;
- invoke_method_thunk(CACHED_METHOD_THUNK(SignalAwaiter, FailureCallback), awaiter, (MonoObject **)&exc);
+ invoke_method_thunk(CACHED_METHOD_THUNK(SignalAwaiter, FailureCallback), awaiter, &exc);
GD_MONO_END_RUNTIME_INVOKE;
if (exc) {
diff --git a/modules/mono/utils/macros.h b/modules/mono/utils/macros.h
index 87295a98f2..e44f254e1c 100644
--- a/modules/mono/utils/macros.h
+++ b/modules/mono/utils/macros.h
@@ -31,10 +31,6 @@
#ifndef UTIL_MACROS_H
#define UTIL_MACROS_H
-#ifndef __has_cpp_attribute
-#define __has_cpp_attribute(attr_token) 0
-#endif
-
#define _GD_VARNAME_CONCAT_B_(m_ignore, m_name) m_name
#define _GD_VARNAME_CONCAT_A_(m_a, m_b, m_c) _GD_VARNAME_CONCAT_B_(hello there, m_a##m_b##m_c)
#define _GD_VARNAME_CONCAT_(m_a, m_b, m_c) _GD_VARNAME_CONCAT_A_(m_a, m_b, m_c)
@@ -61,7 +57,7 @@
// noreturn
// TODO: Get rid of this macro once we upgrade to C++11
-#if __has_cpp_attribute(deprecated)
+#if (__cplusplus >= 201103L)
#define GD_NORETURN [[noreturn]]
#elif defined(__GNUC__)
#define GD_NORETURN __attribute__((noreturn))
diff --git a/modules/mono/utils/mono_reg_utils.cpp b/modules/mono/utils/mono_reg_utils.cpp
index 76b250e038..98aeadc8c8 100644
--- a/modules/mono/utils/mono_reg_utils.cpp
+++ b/modules/mono/utils/mono_reg_utils.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "mono_reg_utils.h"
+#include "core/os/dir_access.h"
#ifdef WINDOWS_ENABLED
@@ -158,8 +159,6 @@ MonoRegInfo find_mono() {
if (_find_mono_in_reg_old("Software\\Novell\\Mono", info) == ERROR_SUCCESS)
return info;
- ERR_PRINT("Cannot find mono in the registry");
-
return MonoRegInfo();
}
@@ -202,6 +201,13 @@ String find_msbuild_tools_path() {
val += "\\";
}
+ // Since VS2019, the directory is simply named "Current"
+ String msbuild_dir = val + "MSBuild\\Current\\Bin";
+ if (DirAccess::exists(msbuild_dir)) {
+ return msbuild_dir;
+ }
+
+ // Directory name "15.0" is used in VS 2017
return val + "MSBuild\\15.0\\Bin";
}
}
diff --git a/modules/mono/utils/string_utils.cpp b/modules/mono/utils/string_utils.cpp
index c390f8b9c2..0ef66577fe 100644
--- a/modules/mono/utils/string_utils.cpp
+++ b/modules/mono/utils/string_utils.cpp
@@ -32,6 +32,8 @@
#include "core/os/file_access.h"
+#include <stdio.h>
+
namespace {
int sfind(const String &p_text, int p_from) {
@@ -184,3 +186,50 @@ Error read_all_file_utf8(const String &p_path, String &r_content) {
r_content = source;
return OK;
}
+
+// TODO: Move to variadic templates once we upgrade to C++11
+
+String str_format(const char *p_format, ...) {
+ va_list list;
+
+ va_start(list, p_format);
+ String res = str_format(p_format, list);
+ va_end(list);
+
+ return res;
+}
+// va_copy was defined in the C99, but not in C++ standards before C++11.
+// When you compile C++ without --std=c++<XX> option, compilers still define
+// va_copy, otherwise you have to use the internal version (__va_copy).
+#if !defined(va_copy)
+#if defined(__GNUC__)
+#define va_copy(d, s) __va_copy((d), (s))
+#else
+#define va_copy(d, s) ((d) = (s))
+#endif
+#endif
+
+#if defined(MINGW_ENABLED) || defined(_MSC_VER)
+#define vsnprintf vsnprintf_s
+#endif
+
+String str_format(const char *p_format, va_list p_list) {
+ va_list list;
+
+ va_copy(list, p_list);
+ int len = vsnprintf(NULL, 0, p_format, list);
+ va_end(list);
+
+ len += 1; // for the trailing '/0'
+
+ char *buffer(memnew_arr(char, len));
+
+ va_copy(list, p_list);
+ vsnprintf(buffer, len, p_format, list);
+ va_end(list);
+
+ String res(buffer);
+ memdelete_arr(buffer);
+
+ return res;
+}
diff --git a/modules/mono/utils/string_utils.h b/modules/mono/utils/string_utils.h
index 61765ccfd8..565b9bb644 100644
--- a/modules/mono/utils/string_utils.h
+++ b/modules/mono/utils/string_utils.h
@@ -34,6 +34,8 @@
#include "core/ustring.h"
#include "core/variant.h"
+#include <stdarg.h>
+
String sformat(const String &p_text, const Variant &p1 = Variant(), const Variant &p2 = Variant(), const Variant &p3 = Variant(), const Variant &p4 = Variant(), const Variant &p5 = Variant());
#ifdef TOOLS_ENABLED
@@ -44,4 +46,15 @@ String escape_csharp_keyword(const String &p_name);
Error read_all_file_utf8(const String &p_path, String &r_content);
+#if defined(__GNUC__)
+#define _PRINTF_FORMAT_ATTRIBUTE_1_0 __attribute__((format(printf, 1, 0)))
+#define _PRINTF_FORMAT_ATTRIBUTE_1_2 __attribute__((format(printf, 1, 2)))
+#else
+#define _PRINTF_FORMAT_ATTRIBUTE_1_0
+#define _PRINTF_FORMAT_ATTRIBUTE_1_2
+#endif
+
+String str_format(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_1_2;
+String str_format(const char *p_format, va_list p_list) _PRINTF_FORMAT_ATTRIBUTE_1_0;
+
#endif // STRING_FORMAT_H
diff --git a/modules/mono/utils/thread_local.h b/modules/mono/utils/thread_local.h
index 276db8830b..488cc2619a 100644
--- a/modules/mono/utils/thread_local.h
+++ b/modules/mono/utils/thread_local.h
@@ -39,7 +39,7 @@
#error Platform or compiler not supported
#endif
-#ifdef __GNUC__
+#if defined(__GNUC__)
#ifdef HAVE_GCC___THREAD
#define _THREAD_LOCAL_(m_t) __thread m_t
@@ -47,7 +47,7 @@
#define USE_CUSTOM_THREAD_LOCAL
#endif
-#elif _MSC_VER
+#elif defined(_MSC_VER)
#ifdef HAVE_DECLSPEC_THREAD
#define _THREAD_LOCAL_(m_t) __declspec(thread) m_t