diff options
Diffstat (limited to 'modules/mono')
119 files changed, 5357 insertions, 2604 deletions
diff --git a/modules/mono/SCsub b/modules/mono/SCsub index e1f5e2ef28..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,270 +29,29 @@ vars.Update(env_mono) if env_mono['mono_glue']: env_mono.Append(CPPDEFINES=['MONO_GLUE_ENABLED']) -# Configure TLS checks - -import tls_configure -conf = Configure(env_mono) -tls_configure.configure(conf) -env_mono = conf.Finish() - - -# 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 + if not os.path.isfile('glue/mono_glue.gen.cpp'): + raise RuntimeError('Missing mono glue sources. Did you forget to generate them?') - 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 +if env_mono['tools'] or env_mono['target'] != 'release': + env_mono.Append(CPPDEFINES=['GD_MONO_HOT_RELOAD']) - 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' +# Configure Thread Local Storage - 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' +import build_scripts.tls_configure as tls_configure - return None - - -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 +conf = Configure(env_mono) +tls_configure.configure(conf) +env_mono = conf.Finish() - 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' +# Configure Mono - if not os.path.isdir(dst_dir): - if os.path.exists(dst_dir): - raise RuntimeError('Target directory is a file') - os.makedirs(dst_dir) +import build_scripts.mono_configure as mono_configure - copyfile(os.path.join(src_dir, asm_file), os.path.join(dst_dir, asm_file)) +mono_configure.configure(env, env_mono) - # Dependencies - copyfile(os.path.join(src_dir, "DotNet.Glob.dll"), os.path.join(dst_dir, "DotNet.Glob.dll")) +# Build GodotSharpTools -if env['tools']: - output_dir = Dir('#bin').abspath - editor_tools_dir = os.path.join(output_dir, 'GodotSharp', 'Tools') +import build_scripts.godotsharptools_build as godotsharptools_build - 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..160580e116 --- /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.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, mono_version) + + mono_lib_path = os.path.join(mono_root, 'lib') + + env.Append(LIBPATH=mono_lib_path) + env_mono.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_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 189699cca8..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,405 +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: - sharedlib_ext = '.dylib' if sys.platform == 'darwin' 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 sys.platform == 'darwin': - # 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 sys.platform == 'darwin': - 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 sys.platform == 'darwin': - 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 943d95bfc9..3c9644127c 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -139,14 +139,24 @@ void CSharpLanguage::finish() { } #endif - // Release gchandle bindings before finalizing mono runtime - script_bindings.clear(); + // Make sure all script binding gchandles are released before finalizing GDMono + for (Map<Object *, CSharpScriptBinding>::Element *E = script_bindings.front(); E; E = E->next()) { + CSharpScriptBinding &script_binding = E->value(); + + if (script_binding.gchandle.is_valid()) { + script_binding.gchandle->release(); + script_binding.inited = false; + } + } if (gdmono) { memdelete(gdmono); gdmono = NULL; } + // Clear here, after finalizing all domains to make sure there is nothing else referencing the elements. + script_bindings.clear(); + finalizing = false; } @@ -439,7 +449,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; } @@ -489,6 +499,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 @@ -520,7 +571,7 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObjec MonoException *exc = NULL; - MonoArray *frames = invoke_method_thunk(CACHED_METHOD_THUNK(System_Diagnostics_StackTrace, GetFrames), p_stack_trace, (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); @@ -544,7 +595,7 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObjec MonoString *file_name; int file_line_num; MonoString *method_decl; - invoke_method_thunk(get_sf_info, frame, &file_name, &file_line_num, &method_decl, (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); @@ -574,11 +625,11 @@ 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); - _UNREACHABLE_(); + GD_UNREACHABLE(); } } } @@ -607,31 +658,10 @@ struct CSharpScriptDepSort { void CSharpLanguage::reload_all_scripts() { -#ifdef DEBUG_ENABLED - - List<Ref<CSharpScript> > scripts; - - { - SCOPED_MUTEX_LOCK(script_instances_mutex); - - SelfList<CSharpScript> *elem = script_list.first(); - while (elem) { - if (elem->self()->get_path().is_resource_file()) { - scripts.push_back(Ref<CSharpScript>(elem->self())); //cast to gdscript to avoid being erased by accident - } - elem = elem->next(); - } - } - - //as scripts are going to be reloaded, must proceed without locking here - - scripts.sort_custom<CSharpScriptDepSort>(); //update in inheritance dependency order - - for (List<Ref<CSharpScript> >::Element *E = scripts.front(); E; E = E->next()) { - E->get()->load_source_code(E->get()->get_path()); - E->get()->reload(true); +#ifdef GD_MONO_HOT_RELOAD + if (is_assembly_reloading_needed()) { + reload_assemblies(false); } - #endif } @@ -639,15 +669,20 @@ void CSharpLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_soft (void)p_script; // UNUSED + CRASH_COND(!Engine::get_singleton()->is_editor_hint()); + #ifdef TOOLS_ENABLED MonoReloadNode::get_singleton()->restart_reload_timer(); +#endif + +#ifdef GD_MONO_HOT_RELOAD if (is_assembly_reloading_needed()) { reload_assemblies(p_soft_reload); } #endif } -#ifdef TOOLS_ENABLED +#ifdef GD_MONO_HOT_RELOAD bool CSharpLanguage::is_assembly_reloading_needed() { if (!gdmono->is_runtime_initialized()) @@ -679,11 +714,13 @@ bool CSharpLanguage::is_assembly_reloading_needed() { return false; // No assembly to load } +#ifdef TOOLS_ENABLED if (!gdmono->get_core_api_assembly() && gdmono->metadata_is_api_assembly_invalidated(APIAssembly::API_CORE)) return false; // The core API assembly to load is invalidated if (!gdmono->get_editor_api_assembly() && gdmono->metadata_is_api_assembly_invalidated(APIAssembly::API_EDITOR)) return false; // The editor API assembly to load is invalidated +#endif return true; } @@ -723,13 +760,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 @@ -781,9 +818,11 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { PlaceHolderScriptInstance *placeholder = scr->placeholder_instance_create(obj); obj->set_script_instance(placeholder); +#ifdef TOOLS_ENABLED // Even though build didn't fail, this tells the placeholder to keep properties and // it allows using property_set_fallback for restoring the state without a valid script. - placeholder->set_build_failed(true); + scr->placeholder_fallback_enabled = true; +#endif // Restore Variant properties state, it will be kept by the placeholder until the next script reloading for (List<Pair<StringName, Variant> >::Element *G = scr->pending_reload_state[obj_id].properties.front(); G; G = G->next()) { @@ -799,13 +838,14 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { for (List<Ref<CSharpScript> >::Element *E = to_reload.front(); E; E = E->next()) { Ref<CSharpScript> scr = E->get(); +#ifdef TOOLS_ENABLED scr->exports_invalidated = true; +#endif scr->signals_invalidated = true; scr->reload(p_soft_reload); scr->update_exports(); { -#ifdef DEBUG_ENABLED for (Set<ObjectID>::Element *F = scr->pending_reload_instances.front(); F; F = F->next()) { ObjectID obj_id = F->get(); Object *obj = ObjectDB::get_instance(obj_id); @@ -829,7 +869,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { CSharpScript::StateBackup &state_backup = scr->pending_reload_state[obj_id]; // Backup placeholder script instance state before replacing it with a script instance - obj->get_script_instance()->get_property_state(state_backup.properties); + si->get_property_state(state_backup.properties); ScriptInstance *script_instance = scr->instance_create(obj); @@ -864,17 +904,18 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { scr->pending_reload_state.erase(obj_id); } -#endif scr->pending_reload_instances.clear(); } } +#ifdef TOOLS_ENABLED // FIXME: Hack to refresh editor in order to display new properties and signals. See if there is a better alternative. if (Engine::get_singleton()->is_editor_hint()) { EditorNode::get_singleton()->get_inspector()->update_tree(); NodeDock::singleton->update_lists(); } +#endif } #endif @@ -958,12 +999,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 +1014,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 +1024,13 @@ bool CSharpLanguage::debug_break(const String &p_error, bool p_allow_continue) { } } +void CSharpLanguage::_uninitialize_script_bindings() { + for (Map<Object *, CSharpScriptBinding>::Element *E = script_bindings.front(); E; E = E->next()) { + CSharpScriptBinding &script_binding = E->value(); + script_binding.inited = false; + } +} + void CSharpLanguage::set_language_index(int p_idx) { ERR_FAIL_COND(lang_idx != -1); @@ -1064,12 +1110,14 @@ CSharpLanguage::~CSharpLanguage() { singleton = NULL; } -void *CSharpLanguage::alloc_instance_binding_data(Object *p_object) { +bool CSharpLanguage::setup_csharp_script_binding(CSharpScriptBinding &r_script_binding, Object *p_object) { #ifdef DEBUG_ENABLED // I don't trust you - if (p_object->get_script_instance()) - CRASH_COND(NULL != CAST_CSHARP_INSTANCE(p_object->get_script_instance())); + if (p_object->get_script_instance()) { + CSharpInstance *csharp_instance = CAST_CSHARP_INSTANCE(p_object->get_script_instance()); + CRASH_COND(csharp_instance != NULL && !csharp_instance->is_destructing_script_instance()); + } #endif StringName type_name = p_object->get_class_name(); @@ -1078,29 +1126,21 @@ void *CSharpLanguage::alloc_instance_binding_data(Object *p_object) { const ClassDB::ClassInfo *classinfo = ClassDB::classes.getptr(type_name); while (classinfo && !classinfo->exposed) classinfo = classinfo->inherits_ptr; - ERR_FAIL_NULL_V(classinfo, NULL); + ERR_FAIL_NULL_V(classinfo, false); type_name = classinfo->name; GDMonoClass *type_class = GDMonoUtils::type_get_proxy_class(type_name); - ERR_FAIL_NULL_V(type_class, NULL); + ERR_FAIL_NULL_V(type_class, false); MonoObject *mono_object = GDMonoUtils::create_managed_for_godot_object(type_class, type_name, p_object); - ERR_FAIL_NULL_V(mono_object, NULL); - - CSharpScriptBinding script_binding; - - script_binding.type_name = type_name; - script_binding.wrapper_class = type_class; // cache - script_binding.gchandle = MonoGCHandle::create_strong(mono_object); - - void *data; + ERR_FAIL_NULL_V(mono_object, false); - { - SCOPED_MUTEX_LOCK(language_bind_mutex); - data = (void *)script_bindings.insert(p_object, script_binding); - } + r_script_binding.inited = true; + r_script_binding.type_name = type_name; + r_script_binding.wrapper_class = type_class; // cache + r_script_binding.gchandle = MonoGCHandle::create_strong(mono_object); // Tie managed to unmanaged Reference *ref = Object::cast_to<Reference>(p_object); @@ -1114,7 +1154,28 @@ void *CSharpLanguage::alloc_instance_binding_data(Object *p_object) { ref->reference(); } - return data; + return true; +} + +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; + + return (void *)insert_script_binding(p_object, script_binding); +} + +Map<Object *, CSharpScriptBinding>::Element *CSharpLanguage::insert_script_binding(Object *p_object, const CSharpScriptBinding &p_script_binding) { + + return script_bindings.insert(p_object, p_script_binding); } void CSharpLanguage::free_instance_binding_data(void *p_data) { @@ -1135,10 +1196,15 @@ void CSharpLanguage::free_instance_binding_data(void *p_data) { Map<Object *, CSharpScriptBinding>::Element *data = (Map<Object *, CSharpScriptBinding>::Element *)p_data; - // Set the native instance field to IntPtr.Zero, if not yet garbage collected - MonoObject *mono_object = data->value().gchandle->get_target(); - if (mono_object) { - CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, NULL); + CSharpScriptBinding &script_binding = data->value(); + + if (script_binding.inited) { + // Set the native instance field to IntPtr.Zero, if not yet garbage collected. + // This is done to avoid trying to dispose the native instance from Dispose(bool). + MonoObject *mono_object = script_binding.gchandle->get_target(); + if (mono_object) { + CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, NULL); + } } script_bindings.erase(data); @@ -1154,9 +1220,10 @@ void CSharpLanguage::refcount_incremented_instance_binding(Object *p_object) { #endif void *data = p_object->get_script_instance_binding(get_language_index()); - if (!data) - return; - Ref<MonoGCHandle> &gchandle = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get().gchandle; + CRASH_COND(!data); + + CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get(); + Ref<MonoGCHandle> &gchandle = script_binding.gchandle; if (ref_owner->reference_get_count() > 1 && gchandle->is_weak()) { // The managed side also holds a reference, hence 1 instead of 0 // The reference count was increased after the managed side was the only one referencing our owner. @@ -1185,11 +1252,12 @@ bool CSharpLanguage::refcount_decremented_instance_binding(Object *p_object) { int refcount = ref_owner->reference_get_count(); void *data = p_object->get_script_instance_binding(get_language_index()); - if (!data) - return refcount == 0; - Ref<MonoGCHandle> &gchandle = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get().gchandle; + CRASH_COND(!data); - if (refcount == 1 && !gchandle->is_weak()) { // The managed side also holds a reference, hence 1 instead of 0 + CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get(); + Ref<MonoGCHandle> &gchandle = script_binding.gchandle; + + if (refcount == 1 && gchandle.is_valid() && !gchandle->is_weak()) { // The managed side also holds a reference, hence 1 instead of 0 // If owner owner is no longer referenced by the unmanaged side, // the managed instance takes responsibility of deleting the owner when GCed. @@ -1233,6 +1301,10 @@ MonoObject *CSharpInstance::get_mono_object() const { return gchandle->get_target(); } +Object *CSharpInstance::get_owner() { + return owner; +} + bool CSharpInstance::set(const StringName &p_name, const Variant &p_value) { ERR_FAIL_COND_V(!script.is_valid(), false); @@ -1243,14 +1315,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())); @@ -1352,6 +1424,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 { @@ -1493,14 +1593,8 @@ bool CSharpInstance::_unreference_owner_unsafe() { // Unsafe refcount decrement. The managed instance also counts as a reference. // See: _reference_owner_unsafe() - bool die = static_cast<Reference *>(owner)->unreference(); - - if (die) { - memdelete(owner); - owner = NULL; - } - - return die; + // Destroying the owner here means self destructing, so we defer the owner destruction to the caller. + return static_cast<Reference *>(owner)->unreference(); } MonoObject *CSharpInstance::_internal_new_managed() { @@ -1508,32 +1602,46 @@ 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); ERR_FAIL_COND_V(script.is_null(), NULL); - if (base_ref) - _reference_owner_unsafe(); - MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, script->script_class->get_mono_ptr()); if (!mono_object) { + // Important to clear this before destroying the script instance here script = Ref<CSharpScript>(); - owner->set_script_instance(NULL); + owner = NULL; + + bool die = _unreference_owner_unsafe(); + // Not ok for the owner to die here. If there is a situation where this can happen, it will be considered a bug. + CRASH_COND(die == true); + ERR_EXPLAIN("Failed to allocate memory for the object"); ERR_FAIL_V(NULL); } + // Tie managed to unmanaged + gchandle = MonoGCHandle::create_strong(mono_object); + + if (base_ref) + _reference_owner_unsafe(); // Here, after assigning the gchandle (for the refcount_incremented callback) + 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); - // Tie managed to unmanaged - gchandle = MonoGCHandle::create_strong(mono_object); - return mono_object; } @@ -1546,25 +1654,36 @@ void CSharpInstance::mono_object_disposed(MonoObject *p_obj) { CSharpLanguage::get_singleton()->release_script_gchandle(p_obj, gchandle); } -void CSharpInstance::mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_finalizer, bool &r_owner_deleted) { +void CSharpInstance::mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_finalizer, bool &r_delete_owner, bool &r_remove_script_instance) { #ifdef DEBUG_ENABLED CRASH_COND(!base_ref); CRASH_COND(gchandle.is_null()); #endif + + r_remove_script_instance = false; + if (_unreference_owner_unsafe()) { - r_owner_deleted = true; + // Safe to self destruct here with memdelete(owner), but it's deferred to the caller to prevent future mistakes. + r_delete_owner = true; } else { - r_owner_deleted = false; + r_delete_owner = false; CSharpLanguage::get_singleton()->release_script_gchandle(p_obj, gchandle); - if (p_is_finalizer && !GDMono::get_singleton()->is_finalizing_scripts_domain()) { - // If the native instance is still alive, then it was - // referenced from another thread before the finalizer could - // unreference it and delete it, so we want to keep it. - // GC.ReRegisterForFinalize(this) is not safe because the objects - // referenced by this could have already been collected. - // Instead we will create a new managed instance here. - _internal_new_managed(); + + if (!p_is_finalizer) { + // If the native instance is still alive and Dispose() was called + // (instead of the finalizer), then we remove the script instance. + r_remove_script_instance = true; + } else if (!GDMono::get_singleton()->is_finalizing_scripts_domain()) { + // If the native instance is still alive and this is called from the finalizer, + // then it was referenced from another thread before the finalizer could + // unreference and delete it, so we want to keep it. + // GC.ReRegisterForFinalize(this) is not safe because the objects referenced by 'this' + // could have already been collected. Instead we will create a new managed instance here. + MonoObject *new_managed = _internal_new_managed(); + if (!new_managed) { + r_remove_script_instance = true; + } } } } @@ -1618,7 +1737,7 @@ bool CSharpInstance::refcount_decremented() { return ref_dying; } -MultiplayerAPI::RPCMode CSharpInstance::_member_get_rpc_mode(GDMonoClassMember *p_member) const { +MultiplayerAPI::RPCMode CSharpInstance::_member_get_rpc_mode(IMonoClassMember *p_member) const { if (p_member->has_attribute(CACHED_CLASS(RemoteAttribute))) return MultiplayerAPI::RPC_MODE_REMOTE; @@ -1760,6 +1879,8 @@ CSharpInstance::CSharpInstance() : CSharpInstance::~CSharpInstance() { + destructing_script_instance = true; + if (gchandle.is_valid()) { if (!predelete_notified && !ref_dying) { // This destructor is not called from the owners destructor. @@ -1772,9 +1893,7 @@ CSharpInstance::~CSharpInstance() { if (mono_object) { MonoException *exc = NULL; - destructing_script_instance = true; GDMonoUtils::dispose(mono_object, &exc); - destructing_script_instance = false; if (exc) { GDMonoUtils::set_pending_exception(exc); @@ -1782,11 +1901,23 @@ CSharpInstance::~CSharpInstance() { } } - gchandle->release(); // Make sure it's released + gchandle->release(); // Make sure the gchandle is released } - if (base_ref && !ref_dying && owner) { // it may be called from the owner's destructor - _unreference_owner_unsafe(); + // If not being called from the owner's destructor, and we still hold a reference to the owner + if (base_ref && !ref_dying && owner && unsafe_referenced) { + // The owner's script or script instance is being replaced (or removed) + + // Transfer ownership to an "instance binding" + + void *data = owner->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index()); + CRASH_COND(data == NULL); + + CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get(); + CRASH_COND(!script_binding.inited); + + bool die = _unreference_owner_unsafe(); + CRASH_COND(die == true); // The "instance binding" should be holding a reference } if (script.is_valid() && owner) { @@ -1830,12 +1961,13 @@ void CSharpScript::_update_exports_values(Map<StringName, Variant> &values, List bool CSharpScript::_update_exports() { #ifdef TOOLS_ENABLED - if (!valid) { - for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) { - E->get()->set_build_failed(true); - } + if (!Engine::get_singleton()->is_editor_hint()) + return false; + + placeholder_fallback_enabled = true; // until proven otherwise + + if (!valid) return false; - } bool changed = false; @@ -1853,13 +1985,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); @@ -1944,6 +2084,8 @@ bool CSharpScript::_update_exports() { tmp_object = NULL; } + placeholder_fallback_enabled = false; + if (placeholders.size()) { // Update placeholders if any Map<StringName, Variant> values; @@ -1951,7 +2093,6 @@ bool CSharpScript::_update_exports() { _update_exports_values(values, propnames); for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) { - E->get()->set_build_failed(false); E->get()->update(propnames, values); } } @@ -2030,7 +2171,7 @@ bool CSharpScript::_get_signal(GDMonoClass *p_class, GDMonoClass *p_delegate, Ve * Returns false if there was an error, otherwise true. * If there was an error, r_prop_info and r_exported are not assigned any value. */ -bool CSharpScript::_get_member_export(GDMonoClass *p_class, GDMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported) { +bool CSharpScript::_get_member_export(GDMonoClass *p_class, IMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported) { StringName name = p_member->get_name(); @@ -2045,15 +2186,16 @@ bool CSharpScript::_get_member_export(GDMonoClass *p_class, GDMonoClassMember *p ManagedType type; - if (p_member->get_member_type() == GDMonoClassMember::MEMBER_TYPE_FIELD) { + if (p_member->get_member_type() == IMonoClassMember::MEMBER_TYPE_FIELD) { type = static_cast<GDMonoField *>(p_member)->get_type(); - } else if (p_member->get_member_type() == GDMonoClassMember::MEMBER_TYPE_PROPERTY) { + } else if (p_member->get_member_type() == IMonoClassMember::MEMBER_TYPE_PROPERTY) { type = static_cast<GDMonoProperty *>(p_member)->get_type(); } else { 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); @@ -2061,7 +2203,7 @@ bool CSharpScript::_get_member_export(GDMonoClass *p_class, GDMonoClassMember *p return true; } - if (p_member->get_member_type() == GDMonoClassMember::MEMBER_TYPE_PROPERTY) { + if (p_member->get_member_type() == IMonoClassMember::MEMBER_TYPE_PROPERTY) { GDMonoProperty *property = static_cast<GDMonoProperty *>(p_member); if (!property->has_getter() || !property->has_setter()) { ERR_PRINTS("Cannot export property because it does not provide a getter or a setter: " + p_class->get_full_name() + "." + name.operator String()); @@ -2078,6 +2220,7 @@ bool CSharpScript::_get_member_export(GDMonoClass *p_class, GDMonoClassMember *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; @@ -2124,7 +2267,7 @@ bool CSharpScript::_get_member_export(GDMonoClass *p_class, GDMonoClassMember *p return false; } - if (val != i) { + if (val != (unsigned int)i) { uses_default_values = false; } @@ -2139,8 +2282,16 @@ bool CSharpScript::_get_member_export(GDMonoClass *p_class, GDMonoClassMember *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) { + hint = PROPERTY_HINT_TYPE_STRING; + hint_string = itos(export_info.array.element_type) + ":"; + } 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); @@ -2235,17 +2386,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); @@ -2339,22 +2491,64 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg /* 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. + ref = Ref<Reference>(static_cast<Reference *>(p_owner)); + } + + // If the object had a script instance binding, dispose it before adding the CSharpInstance + if (p_owner->has_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index())) { + void *data = p_owner->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index()); + CRASH_COND(data == NULL); + + CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get(); + if (script_binding.inited && script_binding.gchandle.is_valid()) { + MonoObject *mono_object = script_binding.gchandle->get_target(); + if (mono_object) { + MonoException *exc = NULL; + GDMonoUtils::dispose(mono_object, &exc); + + if (exc) { + GDMonoUtils::set_pending_exception(exc); + } + } + + script_binding.inited = false; + } + } + CSharpInstance *instance = memnew(CSharpInstance); instance->base_ref = p_isref; instance->script = Ref<CSharpScript>(this); instance->owner = p_owner; instance->owner->set_script_instance(instance); - if (instance->base_ref) - instance->_reference_owner_unsafe(); - /* STEP 2, INITIALIZE AND CONSTRUCT */ MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, script_class->get_mono_ptr()); if (!mono_object) { + // Important to clear this before destroying the script instance here instance->script = Ref<CSharpScript>(); - instance->owner->set_script_instance(NULL); + instance->owner = NULL; + + bool die = instance->_unreference_owner_unsafe(); + // Not ok for the owner to die here. If there is a situation where this can happen, it will be considered a bug. + CRASH_COND(die == true); + + p_owner->set_script_instance(NULL); r_error.error = Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL; ERR_EXPLAIN("Failed to allocate memory for the object"); ERR_FAIL_V(NULL); @@ -2363,6 +2557,9 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg // Tie managed to unmanaged instance->gchandle = MonoGCHandle::create_strong(mono_object); + if (instance->base_ref) + instance->_reference_owner_unsafe(); // Here, after assigning the gchandle (for the refcount_incremented callback) + { SCOPED_MUTEX_LOCK(CSharpLanguage::get_singleton()->script_instances_mutex); instances.insert(instance->owner); @@ -2371,7 +2568,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 */ @@ -2590,6 +2786,7 @@ Error CSharpScript::reload(bool p_keep_state) { } load_script_signals(script_class, native); + _update_exports(); } return OK; @@ -2697,6 +2894,7 @@ CSharpScript::CSharpScript() : #ifdef TOOLS_ENABLED source_changed_cache = false; + placeholder_fallback_enabled = false; exports_invalidated = true; #endif @@ -2831,9 +3029,11 @@ Error ResourceFormatSaverCSharpScript::save(const String &p_path, const RES &p_r file->close(); memdelete(file); +#ifdef TOOLS_ENABLED if (ScriptServer::is_reload_scripts_on_save_enabled()) { CSharpLanguage::get_singleton()->reload_tool_script(p_resource, false); } +#endif return OK; } @@ -2855,6 +3055,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 08466bae58..050527d52b 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -82,10 +82,7 @@ class CSharpScript : public Script { Set<Object *> instances; -#ifdef DEBUG_ENABLED - Set<ObjectID> pending_reload_instances; -#endif - +#ifdef GD_MONO_HOT_RELOAD struct StateBackup { // TODO // Replace with buffer containing the serialized state of managed scripts. @@ -93,8 +90,8 @@ class CSharpScript : public Script { List<Pair<StringName, Variant> > properties; }; -#ifdef TOOLS_ENABLED - Map<ObjectID, CSharpScript::StateBackup> pending_reload_state; + Set<ObjectID> pending_reload_instances; + Map<ObjectID, StateBackup> pending_reload_state; #endif String source; @@ -115,6 +112,7 @@ class CSharpScript : public Script { Map<StringName, Variant> exported_members_defval_cache; // member_default_values_cache Set<PlaceHolderScriptInstance *> placeholders; bool source_changed_cache; + bool placeholder_fallback_enabled; bool exports_invalidated; void _update_exports_values(Map<StringName, Variant> &values, List<PropertyInfo> &propnames); virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder); @@ -129,7 +127,7 @@ class CSharpScript : public Script { bool _update_exports(); #ifdef TOOLS_ENABLED - bool _get_member_export(GDMonoClass *p_class, GDMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported); + bool _get_member_export(GDMonoClass *p_class, IMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported); #endif CSharpInstance *_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_isref, Variant::CallError &r_error); @@ -137,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(); @@ -164,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(); @@ -180,6 +178,10 @@ public: virtual int get_member_line(const StringName &p_member) const; +#ifdef TOOLS_ENABLED + virtual bool is_placeholder_fallback_enabled() const { return placeholder_fallback_enabled; } +#endif + Error load_source_code(const String &p_path); StringName get_script_name() const; @@ -204,8 +206,15 @@ class CSharpInstance : public ScriptInstance { Ref<MonoGCHandle> gchandle; bool _reference_owner_unsafe(); + + /* + * If true is returned, the caller must memdelete the script instance's owner. + */ bool _unreference_owner_unsafe(); + /* + * If NULL is returned, the caller must destroy the script instance by removing it from its owner. + */ MonoObject *_internal_new_managed(); // Do not use unless you know what you are doing @@ -214,13 +223,15 @@ class CSharpInstance : public ScriptInstance { void _call_multilevel(MonoObject *p_mono_object, const StringName &p_method, const Variant **p_args, int p_argcount); - MultiplayerAPI::RPCMode _member_get_rpc_mode(GDMonoClassMember *p_member) const; + MultiplayerAPI::RPCMode _member_get_rpc_mode(IMonoClassMember *p_member) const; public: MonoObject *get_mono_object() const; _FORCE_INLINE_ bool is_destructing_script_instance() { return destructing_script_instance; } + virtual Object *get_owner(); + virtual bool set(const StringName &p_name, const Variant &p_value); virtual bool get(const StringName &p_name, Variant &r_ret) const; virtual void get_property_list(List<PropertyInfo> *p_properties) const; @@ -233,7 +244,12 @@ public: virtual void call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount); void mono_object_disposed(MonoObject *p_obj); - void mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_finalizer, bool &r_owner_deleted); + + /* + * If 'r_delete_owner' is set to true, the caller must memdelete the script instance's owner. Otherwise, if + * 'r_remove_script_instance' is set to true, the caller must destroy the script instance by removing it from its owner. + */ + void mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_finalizer, bool &r_delete_owner, bool &r_remove_script_instance); virtual void refcount_incremented(); virtual bool refcount_decremented(); @@ -253,6 +269,7 @@ public: }; struct CSharpScriptBinding { + bool inited; StringName type_name; GDMonoClass *wrapper_class; Ref<MonoGCHandle> gchandle; @@ -281,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 @@ -292,9 +310,19 @@ class CSharpLanguage : public ScriptLanguage { Dictionary scripts_metadata; + // For debug_break and debug_break_parse + int _debug_parse_err_line; + String _debug_parse_err_file; + String _debug_error; + + friend class GDMono; + void _uninitialize_script_bindings(); + 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); @@ -308,7 +336,7 @@ public: bool debug_break(const String &p_error, bool p_allow_continue = true); bool debug_break_parse(const String &p_file, int p_line, const String &p_error); -#ifdef TOOLS_ENABLED +#ifdef GD_MONO_HOT_RELOAD bool is_assembly_reloading_needed(); void reload_assemblies(bool p_soft_reload); #endif @@ -346,11 +374,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) {} @@ -389,6 +417,9 @@ 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 Vector<StackInfo> stack_trace_get_info(MonoObject *p_stack_trace); #endif @@ -398,6 +429,7 @@ public: }; class ResourceFormatLoaderCSharpScript : public ResourceFormatLoader { + GDCLASS(ResourceFormatLoaderCSharpScript, ResourceFormatLoader) public: virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); virtual void get_recognized_extensions(List<String> *p_extensions) const; @@ -406,6 +438,7 @@ public: }; class ResourceFormatSaverCSharpScript : public ResourceFormatSaver { + GDCLASS(ResourceFormatSaverCSharpScript, ResourceFormatSaver) public: virtual Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0); virtual void get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const; 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 16beacb45c..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; @@ -257,7 +252,7 @@ namespace GodotSharpTools.Build if (null == Parameters) throw new LoggerException("Log directory was not set."); - string[] parameters = Parameters.Split(';'); + string[] parameters = Parameters.Split(new[] { ';' }); string logDir = parameters[0]; diff --git a/modules/mono/editor/GodotSharpTools/Editor/GodotSharpExport.cs b/modules/mono/editor/GodotSharpTools/Editor/GodotSharpExport.cs index 5fd708d539..e45dd2025b 100644 --- a/modules/mono/editor/GodotSharpTools/Editor/GodotSharpExport.cs +++ b/modules/mono/editor/GodotSharpTools/Editor/GodotSharpExport.cs @@ -30,7 +30,7 @@ namespace GodotSharpTools.Editor throw new NotSupportedException("Target platform not supported"); } - templateDirName += debug ? ".debug" : ".release"; + templateDirName += debug ? ".release_debug" : ".release"; string templateDirPath = Path.Combine(GetTemplatesDir(), templateDirName); 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 2ce7837a27..89279c69a6 100644 --- a/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs +++ b/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs @@ -21,6 +21,7 @@ namespace GodotSharpTools.Project mainGroup.AddProperty("DocumentationFile", Path.Combine("$(OutputPath)", "$(AssemblyName).xml")); mainGroup.SetProperty("RootNamespace", "Godot"); mainGroup.SetProperty("ProjectGuid", CoreApiProjectGuid); + mainGroup.SetProperty("BaseIntermediateOutputPath", "obj"); GenAssemblyInfoFile(root, dir, CoreApiProjectName, new string[] { "[assembly: InternalsVisibleTo(\"" + EditorApiProjectName + "\")]" }, @@ -46,6 +47,7 @@ namespace GodotSharpTools.Project mainGroup.AddProperty("DocumentationFile", Path.Combine("$(OutputPath)", "$(AssemblyName).xml")); mainGroup.SetProperty("RootNamespace", "Godot"); mainGroup.SetProperty("ProjectGuid", EditorApiProjectGuid); + mainGroup.SetProperty("BaseIntermediateOutputPath", "obj"); GenAssemblyInfoFile(root, dir, EditorApiProjectName); @@ -138,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"; @@ -147,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 166b3e1324..259c0ffece 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -80,6 +80,7 @@ #define ICALL_GET_METHODBIND ICALL_PREFIX "Object_ClassDB_get_method" #define C_LOCAL_RET "ret" +#define C_LOCAL_VARARG_RET "vararg_ret" #define C_LOCAL_PTRCALL_ARGS "call_args" #define C_MACRO_OBJECT_CONSTRUCT "GODOTSHARP_INSTANCE_OBJECT" @@ -96,14 +97,24 @@ #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(5) +#define BINDINGS_GENERATOR_VERSION UINT32_C(8) -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; BindingsGenerator *BindingsGenerator::singleton = NULL; +static String fix_doc_description(const String &p_bbcode) { + + // 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) { String ret; @@ -172,6 +183,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()); @@ -294,44 +756,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 @@ -355,49 +820,51 @@ void BindingsGenerator::_generate_global_constants(List<String> &p_output) { WARN_PRINTS("Declaring global enum `" + enum_proxy_name + "` inside static class `" + enum_class_name + "`"); } - 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 + + p_output.append("\n#pragma warning restore CS1591\n"); } Error BindingsGenerator::generate_cs_core_project(const String &p_solution_dir, DotNetSolution &r_solution, bool p_verbose_output) { @@ -425,7 +892,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); @@ -471,8 +938,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); } @@ -485,28 +950,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()) @@ -516,7 +981,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"); @@ -583,29 +1048,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()) @@ -615,7 +1080,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"); @@ -704,61 +1169,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()); + OS::get_singleton()->print("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) { @@ -768,31 +1236,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 @@ -801,38 +1269,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 @@ -848,50 +1316,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; @@ -918,15 +1394,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); @@ -971,86 +1449,74 @@ 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" 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); } 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" 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); } - 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); @@ -1062,7 +1528,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()) { @@ -1128,7 +1594,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); } @@ -1137,61 +1606,58 @@ 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("\")]"); } - 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 @@ -1200,16 +1666,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 } @@ -1223,21 +1689,22 @@ 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; } @@ -1249,11 +1716,11 @@ Error BindingsGenerator::generate_glue(const String &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(); @@ -1273,7 +1740,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 @@ -1293,11 +1760,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) { @@ -1306,43 +1773,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; @@ -1351,11 +1818,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; } } @@ -1365,23 +1832,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; } } @@ -1391,14 +1858,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) @@ -1413,23 +1880,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 @@ -1485,15 +1949,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 + "()"); @@ -1501,6 +1965,15 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte String ptrcall_return_type; String initialization; + if (p_imethod.is_vararg && return_type->cname != name_cache.type_Variant) { + // VarArg methods always return Variant, but there are some cases in which MethodInfo provides + // a specific return type. We trust this information is valid. We need a temporary local to keep + // 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.append("\tVariant " C_LOCAL_VARARG_RET ";\n"); + } + if (return_type->is_object_type) { ptrcall_return_type = return_type->is_reference ? "Ref<Reference>" : return_type->c_type; initialization = return_type->is_reference ? "" : " = NULL"; @@ -1508,79 +1981,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) - p_output.push_back(C_LOCAL_RET " = "); + if (!ret_void) { + // See the comment on the C_LOCAL_VARARG_RET declaration + if (return_type->cname != name_cache.type_Variant) { + p_output.append(C_LOCAL_VARARG_RET " = "); + } else { + 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.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; @@ -1701,7 +2177,6 @@ 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); @@ -1709,6 +2184,8 @@ void BindingsGenerator::_populate_object_type_interfaces() { iprop.index = ClassDB::get_property_index(type_cname, iprop.cname, &valid); ERR_FAIL_COND(!valid); + iprop.proxy_name = escape_csharp_keyword(snake_to_pascal_case(iprop.cname)); + // Prevent property and enclosing type from sharing the same name if (iprop.proxy_name == itype.proxy_name) { if (verbose_output) { @@ -1719,6 +2196,8 @@ void BindingsGenerator::_populate_object_type_interfaces() { 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++) { @@ -1860,8 +2339,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; @@ -1876,8 +2355,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; @@ -1892,13 +2371,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); @@ -1930,7 +2409,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); @@ -2011,6 +2490,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()"; @@ -2033,7 +2513,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") @@ -2323,9 +2804,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); @@ -2410,9 +2891,7 @@ void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args) 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) { @@ -2424,9 +2903,7 @@ void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args) } --options_left; - } else if (elem->get() == cs_api_option) { - const List<String>::Element *path_elem = elem->next(); if (path_elem) { diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h index 91c474c4f0..a073c09910 100644 --- a/modules/mono/editor/bindings_generator.h +++ b/modules/mono/editor/bindings_generator.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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) { } }; @@ -321,6 +327,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) @@ -482,6 +497,8 @@ class BindingsGenerator { StringName type_VarArg; StringName type_Object; StringName type_Reference; + StringName type_String; + StringName type_at_GlobalScope; StringName enum_Error; NameCache() { @@ -493,6 +510,8 @@ class BindingsGenerator { 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"); } @@ -511,6 +530,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 +550,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); @@ -539,14 +569,14 @@ 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() {} diff --git a/modules/mono/editor/csharp_project.cpp b/modules/mono/editor/csharp_project.cpp index 416108603b..beeff51bc2 100644 --- a/modules/mono/editor/csharp_project.cpp +++ b/modules/mono/editor/csharp_project.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -228,6 +228,15 @@ Error generate_scripts_metadata(const String &p_project_path, const String &p_ou if (new_dict.size()) { String json = JSON::print(new_dict, "", false); + String base_dir = p_output_path.get_base_dir(); + + if (!DirAccess::exists(base_dir)) { + DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + + Error err = da->make_dir_recursive(base_dir); + ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); + } + Error ferr; FileAccess *f = FileAccess::open(p_output_path, FileAccess::WRITE, &ferr); ERR_EXPLAIN("Cannot open file for writing: " + p_output_path); diff --git a/modules/mono/editor/csharp_project.h b/modules/mono/editor/csharp_project.h index aeeeff50f0..3d5a65f8da 100644 --- a/modules/mono/editor/csharp_project.h +++ b/modules/mono/editor/csharp_project.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/modules/mono/editor/dotnet_solution.cpp b/modules/mono/editor/dotnet_solution.cpp index ab92e2e378..324752cafc 100644 --- a/modules/mono/editor/dotnet_solution.cpp +++ b/modules/mono/editor/dotnet_solution.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/modules/mono/editor/dotnet_solution.h b/modules/mono/editor/dotnet_solution.h index 629605ad18..18933364fa 100644 --- a/modules/mono/editor/dotnet_solution.h +++ b/modules/mono/editor/dotnet_solution.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/modules/mono/editor/godotsharp_builds.cpp b/modules/mono/editor/godotsharp_builds.cpp index 0f12fc5243..00c780d1b7 100644 --- a/modules/mono/editor/godotsharp_builds.cpp +++ b/modules/mono/editor/godotsharp_builds.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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) { @@ -381,6 +373,9 @@ bool GodotSharpBuilds::build_project_blocking(const String &p_config) { bool GodotSharpBuilds::editor_build_callback() { + if (!FileAccess::exists(GodotSharpDirs::get_project_sln_path())) + return true; // No solution to build + 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"); @@ -456,7 +451,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 @@ -464,6 +463,8 @@ GodotSharpBuilds::GodotSharpBuilds() { "," PROP_NAME_MSBUILD_VS #endif "," PROP_NAME_XBUILD)); + + EDITOR_DEF("mono/builds/print_build_output", false); } GodotSharpBuilds::~GodotSharpBuilds() { @@ -514,7 +515,7 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) { // Remove old issues file - String issues_file = "msbuild_issues.csv"; + String issues_file = get_msbuild_issues_filename(); DirAccessRef d = DirAccess::create_for_path(log_dirpath); if (d->file_exists(issues_file)) { Error err = d->remove(issues_file); @@ -581,7 +582,8 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) { exit_code = klass->get_field("exitCode")->get_int_value(mono_object); if (exit_code != 0) { - print_verbose("MSBuild finished with exit code " + itos(exit_code)); + String log_filepath = build_info.get_log_dirpath().plus_file(get_msbuild_log_filename()); + print_verbose("MSBuild exited with code: " + itos(exit_code) + ". Log file: " + log_filepath); } build_tab->on_build_exit(exit_code == 0 ? MonoBuildTab::RESULT_SUCCESS : MonoBuildTab::RESULT_ERROR); diff --git a/modules/mono/editor/godotsharp_builds.h b/modules/mono/editor/godotsharp_builds.h index 7f38b0aa49..652d30538a 100644 --- a/modules/mono/editor/godotsharp_builds.h +++ b/modules/mono/editor/godotsharp_builds.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -76,6 +76,9 @@ public: static void show_build_error_dialog(const String &p_message); + static const char *get_msbuild_issues_filename() { return "msbuild_issues.csv"; } + static const char *get_msbuild_log_filename() { return "msbuild_log.txt"; } + void build_exit_callback(const MonoBuildInfo &p_build_info, int p_exit_code); void restart_build(MonoBuildTab *p_build_tab); diff --git a/modules/mono/editor/godotsharp_editor.cpp b/modules/mono/editor/godotsharp_editor.cpp index cce86efbf5..9d42528927 100644 --- a/modules/mono/editor/godotsharp_editor.cpp +++ b/modules/mono/editor/godotsharp_editor.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -30,6 +30,7 @@ #include "godotsharp_editor.h" +#include "core/message_queue.h" #include "core/os/os.h" #include "core/project_settings.h" #include "scene/gui/control.h" @@ -114,10 +115,33 @@ bool GodotSharpEditor::_create_project_solution() { void GodotSharpEditor::_make_api_solutions_if_needed() { // I'm sick entirely of ProgressDialog + + static int attempts_left = 100; + + if (MessageQueue::get_singleton()->is_flushing() || !SceneTree::get_singleton()) { + ERR_FAIL_COND(attempts_left == 0); // You've got to be kidding + + if (SceneTree::get_singleton()) { + SceneTree::get_singleton()->connect("idle_frame", this, "_make_api_solutions_if_needed", Vector<Variant>()); + } else { + call_deferred("_make_api_solutions_if_needed"); + } + + attempts_left--; + return; + } + + // Recursion guard needed because signals don't play well with ProgressDialog either, but unlike + // the message queue, with signals the collateral damage should be minimal in the worst case. static bool recursion_guard = false; if (!recursion_guard) { recursion_guard = true; + + // Oneshot signals don't play well with ProgressDialog either, so we do it this way instead + SceneTree::get_singleton()->disconnect("idle_frame", this, "_make_api_solutions_if_needed"); + _make_api_solutions_if_needed_impl(); + recursion_guard = false; } } @@ -143,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(); } @@ -164,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) { @@ -199,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); @@ -249,7 +281,29 @@ Error GodotSharpEditor::open_in_external_editor(const Ref<Script> &p_script, int if (vscode_path.empty() || !FileAccess::exists(vscode_path)) { // Try to search it again if it wasn't found last time or if it was removed from its location - vscode_path = path_which("code"); + 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; @@ -367,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 { @@ -400,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); @@ -423,7 +480,7 @@ GodotSharpEditor::GodotSharpEditor(EditorNode *p_editor) { String csproj_path = GodotSharpDirs::get_project_csproj_path(); if (FileAccess::exists(sln_path) && FileAccess::exists(csproj_path)) { - // We can't use EditorProgress here. It calls Main::iterarion() and the main loop is not initialized yet. + // Defer this task because EditorProgress calls Main::iterarion() and the main loop is not yet initialized. call_deferred("_make_api_solutions_if_needed"); } else { bottom_panel_btn->hide(); @@ -432,22 +489,24 @@ 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(); EDITOR_DEF("mono/editor/external_editor", EDITOR_NONE); - String settings_hint_str = "None"; + 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 9fb0e40132..d9523c384c 100644 --- a/modules/mono/editor/godotsharp_editor.h +++ b/modules/mono/editor/godotsharp_editor.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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 34c710320a..ee5fed1a0c 100644 --- a/modules/mono/editor/godotsharp_export.cpp +++ b/modules/mono/editor/godotsharp_export.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -85,53 +85,55 @@ void GodotSharpExport::_export_begin(const Set<String> &p_features, bool p_debug ERR_FAIL_NULL(TOOLS_DOMAIN); ERR_FAIL_NULL(GDMono::get_singleton()->get_editor_tools_assembly()); - String build_config = p_debug ? "Debug" : "Release"; + if (FileAccess::exists(GodotSharpDirs::get_project_sln_path())) { + String build_config = p_debug ? "Debug" : "Release"; - String scripts_metadata_path = GodotSharpDirs::get_res_metadata_dir().plus_file("scripts_metadata." + String(p_debug ? "debug" : "release")); - Error metadata_err = CSharpProject::generate_scripts_metadata(GodotSharpDirs::get_project_csproj_path(), scripts_metadata_path); - ERR_FAIL_COND(metadata_err != OK); + String scripts_metadata_path = GodotSharpDirs::get_res_metadata_dir().plus_file("scripts_metadata." + String(p_debug ? "debug" : "release")); + Error metadata_err = CSharpProject::generate_scripts_metadata(GodotSharpDirs::get_project_csproj_path(), scripts_metadata_path); + ERR_FAIL_COND(metadata_err != OK); - ERR_FAIL_COND(!_add_file(scripts_metadata_path, scripts_metadata_path)); + ERR_FAIL_COND(!_add_file(scripts_metadata_path, scripts_metadata_path)); - ERR_FAIL_COND(!GodotSharpBuilds::build_project_blocking(build_config)); + ERR_FAIL_COND(!GodotSharpBuilds::build_project_blocking(build_config)); - // Add dependency assemblies + // Add dependency assemblies - Map<String, String> dependencies; + Map<String, String> dependencies; - String project_dll_name = ProjectSettings::get_singleton()->get("application/config/name"); - if (project_dll_name.empty()) { - project_dll_name = "UnnamedProject"; - } + String project_dll_name = ProjectSettings::get_singleton()->get("application/config/name"); + if (project_dll_name.empty()) { + project_dll_name = "UnnamedProject"; + } - String project_dll_src_dir = GodotSharpDirs::get_res_temp_assemblies_base_dir().plus_file(build_config); - String project_dll_src_path = project_dll_src_dir.plus_file(project_dll_name + ".dll"); - dependencies.insert(project_dll_name, project_dll_src_path); + String project_dll_src_dir = GodotSharpDirs::get_res_temp_assemblies_base_dir().plus_file(build_config); + String project_dll_src_path = project_dll_src_dir.plus_file(project_dll_name + ".dll"); + dependencies.insert(project_dll_name, project_dll_src_path); - { - MonoDomain *export_domain = GDMonoUtils::create_domain("GodotEngine.ProjectExportDomain"); - ERR_FAIL_NULL(export_domain); - _GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(export_domain); + { + MonoDomain *export_domain = GDMonoUtils::create_domain("GodotEngine.ProjectExportDomain"); + ERR_FAIL_NULL(export_domain); + _GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(export_domain); - _GDMONO_SCOPE_DOMAIN_(export_domain); + _GDMONO_SCOPE_DOMAIN_(export_domain); - GDMonoAssembly *scripts_assembly = NULL; - bool load_success = GDMono::get_singleton()->load_assembly_from(project_dll_name, - project_dll_src_path, &scripts_assembly, /* refonly: */ true); + GDMonoAssembly *scripts_assembly = NULL; + bool load_success = GDMono::get_singleton()->load_assembly_from(project_dll_name, + project_dll_src_path, &scripts_assembly, /* refonly: */ true); - ERR_EXPLAIN("Cannot load refonly assembly: " + project_dll_name); - ERR_FAIL_COND(!load_success); + ERR_EXPLAIN("Cannot load refonly assembly: " + project_dll_name); + ERR_FAIL_COND(!load_success); - Vector<String> search_dirs; - GDMonoAssembly::fill_search_dirs(search_dirs); - Error depend_error = _get_assembly_dependencies(scripts_assembly, search_dirs, dependencies); - ERR_FAIL_COND(depend_error != OK); - } + Vector<String> 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); + } - for (Map<String, String>::Element *E = dependencies.front(); E; E = E->next()) { - String depend_src_path = E->value(); - String depend_dst_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(depend_src_path.get_file()); - ERR_FAIL_COND(!_add_file(depend_src_path, depend_dst_path)); + for (Map<String, String>::Element *E = dependencies.front(); E; E = E->next()) { + String depend_src_path = E->value(); + String depend_dst_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(depend_src_path.get_file()); + ERR_FAIL_COND(!_add_file(depend_src_path, depend_dst_path)); + } } // Mono specific export template extras (data dir) @@ -192,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/godotsharp_export.h b/modules/mono/editor/godotsharp_export.h index 10d4375567..4dc8ea75d5 100644 --- a/modules/mono/editor/godotsharp_export.h +++ b/modules/mono/editor/godotsharp_export.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/modules/mono/editor/mono_bottom_panel.cpp b/modules/mono/editor/mono_bottom_panel.cpp index e89d21d92d..21ce9ca5c4 100644 --- a/modules/mono/editor/mono_bottom_panel.cpp +++ b/modules/mono/editor/mono_bottom_panel.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -30,6 +30,9 @@ #include "mono_bottom_panel.h" +#include "editor/plugins/script_editor_plugin.h" +#include "editor/script_editor_debugger.h" + #include "../csharp_script.h" #include "../godotsharp_dirs.h" #include "csharp_project.h" @@ -122,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); @@ -150,14 +158,32 @@ void MonoBottomPanel::_errors_toggled(bool p_pressed) { void MonoBottomPanel::_build_project_pressed() { - 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); + if (!FileAccess::exists(GodotSharpDirs::get_project_sln_path())) + return; // No solution to build + + 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) { + // Notify running game for hot-reload + ScriptEditor::get_singleton()->get_debugger()->reload_scripts(); + + // Hot-reload in the editor MonoReloadNode::get_singleton()->restart_reload_timer(); + if (CSharpLanguage::get_singleton()->is_assembly_reloading_needed()) { CSharpLanguage::get_singleton()->reload_assemblies(false); } @@ -176,7 +202,7 @@ void MonoBottomPanel::_view_log_pressed() { String log_dirpath = build_tab->get_build_info().get_log_dirpath(); - OS::get_singleton()->shell_open(log_dirpath.plus_file("msbuild_log.txt")); + OS::get_singleton()->shell_open(log_dirpath.plus_file(GodotSharpBuilds::get_msbuild_log_filename())); } } @@ -410,7 +436,7 @@ void MonoBuildTab::on_build_exit(BuildResult result) { build_exited = true; build_result = result; - _load_issues_from_file(logs_dir.plus_file("msbuild_issues.csv")); + _load_issues_from_file(logs_dir.plus_file(GodotSharpBuilds::get_msbuild_issues_filename())); _update_issues_list(); MonoBottomPanel::get_singleton()->raise_build_tab(this); diff --git a/modules/mono/editor/mono_bottom_panel.h b/modules/mono/editor/mono_bottom_panel.h index 03240e9a13..406e46f7ce 100644 --- a/modules/mono/editor/mono_bottom_panel.h +++ b/modules/mono/editor/mono_bottom_panel.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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/mono_build_info.cpp b/modules/mono/editor/mono_build_info.cpp index e4af2aac4f..b386c06435 100644 --- a/modules/mono/editor/mono_build_info.cpp +++ b/modules/mono/editor/mono_build_info.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/modules/mono/editor/mono_build_info.h b/modules/mono/editor/mono_build_info.h index 64ba0f4037..b0ae2ed52e 100644 --- a/modules/mono/editor/mono_build_info.h +++ b/modules/mono/editor/mono_build_info.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/modules/mono/editor/monodevelop_instance.cpp b/modules/mono/editor/monodevelop_instance.cpp index 1d858d80bf..3caa56d1d0 100644 --- a/modules/mono/editor/monodevelop_instance.cpp +++ b/modules/mono/editor/monodevelop_instance.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/modules/mono/editor/monodevelop_instance.h b/modules/mono/editor/monodevelop_instance.h index 29de4a4735..3b3af9607b 100644 --- a/modules/mono/editor/monodevelop_instance.h +++ b/modules/mono/editor/monodevelop_instance.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/modules/mono/editor/script_class_parser.cpp b/modules/mono/editor/script_class_parser.cpp index 9042bff74a..dfb652a7aa 100644 --- a/modules/mono/editor/script_class_parser.cpp +++ b/modules/mono/editor/script_class_parser.cpp @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* script_class_parser.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 "script_class_parser.h" #include "core/map.h" @@ -236,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) { @@ -292,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; @@ -314,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; } @@ -343,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; } @@ -537,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/editor/script_class_parser.h b/modules/mono/editor/script_class_parser.h index 184adebaf2..39003336ac 100644 --- a/modules/mono/editor/script_class_parser.h +++ b/modules/mono/editor/script_class_parser.h @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* script_class_parser.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 SCRIPT_CLASS_PARSER_H #define SCRIPT_CLASS_PARSER_H 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 fc5bb010a9..88fa3323c2 100644 --- a/modules/mono/glue/Managed/Files/Color.cs +++ b/modules/mono/glue/Managed/Files/Color.cs @@ -104,17 +104,26 @@ namespace Godot } } - private static readonly Color black = new Color(0f, 0f, 0f); - - public Color Black + public static Color ColorN(string name, float alpha = 1f) { - get + name = name.Replace(" ", String.Empty); + name = name.Replace("-", String.Empty); + name = name.Replace("_", String.Empty); + name = name.Replace("'", String.Empty); + name = name.Replace(".", String.Empty); + name = name.ToLower(); + + if (!Colors.namedColors.ContainsKey(name)) { - return black; + throw new ArgumentOutOfRangeException($"Invalid Color Name: {name}"); } + + Color color = Colors.namedColors[name]; + color.a = alpha; + return color; } - public float this [int index] + public float this[int index] { get { @@ -380,7 +389,7 @@ namespace Godot return txt; } - // Constructors + // Constructors public Color(float r, float g, float b, float a = 1.0f) { this.r = r; diff --git a/modules/mono/glue/Managed/Files/Colors.cs b/modules/mono/glue/Managed/Files/Colors.cs new file mode 100644 index 0000000000..bc2a1a3bd7 --- /dev/null +++ b/modules/mono/glue/Managed/Files/Colors.cs @@ -0,0 +1,303 @@ +using System; +using System.Collections.Generic; + +namespace Godot +{ + public static class Colors + { + // Color names and values are derived from core/color_names.inc + internal static readonly Dictionary<string, Color> namedColors = new Dictionary<string, Color> { + {"aliceblue", new Color(0.94f, 0.97f, 1.00f)}, + {"antiquewhite", new Color(0.98f, 0.92f, 0.84f)}, + {"aqua", new Color(0.00f, 1.00f, 1.00f)}, + {"aquamarine", new Color(0.50f, 1.00f, 0.83f)}, + {"azure", new Color(0.94f, 1.00f, 1.00f)}, + {"beige", new Color(0.96f, 0.96f, 0.86f)}, + {"bisque", new Color(1.00f, 0.89f, 0.77f)}, + {"black", new Color(0.00f, 0.00f, 0.00f)}, + {"blanchedalmond", new Color(1.00f, 0.92f, 0.80f)}, + {"blue", new Color(0.00f, 0.00f, 1.00f)}, + {"blueviolet", new Color(0.54f, 0.17f, 0.89f)}, + {"brown", new Color(0.65f, 0.16f, 0.16f)}, + {"burlywood", new Color(0.87f, 0.72f, 0.53f)}, + {"cadetblue", new Color(0.37f, 0.62f, 0.63f)}, + {"chartreuse", new Color(0.50f, 1.00f, 0.00f)}, + {"chocolate", new Color(0.82f, 0.41f, 0.12f)}, + {"coral", new Color(1.00f, 0.50f, 0.31f)}, + {"cornflower", new Color(0.39f, 0.58f, 0.93f)}, + {"cornsilk", new Color(1.00f, 0.97f, 0.86f)}, + {"crimson", new Color(0.86f, 0.08f, 0.24f)}, + {"cyan", new Color(0.00f, 1.00f, 1.00f)}, + {"darkblue", new Color(0.00f, 0.00f, 0.55f)}, + {"darkcyan", new Color(0.00f, 0.55f, 0.55f)}, + {"darkgoldenrod", new Color(0.72f, 0.53f, 0.04f)}, + {"darkgray", new Color(0.66f, 0.66f, 0.66f)}, + {"darkgreen", new Color(0.00f, 0.39f, 0.00f)}, + {"darkkhaki", new Color(0.74f, 0.72f, 0.42f)}, + {"darkmagenta", new Color(0.55f, 0.00f, 0.55f)}, + {"darkolivegreen", new Color(0.33f, 0.42f, 0.18f)}, + {"darkorange", new Color(1.00f, 0.55f, 0.00f)}, + {"darkorchid", new Color(0.60f, 0.20f, 0.80f)}, + {"darkred", new Color(0.55f, 0.00f, 0.00f)}, + {"darksalmon", new Color(0.91f, 0.59f, 0.48f)}, + {"darkseagreen", new Color(0.56f, 0.74f, 0.56f)}, + {"darkslateblue", new Color(0.28f, 0.24f, 0.55f)}, + {"darkslategray", new Color(0.18f, 0.31f, 0.31f)}, + {"darkturquoise", new Color(0.00f, 0.81f, 0.82f)}, + {"darkviolet", new Color(0.58f, 0.00f, 0.83f)}, + {"deeppink", new Color(1.00f, 0.08f, 0.58f)}, + {"deepskyblue", new Color(0.00f, 0.75f, 1.00f)}, + {"dimgray", new Color(0.41f, 0.41f, 0.41f)}, + {"dodgerblue", new Color(0.12f, 0.56f, 1.00f)}, + {"firebrick", new Color(0.70f, 0.13f, 0.13f)}, + {"floralwhite", new Color(1.00f, 0.98f, 0.94f)}, + {"forestgreen", new Color(0.13f, 0.55f, 0.13f)}, + {"fuchsia", new Color(1.00f, 0.00f, 1.00f)}, + {"gainsboro", new Color(0.86f, 0.86f, 0.86f)}, + {"ghostwhite", new Color(0.97f, 0.97f, 1.00f)}, + {"gold", new Color(1.00f, 0.84f, 0.00f)}, + {"goldenrod", new Color(0.85f, 0.65f, 0.13f)}, + {"gray", new Color(0.75f, 0.75f, 0.75f)}, + {"green", new Color(0.00f, 1.00f, 0.00f)}, + {"greenyellow", new Color(0.68f, 1.00f, 0.18f)}, + {"honeydew", new Color(0.94f, 1.00f, 0.94f)}, + {"hotpink", new Color(1.00f, 0.41f, 0.71f)}, + {"indianred", new Color(0.80f, 0.36f, 0.36f)}, + {"indigo", new Color(0.29f, 0.00f, 0.51f)}, + {"ivory", new Color(1.00f, 1.00f, 0.94f)}, + {"khaki", new Color(0.94f, 0.90f, 0.55f)}, + {"lavender", new Color(0.90f, 0.90f, 0.98f)}, + {"lavenderblush", new Color(1.00f, 0.94f, 0.96f)}, + {"lawngreen", new Color(0.49f, 0.99f, 0.00f)}, + {"lemonchiffon", new Color(1.00f, 0.98f, 0.80f)}, + {"lightblue", new Color(0.68f, 0.85f, 0.90f)}, + {"lightcoral", new Color(0.94f, 0.50f, 0.50f)}, + {"lightcyan", new Color(0.88f, 1.00f, 1.00f)}, + {"lightgoldenrod", new Color(0.98f, 0.98f, 0.82f)}, + {"lightgray", new Color(0.83f, 0.83f, 0.83f)}, + {"lightgreen", new Color(0.56f, 0.93f, 0.56f)}, + {"lightpink", new Color(1.00f, 0.71f, 0.76f)}, + {"lightsalmon", new Color(1.00f, 0.63f, 0.48f)}, + {"lightseagreen", new Color(0.13f, 0.70f, 0.67f)}, + {"lightskyblue", new Color(0.53f, 0.81f, 0.98f)}, + {"lightslategray", new Color(0.47f, 0.53f, 0.60f)}, + {"lightsteelblue", new Color(0.69f, 0.77f, 0.87f)}, + {"lightyellow", new Color(1.00f, 1.00f, 0.88f)}, + {"lime", new Color(0.00f, 1.00f, 0.00f)}, + {"limegreen", new Color(0.20f, 0.80f, 0.20f)}, + {"linen", new Color(0.98f, 0.94f, 0.90f)}, + {"magenta", new Color(1.00f, 0.00f, 1.00f)}, + {"maroon", new Color(0.69f, 0.19f, 0.38f)}, + {"mediumaquamarine", new Color(0.40f, 0.80f, 0.67f)}, + {"mediumblue", new Color(0.00f, 0.00f, 0.80f)}, + {"mediumorchid", new Color(0.73f, 0.33f, 0.83f)}, + {"mediumpurple", new Color(0.58f, 0.44f, 0.86f)}, + {"mediumseagreen", new Color(0.24f, 0.70f, 0.44f)}, + {"mediumslateblue", new Color(0.48f, 0.41f, 0.93f)}, + {"mediumspringgreen", new Color(0.00f, 0.98f, 0.60f)}, + {"mediumturquoise", new Color(0.28f, 0.82f, 0.80f)}, + {"mediumvioletred", new Color(0.78f, 0.08f, 0.52f)}, + {"midnightblue", new Color(0.10f, 0.10f, 0.44f)}, + {"mintcream", new Color(0.96f, 1.00f, 0.98f)}, + {"mistyrose", new Color(1.00f, 0.89f, 0.88f)}, + {"moccasin", new Color(1.00f, 0.89f, 0.71f)}, + {"navajowhite", new Color(1.00f, 0.87f, 0.68f)}, + {"navyblue", new Color(0.00f, 0.00f, 0.50f)}, + {"oldlace", new Color(0.99f, 0.96f, 0.90f)}, + {"olive", new Color(0.50f, 0.50f, 0.00f)}, + {"olivedrab", new Color(0.42f, 0.56f, 0.14f)}, + {"orange", new Color(1.00f, 0.65f, 0.00f)}, + {"orangered", new Color(1.00f, 0.27f, 0.00f)}, + {"orchid", new Color(0.85f, 0.44f, 0.84f)}, + {"palegoldenrod", new Color(0.93f, 0.91f, 0.67f)}, + {"palegreen", new Color(0.60f, 0.98f, 0.60f)}, + {"paleturquoise", new Color(0.69f, 0.93f, 0.93f)}, + {"palevioletred", new Color(0.86f, 0.44f, 0.58f)}, + {"papayawhip", new Color(1.00f, 0.94f, 0.84f)}, + {"peachpuff", new Color(1.00f, 0.85f, 0.73f)}, + {"peru", new Color(0.80f, 0.52f, 0.25f)}, + {"pink", new Color(1.00f, 0.75f, 0.80f)}, + {"plum", new Color(0.87f, 0.63f, 0.87f)}, + {"powderblue", new Color(0.69f, 0.88f, 0.90f)}, + {"purple", new Color(0.63f, 0.13f, 0.94f)}, + {"rebeccapurple", new Color(0.40f, 0.20f, 0.60f)}, + {"red", new Color(1.00f, 0.00f, 0.00f)}, + {"rosybrown", new Color(0.74f, 0.56f, 0.56f)}, + {"royalblue", new Color(0.25f, 0.41f, 0.88f)}, + {"saddlebrown", new Color(0.55f, 0.27f, 0.07f)}, + {"salmon", new Color(0.98f, 0.50f, 0.45f)}, + {"sandybrown", new Color(0.96f, 0.64f, 0.38f)}, + {"seagreen", new Color(0.18f, 0.55f, 0.34f)}, + {"seashell", new Color(1.00f, 0.96f, 0.93f)}, + {"sienna", new Color(0.63f, 0.32f, 0.18f)}, + {"silver", new Color(0.75f, 0.75f, 0.75f)}, + {"skyblue", new Color(0.53f, 0.81f, 0.92f)}, + {"slateblue", new Color(0.42f, 0.35f, 0.80f)}, + {"slategray", new Color(0.44f, 0.50f, 0.56f)}, + {"snow", new Color(1.00f, 0.98f, 0.98f)}, + {"springgreen", new Color(0.00f, 1.00f, 0.50f)}, + {"steelblue", new Color(0.27f, 0.51f, 0.71f)}, + {"tan", new Color(0.82f, 0.71f, 0.55f)}, + {"teal", new Color(0.00f, 0.50f, 0.50f)}, + {"thistle", new Color(0.85f, 0.75f, 0.85f)}, + {"tomato", new Color(1.00f, 0.39f, 0.28f)}, + {"turquoise", new Color(0.25f, 0.88f, 0.82f)}, + {"violet", new Color(0.93f, 0.51f, 0.93f)}, + {"webgreen", new Color(0.00f, 0.50f, 0.00f)}, + {"webgray", new Color(0.50f, 0.50f, 0.50f)}, + {"webmaroon", new Color(0.50f, 0.00f, 0.00f)}, + {"webpurple", new Color(0.50f, 0.00f, 0.50f)}, + {"wheat", new Color(0.96f, 0.87f, 0.70f)}, + {"white", new Color(1.00f, 1.00f, 1.00f)}, + {"whitesmoke", new Color(0.96f, 0.96f, 0.96f)}, + {"yellow", new Color(1.00f, 1.00f, 0.00f)}, + {"yellowgreen", new Color(0.60f, 0.80f, 0.20f)}, + }; + + public static Color AliceBlue { get { return namedColors["aliceblue"]; } } + public static Color AntiqueWhite { get { return namedColors["antiquewhite"]; } } + public static Color Aqua { get { return namedColors["aqua"]; } } + public static Color Aquamarine { get { return namedColors["aquamarine"]; } } + public static Color Azure { get { return namedColors["azure"]; } } + public static Color Beige { get { return namedColors["beige"]; } } + public static Color Bisque { get { return namedColors["bisque"]; } } + public static Color Black { get { return namedColors["black"]; } } + public static Color BlanchedAlmond { get { return namedColors["blanchedalmond"]; } } + public static Color Blue { get { return namedColors["blue"]; } } + public static Color BlueViolet { get { return namedColors["blueviolet"]; } } + public static Color Brown { get { return namedColors["brown"]; } } + public static Color BurlyWood { get { return namedColors["burlywood"]; } } + public static Color CadetBlue { get { return namedColors["cadetblue"]; } } + public static Color Chartreuse { get { return namedColors["chartreuse"]; } } + public static Color Chocolate { get { return namedColors["chocolate"]; } } + public static Color Coral { get { return namedColors["coral"]; } } + public static Color Cornflower { get { return namedColors["cornflower"]; } } + public static Color Cornsilk { get { return namedColors["cornsilk"]; } } + public static Color Crimson { get { return namedColors["crimson"]; } } + public static Color Cyan { get { return namedColors["cyan"]; } } + public static Color DarkBlue { get { return namedColors["darkblue"]; } } + public static Color DarkCyan { get { return namedColors["darkcyan"]; } } + public static Color DarkGoldenrod { get { return namedColors["darkgoldenrod"]; } } + public static Color DarkGray { get { return namedColors["darkgray"]; } } + public static Color DarkGreen { get { return namedColors["darkgreen"]; } } + public static Color DarkKhaki { get { return namedColors["darkkhaki"]; } } + public static Color DarkMagenta { get { return namedColors["darkmagenta"]; } } + public static Color DarkOliveGreen { get { return namedColors["darkolivegreen"]; } } + public static Color DarkOrange { get { return namedColors["darkorange"]; } } + public static Color DarkOrchid { get { return namedColors["darkorchid"]; } } + public static Color DarkRed { get { return namedColors["darkred"]; } } + public static Color DarkSalmon { get { return namedColors["darksalmon"]; } } + public static Color DarkSeagreen { get { return namedColors["darkseagreen"]; } } + public static Color DarkSlateBlue { get { return namedColors["darkslateblue"]; } } + public static Color DarkSlateGray { get { return namedColors["darkslategray"]; } } + public static Color DarkTurquoise { get { return namedColors["darkturquoise"]; } } + public static Color DarkViolet { get { return namedColors["darkviolet"]; } } + public static Color DeepPink { get { return namedColors["deeppink"]; } } + public static Color DeepSkyBlue { get { return namedColors["deepskyblue"]; } } + public static Color DimGray { get { return namedColors["dimgray"]; } } + public static Color DodgerBlue { get { return namedColors["dodgerblue"]; } } + public static Color Firebrick { get { return namedColors["firebrick"]; } } + public static Color FloralWhite { get { return namedColors["floralwhite"]; } } + public static Color ForestGreen { get { return namedColors["forestgreen"]; } } + public static Color Fuchsia { get { return namedColors["fuchsia"]; } } + public static Color Gainsboro { get { return namedColors["gainsboro"]; } } + public static Color GhostWhite { get { return namedColors["ghostwhite"]; } } + public static Color Gold { get { return namedColors["gold"]; } } + public static Color Goldenrod { get { return namedColors["goldenrod"]; } } + public static Color Gray { get { return namedColors["gray"]; } } + public static Color Green { get { return namedColors["green"]; } } + public static Color GreenYellow { get { return namedColors["greenyellow"]; } } + public static Color Honeydew { get { return namedColors["honeydew"]; } } + public static Color HotPink { get { return namedColors["hotpink"]; } } + public static Color IndianRed { get { return namedColors["indianred"]; } } + public static Color Indigo { get { return namedColors["indigo"]; } } + public static Color Ivory { get { return namedColors["ivory"]; } } + public static Color Khaki { get { return namedColors["khaki"]; } } + public static Color Lavender { get { return namedColors["lavender"]; } } + public static Color LavenderBlush { get { return namedColors["lavenderblush"]; } } + public static Color LawnGreen { get { return namedColors["lawngreen"]; } } + public static Color LemonChiffon { get { return namedColors["lemonchiffon"]; } } + public static Color LightBlue { get { return namedColors["lightblue"]; } } + public static Color LightCoral { get { return namedColors["lightcoral"]; } } + public static Color LightCyan { get { return namedColors["lightcyan"]; } } + public static Color LightGoldenrod { get { return namedColors["lightgoldenrod"]; } } + public static Color LightGray { get { return namedColors["lightgray"]; } } + public static Color LightGreen { get { return namedColors["lightgreen"]; } } + public static Color LightPink { get { return namedColors["lightpink"]; } } + public static Color LightSalmon { get { return namedColors["lightsalmon"]; } } + public static Color LightSeaGreen { get { return namedColors["lightseagreen"]; } } + public static Color LightSkyBlue { get { return namedColors["lightskyblue"]; } } + public static Color LightSlateGray { get { return namedColors["lightslategray"]; } } + public static Color LightSteelBlue { get { return namedColors["lightsteelblue"]; } } + public static Color LightYellow { get { return namedColors["lightyellow"]; } } + public static Color Lime { get { return namedColors["lime"]; } } + public static Color Limegreen { get { return namedColors["limegreen"]; } } + public static Color Linen { get { return namedColors["linen"]; } } + public static Color Magenta { get { return namedColors["magenta"]; } } + public static Color Maroon { get { return namedColors["maroon"]; } } + public static Color MediumAquamarine { get { return namedColors["mediumaquamarine"]; } } + public static Color MediumBlue { get { return namedColors["mediumblue"]; } } + public static Color MediumOrchid { get { return namedColors["mediumorchid"]; } } + public static Color MediumPurple { get { return namedColors["mediumpurple"]; } } + public static Color MediumSeaGreen { get { return namedColors["mediumseagreen"]; } } + public static Color MediumSlateBlue { get { return namedColors["mediumslateblue"]; } } + public static Color MediumSpringGreen { get { return namedColors["mediumspringgreen"]; } } + public static Color MediumTurquoise { get { return namedColors["mediumturquoise"]; } } + public static Color MediumVioletRed { get { return namedColors["mediumvioletred"]; } } + public static Color MidnightBlue { get { return namedColors["midnightblue"]; } } + public static Color MintCream { get { return namedColors["mintcream"]; } } + public static Color MistyRose { get { return namedColors["mistyrose"]; } } + public static Color Moccasin { get { return namedColors["moccasin"]; } } + public static Color NavajoWhite { get { return namedColors["navajowhite"]; } } + public static Color NavyBlue { get { return namedColors["navyblue"]; } } + public static Color OldLace { get { return namedColors["oldlace"]; } } + public static Color Olive { get { return namedColors["olive"]; } } + public static Color OliveDrab { get { return namedColors["olivedrab"]; } } + public static Color Orange { get { return namedColors["orange"]; } } + public static Color OrangeRed { get { return namedColors["orangered"]; } } + public static Color Orchid { get { return namedColors["orchid"]; } } + public static Color PaleGoldenrod { get { return namedColors["palegoldenrod"]; } } + public static Color PaleGreen { get { return namedColors["palegreen"]; } } + public static Color PaleTurquoise { get { return namedColors["paleturquoise"]; } } + public static Color PaleVioletRed { get { return namedColors["palevioletred"]; } } + public static Color PapayaWhip { get { return namedColors["papayawhip"]; } } + public static Color PeachPuff { get { return namedColors["peachpuff"]; } } + public static Color Peru { get { return namedColors["peru"]; } } + public static Color Pink { get { return namedColors["pink"]; } } + public static Color Plum { get { return namedColors["plum"]; } } + public static Color PowderBlue { get { return namedColors["powderblue"]; } } + public static Color Purple { get { return namedColors["purple"]; } } + public static Color RebeccaPurple { get { return namedColors["rebeccapurple"]; } } + public static Color Red { get { return namedColors["red"]; } } + public static Color RosyBrown { get { return namedColors["rosybrown"]; } } + public static Color RoyalBlue { get { return namedColors["royalblue"]; } } + public static Color SaddleBrown { get { return namedColors["saddlebrown"]; } } + public static Color Salmon { get { return namedColors["salmon"]; } } + public static Color SandyBrown { get { return namedColors["sandybrown"]; } } + public static Color SeaGreen { get { return namedColors["seagreen"]; } } + public static Color SeaShell { get { return namedColors["seashell"]; } } + public static Color Sienna { get { return namedColors["sienna"]; } } + public static Color Silver { get { return namedColors["silver"]; } } + public static Color SkyBlue { get { return namedColors["skyblue"]; } } + public static Color SlateBlue { get { return namedColors["slateblue"]; } } + public static Color SlateGray { get { return namedColors["slategray"]; } } + public static Color Snow { get { return namedColors["snow"]; } } + public static Color SpringGreen { get { return namedColors["springgreen"]; } } + public static Color SteelBlue { get { return namedColors["steelblue"]; } } + public static Color Tan { get { return namedColors["tan"]; } } + public static Color Teal { get { return namedColors["teal"]; } } + public static Color Thistle { get { return namedColors["thistle"]; } } + public static Color Tomato { get { return namedColors["tomato"]; } } + public static Color Turquoise { get { return namedColors["turquoise"]; } } + public static Color Violet { get { return namedColors["violet"]; } } + public static Color WebGreen { get { return namedColors["webgreen"]; } } + public static Color WebGray { get { return namedColors["webgray"]; } } + public static Color WebMaroon { get { return namedColors["webmaroon"]; } } + public static Color WebPurple { get { return namedColors["webpurple"]; } } + public static Color Wheat { get { return namedColors["wheat"]; } } + public static Color White { get { return namedColors["white"]; } } + public static Color WhiteSmoke { get { return namedColors["whitesmoke"]; } } + public static Color Yellow { get { return namedColors["yellow"]; } } + public static Color YellowGreen { get { return namedColors["yellowgreen"]; } } + } +} 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) > 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..5b591646ea 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 double 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,22 @@ 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 double 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 +251,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..a064278237 100644 --- a/modules/mono/glue/Managed/Files/Mathf.cs +++ b/modules/mono/glue/Managed/Files/Mathf.cs @@ -261,6 +261,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 +299,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..414762f7b1 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 ratio = Mathf.Epsilon) + { + return Abs(a - b) < ratio; + } } }
\ 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 < 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/Quat.cs b/modules/mono/glue/Managed/Files/Quat.cs index fd1ac01083..d0c15146a5 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; 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..908162ec45 100644 --- a/modules/mono/glue/Managed/Files/Vector2.cs +++ b/modules/mono/glue/Managed/Files/Vector2.cs @@ -84,7 +84,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 +132,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); diff --git a/modules/mono/glue/Managed/Files/Vector3.cs b/modules/mono/glue/Managed/Files/Vector3.cs index f6ff27989d..0c96d346a9 100644 --- a/modules/mono/glue/Managed/Files/Vector3.cs +++ b/modules/mono/glue/Managed/Files/Vector3.cs @@ -126,6 +126,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(); 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 58916c5283..7385014a53 100644 --- a/modules/mono/glue/base_object_glue.cpp +++ b/modules/mono/glue/base_object_glue.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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); @@ -65,14 +67,17 @@ void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr) { void *data = p_ptr->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index()); if (data) { - Ref<MonoGCHandle> &gchandle = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get().gchandle; - if (gchandle.is_valid()) { - CSharpLanguage::release_script_gchandle(p_obj, gchandle); + CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get(); + if (script_binding.inited) { + Ref<MonoGCHandle> &gchandle = script_binding.gchandle; + if (gchandle.is_valid()) { + CSharpLanguage::release_script_gchandle(p_obj, gchandle); + } } } } -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 @@ -85,11 +90,14 @@ void godot_icall_Reference_Disposed(MonoObject *p_obj, Object *p_ptr, bool p_is_ CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(ref->get_script_instance()); if (cs_instance) { if (!cs_instance->is_destructing_script_instance()) { - bool r_owner_deleted; - cs_instance->mono_object_disposed_baseref(p_obj, p_is_finalizer, r_owner_deleted); - if (!r_owner_deleted && !p_is_finalizer) { - // If the native instance is still alive and Dispose() was called - // (instead of the finalizer), then we remove the script instance. + bool delete_owner; + bool remove_script_instance; + + cs_instance->mono_object_disposed_baseref(p_obj, p_is_finalizer, delete_owner, remove_script_instance); + + if (delete_owner) { + memdelete(ref); + } else if (remove_script_instance) { ref->set_script_instance(NULL); } } @@ -105,9 +113,12 @@ void godot_icall_Reference_Disposed(MonoObject *p_obj, Object *p_ptr, bool p_is_ void *data = ref->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index()); if (data) { - Ref<MonoGCHandle> &gchandle = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get().gchandle; - if (gchandle.is_valid()) { - CSharpLanguage::release_script_gchandle(p_obj, gchandle); + CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get(); + if (script_binding.inited) { + Ref<MonoGCHandle> &gchandle = script_binding.gchandle; + if (gchandle.is_valid()) { + CSharpLanguage::release_script_gchandle(p_obj, gchandle); + } } } } @@ -138,7 +149,7 @@ MonoObject *godot_icall_Object_weakref(Object *p_obj) { wref->set_obj(p_obj); } - return GDMonoUtils::create_managed_for_godot_object(CACHED_CLASS(WeakRef), Reference::get_class_static(), Object::cast_to<Object>(wref.ptr())); + return GDMonoUtils::unmanaged_get_managed(wref.ptr()); } Error godot_icall_SignalAwaiter_connect(Object *p_source, MonoString *p_signal, Object *p_target, MonoObject *p_awaiter) { @@ -146,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 2d4d66ebb8..e4ac9fb99d 100644 --- a/modules/mono/glue/base_object_glue.h +++ b/modules/mono/glue/base_object_glue.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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 059e2ff6de..4aef5684fd 100644 --- a/modules/mono/glue/collections_glue.cpp +++ b/modules/mono/glue/collections_glue.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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 b9b1338510..54ed42c3b6 100644 --- a/modules/mono/glue/collections_glue.h +++ b/modules/mono/glue/collections_glue.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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 9f5bcecdd9..d756131ac9 100644 --- a/modules/mono/glue/gd_glue.cpp +++ b/modules/mono/glue/gd_glue.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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) { +double 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 6f846f221d..910979aae3 100644 --- a/modules/mono/glue/gd_glue.h +++ b/modules/mono/glue/gd_glue.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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); +double 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 69c5c6dcdb..1836130b76 100644 --- a/modules/mono/glue/glue_header.h +++ b/modules/mono/glue/glue_header.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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/glue/nodepath_glue.cpp b/modules/mono/glue/nodepath_glue.cpp index 422d73104d..2edc43af8d 100644 --- a/modules/mono/glue/nodepath_glue.cpp +++ b/modules/mono/glue/nodepath_glue.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/modules/mono/glue/nodepath_glue.h b/modules/mono/glue/nodepath_glue.h index 92579399a6..2192444336 100644 --- a/modules/mono/glue/nodepath_glue.h +++ b/modules/mono/glue/nodepath_glue.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/modules/mono/glue/rid_glue.cpp b/modules/mono/glue/rid_glue.cpp index 6c002b5b9d..6a9810d0d4 100644 --- a/modules/mono/glue/rid_glue.cpp +++ b/modules/mono/glue/rid_glue.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/modules/mono/glue/rid_glue.h b/modules/mono/glue/rid_glue.h index c725a9b5de..4b593e8a95 100644 --- a/modules/mono/glue/rid_glue.h +++ b/modules/mono/glue/rid_glue.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/modules/mono/glue/string_glue.cpp b/modules/mono/glue/string_glue.cpp index e1a2f1affd..a5c72160d7 100644 --- a/modules/mono/glue/string_glue.cpp +++ b/modules/mono/glue/string_glue.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/modules/mono/glue/string_glue.h b/modules/mono/glue/string_glue.h index 8b1553e28b..c9052edd40 100644 --- a/modules/mono/glue/string_glue.h +++ b/modules/mono/glue/string_glue.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/modules/mono/godotsharp_defs.h b/modules/mono/godotsharp_defs.h index 5a6a2d1742..0d3b96d789 100644 --- a/modules/mono/godotsharp_defs.h +++ b/modules/mono/godotsharp_defs.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp index d3fb2cb640..09a1fc6fbc 100644 --- a/modules/mono/godotsharp_dirs.cpp +++ b/modules/mono/godotsharp_dirs.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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 35b564be30..556df959e2 100644 --- a/modules/mono/godotsharp_dirs.h +++ b/modules/mono/godotsharp_dirs.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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_gc_handle.cpp b/modules/mono/mono_gc_handle.cpp index f695bddfeb..a9e2136a19 100644 --- a/modules/mono/mono_gc_handle.cpp +++ b/modules/mono/mono_gc_handle.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -65,7 +65,7 @@ Ref<MonoGCHandle> MonoGCHandle::create_weak(MonoObject *p_object) { void MonoGCHandle::release() { #ifdef DEBUG_ENABLED - CRASH_COND(GDMono::get_singleton() == NULL); + CRASH_COND(!released && GDMono::get_singleton() == NULL); #endif if (!released && GDMono::get_singleton()->is_runtime_initialized()) { diff --git a/modules/mono/mono_gc_handle.h b/modules/mono/mono_gc_handle.h index e5aa04e2b8..63b61aff18 100644 --- a/modules/mono/mono_gc_handle.h +++ b/modules/mono/mono_gc_handle.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index a80155bd89..bfb6c13224 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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,16 +56,9 @@ #include "main/main.h" #endif -#ifdef MONO_PRINT_HANDLER_ENABLED -void gdmono_MonoPrintCallback(const char *string, mono_bool is_stdout) { - - if (is_stdout) { - OS::get_singleton()->print(string); - } else { - OS::get_singleton()->printerr(string); - } -} -#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; @@ -90,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) { @@ -102,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; @@ -130,9 +133,14 @@ void gdmono_debug_init() { } #endif - CharString da_args = String("--debugger-agent=transport=dt_socket,address=127.0.0.1:" + itos(da_port) + - ",embedding=1,server=y,suspend=" + (da_suspend ? "y,timeout=" + itos(da_timeout) : "n")) - .utf8(); + CharString da_args = OS::get_singleton()->get_environment("GODOT_MONO_DEBUGGER_AGENT").utf8(); + + if (da_args.length() == 0) { + da_args = String("--debugger-agent=transport=dt_socket,address=127.0.0.1:" + itos(da_port) + + ",embedding=1,server=y,suspend=" + (da_suspend ? "y,timeout=" + itos(da_timeout) : "n")) + .utf8(); + } + // --debugger-agent=help const char *options[] = { "--soft-breakpoints", @@ -145,6 +153,50 @@ void gdmono_debug_init() { } // namespace +void GDMono::add_mono_shared_libs_dir_to_path() { + // By default Mono seems to search shared libraries in the following directories: + // Current working directory, @executable_path@ and PATH + // The parent directory of the image file (assembly where the dllimport method is declared) + // @executable_path@/../lib + // @executable_path@/../Libraries (__MACH__ only) + + // This does not work when embedding Mono unless we use the same directory structure. + // To fix this we append the directory containing our shared libraries to PATH. + +#if defined(WINDOWS_ENABLED) || defined(UNIX_ENABLED) + String path_var("PATH"); + String path_value = OS::get_singleton()->get_environment(path_var); + +#ifdef WINDOWS_ENABLED + path_value += ';'; + + String bundled_bin_dir = GodotSharpDirs::get_data_mono_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 += ':'; + + String bundled_lib_dir = GodotSharpDirs::get_data_mono_lib_dir(); + if (DirAccess::exists(bundled_lib_dir)) { + path_value += bundled_lib_dir; + } else { + // TODO: Do we need to add the lib dir when using the system installed Mono on Unix platforms? + } +#endif // WINDOWS_ENABLED + + OS::get_singleton()->set_environment(path_var, path_value); +#endif // WINDOWS_ENABLED || UNIX_ENABLED +} + void GDMono::initialize() { ERR_FAIL_NULL(Engine::get_singleton()); @@ -157,16 +209,11 @@ void GDMono::initialize() { GDMonoLog::get_singleton()->initialize(); -#ifdef MONO_PRINT_HANDLER_ENABLED - mono_trace_set_print_handler(gdmono_MonoPrintCallback); - mono_trace_set_printerr_handler(gdmono_MonoPrintCallback); -#endif - String assembly_rootdir; 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)) { @@ -176,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(); @@ -208,18 +255,32 @@ 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, config_dir.length() ? config_dir.utf8().get_data() : NULL); + add_mono_shared_libs_dir_to_path(); + GDMonoAssembly::initialize(); + gdmono_profiler_init(); + #ifdef DEBUG_ENABLED gdmono_debug_init(); #endif @@ -228,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"); @@ -285,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); } @@ -322,7 +406,7 @@ namespace GodotSharpBindings { uint64_t get_core_api_hash(); #ifdef TOOLS_ENABLED uint64_t get_editor_api_hash(); -#endif // TOOLS_ENABLED +#endif uint32_t get_bindings_version(); void register_generated_icalls(); @@ -339,29 +423,20 @@ void GDMono::_register_internal_calls() { #endif } -#ifdef DEBUG_METHODS_ENABLED void GDMono::_initialize_and_check_api_hashes() { - api_core_hash = ClassDB::get_api_hash(ClassDB::API_CORE); - #ifdef MONO_GLUE_ENABLED - if (api_core_hash != GodotSharpBindings::get_core_api_hash()) { + if (get_api_core_hash() != GodotSharpBindings::get_core_api_hash()) { ERR_PRINT("Mono: Core API hash mismatch!"); } -#endif #ifdef TOOLS_ENABLED - api_editor_hash = ClassDB::get_api_hash(ClassDB::API_EDITOR); - -#ifdef MONO_GLUE_ENABLED - if (api_editor_hash != GodotSharpBindings::get_editor_api_hash()) { + if (get_api_editor_hash() != GodotSharpBindings::get_editor_api_hash()) { ERR_PRINT("Mono: Editor API hash mismatch!"); } -#endif - #endif // TOOLS_ENABLED +#endif // MONO_GLUE_ENABLED } -#endif // DEBUG_METHODS_ENABLED void GDMono::add_assembly(uint32_t p_domain_id, GDMonoAssembly *p_assembly) { @@ -513,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(); @@ -616,6 +693,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() { @@ -705,8 +799,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)) { @@ -726,7 +818,9 @@ Error GDMono::_unload_scripts_domain() { #endif core_api_assembly_out_of_sync = false; +#ifdef TOOLS_ENABLED editor_api_assembly_out_of_sync = false; +#endif MonoDomain *domain = scripts_domain; scripts_domain = NULL; @@ -759,7 +853,7 @@ Error GDMono::_load_tools_domain() { } #endif -#ifdef TOOLS_ENABLED +#ifdef GD_MONO_HOT_RELOAD Error GDMono::reload_scripts_domain() { ERR_FAIL_COND_V(!runtime_initialized, ERR_BUG); @@ -772,6 +866,8 @@ Error GDMono::reload_scripts_domain() { } } + CSharpLanguage::get_singleton()->_uninitialize_script_bindings(); + Error err = _load_scripts_domain(); if (err != OK) { ERR_PRINT("Mono: Failed to load scripts domain"); @@ -780,14 +876,18 @@ Error GDMono::reload_scripts_domain() { #ifdef MONO_GLUE_ENABLED if (!_load_api_assemblies()) { - if ((core_api_assembly && (core_api_assembly_out_of_sync || !GDMonoUtils::mono_cache.godot_api_cache_updated)) || - (editor_api_assembly && editor_api_assembly_out_of_sync)) { + if ((core_api_assembly && (core_api_assembly_out_of_sync || !GDMonoUtils::mono_cache.godot_api_cache_updated)) +#ifdef TOOLS_ENABLED + || (editor_api_assembly && editor_api_assembly_out_of_sync) +#endif + ) { +#ifdef TOOLS_ENABLED // The assembly was successfully loaded, but the full api could not be cached. // This is most likely an outdated assembly loaded because of an invalid version in the // 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"); @@ -795,16 +895,20 @@ 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"); } return ERR_CANT_RESOLVE; +#else + ERR_PRINT("The loaded API assembly is invalid"); + CRASH_NOW(); +#endif } else { return ERR_CANT_OPEN; } @@ -832,14 +936,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); @@ -899,8 +1009,10 @@ void GDMono::unhandled_exception_hook(MonoObject *p_exc, void *) { if (ScriptDebugger::get_singleton()) ScriptDebugger::get_singleton()->idle_poll(); #endif - abort(); - _UNREACHABLE_(); + + exit(mono_environment_exitcode_get()); + + GD_UNREACHABLE(); } GDMono::GDMono() { @@ -919,7 +1031,9 @@ GDMono::GDMono() { #endif core_api_assembly_out_of_sync = false; +#ifdef TOOLS_ENABLED editor_api_assembly_out_of_sync = false; +#endif corlib_assembly = NULL; core_api_assembly = NULL; @@ -929,23 +1043,29 @@ GDMono::GDMono() { editor_tools_assembly = NULL; #endif -#ifdef DEBUG_METHODS_ENABLED api_core_hash = 0; #ifdef TOOLS_ENABLED api_editor_hash = 0; #endif -#endif } 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"); } } @@ -966,6 +1086,8 @@ GDMono::~GDMono() { mono_jit_cleanup(root_domain); + print_verbose("Mono: Finalized"); + runtime_initialized = false; } @@ -1057,17 +1179,9 @@ void _GodotSharp::_bind_methods() { _GodotSharp::_GodotSharp() { singleton = this; - queue_empty = true; -#ifndef NO_THREADS - queue_mutex = Mutex::create(); -#endif } _GodotSharp::~_GodotSharp() { singleton = NULL; - - if (queue_mutex) { - memdelete(queue_mutex); - } } diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h index fd9551b6de..216c96a612 100644 --- a/modules/mono/mono_gd/gd_mono.h +++ b/modules/mono/mono_gd/gd_mono.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -95,7 +95,9 @@ class GDMono { #endif bool core_api_assembly_out_of_sync; +#ifdef TOOLS_ENABLED bool editor_api_assembly_out_of_sync; +#endif GDMonoAssembly *corlib_assembly; GDMonoAssembly *core_api_assembly; @@ -123,6 +125,8 @@ class GDMono { String _get_api_assembly_metadata_path(); #endif + void _install_trace_listener(); + void _register_internal_calls(); Error _load_scripts_domain(); @@ -132,13 +136,11 @@ class GDMono { Error _load_tools_domain(); #endif -#ifdef DEBUG_METHODS_ENABLED uint64_t api_core_hash; #ifdef TOOLS_ENABLED uint64_t api_editor_hash; #endif void _initialize_and_check_api_hashes(); -#endif GDMonoLog *gdmono_log; @@ -146,15 +148,23 @@ class GDMono { MonoRegInfo mono_reg_info; #endif + void add_mono_shared_libs_dir_to_path(); + protected: static GDMono *singleton; public: -#ifdef DEBUG_METHODS_ENABLED - uint64_t get_api_core_hash() { return api_core_hash; } + uint64_t get_api_core_hash() { + if (api_core_hash == 0) + api_core_hash = ClassDB::get_api_hash(ClassDB::API_CORE); + return api_core_hash; + } #ifdef TOOLS_ENABLED - uint64_t get_api_editor_hash() { return api_editor_hash; } -#endif + uint64_t get_api_editor_hash() { + if (api_editor_hash == 0) + api_editor_hash = ClassDB::get_api_hash(ClassDB::API_EDITOR); + return api_editor_hash; + } #endif #ifdef TOOLS_ENABLED @@ -193,7 +203,7 @@ public: GDMonoClass *get_class(MonoClass *p_raw_class); -#ifdef TOOLS_ENABLED +#ifdef GD_MONO_HOT_RELOAD Error reload_scripts_domain(); #endif @@ -266,12 +276,6 @@ class _GodotSharp : public Object { List<NodePath *> np_delete_queue; List<RID *> rid_delete_queue; - bool queue_empty; - -#ifndef NO_THREADS - Mutex *queue_mutex; -#endif - protected: static _GodotSharp *singleton; static void _bind_methods(); diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp index 72b0204672..8fec28b186 100644 --- a/modules/mono/mono_gd/gd_mono_assembly.cpp +++ b/modules/mono/mono_gd/gd_mono_assembly.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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 2af40ec901..32432af37d 100644 --- a/modules/mono/mono_gd/gd_mono_assembly.h +++ b/modules/mono/mono_gd/gd_mono_assembly.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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 c55f9160bd..4342f46109 100644 --- a/modules/mono/mono_gd/gd_mono_class.cpp +++ b/modules/mono/mono_gd/gd_mono_class.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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 689001f494..249422b844 100644 --- a/modules/mono/mono_gd/gd_mono_class.h +++ b/modules/mono/mono_gd/gd_mono_class.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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 f09e93e662..9779797d1a 100644 --- a/modules/mono/mono_gd/gd_mono_field.cpp +++ b/modules/mono/mono_gd/gd_mono_field.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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: { @@ -505,20 +529,20 @@ bool GDMonoField::is_static() { return mono_field_get_flags(mono_field) & MONO_FIELD_ATTR_STATIC; } -GDMonoClassMember::Visibility GDMonoField::get_visibility() { +IMonoClassMember::Visibility GDMonoField::get_visibility() { switch (mono_field_get_flags(mono_field) & MONO_FIELD_ATTR_FIELD_ACCESS_MASK) { case MONO_FIELD_ATTR_PRIVATE: - return GDMonoClassMember::PRIVATE; + return IMonoClassMember::PRIVATE; case MONO_FIELD_ATTR_FAM_AND_ASSEM: - return GDMonoClassMember::PROTECTED_AND_INTERNAL; + return IMonoClassMember::PROTECTED_AND_INTERNAL; case MONO_FIELD_ATTR_ASSEMBLY: - return GDMonoClassMember::INTERNAL; + return IMonoClassMember::INTERNAL; case MONO_FIELD_ATTR_FAMILY: - return GDMonoClassMember::PROTECTED; + return IMonoClassMember::PROTECTED; case MONO_FIELD_ATTR_PUBLIC: - return GDMonoClassMember::PUBLIC; + return IMonoClassMember::PUBLIC; default: - ERR_FAIL_V(GDMonoClassMember::PRIVATE); + ERR_FAIL_V(IMonoClassMember::PRIVATE); } } diff --git a/modules/mono/mono_gd/gd_mono_field.h b/modules/mono/mono_gd/gd_mono_field.h index a6b368c4d6..e348583370 100644 --- a/modules/mono/mono_gd/gd_mono_field.h +++ b/modules/mono/mono_gd/gd_mono_field.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -32,10 +32,10 @@ #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 GDMonoClassMember { +class GDMonoField : public IMonoClassMember { GDMonoClass *owner; MonoClassField *mono_field; @@ -47,15 +47,15 @@ class GDMonoField : public GDMonoClassMember { MonoCustomAttrInfo *attributes; public: - virtual MemberType get_member_type() { return MEMBER_TYPE_FIELD; } + virtual MemberType get_member_type() GD_FINAL { return MEMBER_TYPE_FIELD; } - virtual StringName get_name() { return name; } + virtual StringName get_name() GD_FINAL { return name; } - virtual bool is_static(); - virtual Visibility get_visibility(); + virtual bool is_static() GD_FINAL; + virtual Visibility get_visibility() GD_FINAL; - virtual bool has_attribute(GDMonoClass *p_attr_class); - virtual MonoObject *get_attribute(GDMonoClass *p_attr_class); + virtual bool has_attribute(GDMonoClass *p_attr_class) GD_FINAL; + virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) GD_FINAL; void fetch_attributes(); _FORCE_INLINE_ ManagedType get_type() const { return type; } diff --git a/modules/mono/mono_gd/gd_mono_header.h b/modules/mono/mono_gd/gd_mono_header.h index 51d0c9b356..d7962eac8b 100644 --- a/modules/mono/mono_gd/gd_mono_header.h +++ b/modules/mono/mono_gd/gd_mono_header.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -35,24 +35,12 @@ class GDMonoAssembly; class GDMonoClass; -class GDMonoClassMember; 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 505c030ca1..63bcfe053c 100644 --- a/modules/mono/mono_gd/gd_mono_internals.cpp +++ b/modules/mono/mono_gd/gd_mono_internals.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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,8 +111,9 @@ 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(); - _UNREACHABLE_(); + // Too bad 'mono_invoke_unhandled_exception_hook' is not exposed to embedders + GDMono::unhandled_exception_hook((MonoObject *)p_exc, NULL); + GD_UNREACHABLE(); } } // namespace GDMonoInternals diff --git a/modules/mono/mono_gd/gd_mono_internals.h b/modules/mono/mono_gd/gd_mono_internals.h index 50cadcedf6..2d77bde27c 100644 --- a/modules/mono/mono_gd/gd_mono_internals.h +++ b/modules/mono/mono_gd/gd_mono_internals.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -45,7 +45,7 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged); * Do not call this function directly. * Use GDMonoUtils::debug_unhandled_exception(MonoException *) instead. */ -_NO_RETURN_ void unhandled_exception(MonoException *p_exc); +GD_NORETURN void unhandled_exception(MonoException *p_exc); } // namespace GDMonoInternals diff --git a/modules/mono/mono_gd/gd_mono_log.cpp b/modules/mono/mono_gd/gd_mono_log.cpp index b7bb2cb2d6..087a7a2e5c 100644 --- a/modules/mono/mono_gd/gd_mono_log.cpp +++ b/modules/mono/mono_gd/gd_mono_log.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -52,7 +52,7 @@ static int log_level_get_id(const char *p_log_level) { return -1; } -void gdmono_MonoLogCallback(const char *log_domain, const char *log_level, const char *message, mono_bool fatal, void *user_data) { +static void mono_log_callback(const char *log_domain, const char *log_level, const char *message, mono_bool fatal, void *user_data) { FileAccess *f = GDMonoLog::get_singleton()->get_log_file(); @@ -72,8 +72,11 @@ void gdmono_MonoLogCallback(const char *log_domain, const char *log_level, const 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 +96,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 +115,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); } } @@ -131,29 +125,71 @@ void GDMonoLog::_delete_old_log_files(const String &p_logs_dir) { da->list_dir_end(); } +static String format(const char *p_fmt, ...) { + va_list args; + + va_start(args, p_fmt); + int len = vsnprintf(NULL, 0, p_fmt, args); + va_end(args); + + len += 1; // for the trailing '/0' + + char *buffer(memnew_arr(char, len)); + + va_start(args, p_fmt); + vsnprintf(buffer, len, p_fmt, args); + va_end(args); + + String res(buffer); + memdelete_arr(buffer); + + return res; +} + 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 = 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); - mono_trace_set_log_handler(gdmono_MonoLogCallback, this); + 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 3b4ff07b7b..91d0557aa3 100644 --- a/modules/mono/mono_gd/gd_mono_log.h +++ b/modules/mono/mono_gd/gd_mono_log.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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 3f0a5d6e50..de4f3650bd 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.cpp +++ b/modules/mono/mono_gd/gd_mono_marshal.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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,66 @@ 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); + + r_export_info->dictionary.key_type = managed_to_variant_type(ManagedType::from_reftype(key_reftype)); + r_export_info->dictionary.value_type = managed_to_variant_type(ManagedType::from_reftype(value_reftype)); + } + 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); + + r_export_info->array.element_type = managed_to_variant_type(ManagedType::from_reftype(elem_reftype)); + } + + 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; @@ -216,7 +256,7 @@ String mono_to_utf16_string(MonoString *p_mono_string) { ret.set(len, 0); CharType *src = (CharType *)mono_string_chars(p_mono_string); - CharType *dst = &(ret.operator[](0)); + CharType *dst = ret.ptrw(); for (int i = 0; i < len; i++) { dst[i] = src[i]; @@ -294,60 +334,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 +493,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 +596,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 +605,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 +633,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()); - - MonoType *raw_type = tclass->get_mono_type(); - - ManagedType type; + ManagedType type = ManagedType::from_class(mono_object_get_class(p_obj)); - 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 +674,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 +748,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 +767,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 +801,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 +814,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 4002f2a225..4a73f9e3e6 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.h +++ b/modules/mono/mono_gd/gd_mono_marshal.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -32,6 +32,7 @@ #define GDMONOMARSHAL_H #include "core/variant.h" + #include "gd_mono.h" #include "gd_mono_utils.h" @@ -56,7 +57,25 @@ 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; + + ArrayInfo() : + element_type(Variant::NIL) {} + } array; + struct DictionaryInfo { + Variant::Type key_type; + Variant::Type value_type; + + 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 @@ -206,9 +225,10 @@ enum { // In the future we may force this if we want to ref return these structs #ifdef GD_MONO_FORCE_INTEROP_STRUCT_COPY -// Sometimes clang-format can be an ass -GD_STATIC_ASSERT(MATCHES_Vector2 &&MATCHES_Rect2 &&MATCHES_Transform2D &&MATCHES_Vector3 && - MATCHES_Basis &&MATCHES_Quat &&MATCHES_Transform &&MATCHES_AABB &&MATCHES_Color &&MATCHES_Plane); +/* clang-format off */ +GD_STATIC_ASSERT(MATCHES_Vector2 && MATCHES_Rect2 && MATCHES_Transform2D && MATCHES_Vector3 && + MATCHES_Basis && MATCHES_Quat && MATCHES_Transform && MATCHES_AABB && MATCHES_Color &&MATCHES_Plane); +/* clang-format on */ #endif } // namespace InteropLayout diff --git a/modules/mono/mono_gd/gd_mono_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp index 6ef6e97f5a..7f11e4671d 100644 --- a/modules/mono/mono_gd/gd_mono_method.cpp +++ b/modules/mono/mono_gd/gd_mono_method.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -78,20 +78,20 @@ bool GDMonoMethod::is_static() { return mono_method_get_flags(mono_method, NULL) & MONO_METHOD_ATTR_STATIC; } -GDMonoClassMember::Visibility GDMonoMethod::get_visibility() { +IMonoClassMember::Visibility GDMonoMethod::get_visibility() { switch (mono_method_get_flags(mono_method, NULL) & MONO_METHOD_ATTR_ACCESS_MASK) { case MONO_METHOD_ATTR_PRIVATE: - return GDMonoClassMember::PRIVATE; + return IMonoClassMember::PRIVATE; case MONO_METHOD_ATTR_FAM_AND_ASSEM: - return GDMonoClassMember::PROTECTED_AND_INTERNAL; + return IMonoClassMember::PROTECTED_AND_INTERNAL; case MONO_METHOD_ATTR_ASSEM: - return GDMonoClassMember::INTERNAL; + return IMonoClassMember::INTERNAL; case MONO_METHOD_ATTR_FAMILY: - return GDMonoClassMember::PROTECTED; + return IMonoClassMember::PROTECTED; case MONO_METHOD_ATTR_PUBLIC: - return GDMonoClassMember::PUBLIC; + return IMonoClassMember::PUBLIC; default: - ERR_FAIL_V(GDMonoClassMember::PRIVATE); + ERR_FAIL_V(IMonoClassMember::PRIVATE); } } diff --git a/modules/mono/mono_gd/gd_mono_method.h b/modules/mono/mono_gd/gd_mono_method.h index 6c3ae5fce0..f74cef438d 100644 --- a/modules/mono/mono_gd/gd_mono_method.h +++ b/modules/mono/mono_gd/gd_mono_method.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -32,10 +32,10 @@ #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 GDMonoClassMember { +class GDMonoMethod : public IMonoClassMember { StringName name; @@ -57,17 +57,17 @@ class GDMonoMethod : public GDMonoClassMember { MonoMethod *mono_method; public: - virtual MemberType get_member_type() { return MEMBER_TYPE_METHOD; } + virtual MemberType get_member_type() GD_FINAL { return MEMBER_TYPE_METHOD; } - virtual StringName get_name() { return name; } + virtual StringName get_name() GD_FINAL { return name; } - virtual bool is_static(); + virtual bool is_static() GD_FINAL; - virtual Visibility get_visibility(); + virtual Visibility get_visibility() GD_FINAL; - virtual bool has_attribute(GDMonoClass *p_attr_class); - virtual MonoObject *get_attribute(GDMonoClass *p_attr_class); - virtual void fetch_attributes(); + virtual bool has_attribute(GDMonoClass *p_attr_class) GD_FINAL; + virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) GD_FINAL; + void fetch_attributes(); _FORCE_INLINE_ int get_parameters_count() { return params_count; } _FORCE_INLINE_ ManagedType get_return_type() { return return_type; } diff --git a/modules/mono/mono_gd/gd_mono_property.cpp b/modules/mono/mono_gd/gd_mono_property.cpp index ce66e0c8db..5842e26241 100644 --- a/modules/mono/mono_gd/gd_mono_property.cpp +++ b/modules/mono/mono_gd/gd_mono_property.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "gd_mono_property.h" #include "gd_mono_class.h" @@ -79,24 +80,24 @@ bool GDMonoProperty::is_static() { return mono_method_get_flags(prop_method, NULL) & MONO_METHOD_ATTR_STATIC; } -GDMonoClassMember::Visibility GDMonoProperty::get_visibility() { +IMonoClassMember::Visibility GDMonoProperty::get_visibility() { MonoMethod *prop_method = mono_property_get_get_method(mono_property); if (prop_method == NULL) prop_method = mono_property_get_set_method(mono_property); switch (mono_method_get_flags(prop_method, NULL) & MONO_METHOD_ATTR_ACCESS_MASK) { case MONO_METHOD_ATTR_PRIVATE: - return GDMonoClassMember::PRIVATE; + return IMonoClassMember::PRIVATE; case MONO_METHOD_ATTR_FAM_AND_ASSEM: - return GDMonoClassMember::PROTECTED_AND_INTERNAL; + return IMonoClassMember::PROTECTED_AND_INTERNAL; case MONO_METHOD_ATTR_ASSEM: - return GDMonoClassMember::INTERNAL; + return IMonoClassMember::INTERNAL; case MONO_METHOD_ATTR_FAMILY: - return GDMonoClassMember::PROTECTED; + return IMonoClassMember::PROTECTED; case MONO_METHOD_ATTR_PUBLIC: - return GDMonoClassMember::PUBLIC; + return IMonoClassMember::PUBLIC; default: - ERR_FAIL_V(GDMonoClassMember::PRIVATE); + ERR_FAIL_V(IMonoClassMember::PRIVATE); } } diff --git a/modules/mono/mono_gd/gd_mono_property.h b/modules/mono/mono_gd/gd_mono_property.h index b3f1e2114a..2700c460b0 100644 --- a/modules/mono/mono_gd/gd_mono_property.h +++ b/modules/mono/mono_gd/gd_mono_property.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,14 +27,15 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef GD_MONO_PROPERTY_H #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 GDMonoClassMember { +class GDMonoProperty : public IMonoClassMember { GDMonoClass *owner; MonoProperty *mono_property; @@ -46,15 +47,15 @@ class GDMonoProperty : public GDMonoClassMember { MonoCustomAttrInfo *attributes; public: - virtual MemberType get_member_type() { return MEMBER_TYPE_PROPERTY; } + virtual MemberType get_member_type() GD_FINAL { return MEMBER_TYPE_PROPERTY; } - virtual StringName get_name() { return name; } + virtual StringName get_name() GD_FINAL { return name; } - virtual bool is_static(); - virtual Visibility get_visibility(); + virtual bool is_static() GD_FINAL; + virtual Visibility get_visibility() GD_FINAL; - virtual bool has_attribute(GDMonoClass *p_attr_class); - virtual MonoObject *get_attribute(GDMonoClass *p_attr_class); + virtual bool has_attribute(GDMonoClass *p_attr_class) GD_FINAL; + virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) GD_FINAL; void fetch_attributes(); bool has_getter(); diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp index 211987d242..bcf5712d16 100644 --- a/modules/mono/mono_gd/gd_mono_utils.cpp +++ b/modules/mono/mono_gd/gd_mono_utils.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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 @@ -265,61 +283,72 @@ void clear_cache() { } MonoObject *unmanaged_get_managed(Object *unmanaged) { - if (unmanaged) { - if (unmanaged->get_script_instance()) { - CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(unmanaged->get_script_instance()); - if (cs_instance) { - return cs_instance->get_mono_object(); - } + if (!unmanaged) + return NULL; + + if (unmanaged->get_script_instance()) { + CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(unmanaged->get_script_instance()); + + if (cs_instance) { + return cs_instance->get_mono_object(); } + } + + // If the owner does not have a CSharpInstance... + + void *data = unmanaged->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index()); + + ERR_FAIL_NULL_V(data, NULL); + + CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->value(); - // If the owner does not have a CSharpInstance... + if (!script_binding.inited) { + SCOPED_MUTEX_LOCK(CSharpLanguage::get_singleton()->get_language_bind_mutex()); - void *data = unmanaged->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index()); + 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 (data) { - CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->value(); + ERR_FAIL_COND_V(!script_binding.inited, NULL); + } + } - Ref<MonoGCHandle> &gchandle = script_binding.gchandle; - ERR_FAIL_COND_V(gchandle.is_null(), NULL); + Ref<MonoGCHandle> &gchandle = script_binding.gchandle; + ERR_FAIL_COND_V(gchandle.is_null(), NULL); - MonoObject *target = gchandle->get_target(); + MonoObject *target = gchandle->get_target(); - if (target) - return target; + if (target) + return target; - CSharpLanguage::get_singleton()->release_script_gchandle(gchandle); + CSharpLanguage::get_singleton()->release_script_gchandle(gchandle); - // Create a new one + // Create a new one #ifdef DEBUG_ENABLED - CRASH_COND(script_binding.type_name == StringName()); - CRASH_COND(script_binding.wrapper_class == NULL); + CRASH_COND(script_binding.type_name == StringName()); + CRASH_COND(script_binding.wrapper_class == NULL); #endif - MonoObject *mono_object = GDMonoUtils::create_managed_for_godot_object(script_binding.wrapper_class, script_binding.type_name, unmanaged); - ERR_FAIL_NULL_V(mono_object, NULL); - - gchandle->set_handle(MonoGCHandle::new_strong_handle(mono_object), MonoGCHandle::STRONG_HANDLE); + MonoObject *mono_object = GDMonoUtils::create_managed_for_godot_object(script_binding.wrapper_class, script_binding.type_name, unmanaged); + ERR_FAIL_NULL_V(mono_object, NULL); - // Tie managed to unmanaged - Reference *ref = Object::cast_to<Reference>(unmanaged); + gchandle->set_handle(MonoGCHandle::new_strong_handle(mono_object), MonoGCHandle::STRONG_HANDLE); - if (ref) { - // 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) + // Tie managed to unmanaged + Reference *ref = Object::cast_to<Reference>(unmanaged); - ref->reference(); - } + if (ref) { + // 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) - return mono_object; - } + ref->reference(); } - return NULL; + return mono_object; } void set_main_thread(MonoThread *p_thread) { @@ -362,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); @@ -565,7 +599,7 @@ void debug_send_unhandled_exception_error(MonoException *p_exc) { if (unexpected_exc) { GDMonoInternals::unhandled_exception(unexpected_exc); - _UNREACHABLE_(); + GD_UNREACHABLE(); } Vector<ScriptLanguage::StackInfo> _si; @@ -598,13 +632,8 @@ 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 - _UNREACHABLE_(); + GD_UNREACHABLE(); } void print_unhandled_exception(MonoException *p_exc) { @@ -615,7 +644,7 @@ void set_pending_exception(MonoException *p_exc) { #ifdef HAS_PENDING_EXCEPTIONS if (get_runtime_invoke_count() == 0) { debug_unhandled_exception(p_exc); - _UNREACHABLE_(); + GD_UNREACHABLE(); } if (!mono_runtime_set_pending_exception(p_exc, false)) { @@ -624,7 +653,7 @@ void set_pending_exception(MonoException *p_exc) { } #else debug_unhandled_exception(p_exc); - _UNREACHABLE_(); + GD_UNREACHABLE(); #endif } @@ -695,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 170df32991..87610e286c 100644 --- a/modules/mono/mono_gd/gd_mono_utils.h +++ b/modules/mono/mono_gd/gd_mono_utils.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -44,23 +44,26 @@ #define UNLIKELY_UNHANDLED_EXCEPTION(m_exc) \ if (unlikely(m_exc != NULL)) { \ GDMonoUtils::debug_unhandled_exception(m_exc); \ - _UNREACHABLE_(); \ + GD_UNREACHABLE(); \ } 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; @@ -214,7 +225,7 @@ void set_exception_message(MonoException *p_exc, String message); void debug_print_unhandled_exception(MonoException *p_exc); void debug_send_unhandled_exception_error(MonoException *p_exc); -_NO_RETURN_ void debug_unhandled_exception(MonoException *p_exc); +GD_NORETURN void debug_unhandled_exception(MonoException *p_exc); void print_unhandled_exception(MonoException *p_exc); /** @@ -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 008ea0e416..553d9edc72 100644 --- a/modules/mono/mono_gd/gd_mono_class_member.h +++ b/modules/mono/mono_gd/i_mono_class_member.h @@ -1,12 +1,12 @@ /*************************************************************************/ -/* gd_mono_class_member.h */ +/* i_mono_class_member.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,14 +27,15 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef GD_MONO_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" #include <mono/metadata/object.h> -class GDMonoClassMember { +class IMonoClassMember { public: enum Visibility { PRIVATE, @@ -50,7 +51,7 @@ public: MEMBER_TYPE_METHOD }; - virtual ~GDMonoClassMember() {} + virtual ~IMonoClassMember() {} virtual MemberType get_member_type() = 0; @@ -64,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/register_types.cpp b/modules/mono/register_types.cpp index f6cb143e8e..3607b6f8b3 100644 --- a/modules/mono/register_types.cpp +++ b/modules/mono/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -35,8 +35,8 @@ #include "csharp_script.h" CSharpLanguage *script_language_cs = NULL; -ResourceFormatLoaderCSharpScript *resource_loader_cs = NULL; -ResourceFormatSaverCSharpScript *resource_saver_cs = NULL; +Ref<ResourceFormatLoaderCSharpScript> resource_loader_cs; +Ref<ResourceFormatSaverCSharpScript> resource_saver_cs; _GodotSharp *_godotsharp = NULL; @@ -52,9 +52,10 @@ void register_mono_types() { script_language_cs->set_language_index(ScriptServer::get_language_count()); ScriptServer::register_language(script_language_cs); - resource_loader_cs = memnew(ResourceFormatLoaderCSharpScript); + resource_loader_cs.instance(); ResourceLoader::add_resource_format_loader(resource_loader_cs); - resource_saver_cs = memnew(ResourceFormatSaverCSharpScript); + + resource_saver_cs.instance(); ResourceSaver::add_resource_format_saver(resource_saver_cs); } @@ -63,10 +64,12 @@ void unregister_mono_types() { if (script_language_cs) memdelete(script_language_cs); - if (resource_loader_cs) - memdelete(resource_loader_cs); - if (resource_saver_cs) - memdelete(resource_saver_cs); + + ResourceLoader::remove_resource_format_loader(resource_loader_cs); + resource_loader_cs.unref(); + + ResourceSaver::remove_resource_format_saver(resource_saver_cs); + resource_saver_cs.unref(); if (_godotsharp) memdelete(_godotsharp); diff --git a/modules/mono/register_types.h b/modules/mono/register_types.h index ab8a7d6463..0601e9a382 100644 --- a/modules/mono/register_types.h +++ b/modules/mono/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/modules/mono/signal_awaiter_utils.cpp b/modules/mono/signal_awaiter_utils.cpp index c6748309f3..5d37e8212f 100644 --- a/modules/mono/signal_awaiter_utils.cpp +++ b/modules/mono/signal_awaiter_utils.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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/signal_awaiter_utils.h b/modules/mono/signal_awaiter_utils.h index 4ec860537b..098008ded7 100644 --- a/modules/mono/signal_awaiter_utils.h +++ b/modules/mono/signal_awaiter_utils.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/modules/mono/utils/macros.h b/modules/mono/utils/macros.h index c801fb2f33..e44f254e1c 100644 --- a/modules/mono/utils/macros.h +++ b/modules/mono/utils/macros.h @@ -1,12 +1,12 @@ /*************************************************************************/ -/* util_macros.h */ +/* macros.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -31,39 +31,53 @@ #ifndef UTIL_MACROS_H #define UTIL_MACROS_H -#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) -#define GD_UNIQUE_NAME(m_name) _GD_VARNAME_CONCAT(m_name, _, __COUNTER__) +#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) +#define GD_UNIQUE_NAME(m_name) _GD_VARNAME_CONCAT_(m_name, _, __COUNTER__) -// noreturn +// static assert +// TODO: Get rid of this macro once we upgrade to C++11 -#if __cpp_static_assert +#ifdef __cpp_static_assert #define GD_STATIC_ASSERT(m_cond) static_assert((m_cond), "Condition '" #m_cond "' failed") #else #define GD_STATIC_ASSERT(m_cond) typedef int GD_UNIQUE_NAME(godot_static_assert)[((m_cond) ? 1 : -1)] #endif -#undef _NO_RETURN_ +// final +// TODO: Get rid of this macro once we upgrade to C++11 + +#if (__cplusplus >= 201103L) +#define GD_FINAL final +#else +#define GD_FINAL +#endif + +// noreturn +// TODO: Get rid of this macro once we upgrade to C++11 -#ifdef __GNUC__ -#define _NO_RETURN_ __attribute__((noreturn)) -#elif _MSC_VER -#define _NO_RETURN_ __declspec(noreturn) +#if (__cplusplus >= 201103L) +#define GD_NORETURN [[noreturn]] +#elif defined(__GNUC__) +#define GD_NORETURN __attribute__((noreturn)) +#elif defined(_MSC_VER) +#define GD_NORETURN __declspec(noreturn) #else -#error Platform or compiler not supported +#define GD_NORETURN +#pragma message "Macro GD_NORETURN will have no effect" #endif // unreachable #if defined(_MSC_VER) -#define _UNREACHABLE_() __assume(0) +#define GD_UNREACHABLE() __assume(0) #elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 -#define _UNREACHABLE_() __builtin_unreachable() +#define GD_UNREACHABLE() __builtin_unreachable() #else -#define _UNREACHABLE_() \ - CRASH_NOW(); \ - do { \ +#define GD_UNREACHABLE() \ + CRASH_NOW(); \ + do { \ } while (true); #endif diff --git a/modules/mono/utils/mono_reg_utils.cpp b/modules/mono/utils/mono_reg_utils.cpp index 6bb6efa92a..98aeadc8c8 100644 --- a/modules/mono/utils/mono_reg_utils.cpp +++ b/modules/mono/utils/mono_reg_utils.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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/mono_reg_utils.h b/modules/mono/utils/mono_reg_utils.h index 26f7e2d3c2..25446a4992 100644 --- a/modules/mono/utils/mono_reg_utils.h +++ b/modules/mono/utils/mono_reg_utils.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/modules/mono/utils/mutex_utils.h b/modules/mono/utils/mutex_utils.h index 07d659b6eb..b8be033cba 100644 --- a/modules/mono/utils/mutex_utils.h +++ b/modules/mono/utils/mutex_utils.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/modules/mono/utils/osx_utils.cpp b/modules/mono/utils/osx_utils.cpp index f520706a0c..f1362be249 100644 --- a/modules/mono/utils/osx_utils.cpp +++ b/modules/mono/utils/osx_utils.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -30,10 +30,10 @@ #include "osx_utils.h" -#include "core/print_string.h" - #ifdef OSX_ENABLED +#include "core/print_string.h" + #include <CoreFoundation/CoreFoundation.h> #include <CoreServices/CoreServices.h> diff --git a/modules/mono/utils/osx_utils.h b/modules/mono/utils/osx_utils.h index 68479b4f44..cc72233058 100644 --- a/modules/mono/utils/osx_utils.h +++ b/modules/mono/utils/osx_utils.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -31,6 +31,7 @@ #include "core/ustring.h" #ifndef OSX_UTILS_H +#define OSX_UTILS_H #ifdef OSX_ENABLED diff --git a/modules/mono/utils/path_utils.cpp b/modules/mono/utils/path_utils.cpp index e663ee3c4a..6e431f51e7 100644 --- a/modules/mono/utils/path_utils.cpp +++ b/modules/mono/utils/path_utils.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -59,7 +59,7 @@ String path_which(const String &p_name) { #ifdef WINDOWS_ENABLED for (int j = 0; j < exts.size(); j++) { - String p2 = p + exts[j]; + String p2 = p + exts[j].to_lower(); // lowercase to reduce risk of case mismatch warning if (FileAccess::exists(p2)) return p2; diff --git a/modules/mono/utils/path_utils.h b/modules/mono/utils/path_utils.h index 3c7b36c0d4..69edf4deb7 100644 --- a/modules/mono/utils/path_utils.h +++ b/modules/mono/utils/path_utils.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/modules/mono/utils/string_utils.cpp b/modules/mono/utils/string_utils.cpp index 6900866725..c390f8b9c2 100644 --- a/modules/mono/utils/string_utils.cpp +++ b/modules/mono/utils/string_utils.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/modules/mono/utils/string_utils.h b/modules/mono/utils/string_utils.h index ee803bd720..61765ccfd8 100644 --- a/modules/mono/utils/string_utils.h +++ b/modules/mono/utils/string_utils.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/modules/mono/utils/thread_local.cpp b/modules/mono/utils/thread_local.cpp index a0e28fca5f..13179bf408 100644 --- a/modules/mono/utils/thread_local.cpp +++ b/modules/mono/utils/thread_local.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/modules/mono/utils/thread_local.h b/modules/mono/utils/thread_local.h index d7d98c47e2..488cc2619a 100644 --- a/modules/mono/utils/thread_local.h +++ b/modules/mono/utils/thread_local.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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 |