diff options
71 files changed, 5676 insertions, 5569 deletions
diff --git a/modules/mono/SCsub b/modules/mono/SCsub index d10ebc7b47..5757917f56 100644 --- a/modules/mono/SCsub +++ b/modules/mono/SCsub @@ -7,21 +7,6 @@ Import("env_modules") env_mono = env_modules.Clone() -if env_mono["tools"]: - # NOTE: It is safe to generate this file here, since this is still executed serially - import build_scripts.gen_cs_glue_version as gen_cs_glue_version - - gen_cs_glue_version.generate_header("glue/GodotSharp", "glue/cs_glue_version.gen.h") - -# Glue sources -if env_mono["mono_glue"]: - env_mono.Append(CPPDEFINES=["MONO_GLUE_ENABLED"]) - - import os.path - - if not os.path.isfile("glue/mono_glue.gen.cpp"): - raise RuntimeError("Mono glue sources not found. Did you forget to run '--generate-mono-glue'?") - if env_mono["tools"] or env_mono["target"] != "release": env_mono.Append(CPPDEFINES=["GD_MONO_HOT_RELOAD"]) @@ -29,22 +14,6 @@ if env_mono["tools"] or env_mono["target"] != "release": mono_configure.configure(env, env_mono) -if env_mono["tools"] and env_mono["mono_glue"] and env_mono["build_cil"]: - # Build Godot API solution - import build_scripts.api_solution_build as api_solution_build - - api_sln_cmd = api_solution_build.build(env_mono) - - # Build GodotTools - import build_scripts.godot_tools_build as godot_tools_build - - godot_tools_build.build(env_mono, api_sln_cmd) - - # Build Godot.NET.Sdk - import build_scripts.godot_net_sdk_build as godot_net_sdk_build - - godot_net_sdk_build.build(env_mono) - # Add sources env_mono.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/mono/build_scripts/api_solution_build.py b/modules/mono/build_scripts/api_solution_build.py deleted file mode 100644 index 9abac22df6..0000000000 --- a/modules/mono/build_scripts/api_solution_build.py +++ /dev/null @@ -1,80 +0,0 @@ -# Build the Godot API solution - -import os - -from SCons.Script import Dir - - -def build_api_solution(source, target, env): - # source and target elements are of type SCons.Node.FS.File, hence why we convert them to str - - module_dir = env["module_dir"] - - solution_path = os.path.join(module_dir, "glue/GodotSharp/GodotSharp.sln") - - build_config = env["solution_build_config"] - - extra_msbuild_args = ["/p:NoWarn=1591"] # Ignore missing documentation warnings - - from .solution_builder import build_solution - - build_solution(env, solution_path, build_config, extra_msbuild_args=extra_msbuild_args) - - # Copy targets - - core_src_dir = os.path.abspath(os.path.join(solution_path, os.pardir, "GodotSharp", "bin", build_config)) - editor_src_dir = os.path.abspath(os.path.join(solution_path, os.pardir, "GodotSharpEditor", "bin", build_config)) - - dst_dir = os.path.abspath(os.path.join(str(target[0]), os.pardir)) - - if not os.path.isdir(dst_dir): - assert not os.path.isfile(dst_dir) - os.makedirs(dst_dir) - - def copy_target(target_path): - from shutil import copy - - filename = os.path.basename(target_path) - - src_path = os.path.join(core_src_dir, filename) - if not os.path.isfile(src_path): - src_path = os.path.join(editor_src_dir, filename) - - copy(src_path, target_path) - - for scons_target in target: - copy_target(str(scons_target)) - - -def build(env_mono): - assert env_mono["tools"] - - target_filenames = [ - "GodotSharp.dll", - "GodotSharp.pdb", - "GodotSharp.xml", - "GodotSharpEditor.dll", - "GodotSharpEditor.pdb", - "GodotSharpEditor.xml", - ] - - depend_cmd = [] - - for build_config in ["Debug", "Release"]: - output_dir = Dir("#bin").abspath - editor_api_dir = os.path.join(output_dir, "GodotSharp", "Api", build_config) - - targets = [os.path.join(editor_api_dir, filename) for filename in target_filenames] - - cmd = env_mono.CommandNoCache( - targets, depend_cmd, build_api_solution, module_dir=os.getcwd(), solution_build_config=build_config - ) - env_mono.AlwaysBuild(cmd) - - # Make the Release build of the API solution depend on the Debug build. - # We do this in order to prevent SCons from building them in parallel, - # which can freak out MSBuild. In many cases, one of the builds would - # hang indefinitely requiring a key to be pressed for it to continue. - depend_cmd = cmd - - return depend_cmd diff --git a/modules/mono/build_scripts/build_assemblies.py b/modules/mono/build_scripts/build_assemblies.py new file mode 100755 index 0000000000..dd96f40f6b --- /dev/null +++ b/modules/mono/build_scripts/build_assemblies.py @@ -0,0 +1,306 @@ +#!/usr/bin/python3 + +import os +import os.path +import shlex +import subprocess +from dataclasses import dataclass + + +def find_dotnet_cli(): + if os.name == "nt": + for hint_dir in os.environ["PATH"].split(os.pathsep): + hint_dir = hint_dir.strip('"') + hint_path = os.path.join(hint_dir, "dotnet") + 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" + else: + for hint_dir in os.environ["PATH"].split(os.pathsep): + hint_dir = hint_dir.strip('"') + hint_path = os.path.join(hint_dir, "dotnet") + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + + +def find_msbuild_standalone_windows(): + msbuild_tools_path = find_msbuild_tools_path_reg() + + if msbuild_tools_path: + return os.path.join(msbuild_tools_path, "MSBuild.exe") + + return None + + +def find_msbuild_mono_windows(mono_prefix): + assert mono_prefix is not None + + mono_bin_dir = os.path.join(mono_prefix, "bin") + msbuild_mono = os.path.join(mono_bin_dir, "msbuild.bat") + + if os.path.isfile(msbuild_mono): + return msbuild_mono + + return None + + +def find_msbuild_mono_unix(): + import sys + + hint_dirs = [] + if sys.platform == "darwin": + hint_dirs[:0] = [ + "/Library/Frameworks/Mono.framework/Versions/Current/bin", + "/usr/local/var/homebrew/linked/mono/bin", + ] + + for hint_dir in hint_dirs: + hint_path = os.path.join(hint_dir, "msbuild") + 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, "msbuild") + 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_tools_path_reg(): + import subprocess + + program_files = os.getenv("PROGRAMFILES(X86)") + if not program_files: + program_files = os.getenv("PROGRAMFILES") + vswhere = os.path.join(program_files, "Microsoft Visual Studio", "Installer", "vswhere.exe") + + vswhere_args = ["-latest", "-products", "*", "-requires", "Microsoft.Component.MSBuild"] + + try: + lines = subprocess.check_output([vswhere] + vswhere_args).splitlines() + + for line in lines: + parts = line.decode("utf-8").split(":", 1) + + if len(parts) < 2 or parts[0] != "installationPath": + continue + + val = parts[1].strip() + + if not val: + raise ValueError("Value of `installationPath` entry is empty") + + # Since VS2019, the directory is simply named "Current" + msbuild_dir = os.path.join(val, "MSBuild", "Current", "Bin") + 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: + print("Error reading output from vswhere: " + str(e)) + except OSError: + pass # Fine, vswhere not found + except (subprocess.CalledProcessError, OSError): + pass + + +@dataclass +class ToolsLocation: + dotnet_cli: str = "" + msbuild_standalone: str = "" + msbuild_mono: str = "" + mono_bin_dir: str = "" + + +def find_any_msbuild_tool(mono_prefix): + # Preference order: dotnet CLI > Standalone MSBuild > Mono's MSBuild + + # Find dotnet CLI + dotnet_cli = find_dotnet_cli() + if dotnet_cli: + return ToolsLocation(dotnet_cli=dotnet_cli) + + # Find standalone MSBuild + if os.name == "nt": + msbuild_standalone = find_msbuild_standalone_windows() + if msbuild_standalone: + return ToolsLocation(msbuild_standalone=msbuild_standalone) + + if mono_prefix: + # Find Mono's MSBuild + if os.name == "nt": + msbuild_mono = find_msbuild_mono_windows(mono_prefix) + if msbuild_mono: + return ToolsLocation(msbuild_mono=msbuild_mono) + else: + msbuild_mono = find_msbuild_mono_unix() + if msbuild_mono: + return ToolsLocation(msbuild_mono=msbuild_mono) + + return None + + +def run_msbuild(tools: ToolsLocation, sln: str, msbuild_args: [str] = None): + if msbuild_args is None: + msbuild_args = [] + + using_msbuild_mono = False + + # Preference order: dotnet CLI > Standalone MSBuild > Mono's MSBuild + if tools.dotnet_cli: + args = [tools.dotnet_cli, "msbuild"] + elif tools.msbuild_standalone: + args = [tools.msbuild_standalone] + elif tools.msbuild_mono: + args = [tools.msbuild_mono] + using_msbuild_mono = True + else: + raise RuntimeError("Path to MSBuild or dotnet CLI not provided.") + + args += [sln] + + if len(msbuild_args) > 0: + args += msbuild_args + + print("Running MSBuild: ", " ".join(shlex.quote(arg) for arg in args), flush=True) + + msbuild_env = os.environ.copy() + + # Needed when running from Developer Command Prompt for VS + if "PLATFORM" in msbuild_env: + del msbuild_env["PLATFORM"] + + if using_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. + msbuild_env.update( + { + "CscToolExe": os.path.join(tools.mono_bin_dir, "csc.bat"), + "VbcToolExe": os.path.join(tools.mono_bin_dir, "vbc.bat"), + "FscToolExe": os.path.join(tools.mono_bin_dir, "fsharpc.bat"), + } + ) + + return subprocess.call(args, env=msbuild_env) + + +def build_godot_api(msbuild_tool, module_dir, output_dir): + target_filenames = [ + "GodotSharp.dll", + "GodotSharp.pdb", + "GodotSharp.xml", + "GodotSharpEditor.dll", + "GodotSharpEditor.pdb", + "GodotSharpEditor.xml", + ] + + for build_config in ["Debug", "Release"]: + editor_api_dir = os.path.join(output_dir, "GodotSharp", "Api", build_config) + + targets = [os.path.join(editor_api_dir, filename) for filename in target_filenames] + + sln = os.path.join(module_dir, "glue/GodotSharp/GodotSharp.sln") + exit_code = run_msbuild( + msbuild_tool, + sln=sln, + msbuild_args=["/restore", "/t:Build", "/p:Configuration=" + build_config, "/p:NoWarn=1591"], + ) + if exit_code != 0: + return exit_code + + # Copy targets + + core_src_dir = os.path.abspath(os.path.join(sln, os.pardir, "GodotSharp", "bin", build_config)) + editor_src_dir = os.path.abspath(os.path.join(sln, os.pardir, "GodotSharpEditor", "bin", build_config)) + + if not os.path.isdir(editor_api_dir): + assert not os.path.isfile(editor_api_dir) + os.makedirs(editor_api_dir) + + def copy_target(target_path): + from shutil import copy + + filename = os.path.basename(target_path) + + src_path = os.path.join(core_src_dir, filename) + if not os.path.isfile(src_path): + src_path = os.path.join(editor_src_dir, filename) + + print(f"Copying assembly to {target_path}...") + copy(src_path, target_path) + + for scons_target in targets: + copy_target(scons_target) + + return 0 + + +def build_all(msbuild_tool, module_dir, output_dir, dev_debug, godot_platform): + # Godot API + exit_code = build_godot_api(msbuild_tool, module_dir, output_dir) + if exit_code != 0: + return exit_code + + # GodotTools + sln = os.path.join(module_dir, "editor/GodotTools/GodotTools.sln") + args = ["/restore", "/t:Build", "/p:Configuration=" + ("Debug" if dev_debug else "Release")] + ( + ["/p:GodotPlatform=" + godot_platform] if godot_platform else [] + ) + exit_code = run_msbuild(msbuild_tool, sln=sln, msbuild_args=args) + if exit_code != 0: + return exit_code + + # Godot.NET.Sdk + sln = os.path.join(module_dir, "editor/Godot.NET.Sdk/Godot.NET.Sdk.sln") + exit_code = run_msbuild(msbuild_tool, sln=sln, msbuild_args=["/restore", "/t:Build", "/p:Configuration=Release"]) + if exit_code != 0: + return exit_code + + return 0 + + +def main(): + import argparse + import sys + + parser = argparse.ArgumentParser(description="Builds all Godot .NET solutions") + parser.add_argument("--godot-output-dir", type=str, required=True) + parser.add_argument( + "--dev-debug", + action="store_true", + default=False, + help="Build GodotTools and Godot.NET.Sdk with 'Configuration=Debug'", + ) + parser.add_argument("--godot-platform", type=str, default="") + parser.add_argument("--mono-prefix", type=str, default="") + + args = parser.parse_args() + + this_script_dir = os.path.dirname(os.path.realpath(__file__)) + module_dir = os.path.abspath(os.path.join(this_script_dir, os.pardir)) + + output_dir = os.path.abspath(args.godot_output_dir) + + msbuild_tool = find_any_msbuild_tool(args.mono_prefix) + + if msbuild_tool is None: + print("Unable to find MSBuild") + sys.exit(1) + + exit_code = build_all(msbuild_tool, module_dir, output_dir, args.godot_platform, args.dev_debug) + sys.exit(exit_code) + + +if __name__ == "__main__": + main() diff --git a/modules/mono/build_scripts/gen_cs_glue_version.py b/modules/mono/build_scripts/gen_cs_glue_version.py deleted file mode 100644 index 98bbb4d9be..0000000000 --- a/modules/mono/build_scripts/gen_cs_glue_version.py +++ /dev/null @@ -1,20 +0,0 @@ -def generate_header(solution_dir, version_header_dst): - import os - - latest_mtime = 0 - for root, dirs, files in os.walk(solution_dir, topdown=True): - dirs[:] = [d for d in dirs if d not in ["Generated"]] # Ignored generated files - files = [f for f in files if f.endswith(".cs")] - for file in files: - filepath = os.path.join(root, file) - mtime = os.path.getmtime(filepath) - latest_mtime = mtime if mtime > latest_mtime else latest_mtime - - glue_version = int(latest_mtime) # The latest modified time will do for now - - with open(version_header_dst, "w") as version_header: - version_header.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") - version_header.write("#ifndef CS_GLUE_VERSION_H\n") - version_header.write("#define CS_GLUE_VERSION_H\n\n") - version_header.write("#define CS_GLUE_VERSION UINT32_C(" + str(glue_version) + ")\n") - version_header.write("\n#endif // CS_GLUE_VERSION_H\n") diff --git a/modules/mono/build_scripts/godot_net_sdk_build.py b/modules/mono/build_scripts/godot_net_sdk_build.py deleted file mode 100644 index 8c5a60d2db..0000000000 --- a/modules/mono/build_scripts/godot_net_sdk_build.py +++ /dev/null @@ -1,55 +0,0 @@ -# Build Godot.NET.Sdk solution - -import os - -from SCons.Script import Dir - - -def build_godot_net_sdk(source, target, env): - # source and target elements are of type SCons.Node.FS.File, hence why we convert them to str - - module_dir = env["module_dir"] - - solution_path = os.path.join(module_dir, "editor/Godot.NET.Sdk/Godot.NET.Sdk.sln") - build_config = "Release" - - from .solution_builder import build_solution - - extra_msbuild_args = ["/p:GodotPlatform=" + env["platform"]] - - build_solution(env, solution_path, build_config, extra_msbuild_args) - # No need to copy targets. The Godot.NET.Sdk csproj takes care of copying them. - - -def get_nupkgs_versions(props_file): - import xml.etree.ElementTree as ET - - tree = ET.parse(props_file) - root = tree.getroot() - - return { - "Godot.NET.Sdk": root.find("./PropertyGroup/PackageVersion_Godot_NET_Sdk").text.strip(), - "Godot.SourceGenerators": root.find("./PropertyGroup/PackageVersion_Godot_SourceGenerators").text.strip(), - } - - -def build(env_mono): - assert env_mono["tools"] - - output_dir = Dir("#bin").abspath - editor_tools_dir = os.path.join(output_dir, "GodotSharp", "Tools") - nupkgs_dir = os.path.join(editor_tools_dir, "nupkgs") - - module_dir = os.getcwd() - - nupkgs_versions = get_nupkgs_versions(os.path.join(module_dir, "SdkPackageVersions.props")) - - target_filenames = [ - "Godot.NET.Sdk.%s.nupkg" % nupkgs_versions["Godot.NET.Sdk"], - "Godot.SourceGenerators.%s.nupkg" % nupkgs_versions["Godot.SourceGenerators"], - ] - - targets = [os.path.join(nupkgs_dir, filename) for filename in target_filenames] - - cmd = env_mono.CommandNoCache(targets, [], build_godot_net_sdk, module_dir=module_dir) - env_mono.AlwaysBuild(cmd) diff --git a/modules/mono/build_scripts/godot_tools_build.py b/modules/mono/build_scripts/godot_tools_build.py deleted file mode 100644 index 3bbbf29d3b..0000000000 --- a/modules/mono/build_scripts/godot_tools_build.py +++ /dev/null @@ -1,38 +0,0 @@ -# Build GodotTools solution - -import os - -from SCons.Script import Dir - - -def build_godot_tools(source, target, env): - # source and target elements are of type SCons.Node.FS.File, hence why we convert them to str - - module_dir = env["module_dir"] - - solution_path = os.path.join(module_dir, "editor/GodotTools/GodotTools.sln") - build_config = "Debug" if env["target"] == "debug" else "Release" - - from .solution_builder import build_solution - - extra_msbuild_args = ["/p:GodotPlatform=" + env["platform"]] - - build_solution(env, solution_path, build_config, extra_msbuild_args) - # No need to copy targets. The GodotTools csproj takes care of copying them. - - -def build(env_mono, api_sln_cmd): - assert env_mono["tools"] - - output_dir = Dir("#bin").abspath - editor_tools_dir = os.path.join(output_dir, "GodotSharp", "Tools") - - target_filenames = ["GodotTools.dll"] - - if env_mono["target"] == "debug": - target_filenames += ["GodotTools.pdb"] - - targets = [os.path.join(editor_tools_dir, filename) for filename in target_filenames] - - cmd = env_mono.CommandNoCache(targets, api_sln_cmd, build_godot_tools, module_dir=os.getcwd()) - env_mono.AlwaysBuild(cmd) diff --git a/modules/mono/build_scripts/solution_builder.py b/modules/mono/build_scripts/solution_builder.py deleted file mode 100644 index 6a621c3c8b..0000000000 --- a/modules/mono/build_scripts/solution_builder.py +++ /dev/null @@ -1,145 +0,0 @@ -import os - - -verbose = False - - -def find_dotnet_cli(): - import os.path - - if os.name == "nt": - for hint_dir in os.environ["PATH"].split(os.pathsep): - hint_dir = hint_dir.strip('"') - hint_path = os.path.join(hint_dir, "dotnet") - 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" - else: - for hint_dir in os.environ["PATH"].split(os.pathsep): - hint_dir = hint_dir.strip('"') - hint_path = os.path.join(hint_dir, "dotnet") - if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): - return hint_path - - -def find_msbuild_unix(): - import os.path - import sys - - hint_dirs = [] - if sys.platform == "darwin": - hint_dirs[:0] = [ - "/Library/Frameworks/Mono.framework/Versions/Current/bin", - "/usr/local/var/homebrew/linked/mono/bin", - ] - - for hint_dir in hint_dirs: - hint_path = os.path.join(hint_dir, "msbuild") - 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, "msbuild") - 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 .mono_reg_utils import find_mono_root_dir, find_msbuild_tools_path_reg - - mono_root = env["mono_prefix"] or find_mono_root_dir(env["bits"]) - - if not mono_root: - raise RuntimeError("Cannot find mono root directory") - - mono_bin_dir = os.path.join(mono_root, "bin") - msbuild_mono = os.path.join(mono_bin_dir, "msbuild.bat") - - msbuild_tools_path = find_msbuild_tools_path_reg() - - if msbuild_tools_path: - return (os.path.join(msbuild_tools_path, "MSBuild.exe"), {}) - - if os.path.isfile(msbuild_mono): - # The (Csc/Vbc/Fsc)ToolExe environment variables are required when - # building with Mono's MSBuild. They must point to the batch files - # in Mono's bin directory to make sure they are executed with Mono. - mono_msbuild_env = { - "CscToolExe": os.path.join(mono_bin_dir, "csc.bat"), - "VbcToolExe": os.path.join(mono_bin_dir, "vbc.bat"), - "FscToolExe": os.path.join(mono_bin_dir, "fsharpc.bat"), - } - return (msbuild_mono, mono_msbuild_env) - - return None - - -def run_command(command, args, env_override=None, name=None): - def cmd_args_to_str(cmd_args): - return " ".join([arg if not " " in arg else '"%s"' % arg for arg in cmd_args]) - - args = [command] + args - - if name is None: - name = os.path.basename(command) - - if verbose: - print("Running '%s': %s" % (name, cmd_args_to_str(args))) - - import subprocess - - try: - if env_override is None: - subprocess.check_call(args) - else: - subprocess.check_call(args, env=env_override) - except subprocess.CalledProcessError as e: - raise RuntimeError("'%s' exited with error code: %s" % (name, e.returncode)) - - -def build_solution(env, solution_path, build_config, extra_msbuild_args=[]): - global verbose - verbose = env["verbose"] - - msbuild_env = os.environ.copy() - - # Needed when running from Developer Command Prompt for VS - if "PLATFORM" in msbuild_env: - del msbuild_env["PLATFORM"] - - msbuild_args = [] - - dotnet_cli = find_dotnet_cli() - - if dotnet_cli: - msbuild_path = dotnet_cli - msbuild_args += ["msbuild"] # `dotnet msbuild` command - else: - # 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] - msbuild_env.update(msbuild_info[1]) - else: - msbuild_path = find_msbuild_unix() - if msbuild_path is None: - raise RuntimeError("Cannot find MSBuild executable") - - print("MSBuild path: " + msbuild_path) - - # Build solution - - msbuild_args += [solution_path, "/restore", "/t:Build", "/p:Configuration=" + build_config] - msbuild_args += extra_msbuild_args - - run_command(msbuild_path, msbuild_args, env_override=msbuild_env, name="msbuild") diff --git a/modules/mono/config.py b/modules/mono/config.py index d895d2d92d..010f1ef1e5 100644 --- a/modules/mono/config.py +++ b/modules/mono/config.py @@ -25,11 +25,11 @@ def get_opts(platform): PathVariable.PathAccept, ), BoolVariable("mono_static", "Statically link Mono", default_mono_static), - BoolVariable("mono_glue", "Build with the Mono glue sources", True), BoolVariable("build_cil", "Build C# solutions", True), BoolVariable( "copy_mono_root", "Make a copy of the Mono installation directory to bundle with the editor", True ), + # TODO: It would be great if this could be detected automatically instead BoolVariable( "mono_bundles_zlib", "Specify if the Mono runtime was built with bundled zlib", default_mono_bundles_zlib ), diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index b95b63cf1f..2e6a6ef7e0 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -57,6 +57,7 @@ #endif #include "godotsharp_dirs.h" +#include "managed_callable.h" #include "mono_gd/gd_mono_cache.h" #include "mono_gd/gd_mono_class.h" #include "mono_gd/gd_mono_marshal.h" @@ -108,6 +109,9 @@ Error CSharpLanguage::execute_file(const String &p_path) { return OK; } +extern void *godotsharp_pinvoke_funcs[101]; +[[maybe_unused]] volatile void **do_not_strip_godotsharp_pinvoke_funcs; + void CSharpLanguage::init() { #ifdef DEBUG_METHODS_ENABLED if (OS::get_singleton()->get_cmdline_args().find("--class-db-json")) { @@ -118,20 +122,18 @@ void CSharpLanguage::init() { } #endif - gdmono = memnew(GDMono); - gdmono->initialize(); + // Hopefully this will be enough for all compilers. Otherwise we could use the printf on fake getenv trick. + do_not_strip_godotsharp_pinvoke_funcs = (volatile void **)godotsharp_pinvoke_funcs; #if defined(TOOLS_ENABLED) && defined(DEBUG_METHODS_ENABLED) - // Generate bindings here, before loading assemblies. 'initialize_load_assemblies' aborts - // the applications if the api assemblies or the main tools assembly is missing, but this - // is not a problem for BindingsGenerator as it only needs the tools project editor assembly. + // Generate the bindings here, before loading assemblies. The Godot assemblies + // may be missing if the glue wasn't generated yet in order to build them. List<String> cmdline_args = OS::get_singleton()->get_cmdline_args(); BindingsGenerator::handle_cmdline_args(cmdline_args); #endif -#ifndef MONO_GLUE_ENABLED - print_line("Run this binary with '--generate-mono-glue path/to/modules/mono/glue'"); -#endif + gdmono = memnew(GDMono); + gdmono->initialize(); if (gdmono->is_runtime_initialized()) { gdmono->initialize_load_assemblies(); @@ -596,7 +598,9 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info() return Vector<StackInfo>(); } _recursion_flag_ = true; - SCOPE_EXIT { _recursion_flag_ = false; }; + SCOPE_EXIT { + _recursion_flag_ = false; + }; GD_MONO_SCOPE_THREAD_ATTACH; @@ -628,7 +632,9 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObjec return Vector<StackInfo>(); } _recursion_flag_ = true; - SCOPE_EXIT { _recursion_flag_ = false; }; + SCOPE_EXIT { + _recursion_flag_ = false; + }; GD_MONO_SCOPE_THREAD_ATTACH; @@ -824,13 +830,13 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { for (SelfList<ManagedCallable> *elem = ManagedCallable::instances.first(); elem; elem = elem->next()) { ManagedCallable *managed_callable = elem->self(); - MonoDelegate *delegate = (MonoDelegate *)managed_callable->delegate_handle.get_target(); - Array serialized_data; MonoObject *managed_serialized_data = GDMonoMarshal::variant_to_mono_object(serialized_data); MonoException *exc = nullptr; - bool success = (bool)CACHED_METHOD_THUNK(DelegateUtils, TrySerializeDelegate).invoke(delegate, managed_serialized_data, &exc); + bool success = (bool)CACHED_METHOD_THUNK(DelegateUtils, TrySerializeDelegateWithGCHandle) + .invoke(managed_callable->delegate_handle, + managed_serialized_data, &exc); if (exc) { GDMonoUtils::debug_print_unhandled_exception(exc); @@ -1149,10 +1155,11 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { const Array &serialized_data = elem.value; MonoObject *managed_serialized_data = GDMonoMarshal::variant_to_mono_object(serialized_data); - MonoDelegate *delegate = nullptr; + void *delegate = nullptr; MonoException *exc = nullptr; - bool success = (bool)CACHED_METHOD_THUNK(DelegateUtils, TryDeserializeDelegate).invoke(managed_serialized_data, &delegate, &exc); + bool success = (bool)CACHED_METHOD_THUNK(DelegateUtils, TryDeserializeDelegateWithGCHandle) + .invoke(managed_serialized_data, &delegate, &exc); if (exc) { GDMonoUtils::debug_print_unhandled_exception(exc); @@ -1161,7 +1168,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { if (success) { ERR_CONTINUE(delegate == nullptr); - managed_callable->set_delegate(delegate); + managed_callable->delegate_handle = delegate; } else if (OS::get_singleton()->is_stdout_verbose()) { OS::get_singleton()->print("Failed to deserialize delegate\n"); } @@ -1289,7 +1296,7 @@ bool CSharpLanguage::debug_break(const String &p_error, bool p_allow_continue) { } } -void CSharpLanguage::_on_scripts_domain_unloaded() { +void CSharpLanguage::_on_scripts_domain_about_to_unload() { for (KeyValue<Object *, CSharpScriptBinding> &E : script_bindings) { CSharpScriptBinding &script_binding = E.value; script_binding.gchandle.release(); @@ -1302,8 +1309,7 @@ void CSharpLanguage::_on_scripts_domain_unloaded() { for (SelfList<ManagedCallable> *elem = ManagedCallable::instances.first(); elem; elem = elem->next()) { ManagedCallable *managed_callable = elem->self(); - managed_callable->delegate_handle.release(); - managed_callable->delegate_invoke = nullptr; + managed_callable->release_delegate_handle(); } } #endif @@ -1455,7 +1461,7 @@ void CSharpLanguage::_instance_binding_free_callback(void *, void *, void *p_bin if (GDMono::get_singleton() == nullptr) { #ifdef DEBUG_ENABLED - CRASH_COND(!csharp_lang->script_bindings.is_empty()); + CRASH_COND(csharp_lang && !csharp_lang->script_bindings.is_empty()); #endif // Mono runtime finalized, all the gchandle bindings were already released return; @@ -1784,7 +1790,8 @@ void CSharpInstance::get_event_signals_state_for_reloading(List<Pair<StringName, MonoObject *managed_serialized_data = GDMonoMarshal::variant_to_mono_object(serialized_data); MonoException *exc = nullptr; - bool success = (bool)CACHED_METHOD_THUNK(DelegateUtils, TrySerializeDelegate).invoke(delegate_field_value, managed_serialized_data, &exc); + bool success = (bool)CACHED_METHOD_THUNK(DelegateUtils, TrySerializeDelegate) + .invoke(delegate_field_value, managed_serialized_data, &exc); if (exc) { GDMonoUtils::debug_print_unhandled_exception(exc); diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h index 823de91bf6..cf4f971b88 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -398,7 +398,7 @@ class CSharpLanguage : public ScriptLanguage { String _debug_error; friend class GDMono; - void _on_scripts_domain_unloaded(); + void _on_scripts_domain_about_to_unload(); #ifdef TOOLS_ENABLED EditorPlugin *godotsharp_editor = nullptr; diff --git a/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs b/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs index dd05f28af0..43b1cf0f64 100644 --- a/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs +++ b/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs @@ -38,7 +38,7 @@ namespace GodotTools _watchTimer = new Timer { OneShot = false, - WaitTime = (float)EditorDef("mono/assembly_watch_interval_sec", 0.5) + WaitTime = 0.5f }; _watchTimer.Timeout += TimerTimeout; AddChild(_watchTimer); diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index 2e628cb576..64fe82664d 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -41,12 +41,20 @@ #include "core/string/ucaps.h" #include "main/main.h" -#include "../glue/cs_glue_version.gen.h" #include "../godotsharp_defs.h" -#include "../mono_gd/gd_mono_marshal.h" #include "../utils/path_utils.h" #include "../utils/string_utils.h" +StringBuilder &operator<<(StringBuilder &r_sb, const String &p_string) { + r_sb.append(p_string); + return r_sb; +} + +StringBuilder &operator<<(StringBuilder &r_sb, const char *p_cstring) { + r_sb.append(p_cstring); + return r_sb; +} + #define CS_INDENT " " // 4 whitespaces #define INDENT1 CS_INDENT @@ -60,50 +68,74 @@ #define OPEN_BLOCK "{\n" #define CLOSE_BLOCK "}\n" -#define OPEN_BLOCK_L2 INDENT2 OPEN_BLOCK INDENT3 -#define OPEN_BLOCK_L3 INDENT3 OPEN_BLOCK INDENT4 +#define OPEN_BLOCK_L1 INDENT1 OPEN_BLOCK +#define OPEN_BLOCK_L2 INDENT2 OPEN_BLOCK +#define OPEN_BLOCK_L3 INDENT3 OPEN_BLOCK #define CLOSE_BLOCK_L2 INDENT2 CLOSE_BLOCK #define CLOSE_BLOCK_L3 INDENT3 CLOSE_BLOCK +#define CLOSE_BLOCK_L4 INDENT4 CLOSE_BLOCK -#define CS_FIELD_MEMORYOWN "memoryOwn" +#define CS_PARAM_MEMORYOWN "memoryOwn" #define CS_PARAM_METHODBIND "method" #define CS_PARAM_INSTANCE "ptr" -#define CS_SMETHOD_GETINSTANCE "GetPtr" +#define CS_STATIC_METHOD_GETINSTANCE "GetPtr" #define CS_METHOD_CALL "Call" +#define CS_PROPERTY_SINGLETON "Singleton" + +#define CS_STATIC_FIELD_NATIVE_CTOR "NativeCtor" +#define CS_STATIC_FIELD_METHOD_BIND_PREFIX "MethodBind" +#define CS_STATIC_FIELD_SIGNAL_NAME_PREFIX "SignalName_" -#define GLUE_HEADER_FILE "glue_header.h" #define ICALL_PREFIX "godot_icall_" -#define SINGLETON_ICALL_SUFFIX "_get_singleton" -#define ICALL_GET_METHODBIND "__ClassDB_get_method" +#define ICALL_CLASSDB_GET_METHOD "ClassDB_get_method" +#define ICALL_CLASSDB_GET_CONSTRUCTOR "ClassDB_get_constructor" #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" - -#define C_NS_MONOUTILS "GDMonoUtils" -#define C_NS_MONOINTERNALS "GDMonoInternals" -#define C_METHOD_TIE_MANAGED_TO_UNMANAGED C_NS_MONOINTERNALS "::tie_managed_to_unmanaged" -#define C_METHOD_UNMANAGED_GET_MANAGED C_NS_MONOUTILS "::unmanaged_get_managed" - -#define C_NS_MONOMARSHAL "GDMonoMarshal" -#define C_METHOD_MANAGED_TO_VARIANT C_NS_MONOMARSHAL "::mono_object_to_variant" -#define C_METHOD_MANAGED_FROM_VARIANT C_NS_MONOMARSHAL "::variant_to_mono_object" -#define C_METHOD_MONOSTR_TO_GODOT C_NS_MONOMARSHAL "::mono_string_to_godot" -#define C_METHOD_MONOSTR_FROM_GODOT C_NS_MONOMARSHAL "::mono_string_from_godot" -#define C_METHOD_MONOARRAY_TO(m_type) C_NS_MONOMARSHAL "::mono_array_to_" #m_type -#define C_METHOD_MONOARRAY_FROM(m_type) C_NS_MONOMARSHAL "::" #m_type "_to_mono_array" -#define C_METHOD_MANAGED_TO_CALLABLE C_NS_MONOMARSHAL "::managed_to_callable" -#define C_METHOD_MANAGED_FROM_CALLABLE C_NS_MONOMARSHAL "::callable_to_managed" -#define C_METHOD_MANAGED_TO_SIGNAL C_NS_MONOMARSHAL "::signal_info_to_callable" -#define C_METHOD_MANAGED_FROM_SIGNAL C_NS_MONOMARSHAL "::callable_to_signal_info" - -#define BINDINGS_GENERATOR_VERSION UINT32_C(13) + +#define C_CLASS_NATIVE_FUNCS "NativeFuncs" +#define C_NS_MONOUTILS "InteropUtils" +#define C_METHOD_TIE_MANAGED_TO_UNMANAGED "NativeInterop." C_NS_MONOUTILS ".TieManagedToUnmanaged" +#define C_METHOD_UNMANAGED_GET_MANAGED C_NS_MONOUTILS ".UnmanagedGetManaged" +#define C_METHOD_ENGINE_GET_SINGLETON "NativeInterop." C_NS_MONOUTILS ".EngineGetSingleton" + +#define C_NS_MONOMARSHAL "Marshaling" +#define C_METHOD_MANAGED_TO_VARIANT C_NS_MONOMARSHAL ".mono_object_to_variant" +#define C_METHOD_MANAGED_FROM_VARIANT C_NS_MONOMARSHAL ".variant_to_mono_object" +#define C_METHOD_MONOSTR_TO_GODOT C_NS_MONOMARSHAL ".mono_string_to_godot" +#define C_METHOD_MONOSTR_FROM_GODOT C_NS_MONOMARSHAL ".mono_string_from_godot" +#define C_METHOD_MONOARRAY_TO(m_type) C_NS_MONOMARSHAL ".mono_array_to_" #m_type +#define C_METHOD_MONOARRAY_FROM(m_type) C_NS_MONOMARSHAL "." #m_type "_to_mono_array" +#define C_METHOD_MANAGED_TO_CALLABLE C_NS_MONOMARSHAL ".ConvertCallableToNative" +#define C_METHOD_MANAGED_FROM_CALLABLE C_NS_MONOMARSHAL ".ConvertCallableToManaged" +#define C_METHOD_MANAGED_TO_SIGNAL C_NS_MONOMARSHAL ".ConvertSignalToNative" +#define C_METHOD_MANAGED_FROM_SIGNAL C_NS_MONOMARSHAL ".ConvertSignalToManaged" // Types that will be ignored by the generator and won't be available in C#. const Vector<String> ignored_types = { "PhysicsServer3DExtension" }; -const char *BindingsGenerator::TypeInterface::DEFAULT_VARARG_C_IN("\t%0 %1_in = %1;\n"); +typedef String string; + +void BindingsGenerator::TypeInterface::postsetup_enum_type(BindingsGenerator::TypeInterface &r_enum_itype) { + // C interface for enums is the same as that of 'uint32_t'. Remember to apply + // any of the changes done here to the 'uint32_t' type interface as well. + + r_enum_itype.cs_type = r_enum_itype.proxy_name; + r_enum_itype.cs_in = "(int)%s"; + r_enum_itype.cs_out = "%5return (%2)%0(%1);"; + + { + // The expected types for parameters and return value in ptrcall are 'int64_t' or 'uint64_t'. + r_enum_itype.c_in = "%5%0 %1_in = %1;\n"; + r_enum_itype.c_out = "%5return (%0)%1;\n"; + r_enum_itype.c_type = "long"; + r_enum_itype.c_arg_in = "&%s_in"; + } + r_enum_itype.c_type_in = "int"; + r_enum_itype.c_type_out = r_enum_itype.c_type_in; + r_enum_itype.class_doc = &EditorHelp::get_doc_data()->class_list[r_enum_itype.proxy_name]; +} 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. @@ -794,47 +826,29 @@ void BindingsGenerator::_apply_prefix_to_enum_constants(BindingsGenerator::EnumI } } -void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) { +Error BindingsGenerator::_populate_method_icalls_table(const TypeInterface &p_itype) { for (const MethodInterface &imethod : p_itype.methods) { if (imethod.is_virtual) { continue; } - const TypeInterface *return_type = _get_type_or_placeholder(imethod.return_type); + const TypeInterface *return_type = _get_type_or_null(imethod.return_type); + ERR_FAIL_NULL_V(return_type, ERR_BUG); // Return type not found - String im_sig = "IntPtr " CS_PARAM_METHODBIND; - String im_unique_sig = imethod.return_type.cname.operator String() + ",IntPtr"; + String im_unique_sig = get_ret_unique_sig(return_type) + ",CallMethodBind"; if (!imethod.is_static) { - im_sig += ", IntPtr " CS_PARAM_INSTANCE; - im_unique_sig += ",IntPtr"; + im_unique_sig += ",CallInstance"; } // Get arguments information int i = 0; for (const ArgumentInterface &iarg : imethod.arguments) { - const TypeInterface *arg_type = _get_type_or_placeholder(iarg.type); - - im_sig += ", "; - im_sig += arg_type->im_type_in; - im_sig += " arg"; - im_sig += itos(i + 1); + const TypeInterface *arg_type = _get_type_or_null(iarg.type); + ERR_FAIL_NULL_V(arg_type, ERR_BUG); // Argument type not found im_unique_sig += ","; - im_unique_sig += get_unique_sig(*arg_type); - - i++; - } - - String im_type_out = return_type->im_type_out; - - if (return_type->ret_as_byref_arg) { - // Doesn't affect the unique signature - im_type_out = "void"; - - im_sig += ", "; - im_sig += return_type->im_type_out; - im_sig += " argRet"; + im_unique_sig += get_arg_unique_sig(*arg_type); i++; } @@ -845,7 +859,15 @@ void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) { icall_method += "_"; icall_method += itos(method_icalls.size()); - InternalCall im_icall = InternalCall(p_itype.api_type, icall_method, im_type_out, im_sig, im_unique_sig); + InternalCall im_icall = InternalCall(p_itype.api_type, icall_method, im_unique_sig); + + im_icall.is_vararg = imethod.is_vararg; + im_icall.is_static = imethod.is_static; + im_icall.return_type = imethod.return_type; + + for (const List<ArgumentInterface>::Element *F = imethod.arguments.front(); F; F = F->next()) { + im_icall.argument_types.push_back(F->get().type); + } List<InternalCall>::Element *match = method_icalls.find(im_icall); @@ -859,6 +881,8 @@ void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) { method_icalls_map.insert(&imethod, &added->get()); } } + + return OK; } void BindingsGenerator::_generate_array_extensions(StringBuilder &p_output) { @@ -874,7 +898,7 @@ void BindingsGenerator::_generate_array_extensions(StringBuilder &p_output) { p_output.append(INDENT2 "/// <param name=\"instance\">The " #m_type " array check.</param>\n"); \ p_output.append(INDENT2 "/// <returns>Whether or not the array is empty.</returns>\n"); \ p_output.append(INDENT2 "public static bool IsEmpty(this " #m_type "[] instance)\n"); \ - p_output.append(INDENT2 OPEN_BLOCK); \ + p_output.append(OPEN_BLOCK_L2); \ p_output.append(INDENT3 "return instance == null || instance.Length == 0;\n"); \ p_output.append(INDENT2 CLOSE_BLOCK); @@ -886,7 +910,7 @@ void BindingsGenerator::_generate_array_extensions(StringBuilder &p_output) { p_output.append(INDENT2 "/// <param name=\"delimiter\">The delimiter to use between items.</param>\n"); \ p_output.append(INDENT2 "/// <returns>A single string with all items.</returns>\n"); \ p_output.append(INDENT2 "public static string Join(this " #m_type "[] instance, string delimiter = \", \")\n"); \ - p_output.append(INDENT2 OPEN_BLOCK); \ + p_output.append(OPEN_BLOCK_L2); \ p_output.append(INDENT3 "return String.Join(delimiter, instance);\n"); \ p_output.append(INDENT2 CLOSE_BLOCK); @@ -897,7 +921,7 @@ void BindingsGenerator::_generate_array_extensions(StringBuilder &p_output) { p_output.append(INDENT2 "/// <param name=\"instance\">The " #m_type " array to convert.</param>\n"); \ p_output.append(INDENT2 "/// <returns>A single string with all items.</returns>\n"); \ p_output.append(INDENT2 "public static string Stringify(this " #m_type "[] instance)\n"); \ - p_output.append(INDENT2 OPEN_BLOCK); \ + p_output.append(OPEN_BLOCK_L2); \ p_output.append(INDENT3 "return \"[\" + instance.Join() + \"]\";\n"); \ p_output.append(INDENT2 CLOSE_BLOCK); @@ -989,7 +1013,7 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) { p_output.append("\n" INDENT1 "public static partial class "); p_output.append(enum_class_name); - p_output.append("\n" INDENT1 OPEN_BLOCK); + p_output.append("\n" OPEN_BLOCK_L1); } if (ienum.is_flags) { @@ -999,7 +1023,7 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) { p_output.append("\n" INDENT1 "public enum "); p_output.append(enum_proxy_name); p_output.append(" : long"); - p_output.append("\n" INDENT1 OPEN_BLOCK); + p_output.append("\n" OPEN_BLOCK_L1); const ConstantInterface &last = ienum.constants.back()->get(); for (const ConstantInterface &iconstant : ienum.constants) { @@ -1106,41 +1130,39 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir) { compile_items.push_back(output_file); } - // Generate sources from compressed files + // Generate native calls StringBuilder cs_icalls_content; cs_icalls_content.append("using System;\n" - "using System.Runtime.CompilerServices;\n" + "using System.Diagnostics.CodeAnalysis;\n" + "using System.Runtime.InteropServices;\n" + "using Godot.NativeInterop;\n" "\n"); cs_icalls_content.append("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK); + cs_icalls_content.append(INDENT1 "[SuppressMessage(\"ReSharper\", \"InconsistentNaming\")]\n"); + cs_icalls_content.append(INDENT1 "[SuppressMessage(\"ReSharper\", \"RedundantUnsafeContext\")]\n"); + cs_icalls_content.append(INDENT1 "[SuppressMessage(\"ReSharper\", \"RedundantNameQualifier\")]\n"); + cs_icalls_content.append("#if NET\n"); + cs_icalls_content.append(INDENT1 "[System.Runtime.CompilerServices.SkipLocalsInit]\n"); + cs_icalls_content.append("#endif\n"); cs_icalls_content.append(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS "\n" INDENT1 "{"); cs_icalls_content.append(MEMBER_BEGIN "internal static ulong godot_api_hash = "); - cs_icalls_content.append(String::num_uint64(GDMono::get_singleton()->get_api_core_hash()) + ";\n"); - 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"); + cs_icalls_content.append(String::num_uint64(ClassDB::get_api_hash(ClassDB::API_CORE)) + ";\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 static extern "); \ - cs_icalls_content.append(m_icall.im_type_out + " "); \ - cs_icalls_content.append(m_icall.name + "("); \ - cs_icalls_content.append(m_icall.im_sig + ");\n"); \ - } + cs_icalls_content.append(MEMBER_BEGIN "private const int VarArgsSpanThreshold = 10;\n"); - for (const InternalCall &internal_call : core_custom_icalls) { - ADD_INTERNAL_CALL(internal_call); - } - for (const InternalCall &internal_call : method_icalls) { - ADD_INTERNAL_CALL(internal_call); + for (const InternalCall &icall : method_icalls) { + if (icall.editor_only) { + continue; + } + Error err = _generate_cs_native_calls(icall, cs_icalls_content); + if (err != OK) { + return err; + } } -#undef ADD_INTERNAL_CALL - cs_icalls_content.append(INDENT1 CLOSE_BLOCK CLOSE_BLOCK); String internal_methods_file = path::join(base_gen_dir, BINDINGS_CLASS_NATIVECALLS ".cs"); @@ -1152,6 +1174,8 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir) { compile_items.push_back(internal_methods_file); + // Generate GeneratedIncludes.props + StringBuilder includes_props_content; includes_props_content.append("<Project>\n" " <ItemGroup>\n"); @@ -1215,39 +1239,40 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_proj_dir) { compile_items.push_back(output_file); } + // Generate native calls + StringBuilder cs_icalls_content; cs_icalls_content.append("using System;\n" - "using System.Runtime.CompilerServices;\n" + "using System.Diagnostics.CodeAnalysis;\n" + "using System.Runtime.InteropServices;\n" + "using Godot.NativeInterop;\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(INDENT1 "[SuppressMessage(\"ReSharper\", \"InconsistentNaming\")]\n"); + cs_icalls_content.append(INDENT1 "[SuppressMessage(\"ReSharper\", \"RedundantUnsafeContext\")]\n"); + cs_icalls_content.append(INDENT1 "[SuppressMessage(\"ReSharper\", \"RedundantNameQualifier\")]\n"); + cs_icalls_content.append("#if NET\n"); + cs_icalls_content.append(INDENT1 "[System.Runtime.CompilerServices.SkipLocalsInit]\n"); + cs_icalls_content.append("#endif\n"); + cs_icalls_content.append(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS_EDITOR "\n" OPEN_BLOCK_L1); 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"); + cs_icalls_content.append(String::num_uint64(ClassDB::get_api_hash(ClassDB::API_EDITOR)) + ";\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 static extern "); \ - cs_icalls_content.append(m_icall.im_type_out + " "); \ - cs_icalls_content.append(m_icall.name + "("); \ - cs_icalls_content.append(m_icall.im_sig + ");\n"); \ - } + cs_icalls_content.append(MEMBER_BEGIN "private const int VarArgsSpanThreshold = 10;\n"); - for (const InternalCall &internal_call : editor_custom_icalls) { - ADD_INTERNAL_CALL(internal_call); - } - for (const InternalCall &internal_call : method_icalls) { - ADD_INTERNAL_CALL(internal_call); - } + cs_icalls_content.append("\n"); -#undef ADD_INTERNAL_CALL + for (const InternalCall &icall : method_icalls) { + if (!icall.editor_only) { + continue; + } + Error err = _generate_cs_native_calls(icall, cs_icalls_content); + if (err != OK) { + return err; + } + } cs_icalls_content.append(INDENT1 CLOSE_BLOCK CLOSE_BLOCK); @@ -1260,6 +1285,8 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_proj_dir) { compile_items.push_back(internal_methods_file); + // Generate GeneratedIncludes.props + StringBuilder includes_props_content; includes_props_content.append("<Project>\n" " <ItemGroup>\n"); @@ -1343,12 +1370,8 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str CRASH_COND(itype.is_singleton); } - List<InternalCall> &custom_icalls = itype.api_type == ClassDB::API_EDITOR ? editor_custom_icalls : core_custom_icalls; - _log("Generating %s.cs...\n", itype.proxy_name.utf8().get_data()); - String ctor_method(ICALL_PREFIX + itype.proxy_name + "_Ctor"); // Used only for derived types - StringBuilder output; output.append("using System;\n"); // IntPtr @@ -1491,53 +1514,96 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str // Add the type name and the singleton pointer as static fields 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(typeof("); - output.append(itype.proxy_name); - output.append(").Name);\n" INDENT4 "return singleton;\n" INDENT3 "}\n" INDENT2 "}\n"); - output.append(MEMBER_BEGIN "private static StringName " BINDINGS_NATIVE_NAME_FIELD " = \""); + output << MEMBER_BEGIN "public static Godot.Object " CS_PROPERTY_SINGLETON "\n" INDENT2 "{\n" + << INDENT3 "get\n" INDENT3 "{\n" INDENT4 "if (singleton == null)\n" + << INDENT5 "singleton = " C_METHOD_ENGINE_GET_SINGLETON "(typeof(" + << itype.proxy_name + << ").Name);\n" INDENT4 "return singleton;\n" INDENT3 "}\n" INDENT2 "}\n"; + + output.append(MEMBER_BEGIN "private static readonly StringName " BINDINGS_NATIVE_NAME_FIELD " = \""); output.append(itype.name); output.append("\";\n"); + } else { + // IMPORTANT: We also generate the static fields for Godot.Object instead of declaring + // them manually in the `Object.base.cs` partial class declaration, because they're + // required by other static fields in this generated partial class declaration. + // Static fields are initialized in order of declaration, but when they're in different + // partial class declarations then it becomes harder to tell (Rider warns about this). - 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 + // Add native name static field - output.append(MEMBER_BEGIN "private static StringName " BINDINGS_NATIVE_NAME_FIELD " = \""); + output.append(MEMBER_BEGIN "private static readonly StringName " BINDINGS_NATIVE_NAME_FIELD " = \""); output.append(itype.name); output.append("\";\n"); - // Add default constructor if (itype.is_instantiable) { - 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.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" INDENT3 "_InitializeGodotScriptInstanceInternals();\n" CLOSE_BLOCK_L2); - } else { - // Hide the constructor + // Add native constructor static field + + String get_constructor_method = ICALL_CLASSDB_GET_CONSTRUCTOR; + + if (itype.is_singleton) { + // Singletons are static classes. They don't derive Godot.Object, + // so we need to specify the type to call the static method. + get_constructor_method = "Object." + get_constructor_method; + } + + output << MEMBER_BEGIN << "[DebuggerBrowsable(DebuggerBrowsableState.Never)]\n" + + << "#if NET\n" + + << INDENT2 "private static unsafe readonly delegate* unmanaged<IntPtr> " + << CS_STATIC_FIELD_NATIVE_CTOR " = " << get_constructor_method + << "(" BINDINGS_NATIVE_NAME_FIELD ");\n" + + << "#else\n" + + // Get rid of this one once we switch to .NET 5/6 + << INDENT2 "private static readonly IntPtr " CS_STATIC_FIELD_NATIVE_CTOR + << " = " << get_constructor_method << "(" BINDINGS_NATIVE_NAME_FIELD ");\n" + + << "#endif\n"; + } + + if (is_derived_type) { + // Add default constructor + if (itype.is_instantiable) { + output << MEMBER_BEGIN "public " << itype.proxy_name << "() : this(" + << (itype.memory_own ? "true" : "false") << ")\n" OPEN_BLOCK_L2; + + // 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 << INDENT3 "if (" BINDINGS_PTR_FIELD " == IntPtr.Zero)\n" OPEN_BLOCK_L3 + + << "#if NET\n" + + << INDENT4 "unsafe\n" INDENT4 OPEN_BLOCK + << INDENT5 BINDINGS_PTR_FIELD " = " CS_STATIC_FIELD_NATIVE_CTOR "();\n" + << CLOSE_BLOCK_L4 + + << "#else\n" + + // Get rid of this one once we switch to .NET 5/6 + << INDENT4 BINDINGS_PTR_FIELD " = _gd__invoke_class_constructor(" CS_STATIC_FIELD_NATIVE_CTOR ");\n" + + << "#endif\n" + + << INDENT4 C_METHOD_TIE_MANAGED_TO_UNMANAGED "(this, " BINDINGS_PTR_FIELD ");\n" + << CLOSE_BLOCK_L3 + << INDENT3 "_InitializeGodotScriptInstanceInternals();\n" CLOSE_BLOCK_L2; + } else { + // Hide the constructor + output.append(MEMBER_BEGIN "internal "); + output.append(itype.proxy_name); + output.append("() {}\n"); + } + + // Add.. em.. trick constructor. Sort of. output.append(MEMBER_BEGIN "internal "); output.append(itype.proxy_name); - output.append("() {}\n"); + output.append("(bool " CS_PARAM_MEMORYOWN ") : base(" CS_PARAM_MEMORYOWN ") {}\n"); } - - // Add.. em.. trick constructor. Sort of. - 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; @@ -1553,22 +1619,6 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str "Failed to generate signal '" + isignal.name + "' for class '" + itype.name + "'."); } - if (itype.is_singleton) { - InternalCall singleton_icall = InternalCall(itype.api_type, ICALL_PREFIX + itype.name + SINGLETON_ICALL_SUFFIX, "IntPtr"); - - if (!find_icall_by_name(singleton_icall.name, custom_icalls)) { - custom_icalls.push_back(singleton_icall); - } - } - - if (is_derived_type && itype.is_instantiable) { - InternalCall ctor_icall = InternalCall(itype.api_type, ctor_method, "IntPtr", itype.proxy_name + " obj"); - - if (!find_icall_by_name(ctor_icall.name, custom_icalls)) { - custom_icalls.push_back(ctor_icall); - } - } - output.append(INDENT1 CLOSE_BLOCK /* class */ CLOSE_BLOCK /* namespace */); @@ -1669,7 +1719,7 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte p_output.append(prop_cs_type); p_output.append(" "); p_output.append(p_iprop.proxy_name); - p_output.append("\n" INDENT2 OPEN_BLOCK); + p_output.append("\n" OPEN_BLOCK_L2); if (getter) { p_output.append(INDENT3 "get\n" @@ -1677,7 +1727,7 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte // TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that) "#pragma warning disable CS0618 // Disable warning about obsolete method\n" - OPEN_BLOCK_L3); + OPEN_BLOCK_L3 INDENT4); p_output.append("return "); p_output.append(getter->proxy_name + "("); @@ -1706,7 +1756,7 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte // TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that) "#pragma warning disable CS0618 // Disable warning about obsolete method\n" - OPEN_BLOCK_L3); + OPEN_BLOCK_L3 INDENT4); p_output.append(setter->proxy_name + "("); if (p_iprop.index != -1) { @@ -1734,7 +1784,8 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte } 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); + const TypeInterface *return_type = _get_type_or_null(p_imethod.return_type); + ERR_FAIL_NULL_V(return_type, ERR_BUG); // Return type not found ERR_FAIL_COND_V_MSG(return_type->is_singleton, ERR_BUG, "Method return type is a singleton: '" + p_itype.name + "." + p_imethod.name + "'."); @@ -1745,10 +1796,11 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf "' from the editor API. Core API cannot have dependencies on the editor API."); } - String method_bind_field = "__method_bind_" + itos(p_method_bind_count); + String method_bind_field = CS_STATIC_FIELD_METHOD_BIND_PREFIX + itos(p_method_bind_count); String arguments_sig; - String cs_in_statements; + StringBuilder cs_in_statements; + bool cs_in_is_unsafe = false; String icall_params = method_bind_field; if (!p_imethod.is_static) { @@ -1760,7 +1812,8 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf // Retrieve information from the arguments const ArgumentInterface &first = p_imethod.arguments.front()->get(); for (const ArgumentInterface &iarg : p_imethod.arguments) { - const TypeInterface *arg_type = _get_type_or_placeholder(iarg.type); + const TypeInterface *arg_type = _get_type_or_null(iarg.type); + ERR_FAIL_NULL_V(arg_type, ERR_BUG); // Argument type not found ERR_FAIL_COND_V_MSG(arg_type->is_singleton, ERR_BUG, "Argument type is a singleton: '" + iarg.name + "' of method '" + p_itype.name + "." + p_imethod.name + "'."); @@ -1816,24 +1869,20 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf String arg_in = iarg.name; arg_in += "_in"; - cs_in_statements += arg_cs_type; - cs_in_statements += " "; - cs_in_statements += arg_in; - cs_in_statements += " = "; - cs_in_statements += iarg.name; + cs_in_statements << INDENT3 << arg_cs_type << " " << arg_in << " = " << iarg.name; if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL) { - cs_in_statements += ".HasValue ? "; + cs_in_statements << ".HasValue ? "; } else { - cs_in_statements += " != null ? "; + cs_in_statements << " != null ? "; } - cs_in_statements += iarg.name; + cs_in_statements << iarg.name; if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL) { - cs_in_statements += ".Value : "; + cs_in_statements << ".Value : "; } else { - cs_in_statements += " : "; + cs_in_statements << " : "; } String cs_type = arg_cs_type; @@ -1843,8 +1892,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf String def_arg = sformat(iarg.default_argument, cs_type); - cs_in_statements += def_arg; - cs_in_statements += ";\n" INDENT3; + cs_in_statements << def_arg << ";\n"; icall_params += arg_type->cs_in.is_empty() ? arg_in : sformat(arg_type->cs_in, arg_in); @@ -1857,16 +1905,25 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf } else { icall_params += arg_type->cs_in.is_empty() ? iarg.name : sformat(arg_type->cs_in, iarg.name); } + + cs_in_is_unsafe |= arg_type->cs_in_is_unsafe; } // Generate method { if (!p_imethod.is_virtual && !p_imethod.requires_object_call) { - p_output.append(MEMBER_BEGIN "[DebuggerBrowsable(DebuggerBrowsableState.Never)]" MEMBER_BEGIN "private static readonly IntPtr "); - p_output.append(method_bind_field); - p_output.append(" = Object." ICALL_GET_METHODBIND "(" BINDINGS_NATIVE_NAME_FIELD ", \""); - p_output.append(p_imethod.name); - p_output.append("\");\n"); + p_output << MEMBER_BEGIN "[DebuggerBrowsable(DebuggerBrowsableState.Never)]" MEMBER_BEGIN "private static readonly IntPtr " + << method_bind_field << " = "; + + if (p_itype.is_singleton) { + // Singletons are static classes. They don't derive Godot.Object, + // so we need to specify the type to call the static method. + p_output << "Object."; + } + + p_output << ICALL_CLASSDB_GET_METHOD "(" BINDINGS_NATIVE_NAME_FIELD ", \"" + << p_imethod.name + << "\");\n"; } if (p_imethod.method_doc && p_imethod.method_doc->description.size()) { @@ -1919,6 +1976,10 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf p_output.append("virtual "); } + if (cs_in_is_unsafe) { + p_output.append("unsafe "); + } + String return_cs_type = return_type->cs_type + _get_generic_type_parameters(*return_type, p_imethod.return_type.generic_type_parameters); p_output.append(return_cs_type + " "); @@ -1929,11 +1990,9 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf // Godot virtual method must be overridden, therefore we return a default value by default. if (return_type->cname == name_cache.type_void) { - p_output.append("return;\n" CLOSE_BLOCK_L2); + p_output.append(CLOSE_BLOCK_L2); } else { - p_output.append("return default("); - p_output.append(return_cs_type); - p_output.append(");\n" CLOSE_BLOCK_L2); + p_output.append(INDENT3 "return default;\n" CLOSE_BLOCK_L2); } return OK; // Won't increment method bind count @@ -1942,7 +2001,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf if (p_imethod.requires_object_call) { // Fallback to Godot's object.Call(string, params) - p_output.append(CS_METHOD_CALL "(\""); + p_output.append(INDENT3 CS_METHOD_CALL "(\""); p_output.append(p_imethod.name); p_output.append("\""); @@ -1966,15 +2025,16 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf im_call += im_icall->name; if (p_imethod.arguments.size()) { - p_output.append(cs_in_statements); + p_output.append(cs_in_statements.as_string()); } if (return_type->cname == name_cache.type_void) { - p_output.append(im_call + "(" + icall_params + ");\n"); + p_output << INDENT3 << im_call << "(" << icall_params << ");\n"; } else if (return_type->cs_out.is_empty()) { - p_output.append("return " + im_call + "(" + icall_params + ");\n"); + p_output << INDENT3 "return " << im_call << "(" << icall_params << ");\n"; } else { - p_output.append(sformat(return_type->cs_out, im_call, icall_params, return_cs_type, return_type->im_type_out)); + p_output.append(sformat(return_type->cs_out, im_call, icall_params, + return_cs_type, return_type->c_type_out, String(), INDENT3)); p_output.append("\n"); } @@ -1992,7 +2052,8 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf // Retrieve information from the arguments const ArgumentInterface &first = p_isignal.arguments.front()->get(); for (const ArgumentInterface &iarg : p_isignal.arguments) { - const TypeInterface *arg_type = _get_type_or_placeholder(iarg.type); + const TypeInterface *arg_type = _get_type_or_null(iarg.type); + ERR_FAIL_NULL_V(arg_type, ERR_BUG); // Argument type not found ERR_FAIL_COND_V_MSG(arg_type->is_singleton, ERR_BUG, "Argument type is a singleton: '" + iarg.name + "' of signal '" + p_itype.name + "." + p_isignal.name + "'."); @@ -2058,7 +2119,8 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf // If so, we could store the pointer we get from `data_unique_pointer()` instead of allocating StringName here. // Cached signal name (StringName) - p_output.append(MEMBER_BEGIN "[DebuggerBrowsable(DebuggerBrowsableState.Never)]" MEMBER_BEGIN "private static StringName __signal_name_"); + p_output.append(MEMBER_BEGIN "[DebuggerBrowsable(DebuggerBrowsableState.Never)]" MEMBER_BEGIN + "private static readonly StringName " CS_STATIC_FIELD_SIGNAL_NAME_PREFIX); p_output.append(p_isignal.name); p_output.append(" = \""); p_output.append(p_isignal.name); @@ -2075,21 +2137,21 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf p_output.append(delegate_name); p_output.append(" "); p_output.append(p_isignal.proxy_name); - p_output.append("\n" OPEN_BLOCK_L2); + p_output.append("\n" OPEN_BLOCK_L2 INDENT3); if (p_itype.is_singleton) { - p_output.append("add => Singleton.Connect(__signal_name_"); + p_output.append("add => " CS_PROPERTY_SINGLETON ".Connect(" CS_STATIC_FIELD_SIGNAL_NAME_PREFIX); } else { - p_output.append("add => Connect(__signal_name_"); + p_output.append("add => Connect(" CS_STATIC_FIELD_SIGNAL_NAME_PREFIX); } p_output.append(p_isignal.name); p_output.append(", new Callable(value));\n"); if (p_itype.is_singleton) { - p_output.append(INDENT3 "remove => Singleton.Disconnect(__signal_name_"); + p_output.append(INDENT3 "remove => " CS_PROPERTY_SINGLETON ".Disconnect(" CS_STATIC_FIELD_SIGNAL_NAME_PREFIX); } else { - p_output.append(INDENT3 "remove => Disconnect(__signal_name_"); + p_output.append(INDENT3 "remove => Disconnect(" CS_STATIC_FIELD_SIGNAL_NAME_PREFIX); } p_output.append(p_isignal.name); @@ -2100,388 +2162,218 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf return OK; } -Error BindingsGenerator::generate_glue(const String &p_output_dir) { - ERR_FAIL_COND_V(!initialized, ERR_UNCONFIGURED); - - bool dir_exists = DirAccess::exists(p_output_dir); - ERR_FAIL_COND_V_MSG(!dir_exists, ERR_FILE_BAD_PATH, "The output directory does not exist."); - - StringBuilder output; - - 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(); - - for (const KeyValue<StringName, TypeInterface> &type_elem : obj_types) { - const TypeInterface &itype = type_elem.value; - - bool is_derived_type = itype.base_name != StringName(); +Error BindingsGenerator::_generate_cs_native_calls(const InternalCall &p_icall, StringBuilder &r_output) { + bool ret_void = p_icall.return_type.cname == name_cache.type_void; - if (!is_derived_type) { - // Some Object assertions - CRASH_COND(itype.cname != name_cache.type_Object); - CRASH_COND(!itype.is_instantiable); - CRASH_COND(itype.api_type != ClassDB::API_CORE); - CRASH_COND(itype.is_singleton); - } - - List<InternalCall> &custom_icalls = itype.api_type == ClassDB::API_EDITOR ? editor_custom_icalls : core_custom_icalls; - - 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 - - for (const MethodInterface &imethod : itype.methods) { - Error method_err = _generate_glue_method(itype, imethod, output); - ERR_FAIL_COND_V_MSG(method_err != OK, method_err, - "Failed to generate method '" + imethod.name + "' for class '" + itype.name + "'."); - } - - if (itype.is_singleton) { - String singleton_icall_name = ICALL_PREFIX + itype.name + SINGLETON_ICALL_SUFFIX; - InternalCall singleton_icall = InternalCall(itype.api_type, singleton_icall_name, "IntPtr"); - - if (!find_icall_by_name(singleton_icall.name, custom_icalls)) { - custom_icalls.push_back(singleton_icall); - } + const TypeInterface *return_type = _get_type_or_null(p_icall.return_type); + ERR_FAIL_NULL_V(return_type, ERR_BUG); // Return type not found - 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"); - } + StringBuilder c_func_sig; + StringBuilder c_in_statements; + StringBuilder c_args_var_content; - if (is_derived_type && itype.is_instantiable) { - InternalCall ctor_icall = InternalCall(itype.api_type, ctor_method, "IntPtr", itype.proxy_name + " obj"); + c_func_sig << "IntPtr " CS_PARAM_METHODBIND; - if (!find_icall_by_name(ctor_icall.name, custom_icalls)) { - custom_icalls.push_back(ctor_icall); - } - - 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"); - } + if (!p_icall.is_static) { + c_func_sig += ", IntPtr " CS_PARAM_INSTANCE; } - output.append("namespace GodotSharpBindings\n" OPEN_BLOCK "\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.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.append("uint32_t get_bindings_version() { return "); - output.append(String::num_uint64(BINDINGS_GENERATOR_VERSION) + "; }\n"); - - output.append("uint32_t get_cs_glue_version() { return "); - output.append(String::num_uint64(CS_GLUE_VERSION) + "; }\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.append("\tGDMonoUtils::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("\", "); \ - output.append(m_icall.name); \ - output.append(");\n"); \ - } - - bool tools_sequence = false; - for (const InternalCall &internal_call : core_custom_icalls) { - if (tools_sequence) { - if (!internal_call.editor_only) { - tools_sequence = false; - output.append("#endif\n"); - } - } else { - if (internal_call.editor_only) { - output.append("#ifdef TOOLS_ENABLED\n"); - tools_sequence = true; - } - } - - ADD_INTERNAL_CALL_REGISTRATION(internal_call); - } - - if (tools_sequence) { - tools_sequence = false; - output.append("#endif\n"); - } - - output.append("#ifdef TOOLS_ENABLED\n"); - for (const InternalCall &internal_call : editor_custom_icalls) { - ADD_INTERNAL_CALL_REGISTRATION(internal_call); - } - output.append("#endif // TOOLS_ENABLED\n"); - - for (const InternalCall &internal_call : method_icalls) { - if (tools_sequence) { - if (!internal_call.editor_only) { - tools_sequence = false; - output.append("#endif\n"); - } - } else { - if (internal_call.editor_only) { - output.append("#ifdef TOOLS_ENABLED\n"); - tools_sequence = true; - } - } - - ADD_INTERNAL_CALL_REGISTRATION(internal_call); - } - - if (tools_sequence) { - tools_sequence = false; - output.append("#endif\n"); - } - -#undef ADD_INTERNAL_CALL_REGISTRATION - - output.append(CLOSE_BLOCK "\n} // namespace GodotSharpBindings\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) { - return save_err; - } - - OS::get_singleton()->print("Mono glue generated successfully\n"); - - return OK; -} - -uint32_t BindingsGenerator::get_version() { - return BINDINGS_GENERATOR_VERSION; -} - -Error BindingsGenerator::_save_file(const String &p_path, const StringBuilder &p_content) { - Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE); - ERR_FAIL_COND_V_MSG(file.is_null(), ERR_FILE_CANT_WRITE, "Cannot open file: '" + p_path + "'."); - - file->store_string(p_content.as_string()); - - return OK; -} - -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 - } - - bool ret_void = p_imethod.return_type.cname == name_cache.type_void; - - const TypeInterface *return_type = _get_type_or_placeholder(p_imethod.return_type); - - String argc_str = itos(p_imethod.arguments.size()); - - String c_func_sig = "MethodBind* " CS_PARAM_METHODBIND; - if (!p_imethod.is_static) { - c_func_sig += ", " + p_itype.c_type_in + " " CS_PARAM_INSTANCE; - } - String c_in_statements; - String c_args_var_content; - // Get arguments information int i = 0; - for (const ArgumentInterface &iarg : p_imethod.arguments) { - const TypeInterface *arg_type = _get_type_or_placeholder(iarg.type); + for (const TypeReference &arg_type_ref : p_icall.argument_types) { + const TypeInterface *arg_type = _get_type_or_null(arg_type_ref); + ERR_FAIL_NULL_V(arg_type, ERR_BUG); // Return type not found String c_param_name = "arg" + itos(i + 1); - if (p_imethod.is_vararg) { - if (i < p_imethod.arguments.size() - 1) { - c_in_statements += sformat(arg_type->c_in.size() ? arg_type->c_in : TypeInterface::DEFAULT_VARARG_C_IN, "Variant", c_param_name); - c_in_statements += "\t" C_LOCAL_PTRCALL_ARGS ".set("; - c_in_statements += itos(i); - c_in_statements += sformat(", &%s_in);\n", c_param_name); + if (p_icall.is_vararg) { + if (i < p_icall.get_arguments_count() - 1) { + string c_in_vararg = arg_type->c_in_vararg; + + if (arg_type->is_object_type) { + c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromGodotObject(%1);\n"; + } + + ERR_FAIL_COND_V_MSG(c_in_vararg.is_empty(), ERR_BUG, + "VarArg support not implemented for parameter type: " + arg_type->name); + + c_in_statements + << sformat(c_in_vararg, return_type->c_type, c_param_name, + String(), String(), String(), INDENT4) + << INDENT4 C_LOCAL_PTRCALL_ARGS "[" << itos(i) + << "] = new IntPtr(&" << c_param_name << "_in);\n"; } } else { if (i > 0) { - c_args_var_content += ", "; + c_args_var_content << ", "; } if (arg_type->c_in.size()) { - c_in_statements += sformat(arg_type->c_in, arg_type->c_type, c_param_name); + c_in_statements << sformat(arg_type->c_in, arg_type->c_type, c_param_name, + String(), String(), String(), INDENT3); } - c_args_var_content += sformat(arg_type->c_arg_in, c_param_name); + c_args_var_content << sformat(arg_type->c_arg_in, c_param_name); } - c_func_sig += ", "; - c_func_sig += arg_type->c_type_in; - c_func_sig += " "; - c_func_sig += c_param_name; + c_func_sig << ", " << arg_type->c_type_in << " " << c_param_name; i++; } - if (return_type->ret_as_byref_arg) { - c_func_sig += ", "; - c_func_sig += return_type->c_type_in; - c_func_sig += " "; - c_func_sig += "arg_ret"; - - i++; - } + String icall_method = p_icall.name; - HashMap<const MethodInterface *, const InternalCall *>::ConstIterator match = method_icalls_map.find(&p_imethod); - ERR_FAIL_NULL_V(match, ERR_BUG); + // Generate icall function - const InternalCall *im_icall = match->value; - String icall_method = im_icall->name; + r_output << MEMBER_BEGIN "internal static unsafe " << (ret_void ? "void" : return_type->c_type_out) << " " + << icall_method << "(" << c_func_sig.as_string() << ") " OPEN_BLOCK; - if (!generated_icall_funcs.find(im_icall)) { - generated_icall_funcs.push_back(im_icall); + if (!ret_void && (!p_icall.is_vararg || return_type->cname != name_cache.type_Variant)) { + String ptrcall_return_type; + String initialization; - if (im_icall->editor_only) { - p_output.append("#ifdef TOOLS_ENABLED\n"); + if (return_type->is_object_type) { + ptrcall_return_type = return_type->is_ref_counted ? "godot_ref" : return_type->c_type; + initialization = " = default"; + } else { + ptrcall_return_type = return_type->c_type; } - // Generate icall function + r_output << INDENT3; - p_output.append((ret_void || return_type->ret_as_byref_arg) ? "void " : return_type->c_type_out + " "); - p_output.append(icall_method); - p_output.append("("); - p_output.append(c_func_sig); - p_output.append(") " OPEN_BLOCK); + if (return_type->is_ref_counted || return_type->c_type_is_disposable_struct) { + r_output << "using "; - if (!ret_void) { - 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 (initialization.is_empty()) { + initialization = " = default"; } + } else if (return_type->c_ret_needs_default_initialization) { + initialization = " = default"; + } - if (return_type->is_object_type) { - ptrcall_return_type = return_type->is_ref_counted ? "Ref<RefCounted>" : return_type->c_type; - initialization = return_type->is_ref_counted ? "" : " = nullptr"; - } else { - ptrcall_return_type = return_type->c_type; - } + r_output << ptrcall_return_type << " " C_LOCAL_RET << initialization << ";\n"; + } - p_output.append("\t" + ptrcall_return_type); - p_output.append(" " C_LOCAL_RET); - p_output.append(initialization + ";\n"); + if (!p_icall.is_static) { + r_output << INDENT3 "if (" CS_PARAM_INSTANCE " == IntPtr.Zero)\n" + << INDENT4 "throw new ArgumentNullException(nameof(" CS_PARAM_INSTANCE "));\n"; + } - String fail_ret = return_type->c_type_out.ends_with("*") && !return_type->ret_as_byref_arg ? "nullptr" : return_type->c_type_out + "()"; + String argc_str = itos(p_icall.get_arguments_count()); - if (!p_imethod.is_static) { - if (return_type->ret_as_byref_arg) { - p_output.append("\tif (" CS_PARAM_INSTANCE " == nullptr) { *arg_ret = "); - p_output.append(fail_ret); - p_output.append("; ERR_FAIL_MSG(\"Parameter ' " CS_PARAM_INSTANCE " ' is null.\"); }\n"); - } else { - p_output.append("\tERR_FAIL_NULL_V(" CS_PARAM_INSTANCE ", "); - p_output.append(fail_ret); - p_output.append(");\n"); - } - } - } else { - if (!p_imethod.is_static) { - p_output.append("\tERR_FAIL_NULL(" CS_PARAM_INSTANCE ");\n"); - } - } + auto generate_call_and_return_stmts = [&](const char *base_indent) { + if (p_icall.is_vararg) { + r_output << base_indent << "godot_variant_call_error vcall_error;\n"; - if (p_imethod.arguments.size()) { - if (p_imethod.is_vararg) { - String vararg_arg = "arg" + argc_str; - String real_argc_str = itos(p_imethod.arguments.size() - 1); // Arguments count without vararg - - 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.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"); - } - } + // MethodBind Call + r_output << base_indent; - if (p_imethod.is_vararg) { - p_output.append("\tCallable::CallError vcall_error;\n\t"); + // 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. 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 " = "); + r_output << "using godot_variant " << C_LOCAL_VARARG_RET " = "; } else { - p_output.append(C_LOCAL_RET " = "); + r_output << "using godot_variant " << C_LOCAL_RET " = "; } } - p_output.append(CS_PARAM_METHODBIND "->call("); - p_output.append(p_imethod.is_static ? "nullptr" : CS_PARAM_INSTANCE); - p_output.append(", "); - p_output.append(p_imethod.arguments.size() ? C_LOCAL_PTRCALL_ARGS ".ptr()" : "nullptr"); - p_output.append(", total_length, vcall_error);\n"); + r_output << C_CLASS_NATIVE_FUNCS ".godotsharp_method_bind_call(" + << CS_PARAM_METHODBIND ", " << (p_icall.is_static ? "IntPtr.Zero" : CS_PARAM_INSTANCE) + << ", " << (p_icall.get_arguments_count() ? "(godot_variant**)" C_LOCAL_PTRCALL_ARGS : "null") + << ", total_length, &vcall_error);\n"; if (!ret_void) { - // 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"); + if (return_type->cname == name_cache.enum_Error) { + r_output << base_indent << C_LOCAL_RET " = VariantUtils.ConvertToInt64(&" C_LOCAL_VARARG_RET ");\n"; + } else { + // TODO: Use something similar to c_in_vararg (see usage above, with error if not implemented) + CRASH_NOW_MSG("Custom VarArg return type not implemented: " + return_type->name); + r_output << base_indent << C_LOCAL_RET " = " C_LOCAL_VARARG_RET ";\n"; + } } } } else { - p_output.append("\t" CS_PARAM_METHODBIND "->ptrcall("); - p_output.append(p_imethod.is_static ? "nullptr" : CS_PARAM_INSTANCE); - p_output.append(", "); - p_output.append(p_imethod.arguments.size() ? C_LOCAL_PTRCALL_ARGS ", " : "nullptr, "); - p_output.append(!ret_void ? "&" C_LOCAL_RET ");\n" : "nullptr);\n"); + // MethodBind PtrCall + r_output << base_indent << C_CLASS_NATIVE_FUNCS ".godotsharp_method_bind_ptrcall(" + << CS_PARAM_METHODBIND ", " << (p_icall.is_static ? "IntPtr.Zero" : CS_PARAM_INSTANCE) + << ", " << (p_icall.get_arguments_count() ? C_LOCAL_PTRCALL_ARGS : "null") + << ", " << (!ret_void ? "&" C_LOCAL_RET ");\n" : "null);\n"); } + // Return statement + if (!ret_void) { if (return_type->c_out.is_empty()) { - p_output.append("\treturn " C_LOCAL_RET ";\n"); - } else if (return_type->ret_as_byref_arg) { - p_output.append(sformat(return_type->c_out, return_type->c_type_out, C_LOCAL_RET, return_type->name, "arg_ret")); + r_output << base_indent << "return " C_LOCAL_RET ";\n"; } else { - p_output.append(sformat(return_type->c_out, return_type->c_type_out, C_LOCAL_RET, return_type->name)); + r_output << sformat(return_type->c_out, return_type->c_type_out, C_LOCAL_RET, + return_type->name, String(), String(), base_indent); } } + }; + + if (p_icall.get_arguments_count()) { + if (p_icall.is_vararg) { + String vararg_arg = "arg" + argc_str; + String real_argc_str = itos(p_icall.get_arguments_count() - 1); // Arguments count without vararg + + p_icall.get_arguments_count(); + + r_output << INDENT3 "int vararg_length = " << vararg_arg << ".Length;\n" + << INDENT3 "int total_length = " << real_argc_str << " + vararg_length;\n"; + + r_output << INDENT3 "Span<godot_variant> varargs_span = vararg_length <= VarArgsSpanThreshold ?\n" + << INDENT4 "stackalloc godot_variant[VarArgsSpanThreshold].Cleared() :\n" + << INDENT4 "new godot_variant[vararg_length];\n"; + + r_output << INDENT3 "Span<IntPtr> " C_LOCAL_PTRCALL_ARGS "_span = total_length <= VarArgsSpanThreshold ?\n" + << INDENT4 "stackalloc IntPtr[VarArgsSpanThreshold] :\n" + << INDENT4 "new IntPtr[total_length];\n"; + + r_output << INDENT3 "using var variantSpanDisposer = new VariantSpanDisposer(varargs_span);\n"; + + r_output << INDENT3 "fixed (godot_variant* varargs = &MemoryMarshal.GetReference(varargs_span))\n" + << INDENT3 "fixed (IntPtr* " C_LOCAL_PTRCALL_ARGS " = " + "&MemoryMarshal.GetReference(" C_LOCAL_PTRCALL_ARGS "_span))\n" + << OPEN_BLOCK_L3; + + r_output << c_in_statements.as_string(); - p_output.append(CLOSE_BLOCK "\n"); + r_output << INDENT4 "for (int i = 0; i < vararg_length; i++) " OPEN_BLOCK + << INDENT5 "varargs[i] = " C_METHOD_MANAGED_TO_VARIANT "(" << vararg_arg << "[i]);\n" + << INDENT5 C_LOCAL_PTRCALL_ARGS "[" << real_argc_str << " + i] = new IntPtr(&varargs[i]);\n" + << CLOSE_BLOCK_L4; - if (im_icall->editor_only) { - p_output.append("#endif // TOOLS_ENABLED\n"); + generate_call_and_return_stmts(INDENT4); + + r_output << CLOSE_BLOCK_L3; + } else { + r_output << c_in_statements.as_string(); + + r_output << INDENT3 "void** " C_LOCAL_PTRCALL_ARGS " = stackalloc void*[" + << argc_str << "] { " << c_args_var_content.as_string() << " };\n"; + + generate_call_and_return_stmts(INDENT3); } + } else { + generate_call_and_return_stmts(INDENT3); } + r_output << CLOSE_BLOCK_L2; + + return OK; +} + +Error BindingsGenerator::_save_file(const String &p_path, const StringBuilder &p_content) { + Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE); + ERR_FAIL_COND_V_MSG(file.is_null(), ERR_FILE_CANT_WRITE, "Cannot open file: '" + p_path + "'."); + + file->store_string(p_content.as_string()); + return OK; } @@ -2514,27 +2406,6 @@ const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_null(con return nullptr; } -const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_placeholder(const TypeReference &p_typeref) { - const TypeInterface *found = _get_type_or_null(p_typeref); - - if (found) { - return found; - } - - ERR_PRINT(String() + "Type not found. Creating placeholder: '" + p_typeref.cname.operator String() + "'."); - - HashMap<StringName, TypeInterface>::ConstIterator match = placeholder_types.find(p_typeref.cname); - - if (match) { - return &match->value; - } - - TypeInterface placeholder; - TypeInterface::create_placeholder_type(placeholder, p_typeref.cname); - - return &placeholder_types.insert(placeholder.cname, placeholder)->value; -} - const String BindingsGenerator::_get_generic_type_parameters(const TypeInterface &p_itype, const List<TypeReference> &p_generic_type_parameters) { if (p_generic_type_parameters.is_empty()) { return ""; @@ -2548,7 +2419,8 @@ const String BindingsGenerator::_get_generic_type_parameters(const TypeInterface int i = 0; String params = "<"; for (const TypeReference ¶m_type : p_generic_type_parameters) { - const TypeInterface *param_itype = _get_type_or_placeholder(param_type); + const TypeInterface *param_itype = _get_type_or_null(param_type); + ERR_FAIL_NULL_V(param_itype, ""); ERR_FAIL_COND_V_MSG(param_itype->is_singleton, "", "Generic type parameter is a singleton: '" + param_itype->name + "'."); @@ -2666,8 +2538,6 @@ bool BindingsGenerator::_arg_default_value_is_assignable_to_type(const Variant & case Variant::RECT2: case Variant::VECTOR3: case Variant::RID: - case Variant::ARRAY: - case Variant::DICTIONARY: case Variant::PACKED_BYTE_ARRAY: case Variant::PACKED_INT32_ARRAY: case Variant::PACKED_INT64_ARRAY: @@ -2680,6 +2550,10 @@ bool BindingsGenerator::_arg_default_value_is_assignable_to_type(const Variant & case Variant::CALLABLE: case Variant::SIGNAL: return p_arg_type.name == Variant::get_type_name(p_val.get_type()); + case Variant::ARRAY: + return p_arg_type.name == Variant::get_type_name(p_val.get_type()) || p_arg_type.cname == name_cache.type_Array_generic; + case Variant::DICTIONARY: + return p_arg_type.name == Variant::get_type_name(p_val.get_type()) || p_arg_type.cname == name_cache.type_Dictionary_generic; case Variant::OBJECT: return p_arg_type.is_object_type; case Variant::VECTOR2I: @@ -2744,18 +2618,24 @@ bool BindingsGenerator::_populate_object_type_interfaces() { itype.is_ref_counted = ClassDB::is_parent_class(type_cname, name_cache.type_RefCounted); itype.memory_own = itype.is_ref_counted; - itype.c_out = "\treturn "; + itype.c_out = "%5return "; itype.c_out += C_METHOD_UNMANAGED_GET_MANAGED; - itype.c_out += itype.is_ref_counted ? "(%1.ptr());\n" : "(%1);\n"; + itype.c_out += itype.is_ref_counted ? "(%1._reference);\n" : "(%1);\n"; - itype.cs_in = itype.is_singleton ? BINDINGS_PTR_FIELD : "Object." CS_SMETHOD_GETINSTANCE "(%0)"; + itype.cs_type = itype.proxy_name; + + if (itype.is_singleton) { + itype.cs_in = "Object." CS_STATIC_METHOD_GETINSTANCE "(" CS_PROPERTY_SINGLETON ")"; + } else { + itype.cs_in = "Object." CS_STATIC_METHOD_GETINSTANCE "(%0)"; + } - itype.c_type = "Object*"; + itype.cs_out = "%5return (%2)%0(%1);"; + + itype.c_arg_in = "(void*)%s"; + itype.c_type = "IntPtr"; itype.c_type_in = itype.c_type; - itype.c_type_out = "MonoObject*"; - itype.cs_type = itype.proxy_name; - itype.im_type_in = "IntPtr"; - itype.im_type_out = itype.proxy_name; + itype.c_type_out = "Object"; // Populate properties @@ -2887,7 +2767,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() { String() + "Return type is reference but hint is not '" _STR(PROPERTY_HINT_RESOURCE_TYPE) "'." + " Are you returning a reference type by pointer? Method: '" + itype.name + "." + imethod.name + "'."); } else if (return_info.type == Variant::ARRAY && return_info.hint == PROPERTY_HINT_ARRAY_TYPE) { - imethod.return_type.cname = Variant::get_type_name(return_info.type); + imethod.return_type.cname = Variant::get_type_name(return_info.type) + "_@generic"; imethod.return_type.generic_type_parameters.push_back(TypeReference(return_info.hint_string)); } else if (return_info.hint == PROPERTY_HINT_RESOURCE_TYPE) { imethod.return_type.cname = return_info.hint_string; @@ -2919,7 +2799,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() { } else if (arginfo.class_name != StringName()) { iarg.type.cname = arginfo.class_name; } else if (arginfo.type == Variant::ARRAY && arginfo.hint == PROPERTY_HINT_ARRAY_TYPE) { - iarg.type.cname = Variant::get_type_name(arginfo.type); + iarg.type.cname = Variant::get_type_name(arginfo.type) + "_@generic"; iarg.type.generic_type_parameters.push_back(TypeReference(arginfo.hint_string)); } else if (arginfo.hint == PROPERTY_HINT_RESOURCE_TYPE) { iarg.type.cname = arginfo.hint_string; @@ -3027,7 +2907,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() { } else if (arginfo.class_name != StringName()) { iarg.type.cname = arginfo.class_name; } else if (arginfo.type == Variant::ARRAY && arginfo.hint == PROPERTY_HINT_ARRAY_TYPE) { - iarg.type.cname = Variant::get_type_name(arginfo.type); + iarg.type.cname = Variant::get_type_name(arginfo.type) + "_@generic"; iarg.type.generic_type_parameters.push_back(TypeReference(arginfo.hint_string)); } else if (arginfo.hint == PROPERTY_HINT_RESOURCE_TYPE) { iarg.type.cname = arginfo.hint_string; @@ -3246,7 +3126,7 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar ERR_FAIL_COND_V_MSG(!p_val.is_zero(), false, "Parameter of type '" + String(r_iarg.type.cname) + "' can only have null/zero as the default value."); - r_iarg.default_argument = "null"; + r_iarg.default_argument = "default"; break; case Variant::ARRAY: r_iarg.default_argument = "new %s { }"; @@ -3325,7 +3205,7 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar r_iarg.default_argument = "default"; break; default: - CRASH_NOW_MSG("Unexpected Variant type: " + itos(p_val.get_type())); + ERR_FAIL_V_MSG(false, "Unexpected Variant type: " + itos(p_val.get_type())); break; } @@ -3341,20 +3221,14 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { TypeInterface itype; -#define INSERT_STRUCT_TYPE(m_type) \ - { \ - itype = TypeInterface::create_value_type(String(#m_type)); \ - itype.c_in = "\t%0 %1_in = MARSHALLED_IN(" #m_type ", %1);\n"; \ - itype.c_out = "\t*%3 = MARSHALLED_OUT(" #m_type ", %1);\n"; \ - itype.c_arg_in = "&%s_in"; \ - itype.c_type_in = "GDMonoMarshal::M_" #m_type "*"; \ - itype.c_type_out = "GDMonoMarshal::M_" #m_type; \ - itype.cs_in = "ref %s"; \ - /* in cs_out, im_type_out (%3) includes the 'out ' part */ \ - itype.cs_out = "%0(%1, %3 argRet); return argRet;"; \ - itype.im_type_out = "out " + itype.cs_type; \ - itype.ret_as_byref_arg = true; \ - builtin_types.insert(itype.cname, itype); \ +#define INSERT_STRUCT_TYPE(m_type) \ + { \ + itype = TypeInterface::create_value_type(String(#m_type)); \ + itype.c_type_in = #m_type "*"; \ + itype.c_type_out = itype.cs_type; \ + itype.cs_in = "&%s"; \ + itype.cs_in_is_unsafe = true; \ + builtin_types.insert(itype.cname, itype); \ } INSERT_STRUCT_TYPE(Vector2) @@ -3370,56 +3244,56 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { INSERT_STRUCT_TYPE(AABB) INSERT_STRUCT_TYPE(Color) INSERT_STRUCT_TYPE(Plane) + INSERT_STRUCT_TYPE(Vector4) + INSERT_STRUCT_TYPE(Vector4i) + INSERT_STRUCT_TYPE(Projection) #undef INSERT_STRUCT_TYPE // bool itype = TypeInterface::create_value_type(String("bool")); - { - // MonoBoolean <---> bool - itype.c_in = "\t%0 %1_in = (%0)%1;\n"; - itype.c_out = "\treturn (%0)%1;\n"; - itype.c_type = "bool"; - itype.c_type_in = "MonoBoolean"; - itype.c_type_out = itype.c_type_in; - itype.c_arg_in = "&%s_in"; - } - itype.im_type_in = itype.name; - itype.im_type_out = itype.name; + itype.c_type = "godot_bool"; + itype.c_type_in = itype.c_type; + itype.c_type_out = itype.c_type; + itype.c_arg_in = "&%s"; + itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromBool(%1);\n"; builtin_types.insert(itype.cname, itype); // Integer types { // C interface for 'uint32_t' is the same as that of enums. Remember to apply // any of the changes done here to 'TypeInterface::postsetup_enum_type' as well. -#define INSERT_INT_TYPE(m_name, m_c_type_in_out, m_c_type) \ - { \ - itype = TypeInterface::create_value_type(String(m_name)); \ - { \ - itype.c_in = "\t%0 %1_in = (%0)%1;\n"; \ - itype.c_out = "\treturn (%0)%1;\n"; \ - itype.c_type = #m_c_type; \ - itype.c_arg_in = "&%s_in"; \ - } \ - itype.c_type_in = #m_c_type_in_out; \ - itype.c_type_out = itype.c_type_in; \ - itype.im_type_in = itype.name; \ - itype.im_type_out = itype.name; \ - builtin_types.insert(itype.cname, itype); \ +#define INSERT_INT_TYPE(m_name) \ + { \ + itype = TypeInterface::create_value_type(String(m_name)); \ + if (itype.name != "long" && itype.name != "ulong") { \ + itype.c_in = "%5%0 %1_in = %1;\n"; \ + itype.c_out = "%5return (%0)%1;\n"; \ + itype.c_type = "long"; \ + itype.c_arg_in = "&%s_in"; \ + } else { \ + itype.c_arg_in = "&%s"; \ + } \ + itype.c_type_in = itype.name; \ + itype.c_type_out = itype.name; \ + itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromInt(%1);\n"; \ + builtin_types.insert(itype.cname, itype); \ } // The expected type for all integers in ptrcall is 'int64_t', so that's what we use for 'c_type' - INSERT_INT_TYPE("sbyte", int8_t, int64_t); - INSERT_INT_TYPE("short", int16_t, int64_t); - INSERT_INT_TYPE("int", int32_t, int64_t); - INSERT_INT_TYPE("long", int64_t, int64_t); - INSERT_INT_TYPE("byte", uint8_t, int64_t); - INSERT_INT_TYPE("ushort", uint16_t, int64_t); - INSERT_INT_TYPE("uint", uint32_t, int64_t); - INSERT_INT_TYPE("ulong", uint64_t, int64_t); + INSERT_INT_TYPE("sbyte"); + INSERT_INT_TYPE("short"); + INSERT_INT_TYPE("int"); + INSERT_INT_TYPE("long"); + INSERT_INT_TYPE("byte"); + INSERT_INT_TYPE("ushort"); + INSERT_INT_TYPE("uint"); + INSERT_INT_TYPE("ulong"); } +#undef INSERT_INT_TYPE + // Floating point types { // float @@ -3427,18 +3301,17 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.name = "float"; itype.cname = itype.name; itype.proxy_name = "float"; + itype.cs_type = itype.proxy_name; { // The expected type for 'float' in ptrcall is 'double' - itype.c_in = "\t%0 %1_in = (%0)%1;\n"; - itype.c_out = "\treturn (%0)%1;\n"; + itype.c_in = "%5%0 %1_in = %1;\n"; + itype.c_out = "%5return (%0)%1;\n"; itype.c_type = "double"; - itype.c_type_in = "float"; - itype.c_type_out = "float"; itype.c_arg_in = "&%s_in"; } - itype.cs_type = itype.proxy_name; - itype.im_type_in = itype.proxy_name; - itype.im_type_out = itype.proxy_name; + itype.c_type_in = itype.proxy_name; + itype.c_type_out = itype.proxy_name; + itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromFloat(%1);\n"; builtin_types.insert(itype.cname, itype); // double @@ -3446,15 +3319,12 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.name = "double"; itype.cname = itype.name; itype.proxy_name = "double"; - { - itype.c_type = "double"; - itype.c_type_in = "double"; - itype.c_type_out = "double"; - itype.c_arg_in = "&%s"; - } itype.cs_type = itype.proxy_name; - itype.im_type_in = itype.proxy_name; - itype.im_type_out = itype.proxy_name; + itype.c_type = "double"; + itype.c_arg_in = "&%s"; + itype.c_type_in = itype.proxy_name; + itype.c_type_out = itype.proxy_name; + itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromFloat(%1);\n"; builtin_types.insert(itype.cname, itype); } @@ -3463,15 +3333,15 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.name = "String"; itype.cname = itype.name; itype.proxy_name = "string"; - itype.c_in = "\t%0 %1_in = " C_METHOD_MONOSTR_TO_GODOT "(%1);\n"; - itype.c_out = "\treturn " C_METHOD_MONOSTR_FROM_GODOT "(%1);\n"; - itype.c_arg_in = "&%s_in"; - itype.c_type = itype.name; - itype.c_type_in = "MonoString*"; - itype.c_type_out = "MonoString*"; itype.cs_type = itype.proxy_name; - itype.im_type_in = itype.proxy_name; - itype.im_type_out = itype.proxy_name; + itype.c_in = "%5using %0 %1_in = " C_METHOD_MONOSTR_TO_GODOT "(%1);\n"; + itype.c_out = "%5return " C_METHOD_MONOSTR_FROM_GODOT "(&%1);\n"; + itype.c_arg_in = "&%s_in"; + itype.c_type = "godot_string"; + itype.c_type_in = itype.cs_type; + itype.c_type_out = itype.cs_type; + itype.c_type_is_disposable_struct = true; + itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromStringTakingOwnershipOfDisposableValue(" C_METHOD_MONOSTR_TO_GODOT "(%1));\n"; builtin_types.insert(itype.cname, itype); // StringName @@ -3479,17 +3349,18 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.name = "StringName"; itype.cname = itype.name; itype.proxy_name = "StringName"; - itype.c_in = "\t%0 %1_in = %1 ? *%1 : StringName();\n"; - itype.c_out = "\treturn memnew(StringName(%1));\n"; - itype.c_arg_in = "&%s_in"; - itype.c_type = itype.name; - itype.c_type_in = itype.c_type + "*"; - itype.c_type_out = itype.c_type + "*"; itype.cs_type = itype.proxy_name; - itype.cs_in = "StringName." CS_SMETHOD_GETINSTANCE "(%0)"; - itype.cs_out = "return new %2(%0(%1));"; - itype.im_type_in = "IntPtr"; - itype.im_type_out = "IntPtr"; + itype.cs_in = "ref %0.NativeValue"; + // Cannot pass null StringName to ptrcall + itype.c_in = "%5using %0 %1_in = " C_CLASS_NATIVE_FUNCS ".godotsharp_string_name_new_copy(%1);\n"; + itype.c_out = "%5return %0.CreateTakingOwnershipOfDisposableValue(%1);\n"; + itype.c_arg_in = "&%s_in"; + itype.c_type = "godot_string_name"; + itype.c_type_in = "ref " + itype.c_type; + itype.c_type_out = itype.cs_type; + itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromStringName(ref %1);\n"; + itype.c_type_is_disposable_struct = false; // [c_out] takes ownership + itype.c_ret_needs_default_initialization = true; builtin_types.insert(itype.cname, itype); // NodePath @@ -3497,15 +3368,17 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.name = "NodePath"; itype.cname = itype.name; itype.proxy_name = "NodePath"; - itype.c_out = "\treturn memnew(NodePath(%1));\n"; - itype.c_type = itype.name; - itype.c_type_in = itype.c_type + "*"; - itype.c_type_out = itype.c_type + "*"; itype.cs_type = itype.proxy_name; - itype.cs_in = "NodePath." CS_SMETHOD_GETINSTANCE "(%0)"; - itype.cs_out = "return new %2(%0(%1));"; - itype.im_type_in = "IntPtr"; - itype.im_type_out = "IntPtr"; + itype.cs_in = "ref %0.NativeValue"; + // Cannot pass null NodePath to ptrcall + itype.c_in = "%5using %0 %1_in = " C_CLASS_NATIVE_FUNCS ".godotsharp_node_path_new_copy(%1);\n"; + itype.c_out = "%5return %0.CreateTakingOwnershipOfDisposableValue(%1);\n"; + itype.c_arg_in = "&%s_in"; + itype.c_type = "godot_node_path"; + itype.c_type_in = "ref " + itype.c_type; + itype.c_type_out = itype.cs_type; + itype.c_type_is_disposable_struct = false; // [c_out] takes ownership + itype.c_ret_needs_default_initialization = true; builtin_types.insert(itype.cname, itype); // RID @@ -3513,15 +3386,11 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.name = "RID"; itype.cname = itype.name; itype.proxy_name = "RID"; - itype.c_out = "\treturn memnew(RID(%1));\n"; - itype.c_type = itype.name; - itype.c_type_in = itype.c_type + "*"; - itype.c_type_out = itype.c_type + "*"; itype.cs_type = itype.proxy_name; - itype.cs_in = "RID." CS_SMETHOD_GETINSTANCE "(%0)"; - itype.cs_out = "return new %2(%0(%1));"; - itype.im_type_in = "IntPtr"; - itype.im_type_out = "IntPtr"; + itype.c_arg_in = "&%s"; + itype.c_type = itype.cs_type; + itype.c_type_in = itype.c_type; + itype.c_type_out = itype.c_type; builtin_types.insert(itype.cname, itype); // Variant @@ -3529,29 +3398,26 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.name = "Variant"; itype.cname = itype.name; itype.proxy_name = "object"; - itype.c_in = "\t%0 %1_in = " C_METHOD_MANAGED_TO_VARIANT "(%1);\n"; - itype.c_out = "\treturn " C_METHOD_MANAGED_FROM_VARIANT "(%1);\n"; - itype.c_arg_in = "&%s_in"; - itype.c_type = itype.name; - itype.c_type_in = "MonoObject*"; - itype.c_type_out = "MonoObject*"; itype.cs_type = itype.proxy_name; - itype.im_type_in = "object"; - itype.im_type_out = itype.proxy_name; + itype.c_in = "%5using %0 %1_in = " C_METHOD_MANAGED_TO_VARIANT "(%1);\n"; + itype.c_out = "%5return " C_METHOD_MANAGED_FROM_VARIANT "(&%1);\n"; + itype.c_arg_in = "&%s_in"; + itype.c_type = "godot_variant"; + itype.c_type_in = itype.cs_type; + itype.c_type_out = itype.cs_type; + itype.c_type_is_disposable_struct = true; builtin_types.insert(itype.cname, itype); // Callable itype = TypeInterface::create_value_type(String("Callable")); - itype.c_in = "\t%0 %1_in = " C_METHOD_MANAGED_TO_CALLABLE "(*%1);\n"; - itype.c_out = "\t*%3 = " C_METHOD_MANAGED_FROM_CALLABLE "(%1);\n"; - itype.c_arg_in = "&%s_in"; - itype.c_type_in = "GDMonoMarshal::M_Callable*"; - itype.c_type_out = "GDMonoMarshal::M_Callable"; itype.cs_in = "ref %s"; - /* in cs_out, im_type_out (%3) includes the 'out ' part */ - itype.cs_out = "%0(%1, %3 argRet); return argRet;"; - itype.im_type_out = "out " + itype.cs_type; - itype.ret_as_byref_arg = true; + itype.c_in = "%5using %0 %1_in = " C_METHOD_MANAGED_TO_CALLABLE "(ref %1);\n"; + itype.c_out = "%5return " C_METHOD_MANAGED_FROM_CALLABLE "(&%1);\n"; + itype.c_arg_in = "&%s_in"; + itype.c_type = "godot_callable"; + itype.c_type_in = "ref " + itype.cs_type; + itype.c_type_out = itype.cs_type; + itype.c_type_is_disposable_struct = true; builtin_types.insert(itype.cname, itype); // Signal @@ -3559,19 +3425,15 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.name = "Signal"; itype.cname = itype.name; itype.proxy_name = "SignalInfo"; - itype.c_in = "\t%0 %1_in = " C_METHOD_MANAGED_TO_SIGNAL "(*%1);\n"; - itype.c_out = "\t*%3 = " C_METHOD_MANAGED_FROM_SIGNAL "(%1);\n"; - itype.c_arg_in = "&%s_in"; - itype.c_type = itype.name; - itype.c_type_in = "GDMonoMarshal::M_SignalInfo*"; - itype.c_type_out = "GDMonoMarshal::M_SignalInfo"; - itype.cs_in = "ref %s"; - /* in cs_out, im_type_out (%3) includes the 'out ' part */ - itype.cs_out = "%0(%1, %3 argRet); return argRet;"; itype.cs_type = itype.proxy_name; - itype.im_type_in = "ref " + itype.cs_type; - itype.im_type_out = "out " + itype.cs_type; - itype.ret_as_byref_arg = true; + itype.cs_in = "ref %s"; + itype.c_in = "%5using %0 %1_in = " C_METHOD_MANAGED_TO_SIGNAL "(ref %1);\n"; + itype.c_out = "%5return " C_METHOD_MANAGED_FROM_SIGNAL "(&%1);\n"; + itype.c_arg_in = "&%s_in"; + itype.c_type = "godot_signal"; + itype.c_type_in = "ref " + itype.cs_type; + itype.c_type_out = itype.cs_type; + itype.c_type_is_disposable_struct = true; builtin_types.insert(itype.cname, itype); // VarArg (fictitious type to represent variable arguments) @@ -3579,46 +3441,44 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.name = "VarArg"; itype.cname = itype.name; itype.proxy_name = "object[]"; - itype.c_in = "\t%0 %1_in = " C_METHOD_MONOARRAY_TO(Array) "(%1);\n"; - itype.c_arg_in = "&%s_in"; - itype.c_type = "Array"; - itype.c_type_in = "MonoArray*"; itype.cs_type = "params object[]"; - itype.im_type_in = "object[]"; + // c_type, c_in and c_arg_in are hard-coded in the generator. + // c_out and c_type_out are not applicable to VarArg. + itype.c_arg_in = "&%s_in"; + itype.c_type_in = "object[]"; builtin_types.insert(itype.cname, itype); -#define INSERT_ARRAY_FULL(m_name, m_type, m_proxy_t) \ - { \ - itype = TypeInterface(); \ - itype.name = #m_name; \ - itype.cname = itype.name; \ - itype.proxy_name = #m_proxy_t "[]"; \ - itype.c_in = "\t%0 %1_in = " C_METHOD_MONOARRAY_TO(m_type) "(%1);\n"; \ - itype.c_out = "\treturn " C_METHOD_MONOARRAY_FROM(m_type) "(%1);\n"; \ - itype.c_arg_in = "&%s_in"; \ - itype.c_type = #m_type; \ - itype.c_type_in = "MonoArray*"; \ - itype.c_type_out = "MonoArray*"; \ - itype.cs_type = itype.proxy_name; \ - itype.im_type_in = itype.proxy_name; \ - itype.im_type_out = itype.proxy_name; \ - builtin_types.insert(itype.name, itype); \ +#define INSERT_ARRAY_FULL(m_name, m_type, m_managed_type, m_proxy_t) \ + { \ + itype = TypeInterface(); \ + itype.name = #m_name; \ + itype.cname = itype.name; \ + itype.proxy_name = #m_proxy_t "[]"; \ + itype.cs_type = itype.proxy_name; \ + itype.c_in = "%5using %0 %1_in = " C_METHOD_MONOARRAY_TO(m_type) "(%1);\n"; \ + itype.c_out = "%5return " C_METHOD_MONOARRAY_FROM(m_type) "(&%1);\n"; \ + itype.c_arg_in = "&%s_in"; \ + itype.c_type = #m_managed_type; \ + itype.c_type_in = itype.proxy_name; \ + itype.c_type_out = itype.proxy_name; \ + itype.c_type_is_disposable_struct = true; \ + builtin_types.insert(itype.name, itype); \ } -#define INSERT_ARRAY(m_type, m_proxy_t) INSERT_ARRAY_FULL(m_type, m_type, m_proxy_t) +#define INSERT_ARRAY(m_type, m_managed_type, m_proxy_t) INSERT_ARRAY_FULL(m_type, m_type, m_managed_type, m_proxy_t) - INSERT_ARRAY(PackedInt32Array, int); - INSERT_ARRAY(PackedInt64Array, long); - INSERT_ARRAY_FULL(PackedByteArray, PackedByteArray, byte); + INSERT_ARRAY(PackedInt32Array, godot_packed_int32_array, int); + INSERT_ARRAY(PackedInt64Array, godot_packed_int64_array, long); + INSERT_ARRAY_FULL(PackedByteArray, PackedByteArray, godot_packed_byte_array, byte); - INSERT_ARRAY(PackedFloat32Array, float); - INSERT_ARRAY(PackedFloat64Array, double); + INSERT_ARRAY(PackedFloat32Array, godot_packed_float32_array, float); + INSERT_ARRAY(PackedFloat64Array, godot_packed_float64_array, double); - INSERT_ARRAY(PackedStringArray, string); + INSERT_ARRAY(PackedStringArray, godot_packed_string_array, string); - INSERT_ARRAY(PackedColorArray, Color); - INSERT_ARRAY(PackedVector2Array, Vector2); - INSERT_ARRAY(PackedVector3Array, Vector3); + INSERT_ARRAY(PackedColorArray, godot_packed_color_array, Color); + INSERT_ARRAY(PackedVector2Array, godot_packed_vector2_array, Vector2); + INSERT_ARRAY(PackedVector3Array, godot_packed_vector3_array, Vector3); #undef INSERT_ARRAY @@ -3628,15 +3488,23 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.cname = itype.name; itype.proxy_name = itype.name; itype.type_parameter_count = 1; - itype.c_out = "\treturn memnew(Array(%1));\n"; - itype.c_type = itype.name; - itype.c_type_in = itype.c_type + "*"; - itype.c_type_out = itype.c_type + "*"; itype.cs_type = BINDINGS_NAMESPACE_COLLECTIONS "." + itype.proxy_name; - itype.cs_in = "%0." CS_SMETHOD_GETINSTANCE "()"; - itype.cs_out = "return new %2(%0(%1));"; - itype.im_type_in = "IntPtr"; - itype.im_type_out = "IntPtr"; + itype.cs_in = "ref %0.NativeValue"; + itype.c_in = "%5using %0 %1_in = " C_CLASS_NATIVE_FUNCS ".godotsharp_array_new_copy(%1);\n"; + itype.c_out = "%5return %0.CreateTakingOwnershipOfDisposableValue(%1);\n"; + itype.c_arg_in = "&%s_in"; + itype.c_type = "godot_array"; + itype.c_type_in = "ref " + itype.c_type; + itype.c_type_out = itype.cs_type; + itype.c_type_is_disposable_struct = false; // [c_out] takes ownership + itype.c_ret_needs_default_initialization = true; + builtin_types.insert(itype.cname, itype); + + // Array_@generic + // Re-use Array's itype + itype.name = "Array_@generic"; + itype.cname = itype.name; + itype.cs_out = "%5return new %2(%0(%1));"; builtin_types.insert(itype.cname, itype); // Dictionary @@ -3645,15 +3513,23 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.cname = itype.name; itype.proxy_name = itype.name; itype.type_parameter_count = 2; - itype.c_out = "\treturn memnew(Dictionary(%1));\n"; - itype.c_type = itype.name; - itype.c_type_in = itype.c_type + "*"; - itype.c_type_out = itype.c_type + "*"; itype.cs_type = BINDINGS_NAMESPACE_COLLECTIONS "." + itype.proxy_name; - itype.cs_in = "%0." CS_SMETHOD_GETINSTANCE "()"; - itype.cs_out = "return new %2(%0(%1));"; - itype.im_type_in = "IntPtr"; - itype.im_type_out = "IntPtr"; + itype.cs_in = "ref %0.NativeValue"; + itype.c_in = "%5using %0 %1_in = " C_CLASS_NATIVE_FUNCS ".godotsharp_dictionary_new_copy(%1);\n"; + itype.c_out = "%5return %0.CreateTakingOwnershipOfDisposableValue(%1);\n"; + itype.c_arg_in = "&%s_in"; + itype.c_type = "godot_dictionary"; + itype.c_type_in = "ref " + itype.c_type; + itype.c_type_out = itype.cs_type; + itype.c_type_is_disposable_struct = false; // [c_out] takes ownership + itype.c_ret_needs_default_initialization = true; + builtin_types.insert(itype.cname, itype); + + // Dictionary_@generic + // Re-use Dictionary's itype + itype.name = "Dictionary_@generic"; + itype.cname = itype.name; + itype.cs_out = "%5return new %2(%0(%1));"; builtin_types.insert(itype.cname, itype); // void (fictitious type to represent the return type of methods that do not return anything) @@ -3661,12 +3537,10 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.name = "void"; itype.cname = itype.name; itype.proxy_name = itype.name; - itype.c_type = itype.name; + itype.cs_type = itype.proxy_name; + itype.c_type = itype.proxy_name; itype.c_type_in = itype.c_type; itype.c_type_out = itype.c_type; - itype.cs_type = itype.proxy_name; - itype.im_type_in = itype.proxy_name; - itype.im_type_out = itype.proxy_name; builtin_types.insert(itype.cname, itype); } @@ -3791,21 +3665,18 @@ void BindingsGenerator::_initialize() { // Generate internal calls (after populating type interfaces and global constants) - core_custom_icalls.clear(); - editor_custom_icalls.clear(); - for (const KeyValue<StringName, TypeInterface> &E : obj_types) { - _generate_method_icalls(E.value); + const TypeInterface &itype = E.value; + Error err = _populate_method_icalls_table(itype); + ERR_FAIL_COND_MSG(err != OK, "Failed to generate icalls table for type: " + itype.name); } initialized = true; } static String generate_all_glue_option = "--generate-mono-glue"; -static String generate_cs_glue_option = "--generate-mono-cs-glue"; -static String generate_cpp_glue_option = "--generate-mono-cpp-glue"; -static void handle_cmdline_options(String glue_dir_path, String cs_dir_path, String cpp_dir_path) { +static void handle_cmdline_options(String glue_dir_path) { BindingsGenerator bindings_generator; bindings_generator.set_log_print_enabled(true); @@ -3814,43 +3685,25 @@ static void handle_cmdline_options(String glue_dir_path, String cs_dir_path, Str return; } - if (glue_dir_path.length()) { - if (bindings_generator.generate_glue(glue_dir_path) != OK) { - ERR_PRINT(generate_all_glue_option + ": Failed to generate the C++ glue."); - } - - if (bindings_generator.generate_cs_api(glue_dir_path.plus_file(API_SOLUTION_NAME)) != OK) { - ERR_PRINT(generate_all_glue_option + ": Failed to generate the C# API."); - } - } + CRASH_COND(glue_dir_path.is_empty()); - if (cs_dir_path.length()) { - if (bindings_generator.generate_cs_api(cs_dir_path) != OK) { - ERR_PRINT(generate_cs_glue_option + ": Failed to generate the C# API."); - } + if (bindings_generator.generate_cs_api(glue_dir_path.plus_file(API_SOLUTION_NAME)) != OK) { + ERR_PRINT(generate_all_glue_option + ": Failed to generate the C# API."); } +} - if (cpp_dir_path.length()) { - if (bindings_generator.generate_glue(cpp_dir_path) != OK) { - ERR_PRINT(generate_cpp_glue_option + ": Failed to generate the C++ glue."); - } - } +static void cleanup_and_exit_godot() { + // Exit once done + Main::cleanup(true); + ::exit(0); } void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args) { - const int NUM_OPTIONS = 2; - String glue_dir_path; - String cs_dir_path; - String cpp_dir_path; - - int options_left = NUM_OPTIONS; - - bool exit_godot = false; const List<String>::Element *elem = p_cmdline_args.front(); - while (elem && options_left) { + while (elem) { if (elem->get() == generate_all_glue_option) { const List<String>::Element *path_elem = elem->next(); @@ -3859,48 +3712,20 @@ void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args) elem = elem->next(); } else { ERR_PRINT(generate_all_glue_option + ": No output directory specified (expected path to '{GODOT_ROOT}/modules/mono/glue')."); - exit_godot = true; - } - - --options_left; - } else if (elem->get() == generate_cs_glue_option) { - const List<String>::Element *path_elem = elem->next(); - - if (path_elem) { - cs_dir_path = path_elem->get(); - elem = elem->next(); - } else { - ERR_PRINT(generate_cs_glue_option + ": No output directory specified."); - exit_godot = true; + // Exit once done with invalid command line arguments + cleanup_and_exit_godot(); } - --options_left; - } else if (elem->get() == generate_cpp_glue_option) { - const List<String>::Element *path_elem = elem->next(); - - if (path_elem) { - cpp_dir_path = path_elem->get(); - elem = elem->next(); - } else { - ERR_PRINT(generate_cpp_glue_option + ": No output directory specified."); - exit_godot = true; - } - - --options_left; + break; } elem = elem->next(); } - if (glue_dir_path.length() || cs_dir_path.length() || cpp_dir_path.length()) { - handle_cmdline_options(glue_dir_path, cs_dir_path, cpp_dir_path); - exit_godot = true; - } - - if (exit_godot) { + if (glue_dir_path.length()) { + handle_cmdline_options(glue_dir_path); // Exit once done - Main::cleanup(true); - ::exit(0); + cleanup_and_exit_godot(); } } diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h index ee170e4558..1e1e8be178 100644 --- a/modules/mono/editor/bindings_generator.h +++ b/modules/mono/editor/bindings_generator.h @@ -229,6 +229,23 @@ class BindingsGenerator { bool is_ref_counted = false; /** + * Determines whether the native return value of this type must be disposed + * by the generated internal call (think of `godot_string`, whose destructor + * must be called). Some structs that are disposable may still disable this + * flag if the ownership is transferred. + */ + bool c_type_is_disposable_struct = false; + + /** + * Determines whether the native return value of this type must be zero initialized + * before its address is passed to ptrcall. This is required for types whose destructor + * is called before being assigned the return value by `PtrToArg::encode`, e.g.: + * Array, Dictionary, String, StringName. + * It's not necessary to set this to `true` if [c_type_is_disposable_struct] is already `true`. + */ + bool c_ret_needs_default_initialization = false; + + /** * Used only by Object-derived types. * Determines if this type is not abstract (incomplete). * e.g.: CanvasItem cannot be instantiated. @@ -242,33 +259,36 @@ class BindingsGenerator { */ bool memory_own = false; - /** - * This must be set to true for any struct bigger than 32-bits. Those cannot be passed/returned by value - * with internal calls, so we must use pointers instead. Returns must be replace with out parameters. - * In this case, [c_out] and [cs_out] must have a different format, explained below. - * The Mono IL interpreter icall trampolines don't support passing structs bigger than 32-bits by value (at least not on WASM). - */ - bool ret_as_byref_arg = false; - // !! The comments of the following fields make reference to other fields via square brackets, e.g.: [field_name] // !! When renaming those fields, make sure to rename their references in the comments // --- C INTERFACE --- - static const char *DEFAULT_VARARG_C_IN; - /** * One or more statements that manipulate the parameter before being passed as argument of a ptrcall. * If the statement adds a local that must be passed as the argument instead of the parameter, * the name of that local must be specified with [c_arg_in]. - * For variadic methods, this field is required and, if empty, [DEFAULT_VARARG_C_IN] is used instead. * Formatting elements: * %0: [c_type] of the parameter * %1: name of the parameter + * %2-4: reserved + * %5: indentation text */ String c_in; /** + * One or more statements that manipulate the parameter before being passed as argument of a vararg call. + * If the statement adds a local that must be passed as the argument instead of the parameter, + * the name of that local must be specified with [c_arg_in]. + * Formatting elements: + * %0: [c_type] of the parameter + * %1: name of the parameter + * %2-4: reserved + * %5: indentation text + */ + String c_in_vararg; + + /** * Determines the expression that will be passed as argument to ptrcall. * By default the value equals the name of the parameter, * this varies for types that require special manipulation via [c_in]. @@ -291,7 +311,8 @@ class BindingsGenerator { * %0: [c_type_out] of the return type * %1: name of the variable to be returned * %2: [name] of the return type - * %3: name of the parameter that must be assigned the return value + * %3-4: reserved + * %5: indentation text */ String c_out; @@ -330,6 +351,7 @@ class BindingsGenerator { * %0 or %s: name of the parameter */ String cs_in; + bool cs_in_is_unsafe = false; /** * One or more statements that determine how a variable of this type is returned from a method. @@ -338,7 +360,9 @@ class BindingsGenerator { * %0: internal method name * %1: internal method call arguments without surrounding parenthesis * %2: [cs_type] of the return type - * %3: [im_type_out] of the return type + * %3: [c_type_out] of the return type + * %4: reserved + * %5: indentation text */ String cs_out; @@ -348,16 +372,6 @@ class BindingsGenerator { */ String cs_type; - /** - * Type used for parameters of internal call methods. - */ - String im_type_in; - - /** - * Type used for the return type of internal call methods. - */ - String im_type_out; - const DocData::ClassDoc *class_doc = nullptr; List<ConstantInterface> constants; @@ -432,8 +446,8 @@ class BindingsGenerator { itype.c_type = itype.name; itype.cs_type = itype.proxy_name; - itype.im_type_in = "ref " + itype.proxy_name; - itype.im_type_out = itype.proxy_name; + itype.c_type_in = itype.proxy_name + "*"; + itype.c_type_out = itype.proxy_name; itype.class_doc = &EditorHelp::get_doc_data()->class_list[itype.proxy_name]; } @@ -467,65 +481,27 @@ class BindingsGenerator { return itype; } - static void create_placeholder_type(TypeInterface &r_itype, const StringName &p_cname) { - r_itype.name = p_cname; - r_itype.cname = p_cname; - r_itype.proxy_name = r_itype.name; - - r_itype.c_type = r_itype.name; - r_itype.c_type_in = "MonoObject*"; - r_itype.c_type_out = "MonoObject*"; - r_itype.cs_type = r_itype.proxy_name; - r_itype.im_type_in = r_itype.proxy_name; - r_itype.im_type_out = r_itype.proxy_name; - } - - static void postsetup_enum_type(TypeInterface &r_enum_itype) { - // C interface for enums is the same as that of 'uint32_t'. Remember to apply - // any of the changes done here to the 'uint32_t' type interface as well. - - r_enum_itype.c_arg_in = "&%s_in"; - { - // The expected types for parameters and return value in ptrcall are 'int64_t' or 'uint64_t'. - r_enum_itype.c_in = "\t%0 %1_in = (%0)%1;\n"; - r_enum_itype.c_out = "\treturn (%0)%1;\n"; - r_enum_itype.c_type = "int64_t"; - } - r_enum_itype.c_type_in = "int32_t"; - r_enum_itype.c_type_out = r_enum_itype.c_type_in; - - r_enum_itype.cs_type = r_enum_itype.proxy_name; - r_enum_itype.cs_in = "(int)%s"; - r_enum_itype.cs_out = "return (%2)%0(%1);"; - r_enum_itype.im_type_in = "int"; - r_enum_itype.im_type_out = "int"; - r_enum_itype.class_doc = &EditorHelp::get_doc_data()->class_list[r_enum_itype.proxy_name]; - } + static void postsetup_enum_type(TypeInterface &r_enum_itype); TypeInterface() {} }; struct InternalCall { String name; - String im_type_out; // Return type for the C# method declaration. Also used as companion of [unique_siq] - String im_sig; // Signature for the C# method declaration String unique_sig; // Unique signature to avoid duplicates in containers bool editor_only = false; - InternalCall() {} + bool is_vararg = false; + bool is_static = false; + TypeReference return_type; + List<TypeReference> argument_types; - InternalCall(const String &p_name, const String &p_im_type_out, const String &p_im_sig = String(), const String &p_unique_sig = String()) { - name = p_name; - im_type_out = p_im_type_out; - im_sig = p_im_sig; - unique_sig = p_unique_sig; - editor_only = false; - } + _FORCE_INLINE_ int get_arguments_count() const { return argument_types.size(); } + + InternalCall() {} - InternalCall(ClassDB::APIType api_type, const String &p_name, const String &p_im_type_out, const String &p_im_sig = String(), const String &p_unique_sig = String()) { + InternalCall(ClassDB::APIType api_type, const String &p_name, const String &p_unique_sig = String()) { name = p_name; - im_type_out = p_im_type_out; - im_sig = p_im_sig; unique_sig = p_unique_sig; editor_only = api_type == ClassDB::API_EDITOR; } @@ -540,7 +516,6 @@ class BindingsGenerator { HashMap<StringName, TypeInterface> obj_types; - HashMap<StringName, TypeInterface> placeholder_types; HashMap<StringName, TypeInterface> builtin_types; HashMap<StringName, TypeInterface> enum_types; @@ -548,13 +523,9 @@ class BindingsGenerator { List<ConstantInterface> global_constants; List<InternalCall> method_icalls; + /// Stores the unique internal calls from [method_icalls] that are assigned to each method. HashMap<const MethodInterface *, const InternalCall *> method_icalls_map; - List<const InternalCall *> generated_icall_funcs; - - List<InternalCall> core_custom_icalls; - List<InternalCall> editor_custom_icalls; - HashMap<StringName, List<StringName>> blacklisted_methods; void _initialize_blacklisted_methods(); @@ -571,6 +542,8 @@ class BindingsGenerator { StringName type_String = StaticCString::create("String"); StringName type_StringName = StaticCString::create("StringName"); StringName type_NodePath = StaticCString::create("NodePath"); + StringName type_Array_generic = StaticCString::create("Array_@generic"); + StringName type_Dictionary_generic = StaticCString::create("Dictionary_@generic"); StringName type_at_GlobalScope = StaticCString::create("@GlobalScope"); StringName enum_Error = StaticCString::create("Error"); @@ -595,12 +568,14 @@ class BindingsGenerator { StringName type_Vector4i = StaticCString::create("Vector4i"); // Object not included as it must be checked for all derived classes - static constexpr int nullable_types_count = 17; + static constexpr int nullable_types_count = 18; StringName nullable_types[nullable_types_count] = { type_String, type_StringName, type_NodePath, + type_Array_generic, + type_Dictionary_generic, StaticCString::create(_STR(Array)), StaticCString::create(_STR(Dictionary)), StaticCString::create(_STR(Callable)), @@ -636,17 +611,6 @@ class BindingsGenerator { NameCache name_cache; - const List<InternalCall>::Element *find_icall_by_name(const String &p_name, const List<InternalCall> &p_list) { - const List<InternalCall>::Element *it = p_list.front(); - while (it) { - if (it->get().name == p_name) { - return it; - } - it = it->next(); - } - return nullptr; - } - const ConstantInterface *find_constant_by_name(const String &p_name, const List<ConstantInterface> &p_constants) const { for (const ConstantInterface &E : p_constants) { if (E.name == p_name) { @@ -657,18 +621,38 @@ class BindingsGenerator { return nullptr; } - inline String get_unique_sig(const TypeInterface &p_type) { - if (p_type.is_ref_counted) { - return "Ref"; - } else if (p_type.is_object_type) { + inline String get_arg_unique_sig(const TypeInterface &p_type) { + // For parameters, we treat reference and non-reference derived types the same. + if (p_type.is_object_type) { return "Obj"; } else if (p_type.is_enum) { return "int"; + } else if (p_type.cname == name_cache.type_Array_generic) { + return "Array"; + } else if (p_type.cname == name_cache.type_Dictionary_generic) { + return "Dictionary"; } return p_type.name; } + inline String get_ret_unique_sig(const TypeInterface *p_type) { + // Reference derived return types are treated differently. + if (p_type->is_ref_counted) { + return "Ref"; + } else if (p_type->is_object_type) { + return "Obj"; + } else if (p_type->is_enum) { + return "int"; + } else if (p_type->cname == name_cache.type_Array_generic) { + return "Array"; + } else if (p_type->cname == name_cache.type_Dictionary_generic) { + return "Dictionary"; + } + + return p_type->name; + } + String bbcode_to_xml(const String &p_bbcode, const TypeInterface *p_itype); void _append_xml_method(StringBuilder &p_xml_output, const TypeInterface *p_target_itype, const StringName &p_target_cname, const String &p_link_target, const Vector<String> &p_link_target_parts); @@ -682,10 +666,9 @@ class BindingsGenerator { int _determine_enum_prefix(const EnumInterface &p_ienum); void _apply_prefix_to_enum_constants(EnumInterface &p_ienum, int p_prefix_length); - void _generate_method_icalls(const TypeInterface &p_itype); + Error _populate_method_icalls_table(const TypeInterface &p_itype); const TypeInterface *_get_type_or_null(const TypeReference &p_typeref); - const TypeInterface *_get_type_or_placeholder(const TypeReference &p_typeref); const String _get_generic_type_parameters(const TypeInterface &p_itype, const List<TypeReference> &p_generic_type_parameters); @@ -706,11 +689,11 @@ class BindingsGenerator { Error _generate_cs_method(const TypeInterface &p_itype, const MethodInterface &p_imethod, int &p_method_bind_count, StringBuilder &p_output); Error _generate_cs_signal(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::SignalInterface &p_isignal, StringBuilder &p_output); + Error _generate_cs_native_calls(const InternalCall &p_icall, StringBuilder &r_output); + void _generate_array_extensions(StringBuilder &p_output); void _generate_global_constants(StringBuilder &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 StringBuilder &p_content); void _log(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3; @@ -721,15 +704,12 @@ public: Error generate_cs_core_project(const String &p_proj_dir); Error generate_cs_editor_project(const String &p_proj_dir); Error generate_cs_api(const String &p_output_dir); - Error generate_glue(const String &p_output_dir); _FORCE_INLINE_ bool is_log_print_enabled() { return log_print_enabled; } _FORCE_INLINE_ void set_log_print_enabled(bool p_enabled) { log_print_enabled = p_enabled; } _FORCE_INLINE_ bool is_initialized() { return initialized; } - static uint32_t get_version(); - static void handle_cmdline_args(const List<String> &p_cmdline_args); BindingsGenerator() { diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp index f830c7ffe1..102d27378f 100644 --- a/modules/mono/editor/editor_internal_calls.cpp +++ b/modules/mono/editor/editor_internal_calls.cpp @@ -46,7 +46,6 @@ #include "main/main.h" #include "../csharp_script.h" -#include "../glue/cs_glue_version.gen.h" #include "../godotsharp_dirs.h" #include "../mono_gd/gd_mono_marshal.h" #include "../utils/macos_utils.h" diff --git a/modules/mono/glue/GodotSharp/GodotSharp.sln.DotSettings b/modules/mono/glue/GodotSharp/GodotSharp.sln.DotSettings new file mode 100644 index 0000000000..3103fa78c7 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp.sln.DotSettings @@ -0,0 +1,7 @@ +<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> + <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=GC/@EntryIndexedValue">GC</s:String> + <s:Boolean x:Key="/Default/UserDictionary/Words/=gdnative/@EntryIndexedValue">True</s:Boolean> + <s:Boolean x:Key="/Default/UserDictionary/Words/=godotsharp/@EntryIndexedValue">True</s:Boolean> + <s:Boolean x:Key="/Default/UserDictionary/Words/=icall/@EntryIndexedValue">True</s:Boolean> + <s:Boolean x:Key="/Default/UserDictionary/Words/=quat/@EntryIndexedValue">True</s:Boolean> + <s:Boolean x:Key="/Default/UserDictionary/Words/=vcall/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs index a412047196..c32895baab 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs @@ -1,47 +1,28 @@ using System; using System.Collections.Generic; using System.Collections; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; +using Godot.NativeInterop; namespace Godot.Collections { - internal class ArraySafeHandle : SafeHandle - { - public ArraySafeHandle(IntPtr handle) : base(IntPtr.Zero, true) - { - this.handle = handle; - } - - public override bool IsInvalid - { - get { return handle == IntPtr.Zero; } - } - - protected override bool ReleaseHandle() - { - Array.godot_icall_Array_Dtor(handle); - return true; - } - } - /// <summary> /// Wrapper around Godot's Array class, an array of Variant /// typed elements allocated in the engine in C++. Useful when /// interfacing with the engine. Otherwise prefer .NET collections /// such as <see cref="System.Array"/> or <see cref="List{T}"/>. /// </summary> - public class Array : IList, IDisposable + public sealed class Array : IList, IDisposable { - private ArraySafeHandle _safeHandle; - private bool _disposed = false; + internal godot_array NativeValue; /// <summary> /// Constructs a new empty <see cref="Array"/>. /// </summary> public Array() { - _safeHandle = new ArraySafeHandle(godot_icall_Array_Ctor()); + godot_icall_Array_Ctor(out NativeValue); } /// <summary> @@ -58,6 +39,7 @@ namespace Godot.Collections Add(element); } + // TODO: This must be removed. Lots of silent mistakes as it takes pretty much anything. /// <summary> /// Constructs a new <see cref="Array"/> from the given objects. /// </summary> @@ -69,25 +51,37 @@ namespace Godot.Collections { throw new NullReferenceException($"Parameter '{nameof(array)} cannot be null.'"); } - _safeHandle = new ArraySafeHandle(godot_icall_Array_Ctor_MonoArray(array)); + + godot_icall_Array_Ctor_MonoArray(array, out NativeValue); } - internal Array(ArraySafeHandle handle) + private Array(godot_array nativeValueToOwn) { - _safeHandle = handle; + NativeValue = nativeValueToOwn; } - internal Array(IntPtr handle) + // Explicit name to make it very clear + internal static Array CreateTakingOwnershipOfDisposableValue(godot_array nativeValueToOwn) + => new Array(nativeValueToOwn); + + ~Array() { - _safeHandle = new ArraySafeHandle(handle); + Dispose(false); } - internal IntPtr GetPtr() + /// <summary> + /// Disposes of this <see cref="Array"/>. + /// </summary> + public void Dispose() { - if (_disposed) - throw new ObjectDisposedException(GetType().FullName); + Dispose(true); + GC.SuppressFinalize(this); + } - return _safeHandle.DangerousGetHandle(); + public void Dispose(bool disposing) + { + // Always dispose `NativeValue` even if disposing is true + NativeValue.Dispose(); } /// <summary> @@ -97,7 +91,9 @@ namespace Godot.Collections /// <returns>A new Godot Array.</returns> public Array Duplicate(bool deep = false) { - return new Array(godot_icall_Array_Duplicate(GetPtr(), deep)); + godot_array newArray; + godot_icall_Array_Duplicate(ref NativeValue, deep, out newArray); + return CreateTakingOwnershipOfDisposableValue(newArray); } /// <summary> @@ -107,7 +103,7 @@ namespace Godot.Collections /// <returns><see cref="Error.Ok"/> if successful, or an error code.</returns> public Error Resize(int newSize) { - return godot_icall_Array_Resize(GetPtr(), newSize); + return godot_icall_Array_Resize(ref NativeValue, newSize); } /// <summary> @@ -115,7 +111,7 @@ namespace Godot.Collections /// </summary> public void Shuffle() { - godot_icall_Array_Shuffle(GetPtr()); + godot_icall_Array_Shuffle(ref NativeValue); } /// <summary> @@ -126,26 +122,9 @@ namespace Godot.Collections /// <returns>A new Godot Array with the contents of both arrays.</returns> public static Array operator +(Array left, Array right) { - return new Array(godot_icall_Array_Concatenate(left.GetPtr(), right.GetPtr())); - } - - // IDisposable - - /// <summary> - /// Disposes of this <see cref="Array"/>. - /// </summary> - public void Dispose() - { - if (_disposed) - return; - - if (_safeHandle != null) - { - _safeHandle.Dispose(); - _safeHandle = null; - } - - _disposed = true; + godot_array newArray; + godot_icall_Array_Concatenate(ref left.NativeValue, ref right.NativeValue, out newArray); + return CreateTakingOwnershipOfDisposableValue(newArray); } // IList @@ -160,8 +139,16 @@ namespace Godot.Collections /// <value>The object at the given <paramref name="index"/>.</value> public object this[int index] { - get => godot_icall_Array_At(GetPtr(), index); - set => godot_icall_Array_SetAt(GetPtr(), index, value); + get + { + godot_icall_Array_At(ref NativeValue, index, out godot_variant elem); + unsafe + { + using (elem) + return Marshaling.variant_to_mono_object(&elem); + } + } + set => godot_icall_Array_SetAt(ref NativeValue, index, value); } /// <summary> @@ -170,19 +157,19 @@ namespace Godot.Collections /// </summary> /// <param name="value">The object to add.</param> /// <returns>The new size after adding the object.</returns> - public int Add(object value) => godot_icall_Array_Add(GetPtr(), value); + public int Add(object value) => godot_icall_Array_Add(ref NativeValue, value); /// <summary> /// Checks if this <see cref="Array"/> contains the given object. /// </summary> /// <param name="value">The item to look for.</param> /// <returns>Whether or not this array contains the given object.</returns> - public bool Contains(object value) => godot_icall_Array_Contains(GetPtr(), value); + public bool Contains(object value) => godot_icall_Array_Contains(ref NativeValue, value); /// <summary> /// Erases all items from this <see cref="Array"/>. /// </summary> - public void Clear() => godot_icall_Array_Clear(GetPtr()); + public void Clear() => godot_icall_Array_Clear(ref NativeValue); /// <summary> /// Searches this <see cref="Array"/> for an object @@ -190,7 +177,7 @@ namespace Godot.Collections /// </summary> /// <param name="value">The object to search for.</param> /// <returns>The index of the object, or -1 if not found.</returns> - public int IndexOf(object value) => godot_icall_Array_IndexOf(GetPtr(), value); + public int IndexOf(object value) => godot_icall_Array_IndexOf(ref NativeValue, value); /// <summary> /// Inserts a new object at a given position in the array. @@ -200,20 +187,20 @@ namespace Godot.Collections /// </summary> /// <param name="index">The index to insert at.</param> /// <param name="value">The object to insert.</param> - public void Insert(int index, object value) => godot_icall_Array_Insert(GetPtr(), index, value); + public void Insert(int index, object value) => godot_icall_Array_Insert(ref NativeValue, index, value); /// <summary> /// Removes the first occurrence of the specified <paramref name="value"/> /// from this <see cref="Array"/>. /// </summary> /// <param name="value">The value to remove.</param> - public void Remove(object value) => godot_icall_Array_Remove(GetPtr(), value); + public void Remove(object value) => godot_icall_Array_Remove(ref NativeValue, value); /// <summary> /// Removes an element from this <see cref="Array"/> by index. /// </summary> /// <param name="index">The index of the element to remove.</param> - public void RemoveAt(int index) => godot_icall_Array_RemoveAt(GetPtr(), index); + public void RemoveAt(int index) => godot_icall_Array_RemoveAt(ref NativeValue, index); // ICollection @@ -222,7 +209,7 @@ namespace Godot.Collections /// This is also known as the size or length of the array. /// </summary> /// <returns>The number of elements.</returns> - public int Count => godot_icall_Array_Count(GetPtr()); + public int Count => godot_icall_Array_Count(ref NativeValue); object ICollection.SyncRoot => this; @@ -243,7 +230,7 @@ namespace Godot.Collections 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, index); + godot_icall_Array_CopyTo(ref NativeValue, array, index); } // IEnumerable @@ -268,73 +255,71 @@ namespace Godot.Collections /// <returns>A string representation of this array.</returns> public override string ToString() { - return godot_icall_Array_ToString(GetPtr()); + return godot_icall_Array_ToString(ref NativeValue); } [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern IntPtr godot_icall_Array_Ctor(); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern IntPtr godot_icall_Array_Ctor_MonoArray(System.Array array); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_Dtor(IntPtr ptr); + internal static extern void godot_icall_Array_Ctor(out godot_array dest); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern object godot_icall_Array_At(IntPtr ptr, int index); + internal static extern void godot_icall_Array_Ctor_MonoArray(System.Array array, out godot_array dest); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern object godot_icall_Array_At_Generic(IntPtr ptr, int index, int elemTypeEncoding, IntPtr elemTypeClass); + internal static extern void godot_icall_Array_At(ref godot_array ptr, int index, out godot_variant elem); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_SetAt(IntPtr ptr, int index, object value); + internal static extern void godot_icall_Array_SetAt(ref godot_array ptr, int index, object value); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern int godot_icall_Array_Count(IntPtr ptr); + internal static extern int godot_icall_Array_Count(ref godot_array ptr); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern int godot_icall_Array_Add(IntPtr ptr, object item); + internal static extern int godot_icall_Array_Add(ref godot_array ptr, object item); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_Clear(IntPtr ptr); + internal static extern void godot_icall_Array_Clear(ref godot_array ptr); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern IntPtr godot_icall_Array_Concatenate(IntPtr left, IntPtr right); + internal static extern void godot_icall_Array_Concatenate(ref godot_array left, ref godot_array right, out godot_array dest); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_Array_Contains(IntPtr ptr, object item); + internal static extern bool godot_icall_Array_Contains(ref godot_array ptr, object item); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_CopyTo(IntPtr ptr, System.Array array, int arrayIndex); + internal static extern void godot_icall_Array_CopyTo(ref godot_array ptr, System.Array array, int arrayIndex); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern IntPtr godot_icall_Array_Duplicate(IntPtr ptr, bool deep); + internal static extern void godot_icall_Array_Duplicate(ref godot_array ptr, bool deep, out godot_array dest); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern int godot_icall_Array_IndexOf(IntPtr ptr, object item); + internal static extern int godot_icall_Array_IndexOf(ref godot_array ptr, object item); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_Insert(IntPtr ptr, int index, object item); + internal static extern void godot_icall_Array_Insert(ref godot_array ptr, int index, object item); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_Array_Remove(IntPtr ptr, object item); + internal static extern bool godot_icall_Array_Remove(ref godot_array ptr, object item); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_RemoveAt(IntPtr ptr, int index); + internal static extern void godot_icall_Array_RemoveAt(ref godot_array ptr, int index); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern Error godot_icall_Array_Resize(IntPtr ptr, int newSize); + internal static extern Error godot_icall_Array_Resize(ref godot_array ptr, int newSize); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern Error godot_icall_Array_Shuffle(IntPtr ptr); + internal static extern Error godot_icall_Array_Shuffle(ref godot_array ptr); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_Generic_GetElementTypeInfo(Type elemType, out int elemTypeEncoding, out IntPtr elemTypeClass); + internal static extern string godot_icall_Array_ToString(ref godot_array ptr); + } - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern string godot_icall_Array_ToString(IntPtr ptr); + internal interface IGenericGodotArray + { + Array UnderlyingArray { get; } + Type TypeOfElements { get; } } + // TODO: Now we should be able to avoid boxing /// <summary> /// Typed wrapper around Godot's Array class, an array of Variant /// typed elements allocated in the engine in C++. Useful when @@ -342,24 +327,29 @@ namespace Godot.Collections /// such as arrays or <see cref="List{T}"/>. /// </summary> /// <typeparam name="T">The type of the array.</typeparam> - public class Array<T> : IList<T>, ICollection<T>, IEnumerable<T> + [SuppressMessage("ReSharper", "RedundantExtendsListEntry")] + public sealed class Array<T> : IList<T>, ICollection<T>, IEnumerable<T>, IGenericGodotArray { - private Array _objectArray; + private readonly Array _underlyingArray; - internal static int elemTypeEncoding; - internal static IntPtr elemTypeClass; + internal ref godot_array NativeValue => ref _underlyingArray.NativeValue; - static Array() - { - Array.godot_icall_Array_Generic_GetElementTypeInfo(typeof(T), out elemTypeEncoding, out elemTypeClass); - } + // ReSharper disable StaticMemberInGenericType + // Warning is about unique static fields being created for each generic type combination: + // https://www.jetbrains.com/help/resharper/StaticMemberInGenericType.html + // In our case this is exactly what we want. + private static readonly Type TypeOfElements = typeof(T); + // ReSharper restore StaticMemberInGenericType + + Array IGenericGodotArray.UnderlyingArray => _underlyingArray; + Type IGenericGodotArray.TypeOfElements => TypeOfElements; /// <summary> /// Constructs a new empty <see cref="Array{T}"/>. /// </summary> public Array() { - _objectArray = new Array(); + _underlyingArray = new Array(); } /// <summary> @@ -372,7 +362,7 @@ namespace Godot.Collections if (collection == null) throw new NullReferenceException($"Parameter '{nameof(collection)} cannot be null.'"); - _objectArray = new Array(collection); + _underlyingArray = new Array(collection); } /// <summary> @@ -386,7 +376,8 @@ namespace Godot.Collections { throw new NullReferenceException($"Parameter '{nameof(array)} cannot be null.'"); } - _objectArray = new Array(array); + + _underlyingArray = new Array(array); } /// <summary> @@ -395,23 +386,12 @@ namespace Godot.Collections /// <param name="array">The untyped array to construct from.</param> public Array(Array array) { - _objectArray = array; - } - - internal Array(IntPtr handle) - { - _objectArray = new Array(handle); - } - - internal Array(ArraySafeHandle handle) - { - _objectArray = new Array(handle); + _underlyingArray = array; } - internal IntPtr GetPtr() - { - return _objectArray.GetPtr(); - } + // Explicit name to make it very clear + internal static Array<T> CreateTakingOwnershipOfDisposableValue(godot_array nativeValueToOwn) + => new Array<T>(Array.CreateTakingOwnershipOfDisposableValue(nativeValueToOwn)); /// <summary> /// Converts this typed <see cref="Array{T}"/> to an untyped <see cref="Array"/>. @@ -419,7 +399,7 @@ namespace Godot.Collections /// <param name="from">The typed array to convert.</param> public static explicit operator Array(Array<T> from) { - return from._objectArray; + return from._underlyingArray; } /// <summary> @@ -429,7 +409,7 @@ namespace Godot.Collections /// <returns>A new Godot Array.</returns> public Array<T> Duplicate(bool deep = false) { - return new Array<T>(_objectArray.Duplicate(deep)); + return new Array<T>(_underlyingArray.Duplicate(deep)); } /// <summary> @@ -439,7 +419,7 @@ namespace Godot.Collections /// <returns><see cref="Error.Ok"/> if successful, or an error code.</returns> public Error Resize(int newSize) { - return _objectArray.Resize(newSize); + return _underlyingArray.Resize(newSize); } /// <summary> @@ -447,7 +427,7 @@ namespace Godot.Collections /// </summary> public void Shuffle() { - _objectArray.Shuffle(); + _underlyingArray.Shuffle(); } /// <summary> @@ -458,7 +438,7 @@ namespace Godot.Collections /// <returns>A new Godot Array with the contents of both arrays.</returns> public static Array<T> operator +(Array<T> left, Array<T> right) { - return new Array<T>(left._objectArray + right._objectArray); + return new Array<T>(left._underlyingArray + right._underlyingArray); } // IList<T> @@ -469,8 +449,16 @@ namespace Godot.Collections /// <value>The value at the given <paramref name="index"/>.</value> public T this[int index] { - get { return (T)Array.godot_icall_Array_At_Generic(GetPtr(), index, elemTypeEncoding, elemTypeClass); } - set { _objectArray[index] = value; } + get + { + Array.godot_icall_Array_At(ref _underlyingArray.NativeValue, index, out godot_variant elem); + unsafe + { + using (elem) + return (T)Marshaling.variant_to_mono_object_of_type(&elem, TypeOfElements); + } + } + set => _underlyingArray[index] = value; } /// <summary> @@ -481,7 +469,7 @@ namespace Godot.Collections /// <returns>The index of the item, or -1 if not found.</returns> public int IndexOf(T item) { - return _objectArray.IndexOf(item); + return _underlyingArray.IndexOf(item); } /// <summary> @@ -494,7 +482,7 @@ namespace Godot.Collections /// <param name="item">The item to insert.</param> public void Insert(int index, T item) { - _objectArray.Insert(index, item); + _underlyingArray.Insert(index, item); } /// <summary> @@ -503,7 +491,7 @@ namespace Godot.Collections /// <param name="index">The index of the element to remove.</param> public void RemoveAt(int index) { - _objectArray.RemoveAt(index); + _underlyingArray.RemoveAt(index); } // ICollection<T> @@ -513,10 +501,7 @@ namespace Godot.Collections /// This is also known as the size or length of the array. /// </summary> /// <returns>The number of elements.</returns> - public int Count - { - get { return _objectArray.Count; } - } + public int Count => _underlyingArray.Count; bool ICollection<T>.IsReadOnly => false; @@ -528,7 +513,7 @@ namespace Godot.Collections /// <returns>The new size after adding the item.</returns> public void Add(T item) { - _objectArray.Add(item); + _underlyingArray.Add(item); } /// <summary> @@ -536,7 +521,7 @@ namespace Godot.Collections /// </summary> public void Clear() { - _objectArray.Clear(); + _underlyingArray.Clear(); } /// <summary> @@ -546,7 +531,7 @@ namespace Godot.Collections /// <returns>Whether or not this array contains the given item.</returns> public bool Contains(T item) { - return _objectArray.Contains(item); + return _underlyingArray.Contains(item); } /// <summary> @@ -563,17 +548,14 @@ namespace Godot.Collections if (arrayIndex < 0) throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Number was less than the array's lower bound in the first dimension."); - // TODO This may be quite slow because every element access is an internal call. - // It could be moved entirely to an internal call if we find out how to do the cast there. - - int count = _objectArray.Count; + int count = _underlyingArray.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++) { - array[arrayIndex] = (T)this[i]; + array[arrayIndex] = this[i]; arrayIndex++; } } @@ -586,7 +568,7 @@ namespace Godot.Collections /// <returns>A <see langword="bool"/> indicating success or failure.</returns> public bool Remove(T item) { - return Array.godot_icall_Array_Remove(GetPtr(), item); + return Array.godot_icall_Array_Remove(ref _underlyingArray.NativeValue, item); } // IEnumerable<T> @@ -597,23 +579,20 @@ namespace Godot.Collections /// <returns>An enumerator.</returns> public IEnumerator<T> GetEnumerator() { - int count = _objectArray.Count; + int count = _underlyingArray.Count; for (int i = 0; i < count; i++) { - yield return (T)this[i]; + yield return this[i]; } } - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); /// <summary> /// Converts this <see cref="Array{T}"/> to a string. /// </summary> /// <returns>A string representation of this array.</returns> - public override string ToString() => _objectArray.ToString(); + public override string ToString() => _underlyingArray.ToString(); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs index 1dca9e6ea7..187d910f9f 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs @@ -4,11 +4,53 @@ using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Godot.NativeInterop; namespace Godot { internal static class DelegateUtils { + // TODO: Move somewhere else once we need to for things other than delegates + internal static void FreeGCHandle(IntPtr delegateGCHandle) + => GCHandle.FromIntPtr(delegateGCHandle).Free(); + + internal static bool DelegateEquals(IntPtr delegateGCHandleA, IntPtr delegateGCHandleB) + { + var @delegateA = (Delegate)GCHandle.FromIntPtr(delegateGCHandleA).Target; + var @delegateB = (Delegate)GCHandle.FromIntPtr(delegateGCHandleB).Target; + return @delegateA == @delegateB; + } + + internal static unsafe void InvokeWithVariantArgs(IntPtr delegateGCHandle, godot_variant** args, uint argc, godot_variant* ret) + { + // TODO: Optimize + var @delegate = (Delegate)GCHandle.FromIntPtr(delegateGCHandle).Target; + var managedArgs = new object[argc]; + + var parameterInfos = @delegate.Method.GetParameters(); + + var paramsLength = parameterInfos.Length; + + if (argc != paramsLength) + { + throw new InvalidOperationException( + $"The delegate expects {paramsLength} arguments, but received {argc}."); + } + + for (uint i = 0; i < argc; i++) + { + managedArgs[i] = Marshaling.variant_to_mono_object_of_type( + args[i], parameterInfos[i].ParameterType); + } + + object invokeRet = @delegate.DynamicInvoke(managedArgs); + + *ret = Marshaling.mono_object_to_variant(invokeRet); + } + + // TODO: Check if we should be using BindingFlags.DeclaredOnly (would give better reflection performance). + private enum TargetKind : uint { Static, @@ -16,7 +58,10 @@ namespace Godot CompilerGenerated } - internal static bool TrySerializeDelegate(Delegate @delegate, Collections.Array serializedData) + internal static bool TrySerializeDelegateWithGCHandle(IntPtr delegateGCHandle, Collections.Array serializedData) + => TrySerializeDelegate((Delegate)GCHandle.FromIntPtr(delegateGCHandle).Target, serializedData); + + private static bool TrySerializeDelegate(Delegate @delegate, Collections.Array serializedData) { if (@delegate is MulticastDelegate multicastDelegate) { @@ -72,12 +117,14 @@ namespace Godot return true; } } + // ReSharper disable once RedundantNameQualifier case Godot.Object godotObject: { using (var stream = new MemoryStream()) using (var writer = new BinaryWriter(stream)) { writer.Write((ulong)TargetKind.GodotObject); + // ReSharper disable once RedundantCast writer.Write((ulong)godotObject.GetInstanceId()); SerializeType(writer, @delegate.GetType()); @@ -93,7 +140,7 @@ namespace Godot { Type targetType = target.GetType(); - if (targetType.GetCustomAttribute(typeof(CompilerGeneratedAttribute), true) != null) + if (targetType.IsDefined(typeof(CompilerGeneratedAttribute), true)) { // Compiler generated. Probably a closure. Try to serialize it. @@ -213,6 +260,13 @@ namespace Godot } } + private static bool TryDeserializeDelegateWithGCHandle(Collections.Array serializedData, out IntPtr delegateGCHandle) + { + bool res = TryDeserializeDelegate(serializedData, out Delegate @delegate); + delegateGCHandle = GCHandle.ToIntPtr(GCHandle.Alloc(@delegate)); + return res; + } + private static bool TryDeserializeDelegate(Collections.Array serializedData, out Delegate @delegate) { if (serializedData.Count == 1) @@ -276,6 +330,7 @@ namespace Godot case TargetKind.GodotObject: { ulong objectId = reader.ReadUInt64(); + // ReSharper disable once RedundantNameQualifier Godot.Object godotObject = GD.InstanceFromId(objectId); if (godotObject == null) return false; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs index e80b6af68f..d0c7e4523b 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs @@ -2,46 +2,28 @@ using System; using System.Collections.Generic; using System.Collections; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; +using Godot.NativeInterop; using System.Diagnostics.CodeAnalysis; namespace Godot.Collections { - internal class DictionarySafeHandle : SafeHandle - { - public DictionarySafeHandle(IntPtr handle) : base(IntPtr.Zero, true) - { - this.handle = handle; - } - - public override bool IsInvalid - { - get { return handle == IntPtr.Zero; } - } - - protected override bool ReleaseHandle() - { - Dictionary.godot_icall_Dictionary_Dtor(handle); - return true; - } - } - /// <summary> /// Wrapper around Godot's Dictionary class, a dictionary of Variant /// typed elements allocated in the engine in C++. Useful when /// interfacing with the engine. /// </summary> - public class Dictionary : IDictionary, IDisposable + public sealed class Dictionary : + IDictionary, + IDisposable { - private DictionarySafeHandle _safeHandle; - private bool _disposed = false; + internal godot_dictionary NativeValue; /// <summary> /// Constructs a new empty <see cref="Dictionary"/>. /// </summary> public Dictionary() { - _safeHandle = new DictionarySafeHandle(godot_icall_Dictionary_Ctor()); + godot_icall_Dictionary_Ctor(out NativeValue); } /// <summary> @@ -58,22 +40,18 @@ namespace Godot.Collections Add(entry.Key, entry.Value); } - internal Dictionary(DictionarySafeHandle handle) + private Dictionary(godot_dictionary nativeValueToOwn) { - _safeHandle = handle; + NativeValue = nativeValueToOwn; } - internal Dictionary(IntPtr handle) - { - _safeHandle = new DictionarySafeHandle(handle); - } + // Explicit name to make it very clear + internal static Dictionary CreateTakingOwnershipOfDisposableValue(godot_dictionary nativeValueToOwn) + => new Dictionary(nativeValueToOwn); - internal IntPtr GetPtr() + ~Dictionary() { - if (_disposed) - throw new ObjectDisposedException(GetType().FullName); - - return _safeHandle.DangerousGetHandle(); + Dispose(false); } /// <summary> @@ -81,16 +59,14 @@ namespace Godot.Collections /// </summary> public void Dispose() { - if (_disposed) - return; - - if (_safeHandle != null) - { - _safeHandle.Dispose(); - _safeHandle = null; - } + Dispose(true); + GC.SuppressFinalize(this); + } - _disposed = true; + public void Dispose(bool disposing) + { + // Always dispose `NativeValue` even if disposing is true + NativeValue.Dispose(); } /// <summary> @@ -100,7 +76,9 @@ namespace Godot.Collections /// <returns>A new Godot Dictionary.</returns> public Dictionary Duplicate(bool deep = false) { - return new Dictionary(godot_icall_Dictionary_Duplicate(GetPtr(), deep)); + godot_dictionary newDictionary; + godot_icall_Dictionary_Duplicate(ref NativeValue, deep, out newDictionary); + return CreateTakingOwnershipOfDisposableValue(newDictionary); } // IDictionary @@ -112,8 +90,9 @@ namespace Godot.Collections { get { - IntPtr handle = godot_icall_Dictionary_Keys(GetPtr()); - return new Array(new ArraySafeHandle(handle)); + godot_array keysArray; + godot_icall_Dictionary_Keys(ref NativeValue, out keysArray); + return Array.CreateTakingOwnershipOfDisposableValue(keysArray); } } @@ -124,16 +103,19 @@ namespace Godot.Collections { get { - IntPtr handle = godot_icall_Dictionary_Values(GetPtr()); - return new Array(new ArraySafeHandle(handle)); + godot_array valuesArray; + godot_icall_Dictionary_Values(ref NativeValue, out valuesArray); + return Array.CreateTakingOwnershipOfDisposableValue(valuesArray); } } private (Array keys, Array values, int count) GetKeyValuePairs() { - int count = godot_icall_Dictionary_KeyValuePairs(GetPtr(), out IntPtr keysHandle, out IntPtr valuesHandle); - Array keys = new Array(new ArraySafeHandle(keysHandle)); - Array values = new Array(new ArraySafeHandle(valuesHandle)); + godot_array keysArray; + godot_array valuesArray; + int count = godot_icall_Dictionary_KeyValuePairs(ref NativeValue, out keysArray, out valuesArray); + var keys = Array.CreateTakingOwnershipOfDisposableValue(keysArray); + var values = Array.CreateTakingOwnershipOfDisposableValue(valuesArray); return (keys, values, count); } @@ -147,8 +129,16 @@ namespace Godot.Collections /// <value>The object at the given <paramref name="key"/>.</value> public object this[object key] { - get => godot_icall_Dictionary_GetValue(GetPtr(), key); - set => godot_icall_Dictionary_SetValue(GetPtr(), key, value); + get + { + godot_icall_Dictionary_GetValue(ref NativeValue, key, out godot_variant value); + unsafe + { + using (value) + return Marshaling.variant_to_mono_object(&value); + } + } + set => godot_icall_Dictionary_SetValue(ref NativeValue, key, value); } /// <summary> @@ -157,19 +147,19 @@ namespace Godot.Collections /// </summary> /// <param name="key">The key at which to add the object.</param> /// <param name="value">The object to add.</param> - public void Add(object key, object value) => godot_icall_Dictionary_Add(GetPtr(), key, value); + public void Add(object key, object value) => godot_icall_Dictionary_Add(ref NativeValue, key, value); /// <summary> /// Erases all items from this <see cref="Dictionary"/>. /// </summary> - public void Clear() => godot_icall_Dictionary_Clear(GetPtr()); + public void Clear() => godot_icall_Dictionary_Clear(ref NativeValue); /// <summary> /// Checks if this <see cref="Dictionary"/> contains the given key. /// </summary> /// <param name="key">The key to look for.</param> /// <returns>Whether or not this dictionary contains the given key.</returns> - public bool Contains(object key) => godot_icall_Dictionary_ContainsKey(GetPtr(), key); + public bool Contains(object key) => godot_icall_Dictionary_ContainsKey(ref NativeValue, key); /// <summary> /// Gets an enumerator for this <see cref="Dictionary"/>. @@ -181,7 +171,7 @@ namespace Godot.Collections /// Removes an element from this <see cref="Dictionary"/> by key. /// </summary> /// <param name="key">The key of the element to remove.</param> - public void Remove(object key) => godot_icall_Dictionary_RemoveKey(GetPtr(), key); + public void Remove(object key) => godot_icall_Dictionary_RemoveKey(ref NativeValue, key); // ICollection @@ -194,7 +184,7 @@ namespace Godot.Collections /// This is also known as the size or length of the dictionary. /// </summary> /// <returns>The number of elements.</returns> - public int Count => godot_icall_Dictionary_Count(GetPtr()); + public int Count => godot_icall_Dictionary_Count(ref NativeValue); /// <summary> /// Copies the elements of this <see cref="Dictionary"/> to the given @@ -258,8 +248,17 @@ namespace Godot.Collections private void UpdateEntry() { _dirty = false; - godot_icall_Dictionary_KeyValuePairAt(_dictionary.GetPtr(), _index, out object key, out object value); - _entry = new DictionaryEntry(key, value); + godot_icall_Dictionary_KeyValuePairAt(ref _dictionary.NativeValue, _index, out var vKey, out var vValue); + unsafe + { + using (vKey) + using (vValue) + { + var key = Marshaling.variant_to_mono_object(&vKey); + var value = Marshaling.variant_to_mono_object(&vValue); + _entry = new DictionaryEntry(key, value); + } + } } public object Key => Entry.Key; @@ -286,76 +285,70 @@ namespace Godot.Collections /// <returns>A string representation of this dictionary.</returns> public override string ToString() { - return godot_icall_Dictionary_ToString(GetPtr()); + return godot_icall_Dictionary_ToString(ref NativeValue); } [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern IntPtr godot_icall_Dictionary_Ctor(); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_Dtor(IntPtr ptr); + internal static extern void godot_icall_Dictionary_Ctor(out godot_dictionary dest); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern object godot_icall_Dictionary_GetValue(IntPtr ptr, object key); + internal static extern void godot_icall_Dictionary_GetValue(ref godot_dictionary ptr, object key, out godot_variant value); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern object godot_icall_Dictionary_GetValue_Generic(IntPtr ptr, object key, int valTypeEncoding, IntPtr valTypeClass); + internal static extern void godot_icall_Dictionary_SetValue(ref godot_dictionary ptr, object key, object value); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_SetValue(IntPtr ptr, object key, object value); + internal static extern void godot_icall_Dictionary_Keys(ref godot_dictionary ptr, out godot_array dest); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern IntPtr godot_icall_Dictionary_Keys(IntPtr ptr); + internal static extern void godot_icall_Dictionary_Values(ref godot_dictionary ptr, out godot_array dest); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern IntPtr godot_icall_Dictionary_Values(IntPtr ptr); + internal static extern int godot_icall_Dictionary_KeyValuePairs(ref godot_dictionary ptr, out godot_array keys, out godot_array values); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern int godot_icall_Dictionary_Count(IntPtr ptr); + internal static extern void godot_icall_Dictionary_KeyValuePairAt(ref godot_dictionary ptr, int index, out godot_variant key, out godot_variant value); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern int godot_icall_Dictionary_KeyValuePairs(IntPtr ptr, out IntPtr keys, out IntPtr values); + internal static extern void godot_icall_Dictionary_Add(ref godot_dictionary ptr, object key, object value); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_KeyValuePairAt(IntPtr ptr, int index, out object key, out object value); + internal static extern int godot_icall_Dictionary_Count(ref godot_dictionary ptr); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_KeyValuePairAt_Generic(IntPtr ptr, int index, out object key, out object value, int valueTypeEncoding, IntPtr valueTypeClass); + internal static extern void godot_icall_Dictionary_Clear(ref godot_dictionary ptr); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_Add(IntPtr ptr, object key, object value); + internal static extern bool godot_icall_Dictionary_Contains(ref godot_dictionary ptr, object key, object value); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_Clear(IntPtr ptr); + internal static extern bool godot_icall_Dictionary_ContainsKey(ref godot_dictionary ptr, object key); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_Dictionary_Contains(IntPtr ptr, object key, object value); + internal static extern void godot_icall_Dictionary_Duplicate(ref godot_dictionary ptr, bool deep, out godot_dictionary dest); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_Dictionary_ContainsKey(IntPtr ptr, object key); + internal static extern bool godot_icall_Dictionary_RemoveKey(ref godot_dictionary ptr, object key); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern IntPtr godot_icall_Dictionary_Duplicate(IntPtr ptr, bool deep); + internal static extern bool godot_icall_Dictionary_Remove(ref godot_dictionary ptr, object key, object value); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_Dictionary_RemoveKey(IntPtr ptr, object key); + internal static extern bool godot_icall_Dictionary_TryGetValue(ref godot_dictionary ptr, object key, out godot_variant value); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_Dictionary_Remove(IntPtr ptr, object key, object value); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_Dictionary_TryGetValue(IntPtr ptr, object key, out object value); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_Dictionary_TryGetValue_Generic(IntPtr ptr, object key, out object value, int valTypeEncoding, IntPtr valTypeClass); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_Generic_GetValueTypeInfo(Type valueType, out int valTypeEncoding, out IntPtr valTypeClass); + internal static extern string godot_icall_Dictionary_ToString(ref godot_dictionary ptr); + } - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern string godot_icall_Dictionary_ToString(IntPtr ptr); + internal interface IGenericGodotDictionary + { + Dictionary UnderlyingDictionary { get; } + Type TypeOfKeys { get; } + Type TypeOfValues { get; } } + // TODO: Now we should be able to avoid boxing + /// <summary> /// Typed wrapper around Godot's Dictionary class, a dictionary of Variant /// typed elements allocated in the engine in C++. Useful when @@ -364,24 +357,32 @@ namespace Godot.Collections /// </summary> /// <typeparam name="TKey">The type of the dictionary's keys.</typeparam> /// <typeparam name="TValue">The type of the dictionary's values.</typeparam> - public class Dictionary<TKey, TValue> : IDictionary<TKey, TValue> + public sealed class Dictionary<TKey, TValue> : + IDictionary<TKey, TValue>, IGenericGodotDictionary { - private readonly Dictionary _objectDict; + private readonly Dictionary _underlyingDict; - internal static int valTypeEncoding; - internal static IntPtr valTypeClass; + internal ref godot_dictionary NativeValue => ref _underlyingDict.NativeValue; - static Dictionary() - { - Dictionary.godot_icall_Dictionary_Generic_GetValueTypeInfo(typeof(TValue), out valTypeEncoding, out valTypeClass); - } + // ReSharper disable StaticMemberInGenericType + // Warning is about unique static fields being created for each generic type combination: + // https://www.jetbrains.com/help/resharper/StaticMemberInGenericType.html + // In our case this is exactly what we want. + private static readonly Type TypeOfKeys = typeof(TKey); + + private static readonly Type TypeOfValues = typeof(TValue); + // ReSharper restore StaticMemberInGenericType + + Dictionary IGenericGodotDictionary.UnderlyingDictionary => _underlyingDict; + Type IGenericGodotDictionary.TypeOfKeys => TypeOfKeys; + Type IGenericGodotDictionary.TypeOfValues => TypeOfValues; /// <summary> /// Constructs a new empty <see cref="Dictionary{TKey, TValue}"/>. /// </summary> public Dictionary() { - _objectDict = new Dictionary(); + _underlyingDict = new Dictionary(); } /// <summary> @@ -391,19 +392,13 @@ namespace Godot.Collections /// <returns>A new Godot Dictionary.</returns> public Dictionary(IDictionary<TKey, TValue> dictionary) { - _objectDict = new Dictionary(); + _underlyingDict = 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); - } + Add(entry.Key, entry.Value); } /// <summary> @@ -413,18 +408,12 @@ namespace Godot.Collections /// <returns>A new Godot Dictionary.</returns> public Dictionary(Dictionary dictionary) { - _objectDict = dictionary; + _underlyingDict = dictionary; } - internal Dictionary(IntPtr handle) - { - _objectDict = new Dictionary(handle); - } - - internal Dictionary(DictionarySafeHandle handle) - { - _objectDict = new Dictionary(handle); - } + // Explicit name to make it very clear + internal static Dictionary<TKey, TValue> CreateTakingOwnershipOfDisposableValue(godot_dictionary nativeValueToOwn) + => new Dictionary<TKey, TValue>(Dictionary.CreateTakingOwnershipOfDisposableValue(nativeValueToOwn)); /// <summary> /// Converts this typed <see cref="Dictionary{TKey, TValue}"/> to an untyped <see cref="Dictionary"/>. @@ -432,12 +421,7 @@ namespace Godot.Collections /// <param name="from">The typed dictionary to convert.</param> public static explicit operator Dictionary(Dictionary<TKey, TValue> from) { - return from._objectDict; - } - - internal IntPtr GetPtr() - { - return _objectDict.GetPtr(); + return from._underlyingDict; } /// <summary> @@ -447,7 +431,7 @@ namespace Godot.Collections /// <returns>A new Godot Dictionary.</returns> public Dictionary<TKey, TValue> Duplicate(bool deep = false) { - return new Dictionary<TKey, TValue>(_objectDict.Duplicate(deep)); + return new Dictionary<TKey, TValue>(_underlyingDict.Duplicate(deep)); } // IDictionary<TKey, TValue> @@ -458,8 +442,16 @@ namespace Godot.Collections /// <value>The value at the given <paramref name="key"/>.</value> public TValue this[TKey key] { - get { return (TValue)Dictionary.godot_icall_Dictionary_GetValue_Generic(_objectDict.GetPtr(), key, valTypeEncoding, valTypeClass); } - set { _objectDict[key] = value; } + get + { + Dictionary.godot_icall_Dictionary_GetValue(ref _underlyingDict.NativeValue, key, out godot_variant value); + unsafe + { + using (value) + return (TValue)Marshaling.variant_to_mono_object_of_type(&value, TypeOfValues); + } + } + set => _underlyingDict[key] = value; } /// <summary> @@ -469,8 +461,9 @@ namespace Godot.Collections { get { - IntPtr handle = Dictionary.godot_icall_Dictionary_Keys(_objectDict.GetPtr()); - return new Array<TKey>(new ArraySafeHandle(handle)); + godot_array keyArray; + Dictionary.godot_icall_Dictionary_Keys(ref _underlyingDict.NativeValue, out keyArray); + return Array<TKey>.CreateTakingOwnershipOfDisposableValue(keyArray); } } @@ -481,15 +474,25 @@ namespace Godot.Collections { get { - IntPtr handle = Dictionary.godot_icall_Dictionary_Values(_objectDict.GetPtr()); - return new Array<TValue>(new ArraySafeHandle(handle)); + godot_array valuesArray; + Dictionary.godot_icall_Dictionary_Values(ref _underlyingDict.NativeValue, out valuesArray); + return Array<TValue>.CreateTakingOwnershipOfDisposableValue(valuesArray); } } private KeyValuePair<TKey, TValue> GetKeyValuePair(int index) { - Dictionary.godot_icall_Dictionary_KeyValuePairAt_Generic(GetPtr(), index, out object key, out object value, valTypeEncoding, valTypeClass); - return new KeyValuePair<TKey, TValue>((TKey)key, (TValue)value); + Dictionary.godot_icall_Dictionary_KeyValuePairAt(ref _underlyingDict.NativeValue, index, out var vKey, out var vValue); + unsafe + { + using (vKey) + using (vValue) + { + var key = Marshaling.variant_to_mono_object_of_type(&vKey, TypeOfKeys); + var value = Marshaling.variant_to_mono_object_of_type(&vValue, TypeOfValues); + return new KeyValuePair<TKey, TValue>((TKey)key, (TValue)value); + } + } } /// <summary> @@ -500,7 +503,7 @@ namespace Godot.Collections /// <param name="value">The object to add.</param> public void Add(TKey key, TValue value) { - _objectDict.Add(key, value); + _underlyingDict.Add(key, value); } /// <summary> @@ -510,7 +513,7 @@ namespace Godot.Collections /// <returns>Whether or not this dictionary contains the given key.</returns> public bool ContainsKey(TKey key) { - return _objectDict.Contains(key); + return _underlyingDict.Contains(key); } /// <summary> @@ -519,7 +522,7 @@ namespace Godot.Collections /// <param name="key">The key of the element to remove.</param> public bool Remove(TKey key) { - return Dictionary.godot_icall_Dictionary_RemoveKey(GetPtr(), key); + return Dictionary.godot_icall_Dictionary_RemoveKey(ref _underlyingDict.NativeValue, key); } /// <summary> @@ -530,8 +533,18 @@ namespace Godot.Collections /// <returns>If an object was found for the given <paramref name="key"/>.</returns> public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) { - bool found = Dictionary.godot_icall_Dictionary_TryGetValue_Generic(GetPtr(), key, out object retValue, valTypeEncoding, valTypeClass); - value = found ? (TValue)retValue : default; + bool found = Dictionary.godot_icall_Dictionary_TryGetValue(ref _underlyingDict.NativeValue, key, out godot_variant retValue); + + unsafe + { + using (retValue) + { + value = found ? + (TValue)Marshaling.variant_to_mono_object_of_type(&retValue, TypeOfValues) : + default; + } + } + return found; } @@ -542,16 +555,13 @@ namespace Godot.Collections /// This is also known as the size or length of the dictionary. /// </summary> /// <returns>The number of elements.</returns> - public int Count - { - get { return _objectDict.Count; } - } + public int Count => _underlyingDict.Count; bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly => false; void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item) { - _objectDict.Add(item.Key, item.Value); + _underlyingDict.Add(item.Key, item.Value); } /// <summary> @@ -559,12 +569,12 @@ namespace Godot.Collections /// </summary> public void Clear() { - _objectDict.Clear(); + _underlyingDict.Clear(); } bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item) { - return _objectDict.Contains(new KeyValuePair<object, object>(item.Key, item.Value)); + return _underlyingDict.Contains(new KeyValuePair<object, object>(item.Key, item.Value)); } /// <summary> @@ -595,8 +605,7 @@ namespace Godot.Collections bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item) { - return Dictionary.godot_icall_Dictionary_Remove(GetPtr(), item.Key, item.Value); - ; + return Dictionary.godot_icall_Dictionary_Remove(ref _underlyingDict.NativeValue, item.Key, item.Value); } // IEnumerable<KeyValuePair<TKey, TValue>> @@ -613,15 +622,12 @@ namespace Godot.Collections } } - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); /// <summary> /// Converts this <see cref="Dictionary{TKey, TValue}"/> to a string. /// </summary> /// <returns>A string representation of this dictionary.</returns> - public override string ToString() => _objectDict.ToString(); + public override string ToString() => _underlyingDict.ToString(); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs index df130a5c77..7922f38ac5 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using Godot.Collections; +using Godot.NativeInterop; namespace Godot { @@ -12,10 +13,12 @@ namespace Godot /// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam> public Array<T> GetNodesInGroup<T>(StringName group) where T : class { - return new Array<T>(godot_icall_SceneTree_get_nodes_in_group_Generic(GetPtr(this), StringName.GetPtr(group), typeof(T))); + godot_array array; + godot_icall_SceneTree_get_nodes_in_group_Generic(GetPtr(this), ref group.NativeValue, typeof(T), out array); + return Array<T>.CreateTakingOwnershipOfDisposableValue(array); } [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern IntPtr godot_icall_SceneTree_get_nodes_in_group_Generic(IntPtr obj, IntPtr group, Type elemType); + internal static extern void godot_icall_SceneTree_get_nodes_in_group_Generic(IntPtr obj, ref godot_string_name group, Type elemType, out godot_array dest); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs index 236d0666bc..e8ea8f379b 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs @@ -6,6 +6,7 @@ using real_t = System.Single; using System; using System.Collections.Generic; using System.Runtime.CompilerServices; +using Godot.NativeInterop; namespace Godot { @@ -26,9 +27,10 @@ namespace Godot /// <param name="bytes">Byte array that will be decoded to a <c>Variant</c>.</param> /// <param name="allowObjects">If objects should be decoded.</param> /// <returns>The decoded <c>Variant</c>.</returns> - public static object Bytes2Var(byte[] bytes, bool allowObjects = false) + public static unsafe object Bytes2Var(byte[] bytes, bool allowObjects = false) { - return godot_icall_GD_bytes2var(bytes, allowObjects); + using var varBytes = Marshaling.mono_array_to_PackedByteArray(bytes); + return godot_icall_GD_bytes2var(&varBytes, allowObjects); } /// <summary> @@ -527,7 +529,7 @@ namespace Godot /// <returns>If the class exists in <see cref="ClassDB"/>.</returns> public static bool TypeExists(StringName type) { - return godot_icall_GD_type_exists(StringName.GetPtr(type)); + return godot_icall_GD_type_exists(ref type.NativeValue); } /// <summary> @@ -539,9 +541,14 @@ namespace Godot /// <param name="var">Variant that will be encoded.</param> /// <param name="fullObjects">If objects should be serialized.</param> /// <returns>The <c>Variant</c> encoded as an array of bytes.</returns> - public static byte[] Var2Bytes(object var, bool fullObjects = false) + public static unsafe byte[] Var2Bytes(object var, bool fullObjects = false) { - return godot_icall_GD_var2bytes(var, fullObjects); + godot_packed_byte_array varBytes; + godot_icall_GD_var2bytes(var, fullObjects, &varBytes); + using (varBytes) + { + return Marshaling.PackedByteArray_to_mono_array(&varBytes); + } } /// <summary> @@ -576,7 +583,7 @@ namespace Godot } [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern object godot_icall_GD_bytes2var(byte[] bytes, bool allowObjects); + internal static extern unsafe object godot_icall_GD_bytes2var(godot_packed_byte_array* bytes, bool allowObjects); [MethodImpl(MethodImplOptions.InternalCall)] internal static extern object godot_icall_GD_convert(object what, Variant.Type type); @@ -636,10 +643,10 @@ namespace Godot internal static extern object godot_icall_GD_str2var(string str); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_GD_type_exists(IntPtr type); + internal static extern bool godot_icall_GD_type_exists(ref godot_string_name type); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern byte[] godot_icall_GD_var2bytes(object what, bool fullObjects); + internal static extern unsafe void godot_icall_GD_var2bytes(object what, bool fullObjects, godot_packed_byte_array* bytes); [MethodImpl(MethodImplOptions.InternalCall)] internal static extern string godot_icall_GD_var2str(object var); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs index 50ae2eb112..733a8ac114 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs @@ -6,149 +6,8 @@ namespace Godot internal static class MarshalUtils { /// <summary> - /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/> - /// is <see cref="Collections.Array{T}"/>; otherwise returns <see langword="false"/>. - /// </summary> - /// <exception cref="InvalidOperationException"> - /// Thrown when the given <paramref name="type"/> is not a generic type. - /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>. - /// </exception> - private static bool TypeIsGenericArray(Type type) => - type.GetGenericTypeDefinition() == typeof(Collections.Array<>); - - /// <summary> - /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/> - /// is <see cref="Collections.Dictionary{TKey, TValue}"/>; otherwise returns <see langword="false"/>. - /// </summary> - /// <exception cref="InvalidOperationException"> - /// Thrown when the given <paramref name="type"/> is not a generic type. - /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>. - /// </exception> - private static bool TypeIsGenericDictionary(Type type) => - type.GetGenericTypeDefinition() == typeof(Collections.Dictionary<,>); - - /// <summary> - /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/> - /// is <see cref="List{T}"/>; otherwise returns <see langword="false"/>. - /// </summary> - /// <exception cref="InvalidOperationException"> - /// Thrown when the given <paramref name="type"/> is not a generic type. - /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>. - /// </exception> - private static bool TypeIsSystemGenericList(Type type) => - type.GetGenericTypeDefinition() == typeof(List<>); - - /// <summary> - /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/> - /// is <see cref="Dictionary{TKey, TValue}"/>; otherwise returns <see langword="false"/>. - /// </summary> - /// <exception cref="InvalidOperationException"> - /// Thrown when the given <paramref name="type"/> is not a generic type. - /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>. - /// </exception> - private static bool TypeIsSystemGenericDictionary(Type type) => - type.GetGenericTypeDefinition() == typeof(Dictionary<,>); - - /// <summary> - /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/> - /// is <see cref="IEnumerable{T}"/>; otherwise returns <see langword="false"/>. - /// </summary> - /// <exception cref="InvalidOperationException"> - /// Thrown when the given <paramref name="type"/> is not a generic type. - /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>. - /// </exception> - private static bool TypeIsGenericIEnumerable(Type type) => type.GetGenericTypeDefinition() == typeof(IEnumerable<>); - - /// <summary> - /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/> - /// is <see cref="ICollection{T}"/>; otherwise returns <see langword="false"/>. - /// </summary> - /// <exception cref="InvalidOperationException"> - /// Thrown when the given <paramref name="type"/> is not a generic type. - /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>. - /// </exception> - private static bool TypeIsGenericICollection(Type type) => type.GetGenericTypeDefinition() == typeof(ICollection<>); - - /// <summary> - /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/> - /// is <see cref="IDictionary{TKey, TValue}"/>; otherwise returns <see langword="false"/>. - /// </summary> - /// <exception cref="InvalidOperationException"> - /// Thrown when the given <paramref name="type"/> is not a generic type. - /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>. - /// </exception> - private static bool TypeIsGenericIDictionary(Type type) => type.GetGenericTypeDefinition() == typeof(IDictionary<,>); - - /// <summary> /// Returns <see langword="true"/> if the <see cref="FlagsAttribute"/> is applied to the given type. /// </summary> private static bool TypeHasFlagsAttribute(Type type) => type.IsDefined(typeof(FlagsAttribute), false); - - /// <summary> - /// Returns the generic type definition of <paramref name="type"/>. - /// </summary> - /// <exception cref="InvalidOperationException"> - /// Thrown when the given <paramref name="type"/> is not a generic type. - /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>. - /// </exception> - private static void GetGenericTypeDefinition(Type type, out Type genericTypeDefinition) - { - genericTypeDefinition = type.GetGenericTypeDefinition(); - } - - /// <summary> - /// Gets the element type for the given <paramref name="arrayType"/>. - /// </summary> - /// <param name="arrayType">Type for the generic array.</param> - /// <param name="elementType">Element type for the generic array.</param> - /// <exception cref="InvalidOperationException"> - /// Thrown when the given <paramref name="arrayType"/> is not a generic type. - /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>. - /// </exception> - private static void ArrayGetElementType(Type arrayType, out Type elementType) - { - elementType = arrayType.GetGenericArguments()[0]; - } - - /// <summary> - /// Gets the key type and the value type for the given <paramref name="dictionaryType"/>. - /// </summary> - /// <param name="dictionaryType">The type for the generic dictionary.</param> - /// <param name="keyType">Key type for the generic dictionary.</param> - /// <param name="valueType">Value type for the generic dictionary.</param> - /// <exception cref="InvalidOperationException"> - /// Thrown when the given <paramref name="dictionaryType"/> is not a generic type. - /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>. - /// </exception> - private static void DictionaryGetKeyValueTypes(Type dictionaryType, out Type keyType, out Type valueType) - { - var genericArgs = dictionaryType.GetGenericArguments(); - keyType = genericArgs[0]; - valueType = genericArgs[1]; - } - - /// <summary> - /// Constructs a new <see cref="Type"/> from <see cref="Collections.Array{T}"/> - /// where the generic type for the elements is <paramref name="elemType"/>. - /// </summary> - /// <param name="elemType">Element type for the array.</param> - /// <returns>The generic array type with the specified element type.</returns> - private static Type MakeGenericArrayType(Type elemType) - { - return typeof(Collections.Array<>).MakeGenericType(elemType); - } - - /// <summary> - /// Constructs a new <see cref="Type"/> from <see cref="Collections.Dictionary{TKey, TValue}"/> - /// where the generic type for the keys is <paramref name="keyType"/> and - /// for the values is <paramref name="valueType"/>. - /// </summary> - /// <param name="keyType">Key type for the dictionary.</param> - /// <param name="valueType">Key type for the dictionary.</param> - /// <returns>The generic dictionary type with the specified key and value types.</returns> - private static Type MakeGenericDictionaryType(Type keyType, Type valueType) - { - return typeof(Collections.Dictionary<,>).MakeGenericType(keyType, valueType); - } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs new file mode 100644 index 0000000000..6ee8cbc0bc --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs @@ -0,0 +1,438 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; + +#endif + +namespace Godot.NativeInterop +{ + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_bool + { + public byte _value; + + public unsafe godot_bool(bool value) => _value = *(byte*)&value; + + public static unsafe implicit operator bool(godot_bool godotBool) => *(bool*)&godotBool._value; + public static implicit operator godot_bool(bool @bool) => new godot_bool(@bool); + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_ref : IDisposable + { + internal IntPtr _reference; + + public void Dispose() + { + if (_reference == IntPtr.Zero) + return; + NativeFuncs.godotsharp_ref_destroy(ref this); + _reference = IntPtr.Zero; + } + } + + [SuppressMessage("ReSharper", "InconsistentNaming")] + internal enum godot_variant_call_error_error + { + GODOT_CALL_ERROR_CALL_OK = 0, + GODOT_CALL_ERROR_CALL_ERROR_INVALID_METHOD, + GODOT_CALL_ERROR_CALL_ERROR_INVALID_ARGUMENT, + GODOT_CALL_ERROR_CALL_ERROR_TOO_MANY_ARGUMENTS, + GODOT_CALL_ERROR_CALL_ERROR_TOO_FEW_ARGUMENTS, + GODOT_CALL_ERROR_CALL_ERROR_INSTANCE_IS_NULL, + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_variant_call_error + { + godot_variant_call_error_error error; + int argument; + int expected; + } + + [StructLayout(LayoutKind.Explicit)] + // ReSharper disable once InconsistentNaming + internal struct godot_variant : IDisposable + { + // Variant.Type is generated as an enum of type long, so we can't use for the field as it must only take 32-bits. + [FieldOffset(0)] private int _typeField; + + // There's padding here + + [FieldOffset(8)] internal godot_variant_data _data; + + public Variant.Type _type + { + get => (Variant.Type)_typeField; + set => _typeField = (int)value; + } + + [StructLayout(LayoutKind.Explicit)] + // ReSharper disable once InconsistentNaming + internal unsafe struct godot_variant_data + { + [FieldOffset(0)] public godot_bool _bool; + [FieldOffset(0)] public long _int; + [FieldOffset(0)] public double _float; + [FieldOffset(0)] public Transform2D* _transform2d; + [FieldOffset(0)] public AABB* _aabb; + [FieldOffset(0)] public Basis* _basis; + [FieldOffset(0)] public Transform3D* _transform3d; + [FieldOffset(0)] public Vector4* _vector4; + [FieldOffset(0)] public Vector4i* _vector4i; + [FieldOffset(0)] public Projection* _projection; + [FieldOffset(0)] private godot_variant_data_mem _mem; + + // The following fields are not in the C++ union, but this is how they're stored in _mem. + [FieldOffset(0)] public godot_string_name _m_string_name; + [FieldOffset(0)] public godot_string _m_string; + [FieldOffset(0)] public Vector3 _m_vector3; + [FieldOffset(0)] public Vector3i _m_vector3i; + [FieldOffset(0)] public Vector2 _m_vector2; + [FieldOffset(0)] public Vector2i _m_vector2i; + [FieldOffset(0)] public Rect2 _m_rect2; + [FieldOffset(0)] public Rect2i _m_rect2i; + [FieldOffset(0)] public Plane _m_plane; + [FieldOffset(0)] public Quaternion _m_quaternion; + [FieldOffset(0)] public Color _m_color; + [FieldOffset(0)] public godot_node_path _m_node_path; + [FieldOffset(0)] public RID _m_rid; + [FieldOffset(0)] public godot_variant_obj_data _m_obj_data; + [FieldOffset(0)] public godot_callable _m_callable; + [FieldOffset(0)] public godot_signal _m_signal; + [FieldOffset(0)] public godot_dictionary _m_dictionary; + [FieldOffset(0)] public godot_array _m_array; + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_variant_obj_data + { + public UInt64 id; + public IntPtr obj; + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + private struct godot_variant_data_mem + { +#pragma warning disable 169 + private real_t _mem0; + private real_t _mem1; + private real_t _mem2; + private real_t _mem3; +#pragma warning restore 169 + } + } + + public void Dispose() + { + switch (_type) + { + case Variant.Type.Nil: + case Variant.Type.Bool: + case Variant.Type.Int: + case Variant.Type.Float: + case Variant.Type.Vector2: + case Variant.Type.Vector2i: + case Variant.Type.Rect2: + case Variant.Type.Rect2i: + case Variant.Type.Vector3: + case Variant.Type.Vector3i: + case Variant.Type.Plane: + case Variant.Type.Quaternion: + case Variant.Type.Color: + case Variant.Type.Rid: + return; + } + + NativeFuncs.godotsharp_variant_destroy(ref this); + _type = Variant.Type.Nil; + } + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_string : IDisposable + { + internal IntPtr _ptr; + + public void Dispose() + { + if (_ptr == IntPtr.Zero) + return; + NativeFuncs.godotsharp_string_destroy(ref this); + _ptr = IntPtr.Zero; + } + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_string_name : IDisposable + { + internal IntPtr _data; + + public void Dispose() + { + if (_data == IntPtr.Zero) + return; + NativeFuncs.godotsharp_string_name_destroy(ref this); + _data = IntPtr.Zero; + } + + // An static method because an instance method could result in a hidden copy if called on an `in` parameter. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsEmpty(in godot_string_name name) => + // This is all that's needed to check if it's empty. Equivalent to `== StringName()` in C++. + name._data == IntPtr.Zero; + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_node_path : IDisposable + { + internal IntPtr _data; + + public void Dispose() + { + if (_data == IntPtr.Zero) + return; + NativeFuncs.godotsharp_node_path_destroy(ref this); + _data = IntPtr.Zero; + } + + // An static method because an instance method could result in a hidden copy if called on an `in` parameter. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsEmpty(in godot_node_path nodePath) => + // This is all that's needed to check if it's empty. It's what the `is_empty()` C++ method does. + nodePath._data == IntPtr.Zero; + } + + [StructLayout(LayoutKind.Explicit)] + // ReSharper disable once InconsistentNaming + internal struct godot_signal : IDisposable + { + [FieldOffset(0)] public godot_string_name _name; + + // There's padding here on 32-bit + + [FieldOffset(8)] public UInt64 _objectId; + + public void Dispose() + { + if (_name._data == IntPtr.Zero) + return; + NativeFuncs.godotsharp_signal_destroy(ref this); + _name._data = IntPtr.Zero; + } + } + + [StructLayout(LayoutKind.Explicit)] + // ReSharper disable once InconsistentNaming + internal struct godot_callable : IDisposable + { + [FieldOffset(0)] public godot_string_name _method; + + // There's padding here on 32-bit + + [FieldOffset(8)] public UInt64 _objectId; + [FieldOffset(8)] public IntPtr _custom; + + public void Dispose() + { + if (_method._data == IntPtr.Zero && _custom == IntPtr.Zero) + return; + NativeFuncs.godotsharp_callable_destroy(ref this); + _method._data = IntPtr.Zero; + _custom = IntPtr.Zero; + } + } + + // A correctly constructed value needs to call the native default constructor to allocate `_p`. + // Don't pass a C# default constructed `godot_array` to native code, unless it's going to + // be re-assigned a new value (the copy constructor checks if `_p` is null so that's fine). + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_array : IDisposable + { + internal IntPtr _p; + + public void Dispose() + { + if (_p == IntPtr.Zero) + return; + NativeFuncs.godotsharp_array_destroy(ref this); + _p = IntPtr.Zero; + } + } + + // IMPORTANT: + // A correctly constructed value needs to call the native default constructor to allocate `_p`. + // Don't pass a C# default constructed `godot_dictionary` to native code, unless it's going to + // be re-assigned a new value (the copy constructor checks if `_p` is null so that's fine). + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_dictionary : IDisposable + { + internal IntPtr _p; + + public void Dispose() + { + if (_p == IntPtr.Zero) + return; + NativeFuncs.godotsharp_dictionary_destroy(ref this); + _p = IntPtr.Zero; + } + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_packed_byte_array : IDisposable + { + internal IntPtr _writeProxy; + internal IntPtr _ptr; + + public void Dispose() + { + if (_ptr == IntPtr.Zero) + return; + NativeFuncs.godotsharp_packed_byte_array_destroy(ref this); + _ptr = IntPtr.Zero; + } + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_packed_int32_array : IDisposable + { + internal IntPtr _writeProxy; + internal IntPtr _ptr; + + public void Dispose() + { + if (_ptr == IntPtr.Zero) + return; + NativeFuncs.godotsharp_packed_int32_array_destroy(ref this); + _ptr = IntPtr.Zero; + } + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_packed_int64_array : IDisposable + { + internal IntPtr _writeProxy; + internal IntPtr _ptr; + + public void Dispose() + { + if (_ptr == IntPtr.Zero) + return; + NativeFuncs.godotsharp_packed_int64_array_destroy(ref this); + _ptr = IntPtr.Zero; + } + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_packed_float32_array : IDisposable + { + internal IntPtr _writeProxy; + internal IntPtr _ptr; + + public void Dispose() + { + if (_ptr == IntPtr.Zero) + return; + NativeFuncs.godotsharp_packed_float32_array_destroy(ref this); + _ptr = IntPtr.Zero; + } + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_packed_float64_array : IDisposable + { + internal IntPtr _writeProxy; + internal IntPtr _ptr; + + public void Dispose() + { + if (_ptr == IntPtr.Zero) + return; + NativeFuncs.godotsharp_packed_float64_array_destroy(ref this); + _ptr = IntPtr.Zero; + } + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_packed_string_array : IDisposable + { + internal IntPtr _writeProxy; + internal IntPtr _ptr; + + public void Dispose() + { + if (_ptr == IntPtr.Zero) + return; + NativeFuncs.godotsharp_packed_string_array_destroy(ref this); + _ptr = IntPtr.Zero; + } + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_packed_vector2_array : IDisposable + { + internal IntPtr _writeProxy; + internal IntPtr _ptr; + + public void Dispose() + { + if (_ptr == IntPtr.Zero) + return; + NativeFuncs.godotsharp_packed_vector2_array_destroy(ref this); + _ptr = IntPtr.Zero; + } + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_packed_vector3_array : IDisposable + { + internal IntPtr _writeProxy; + internal IntPtr _ptr; + + public void Dispose() + { + if (_ptr == IntPtr.Zero) + return; + NativeFuncs.godotsharp_packed_vector3_array_destroy(ref this); + _ptr = IntPtr.Zero; + } + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_packed_color_array : IDisposable + { + internal IntPtr _writeProxy; + internal IntPtr _ptr; + + public void Dispose() + { + if (_ptr == IntPtr.Zero) + return; + NativeFuncs.godotsharp_packed_color_array_destroy(ref this); + _ptr = IntPtr.Zero; + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs new file mode 100644 index 0000000000..08d49bb937 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs @@ -0,0 +1,32 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Godot.NativeInterop +{ + internal static class InteropUtils + { + public static Object UnmanagedGetManaged(IntPtr unmanaged) + { + // TODO: Move to C# + return internal_unmanaged_get_managed(unmanaged); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern Object internal_unmanaged_get_managed(IntPtr unmanaged); + + public static void TieManagedToUnmanaged(Object managed, IntPtr unmanaged) + { + // TODO: Move to C# + internal_tie_managed_to_unmanaged(managed, unmanaged); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern void internal_tie_managed_to_unmanaged(Object managed, IntPtr unmanaged); + + public static unsafe Object EngineGetSingleton(string name) + { + using godot_string src = Marshaling.mono_string_to_godot(name); + return UnmanagedGetManaged(NativeFuncs.godotsharp_engine_get_singleton(&src)); + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs new file mode 100644 index 0000000000..9b6f9633d1 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs @@ -0,0 +1,1370 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Runtime.InteropServices; + +// ReSharper disable InconsistentNaming + +namespace Godot.NativeInterop +{ + // We want to use full name qualifiers here even if redundant for clarity + [SuppressMessage("ReSharper", "RedundantNameQualifier")] + internal static class Marshaling + { + public static unsafe void SetFieldValue(FieldInfo fieldInfo, object obj, godot_variant* value) + { + var valueObj = variant_to_mono_object_of_type(value, fieldInfo.FieldType); + fieldInfo.SetValue(obj, valueObj); + } + + public static Variant.Type managed_to_variant_type(Type type, ref bool r_nil_is_variant) + { + switch (Type.GetTypeCode(type)) + { + case TypeCode.Boolean: + return Variant.Type.Bool; + case TypeCode.Char: + return Variant.Type.Int; + case TypeCode.SByte: + return Variant.Type.Int; + case TypeCode.Int16: + return Variant.Type.Int; + case TypeCode.Int32: + return Variant.Type.Int; + case TypeCode.Int64: + return Variant.Type.Int; + case TypeCode.Byte: + return Variant.Type.Int; + case TypeCode.UInt16: + return Variant.Type.Int; + case TypeCode.UInt32: + return Variant.Type.Int; + case TypeCode.UInt64: + return Variant.Type.Int; + case TypeCode.Single: + return Variant.Type.Float; + case TypeCode.Double: + return Variant.Type.Float; + case TypeCode.String: + return Variant.Type.String; + default: + { + if (type == typeof(Vector2)) + return Variant.Type.Vector2; + + if (type == typeof(Vector2i)) + return Variant.Type.Vector2i; + + if (type == typeof(Rect2)) + return Variant.Type.Rect2; + + if (type == typeof(Rect2i)) + return Variant.Type.Rect2i; + + if (type == typeof(Transform2D)) + return Variant.Type.Transform2d; + + if (type == typeof(Vector3)) + return Variant.Type.Vector3; + + if (type == typeof(Vector3i)) + return Variant.Type.Vector3i; + + if (type == typeof(Vector4)) + return Variant.Type.Vector4; + + if (type == typeof(Vector4i)) + return Variant.Type.Vector4i; + + if (type == typeof(Basis)) + return Variant.Type.Basis; + + if (type == typeof(Quaternion)) + return Variant.Type.Quaternion; + + if (type == typeof(Transform3D)) + return Variant.Type.Transform3d; + + if (type == typeof(Projection)) + return Variant.Type.Projection; + + if (type == typeof(AABB)) + return Variant.Type.Aabb; + + if (type == typeof(Color)) + return Variant.Type.Color; + + if (type == typeof(Plane)) + return Variant.Type.Plane; + + if (type == typeof(Callable)) + return Variant.Type.Callable; + + if (type == typeof(SignalInfo)) + return Variant.Type.Signal; + + if (type.IsEnum) + return Variant.Type.Int; + + if (type.IsArray || type.IsSZArray) + { + if (type == typeof(Byte[])) + return Variant.Type.PackedByteArray; + + if (type == typeof(Int32[])) + return Variant.Type.PackedInt32Array; + + if (type == typeof(Int64[])) + return Variant.Type.PackedInt64Array; + + if (type == typeof(float[])) + return Variant.Type.PackedFloat32Array; + + if (type == typeof(double[])) + return Variant.Type.PackedFloat64Array; + + if (type == typeof(string[])) + return Variant.Type.PackedStringArray; + + if (type == typeof(Vector2[])) + return Variant.Type.PackedVector2Array; + + if (type == typeof(Vector3[])) + return Variant.Type.PackedVector3Array; + + if (type == typeof(Color[])) + return Variant.Type.PackedColorArray; + + if (typeof(Godot.Object[]).IsAssignableFrom(type)) + return Variant.Type.Array; + + if (type == typeof(object[])) + return Variant.Type.Array; + } + else if (type.IsGenericType) + { + var genericTypeDefinition = type.GetGenericTypeDefinition(); + + if (genericTypeDefinition == typeof(Collections.Dictionary<,>)) + return Variant.Type.Dictionary; + + if (genericTypeDefinition == typeof(Collections.Array<>)) + return Variant.Type.Array; + + if (genericTypeDefinition == typeof(System.Collections.Generic.Dictionary<,>)) + return Variant.Type.Dictionary; + + if (genericTypeDefinition == typeof(System.Collections.Generic.List<>)) + return Variant.Type.Array; + + if (genericTypeDefinition == typeof(IDictionary<,>)) + return Variant.Type.Dictionary; + + if (genericTypeDefinition == typeof(ICollection<>) || genericTypeDefinition == typeof(IEnumerable<>)) + return Variant.Type.Array; + } + else if (type == typeof(object)) + { + r_nil_is_variant = true; + return Variant.Type.Nil; + } + else + { + if (typeof(Godot.Object).IsAssignableFrom(type)) + return Variant.Type.Object; + + if (typeof(StringName) == type) + return Variant.Type.StringName; + + if (typeof(NodePath) == type) + return Variant.Type.NodePath; + + if (typeof(RID) == type) + return Variant.Type.Rid; + + if (typeof(Collections.Dictionary) == type || typeof(System.Collections.IDictionary) == type) + return Variant.Type.Dictionary; + + if (typeof(Collections.Array) == type || + typeof(System.Collections.ICollection) == type || + typeof(System.Collections.IEnumerable) == type) + { + return Variant.Type.Array; + } + } + + break; + } + } + + r_nil_is_variant = false; + + // Unknown + return Variant.Type.Nil; + } + + public static bool try_get_array_element_type(Type p_array_type, out Type r_elem_type) + { + if (p_array_type.IsArray || p_array_type.IsSZArray) + { + r_elem_type = p_array_type.GetElementType(); + return true; + } + else if (p_array_type.IsGenericType) + { + var genericTypeDefinition = p_array_type.GetGenericTypeDefinition(); + + if (typeof(Collections.Array) == genericTypeDefinition || + typeof(System.Collections.Generic.List<>) == genericTypeDefinition || + typeof(System.Collections.ICollection) == genericTypeDefinition || + typeof(System.Collections.IEnumerable) == genericTypeDefinition) + { + r_elem_type = p_array_type.GetGenericArguments()[0]; + return true; + } + } + + r_elem_type = null; + return false; + } + + /* TODO: Reflection and type checking each time is slow. This will be replaced with source generators. */ + + public static godot_variant mono_object_to_variant(object p_obj) + { + return mono_object_to_variant_impl(p_obj); + } + + public static godot_variant mono_object_to_variant_no_err(object p_obj) + { + return mono_object_to_variant_impl(p_obj); + } + + // TODO: Only called from C++. Remove once no longer needed. + private static unsafe void mono_object_to_variant_out(object p_obj, bool p_fail_with_err, godot_variant* r_ret) + => *r_ret = mono_object_to_variant_impl(p_obj, p_fail_with_err); + + private static unsafe godot_variant mono_object_to_variant_impl(object p_obj, bool p_fail_with_err = true) + { + if (p_obj == null) + return new godot_variant(); + + switch (p_obj) + { + case bool @bool: + return VariantUtils.CreateFromBool(@bool); + case char @char: + return VariantUtils.CreateFromInt(@char); + case SByte @int8: + return VariantUtils.CreateFromInt(@int8); + case Int16 @int16: + return VariantUtils.CreateFromInt(@int16); + case Int32 @int32: + return VariantUtils.CreateFromInt(@int32); + case Int64 @int64: + return VariantUtils.CreateFromInt(@int64); + case Byte @uint8: + return VariantUtils.CreateFromInt(@uint8); + case UInt16 @uint16: + return VariantUtils.CreateFromInt(@uint16); + case UInt32 @uint32: + return VariantUtils.CreateFromInt(@uint32); + case UInt64 @uint64: + return VariantUtils.CreateFromInt(@uint64); + case float @float: + return VariantUtils.CreateFromFloat(@float); + case double @double: + return VariantUtils.CreateFromFloat(@double); + case Vector2 @vector2: + return VariantUtils.CreateFromVector2(@vector2); + case Vector2i @vector2i: + return VariantUtils.CreateFromVector2i(@vector2i); + case Rect2 @rect2: + return VariantUtils.CreateFromRect2(@rect2); + case Rect2i @rect2i: + return VariantUtils.CreateFromRect2i(@rect2i); + case Transform2D @transform2D: + return VariantUtils.CreateFromTransform2D(@transform2D); + case Vector3 @vector3: + return VariantUtils.CreateFromVector3(@vector3); + case Vector3i @vector3i: + return VariantUtils.CreateFromVector3i(@vector3i); + case Vector4 @vector4: + return VariantUtils.CreateFromVector4(@vector4); + case Vector4i @vector4i: + return VariantUtils.CreateFromVector4i(@vector4i); + case Basis @basis: + return VariantUtils.CreateFromBasis(@basis); + case Quaternion @quaternion: + return VariantUtils.CreateFromQuaternion(@quaternion); + case Transform3D @transform3d: + return VariantUtils.CreateFromTransform3D(@transform3d); + case Projection @projection: + return VariantUtils.CreateFromProjection(@projection); + case AABB @aabb: + return VariantUtils.CreateFromAABB(@aabb); + case Color @color: + return VariantUtils.CreateFromColor(@color); + case Plane @plane: + return VariantUtils.CreateFromPlane(@plane); + case Callable @callable: + return VariantUtils.CreateFromCallableTakingOwnershipOfDisposableValue( + ConvertCallableToNative(ref @callable)); + case SignalInfo @signalInfo: + return VariantUtils.CreateFromSignalTakingOwnershipOfDisposableValue( + ConvertSignalToNative(ref @signalInfo)); + case Enum @enum: + return VariantUtils.CreateFromInt(Convert.ToInt64(@enum)); + case string @string: + { + return VariantUtils.CreateFromStringTakingOwnershipOfDisposableValue( + mono_string_to_godot(@string)); + } + case Byte[] byteArray: + { + using godot_packed_byte_array array = mono_array_to_PackedByteArray(byteArray); + return VariantUtils.CreateFromPackedByteArray(&array); + } + case Int32[] int32Array: + { + using godot_packed_int32_array array = mono_array_to_PackedInt32Array(int32Array); + return VariantUtils.CreateFromPackedInt32Array(&array); + } + case Int64[] int64Array: + { + using godot_packed_int64_array array = mono_array_to_PackedInt64Array(int64Array); + return VariantUtils.CreateFromPackedInt64Array(&array); + } + case float[] floatArray: + { + using godot_packed_float32_array array = mono_array_to_PackedFloat32Array(floatArray); + return VariantUtils.CreateFromPackedFloat32Array(&array); + } + case double[] doubleArray: + { + using godot_packed_float64_array array = mono_array_to_PackedFloat64Array(doubleArray); + return VariantUtils.CreateFromPackedFloat64Array(&array); + } + case string[] stringArray: + { + using godot_packed_string_array array = mono_array_to_PackedStringArray(stringArray); + return VariantUtils.CreateFromPackedStringArray(&array); + } + case Vector2[] vector2Array: + { + using godot_packed_vector2_array array = mono_array_to_PackedVector2Array(vector2Array); + return VariantUtils.CreateFromPackedVector2Array(&array); + } + case Vector3[] vector3Array: + { + using godot_packed_vector3_array array = mono_array_to_PackedVector3Array(vector3Array); + return VariantUtils.CreateFromPackedVector3Array(&array); + } + case Color[] colorArray: + { + using godot_packed_color_array array = mono_array_to_PackedColorArray(colorArray); + return VariantUtils.CreateFromPackedColorArray(&array); + } + case Godot.Object[] godotObjectArray: + { + // ReSharper disable once CoVariantArrayConversion + using godot_array array = mono_array_to_Array(godotObjectArray); + return VariantUtils.CreateFromArray(&array); + } + case object[] objectArray: // Last one to avoid catching others like string[] and Godot.Object[] + { + // The pattern match for `object[]` catches arrays on any reference type, + // so we need to check the actual type to make sure it's truly `object[]`. + if (objectArray.GetType() == typeof(object[])) + { + using godot_array array = mono_array_to_Array(objectArray); + return VariantUtils.CreateFromArray(&array); + } + + if (p_fail_with_err) + { + GD.PushError("Attempted to convert a managed array of unmarshallable element type to Variant."); + return new godot_variant(); + } + else + { + return new godot_variant(); + } + } + case Godot.Object godotObject: + return VariantUtils.CreateFromGodotObject(godotObject.NativeInstance); + case StringName stringName: + return VariantUtils.CreateFromStringName(ref stringName.NativeValue); + case NodePath nodePath: + return VariantUtils.CreateFromNodePath(ref nodePath.NativeValue); + case RID rid: + return VariantUtils.CreateFromRID(rid); + case Collections.Dictionary godotDictionary: + return VariantUtils.CreateFromDictionary(godotDictionary.NativeValue); + case Collections.Array godotArray: + return VariantUtils.CreateFromArray(godotArray.NativeValue); + case Collections.IGenericGodotDictionary genericGodotDictionary: + { + var godotDict = genericGodotDictionary.UnderlyingDictionary; + if (godotDict == null) + return new godot_variant(); + return VariantUtils.CreateFromDictionary(godotDict.NativeValue); + } + case Collections.IGenericGodotArray genericGodotArray: + { + var godotArray = genericGodotArray.UnderlyingArray; + if (godotArray == null) + return new godot_variant(); + return VariantUtils.CreateFromArray(godotArray.NativeValue); + } + default: + { + var type = p_obj.GetType(); + + if (type.IsGenericType) + { + var genericTypeDefinition = type.GetGenericTypeDefinition(); + + if (genericTypeDefinition == typeof(System.Collections.Generic.Dictionary<,>)) + { + // TODO: Validate key and value types are compatible with Variant +#if NET + Collections.IGenericGodotDictionary genericGodotDictionary = IDictionaryToGenericGodotDictionary((dynamic)p_obj); +#else + var genericArguments = type.GetGenericArguments(); + + // With .NET Standard we need a package reference for Microsoft.CSharp in order to + // use dynamic, so we have this workaround for now until we switch to .NET 5/6. + var method = typeof(Marshaling).GetMethod(nameof(IDictionaryToGenericGodotDictionary), + BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.DeclaredOnly)! + .MakeGenericMethod(genericArguments[0], genericArguments[1]); + + var genericGodotDictionary = (Collections.IGenericGodotDictionary)method + .Invoke(null, new[] {p_obj}); +#endif + + var godotDict = genericGodotDictionary.UnderlyingDictionary; + if (godotDict == null) + return new godot_variant(); + return VariantUtils.CreateFromDictionary(godotDict.NativeValue); + } + + if (genericTypeDefinition == typeof(System.Collections.Generic.List<>)) + { + // TODO: Validate element type is compatible with Variant +#if NET + var nativeGodotArray = mono_array_to_Array(System.Runtime.InteropServices.CollectionsMarshal.AsSpan((dynamic)p_obj)); +#else + // With .NET Standard we need a package reference for Microsoft.CSharp in order to + // use dynamic, so we have this workaround for now until we switch to .NET 5/6. + // Also CollectionsMarshal.AsSpan is not available with .NET Standard. + + var collection = (System.Collections.ICollection)p_obj; + var array = new object[collection.Count]; + collection.CopyTo(array, 0); + var nativeGodotArray = mono_array_to_Array(array); +#endif + return VariantUtils.CreateFromArray(&nativeGodotArray); + } + } + + break; + } + } + + if (p_fail_with_err) + { + GD.PushError("Attempted to convert an unmarshallable managed type to Variant. Name: '" + + p_obj.GetType().FullName + "."); + return new godot_variant(); + } + else + { + return new godot_variant(); + } + } + + private static Collections.Dictionary<TKey, TValue> IDictionaryToGenericGodotDictionary<TKey, TValue> + (IDictionary<TKey, TValue> dictionary) => new(dictionary); + + public static unsafe string variant_to_mono_string(godot_variant* p_var) + { + switch ((*p_var)._type) + { + case Variant.Type.Nil: + return null; // Otherwise, Variant -> String would return the string "Null" + case Variant.Type.String: + { + // We avoid the internal call if the stored type is the same we want. + return mono_string_from_godot(&(*p_var)._data._m_string); + } + default: + { + using godot_string godotString = NativeFuncs.godotsharp_variant_as_string(p_var); + return mono_string_from_godot(&godotString); + } + } + } + + public static unsafe object variant_to_mono_object_of_type(godot_variant* p_var, Type type) + { + // This function is only needed to set the value of properties. Fields have their own implementation, set_value_from_variant. + switch (Type.GetTypeCode(type)) + { + case TypeCode.Boolean: + return VariantUtils.ConvertToBool(p_var); + case TypeCode.Char: + return VariantUtils.ConvertToChar(p_var); + case TypeCode.SByte: + return VariantUtils.ConvertToInt8(p_var); + case TypeCode.Int16: + return VariantUtils.ConvertToInt16(p_var); + case TypeCode.Int32: + return VariantUtils.ConvertToInt32(p_var); + case TypeCode.Int64: + return VariantUtils.ConvertToInt64(p_var); + case TypeCode.Byte: + return VariantUtils.ConvertToUInt8(p_var); + case TypeCode.UInt16: + return VariantUtils.ConvertToUInt16(p_var); + case TypeCode.UInt32: + return VariantUtils.ConvertToUInt32(p_var); + case TypeCode.UInt64: + return VariantUtils.ConvertToUInt64(p_var); + case TypeCode.Single: + return VariantUtils.ConvertToFloat32(p_var); + case TypeCode.Double: + return VariantUtils.ConvertToFloat64(p_var); + case TypeCode.String: + return variant_to_mono_string(p_var); + default: + { + if (type == typeof(Vector2)) + return VariantUtils.ConvertToVector2(p_var); + + if (type == typeof(Vector2i)) + return VariantUtils.ConvertToVector2i(p_var); + + if (type == typeof(Rect2)) + return VariantUtils.ConvertToRect2(p_var); + + if (type == typeof(Rect2i)) + return VariantUtils.ConvertToRect2i(p_var); + + if (type == typeof(Transform2D)) + return VariantUtils.ConvertToTransform2D(p_var); + + if (type == typeof(Vector3)) + return VariantUtils.ConvertToVector3(p_var); + + if (type == typeof(Vector3i)) + return VariantUtils.ConvertToVector3i(p_var); + + if (type == typeof(Vector4)) + return VariantUtils.ConvertToVector4(p_var); + + if (type == typeof(Vector4i)) + return VariantUtils.ConvertToVector4i(p_var); + + if (type == typeof(Basis)) + return VariantUtils.ConvertToBasis(p_var); + + if (type == typeof(Quaternion)) + return VariantUtils.ConvertToQuaternion(p_var); + + if (type == typeof(Transform3D)) + return VariantUtils.ConvertToTransform3D(p_var); + + if (type == typeof(Projection)) + return VariantUtils.ConvertToProjection(p_var); + + if (type == typeof(AABB)) + return VariantUtils.ConvertToAABB(p_var); + + if (type == typeof(Color)) + return VariantUtils.ConvertToColor(p_var); + + if (type == typeof(Plane)) + return VariantUtils.ConvertToPlane(p_var); + + if (type == typeof(Callable)) + { + using godot_callable callable = NativeFuncs.godotsharp_variant_as_callable(p_var); + return ConvertCallableToManaged(&callable); + } + + if (type == typeof(SignalInfo)) + { + using godot_signal signal = NativeFuncs.godotsharp_variant_as_signal(p_var); + return ConvertSignalToManaged(&signal); + } + + if (type.IsEnum) + { + var enumUnderlyingType = type.GetEnumUnderlyingType(); + switch (Type.GetTypeCode(enumUnderlyingType)) + { + case TypeCode.SByte: + return VariantUtils.ConvertToInt8(p_var); + case TypeCode.Int16: + return VariantUtils.ConvertToInt16(p_var); + case TypeCode.Int32: + return VariantUtils.ConvertToInt32(p_var); + case TypeCode.Int64: + return VariantUtils.ConvertToInt64(p_var); + case TypeCode.Byte: + return VariantUtils.ConvertToUInt8(p_var); + case TypeCode.UInt16: + return VariantUtils.ConvertToUInt16(p_var); + case TypeCode.UInt32: + return VariantUtils.ConvertToUInt32(p_var); + case TypeCode.UInt64: + return VariantUtils.ConvertToUInt64(p_var); + default: + { + GD.PushError("Attempted to convert Variant to enum value of unsupported underlying type. Name: " + + type.FullName + " : " + enumUnderlyingType.FullName + "."); + return null; + } + } + } + + if (type.IsArray || type.IsSZArray) + return variant_to_mono_array_of_type(p_var, type); + else if (type.IsGenericType) + return variant_to_mono_object_of_genericinst(p_var, type); + else if (type == typeof(object)) + return variant_to_mono_object(p_var); + if (variant_to_mono_object_of_class(p_var, type, out object res)) + return res; + + break; + } + } + + GD.PushError("Attempted to convert Variant to unsupported type. Name: " + + type.FullName + "."); + return null; + } + + private static unsafe object variant_to_mono_array_of_type(godot_variant* p_var, Type type) + { + if (type == typeof(Byte[])) + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_byte_array(p_var); + return PackedByteArray_to_mono_array(&packedArray); + } + + if (type == typeof(Int32[])) + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_int32_array(p_var); + return PackedInt32Array_to_mono_array(&packedArray); + } + + if (type == typeof(Int64[])) + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_int64_array(p_var); + return PackedInt64Array_to_mono_array(&packedArray); + } + + if (type == typeof(float[])) + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_float32_array(p_var); + return PackedFloat32Array_to_mono_array(&packedArray); + } + + if (type == typeof(double[])) + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_float64_array(p_var); + return PackedFloat64Array_to_mono_array(&packedArray); + } + + if (type == typeof(string[])) + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_string_array(p_var); + return PackedStringArray_to_mono_array(&packedArray); + } + + if (type == typeof(Vector2[])) + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector2_array(p_var); + return PackedVector2Array_to_mono_array(&packedArray); + } + + if (type == typeof(Vector3[])) + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector3_array(p_var); + return PackedVector3Array_to_mono_array(&packedArray); + } + + if (type == typeof(Color[])) + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_color_array(p_var); + return PackedColorArray_to_mono_array(&packedArray); + } + + if (typeof(Godot.Object[]).IsAssignableFrom(type)) + { + using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var); + return Array_to_mono_array_of_type(&godotArray, type); + } + + if (type == typeof(object[])) + { + using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var); + return Array_to_mono_array(&godotArray); + } + + GD.PushError("Attempted to convert Variant to array of unsupported element type. Name: " + + type.GetElementType()!.FullName + "."); + return null; + } + + private static unsafe bool variant_to_mono_object_of_class(godot_variant* p_var, Type type, out object res) + { + if (typeof(Godot.Object).IsAssignableFrom(type)) + { + res = InteropUtils.UnmanagedGetManaged(VariantUtils.ConvertToGodotObject(p_var)); + return true; + } + + if (typeof(StringName) == type) + { + res = StringName.CreateTakingOwnershipOfDisposableValue( + VariantUtils.ConvertToStringName(p_var)); + return true; + } + + if (typeof(NodePath) == type) + { + res = NodePath.CreateTakingOwnershipOfDisposableValue( + VariantUtils.ConvertToNodePath(p_var)); + return true; + } + + if (typeof(RID) == type) + { + res = VariantUtils.ConvertToRID(p_var); + return true; + } + + if (typeof(Collections.Dictionary) == type || typeof(System.Collections.IDictionary) == type) + { + res = Collections.Dictionary.CreateTakingOwnershipOfDisposableValue( + VariantUtils.ConvertToDictionary(p_var)); + return true; + } + + if (typeof(Collections.Array) == type || + typeof(System.Collections.ICollection) == type || + typeof(System.Collections.IEnumerable) == type) + { + res = Collections.Array.CreateTakingOwnershipOfDisposableValue( + VariantUtils.ConvertToArray(p_var)); + return true; + } + + res = null; + return false; + } + + private static unsafe object variant_to_mono_object_of_genericinst(godot_variant* p_var, Type type) + { + static object variant_to_generic_godot_collections_dictionary(godot_variant* p_var, Type fullType) + { + var underlyingDict = Collections.Dictionary.CreateTakingOwnershipOfDisposableValue( + VariantUtils.ConvertToDictionary(p_var)); + return Activator.CreateInstance(fullType, + BindingFlags.Public | BindingFlags.Instance, null, + args: new object[] {underlyingDict}, null); + } + + static object variant_to_generic_godot_collections_array(godot_variant* p_var, Type fullType) + { + var underlyingArray = Collections.Array.CreateTakingOwnershipOfDisposableValue( + VariantUtils.ConvertToArray(p_var)); + return Activator.CreateInstance(fullType, + BindingFlags.Public | BindingFlags.Instance, null, + args: new object[] {underlyingArray}, null); + } + + var genericTypeDefinition = type.GetGenericTypeDefinition(); + + if (genericTypeDefinition == typeof(Collections.Dictionary<,>)) + return variant_to_generic_godot_collections_dictionary(p_var, type); + + if (genericTypeDefinition == typeof(Collections.Array<>)) + return variant_to_generic_godot_collections_array(p_var, type); + + if (genericTypeDefinition == typeof(System.Collections.Generic.Dictionary<,>)) + { + using var godotDictionary = Collections.Dictionary.CreateTakingOwnershipOfDisposableValue( + VariantUtils.ConvertToDictionary(p_var)); + + var dictionary = (System.Collections.IDictionary)Activator.CreateInstance(type, + BindingFlags.Public | BindingFlags.Instance, null, + args: new object[] + { + /* capacity: */ godotDictionary.Count + }, null); + + foreach (System.Collections.DictionaryEntry pair in godotDictionary) + dictionary.Add(pair.Key, pair.Value); + + return dictionary; + } + + if (genericTypeDefinition == typeof(System.Collections.Generic.List<>)) + { + using var godotArray = Collections.Array.CreateTakingOwnershipOfDisposableValue( + VariantUtils.ConvertToArray(p_var)); + + var list = (System.Collections.IList)Activator.CreateInstance(type, + BindingFlags.Public | BindingFlags.Instance, null, + args: new object[] + { + /* capacity: */ godotArray.Count + }, null); + + foreach (object elem in godotArray) + list.Add(elem); + + return list; + } + + if (genericTypeDefinition == typeof(IDictionary<,>)) + { + var genericArgs = type.GetGenericArguments(); + var keyType = genericArgs[0]; + var valueType = genericArgs[1]; + var genericGodotDictionaryType = typeof(Collections.Dictionary<,>) + .MakeGenericType(keyType, valueType); + + return variant_to_generic_godot_collections_dictionary(p_var, genericGodotDictionaryType); + } + + if (genericTypeDefinition == typeof(ICollection<>) || genericTypeDefinition == typeof(IEnumerable<>)) + { + var elementType = type.GetGenericArguments()[0]; + var genericGodotArrayType = typeof(Collections.Array<>) + .MakeGenericType(elementType); + + return variant_to_generic_godot_collections_array(p_var, genericGodotArrayType); + } + + return null; + } + + public static unsafe object variant_to_mono_object(godot_variant* p_var) + { + switch ((*p_var)._type) + { + case Variant.Type.Bool: + return (bool)(*p_var)._data._bool; + case Variant.Type.Int: + return (*p_var)._data._int; + case Variant.Type.Float: + { +#if REAL_T_IS_DOUBLE + return (*p_var)._data._float; +#else + return (float)(*p_var)._data._float; +#endif + } + case Variant.Type.String: + return mono_string_from_godot(&(*p_var)._data._m_string); + case Variant.Type.Vector2: + return (*p_var)._data._m_vector2; + case Variant.Type.Vector2i: + return (*p_var)._data._m_vector2i; + case Variant.Type.Rect2: + return (*p_var)._data._m_rect2; + case Variant.Type.Rect2i: + return (*p_var)._data._m_rect2i; + case Variant.Type.Vector3: + return (*p_var)._data._m_vector3; + case Variant.Type.Vector3i: + return (*p_var)._data._m_vector3i; + case Variant.Type.Transform2d: + return *(*p_var)._data._transform2d; + case Variant.Type.Vector4: + return *(*p_var)._data._vector4; + case Variant.Type.Vector4i: + return *(*p_var)._data._vector4i; + case Variant.Type.Plane: + return (*p_var)._data._m_plane; + case Variant.Type.Quaternion: + return (*p_var)._data._m_quaternion; + case Variant.Type.Aabb: + return *(*p_var)._data._aabb; + case Variant.Type.Basis: + return *(*p_var)._data._basis; + case Variant.Type.Transform3d: + return *(*p_var)._data._transform3d; + case Variant.Type.Projection: + return *(*p_var)._data._projection; + case Variant.Type.Color: + return (*p_var)._data._m_color; + case Variant.Type.StringName: + { + // The Variant owns the value, so we need to make a copy + return StringName.CreateTakingOwnershipOfDisposableValue( + NativeFuncs.godotsharp_string_name_new_copy(&(*p_var)._data._m_string_name)); + } + case Variant.Type.NodePath: + { + // The Variant owns the value, so we need to make a copy + return NodePath.CreateTakingOwnershipOfDisposableValue( + NativeFuncs.godotsharp_node_path_new_copy(&(*p_var)._data._m_node_path)); + } + case Variant.Type.Rid: + return (*p_var)._data._m_rid; + case Variant.Type.Object: + return InteropUtils.UnmanagedGetManaged((*p_var)._data._m_obj_data.obj); + case Variant.Type.Callable: + return ConvertCallableToManaged(&(*p_var)._data._m_callable); + case Variant.Type.Signal: + return ConvertSignalToManaged(&(*p_var)._data._m_signal); + case Variant.Type.Dictionary: + { + // The Variant owns the value, so we need to make a copy + return Collections.Dictionary.CreateTakingOwnershipOfDisposableValue( + NativeFuncs.godotsharp_dictionary_new_copy(&(*p_var)._data._m_dictionary)); + } + case Variant.Type.Array: + { + // The Variant owns the value, so we need to make a copy + return Collections.Array.CreateTakingOwnershipOfDisposableValue( + NativeFuncs.godotsharp_array_new_copy(&(*p_var)._data._m_array)); + } + case Variant.Type.PackedByteArray: + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_byte_array(p_var); + return PackedByteArray_to_mono_array(&packedArray); + } + case Variant.Type.PackedInt32Array: + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_int32_array(p_var); + return PackedInt32Array_to_mono_array(&packedArray); + } + case Variant.Type.PackedInt64Array: + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_int64_array(p_var); + return PackedInt64Array_to_mono_array(&packedArray); + } + case Variant.Type.PackedFloat32Array: + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_float32_array(p_var); + return PackedFloat32Array_to_mono_array(&packedArray); + } + case Variant.Type.PackedFloat64Array: + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_float64_array(p_var); + return PackedFloat64Array_to_mono_array(&packedArray); + } + case Variant.Type.PackedStringArray: + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_string_array(p_var); + return PackedStringArray_to_mono_array(&packedArray); + } + case Variant.Type.PackedVector2Array: + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector2_array(p_var); + return PackedVector2Array_to_mono_array(&packedArray); + } + case Variant.Type.PackedVector3Array: + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector3_array(p_var); + return PackedVector3Array_to_mono_array(&packedArray); + } + case Variant.Type.PackedColorArray: + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_color_array(p_var); + return PackedColorArray_to_mono_array(&packedArray); + } + default: + return null; + } + } + + // String + + public static unsafe godot_string mono_string_to_godot(string p_mono_string) + { + if (p_mono_string == null) + return new godot_string(); + + fixed (char* methodChars = p_mono_string) + { + godot_string dest; + NativeFuncs.godotsharp_string_new_with_utf16_chars(&dest, methodChars); + return dest; + } + } + + public static unsafe string mono_string_from_godot(godot_string* p_string) + { + if ((*p_string)._ptr == IntPtr.Zero) + return string.Empty; + + const int sizeOfChar32 = 4; + byte* bytes = (byte*)(*p_string)._ptr; + int size = *((int*)(*p_string)._ptr - 1); + if (size == 0) + return string.Empty; + size -= 1; // zero at the end + int sizeInBytes = size * sizeOfChar32; + return System.Text.Encoding.UTF32.GetString(bytes, sizeInBytes); + } + + // Callable + + public static godot_callable ConvertCallableToNative(ref Callable p_managed_callable) + { + if (p_managed_callable.Delegate != null) + { + unsafe + { + godot_callable callable; + NativeFuncs.godotsharp_callable_new_with_delegate( + GCHandle.ToIntPtr(GCHandle.Alloc(p_managed_callable.Delegate)), &callable); + return callable; + } + } + else + { + unsafe + { + godot_string_name method; + + if (p_managed_callable.Method != null && !p_managed_callable.Method.IsEmpty) + { + godot_string_name src = p_managed_callable.Method.NativeValue; + method = NativeFuncs.godotsharp_string_name_new_copy(&src); + } + else + { + method = default; + } + + return new godot_callable + { + _method = method, // Takes ownership of disposable + _objectId = p_managed_callable.Target.GetInstanceId() + }; + } + } + } + + public static unsafe Callable ConvertCallableToManaged(godot_callable* p_callable) + { + IntPtr delegateGCHandle; + IntPtr godotObject; + godot_string_name name; + + if (NativeFuncs.godotsharp_callable_get_data_for_marshalling( + p_callable, &delegateGCHandle, &godotObject, &name)) + { + if (delegateGCHandle != IntPtr.Zero) + { + return new Callable((Delegate)GCHandle.FromIntPtr(delegateGCHandle).Target); + } + else + { + return new Callable( + InteropUtils.UnmanagedGetManaged(godotObject), + StringName.CreateTakingOwnershipOfDisposableValue(name)); + } + } + + // Some other unsupported callable + return new Callable(); + } + + // SignalInfo + + public static godot_signal ConvertSignalToNative(ref SignalInfo p_managed_signal) + { + ulong ownerId = p_managed_signal.Owner.GetInstanceId(); + unsafe + { + godot_string_name name; + + if (p_managed_signal.Name != null && !p_managed_signal.Name.IsEmpty) + { + godot_string_name src = p_managed_signal.Name.NativeValue; + name = NativeFuncs.godotsharp_string_name_new_copy(&src); + } + else + { + name = default; + } + + return new godot_signal() + { + _name = name, + _objectId = ownerId + }; + } + } + + public static unsafe SignalInfo ConvertSignalToManaged(godot_signal* p_signal) + { + var owner = GD.InstanceFromId((*p_signal)._objectId); + var name = StringName.CreateTakingOwnershipOfDisposableValue( + NativeFuncs.godotsharp_string_name_new_copy(&(*p_signal)._name)); + return new SignalInfo(owner, name); + } + + // Array + + public static unsafe object[] Array_to_mono_array(godot_array* p_array) + { + var array = Collections.Array.CreateTakingOwnershipOfDisposableValue( + NativeFuncs.godotsharp_array_new_copy(p_array)); + + int length = array.Count; + var ret = new object[length]; + + array.CopyTo(ret, 0); // variant_to_mono_object handled by Collections.Array + + return ret; + } + + public static unsafe object Array_to_mono_array_of_type(godot_array* p_array, Type type) + { + var array = Collections.Array.CreateTakingOwnershipOfDisposableValue( + NativeFuncs.godotsharp_array_new_copy(p_array)); + + int length = array.Count; + object ret = Activator.CreateInstance(type, length); + + array.CopyTo((object[])ret, 0); // variant_to_mono_object handled by Collections.Array + + return ret; + } + + public static godot_array mono_array_to_Array(Span<object> p_array) + { + if (p_array.IsEmpty) + { + godot_array ret; + Collections.Array.godot_icall_Array_Ctor(out ret); + return ret; + } + + using var array = new Collections.Array(); + array.Resize(p_array.Length); + + for (int i = 0; i < p_array.Length; i++) + array[i] = p_array[i]; + + godot_array src = array.NativeValue; + unsafe + { + return NativeFuncs.godotsharp_array_new_copy(&src); + } + } + + // PackedByteArray + + public static unsafe byte[] PackedByteArray_to_mono_array(godot_packed_byte_array* p_array) + { + byte* buffer = (byte*)(*p_array)._ptr; + int size = *((int*)(*p_array)._ptr - 1); + var array = new byte[size]; + fixed (byte* dest = array) + Buffer.MemoryCopy(buffer, dest, size, size); + return array; + } + + public static unsafe godot_packed_byte_array mono_array_to_PackedByteArray(Span<byte> p_array) + { + if (p_array.IsEmpty) + return new godot_packed_byte_array(); + fixed (byte* src = p_array) + return NativeFuncs.godotsharp_packed_byte_array_new_mem_copy(src, p_array.Length); + } + + // PackedInt32Array + + public static unsafe int[] PackedInt32Array_to_mono_array(godot_packed_int32_array* p_array) + { + int* buffer = (int*)(*p_array)._ptr; + int size = *((int*)(*p_array)._ptr - 1); + int sizeInBytes = size * sizeof(int); + var array = new int[size]; + fixed (int* dest = array) + Buffer.MemoryCopy(buffer, dest, sizeInBytes, sizeInBytes); + return array; + } + + public static unsafe godot_packed_int32_array mono_array_to_PackedInt32Array(Span<int> p_array) + { + if (p_array.IsEmpty) + return new godot_packed_int32_array(); + fixed (int* src = p_array) + return NativeFuncs.godotsharp_packed_int32_array_new_mem_copy(src, p_array.Length); + } + + // PackedInt64Array + + public static unsafe long[] PackedInt64Array_to_mono_array(godot_packed_int64_array* p_array) + { + long* buffer = (long*)(*p_array)._ptr; + int size = *((int*)(*p_array)._ptr - 1); + int sizeInBytes = size * sizeof(long); + var array = new long[size]; + fixed (long* dest = array) + Buffer.MemoryCopy(buffer, dest, sizeInBytes, sizeInBytes); + return array; + } + + public static unsafe godot_packed_int64_array mono_array_to_PackedInt64Array(Span<long> p_array) + { + if (p_array.IsEmpty) + return new godot_packed_int64_array(); + fixed (long* src = p_array) + return NativeFuncs.godotsharp_packed_int64_array_new_mem_copy(src, p_array.Length); + } + + // PackedFloat32Array + + public static unsafe float[] PackedFloat32Array_to_mono_array(godot_packed_float32_array* p_array) + { + float* buffer = (float*)(*p_array)._ptr; + int size = *((int*)(*p_array)._ptr - 1); + int sizeInBytes = size * sizeof(float); + var array = new float[size]; + fixed (float* dest = array) + Buffer.MemoryCopy(buffer, dest, sizeInBytes, sizeInBytes); + return array; + } + + public static unsafe godot_packed_float32_array mono_array_to_PackedFloat32Array(Span<float> p_array) + { + if (p_array.IsEmpty) + return new godot_packed_float32_array(); + fixed (float* src = p_array) + return NativeFuncs.godotsharp_packed_float32_array_new_mem_copy(src, p_array.Length); + } + + // PackedFloat64Array + + public static unsafe double[] PackedFloat64Array_to_mono_array(godot_packed_float64_array* p_array) + { + double* buffer = (double*)(*p_array)._ptr; + int size = *((int*)(*p_array)._ptr - 1); + int sizeInBytes = size * sizeof(double); + var array = new double[size]; + fixed (double* dest = array) + Buffer.MemoryCopy(buffer, dest, sizeInBytes, sizeInBytes); + return array; + } + + public static unsafe godot_packed_float64_array mono_array_to_PackedFloat64Array(Span<double> p_array) + { + if (p_array.IsEmpty) + return new godot_packed_float64_array(); + fixed (double* src = p_array) + return NativeFuncs.godotsharp_packed_float64_array_new_mem_copy(src, p_array.Length); + } + + // PackedStringArray + + public static unsafe string[] PackedStringArray_to_mono_array(godot_packed_string_array* p_array) + { + godot_string* buffer = (godot_string*)(*p_array)._ptr; + if (buffer == null) + return new string[] { }; + int size = *((int*)(*p_array)._ptr - 1); + var array = new string[size]; + for (int i = 0; i < size; i++) + array[i] = mono_string_from_godot(&buffer[i]); + return array; + } + + public static unsafe godot_packed_string_array mono_array_to_PackedStringArray(Span<string> p_array) + { + godot_packed_string_array dest = new godot_packed_string_array(); + + if (p_array.IsEmpty) + return dest; + + /* TODO: Replace godotsharp_packed_string_array_add with a single internal call to + get the write address. We can't use `dest._ptr` directly for writing due to COW. */ + + for (int i = 0; i < p_array.Length; i++) + { + using godot_string godotStrElem = mono_string_to_godot(p_array[i]); + NativeFuncs.godotsharp_packed_string_array_add(&dest, &godotStrElem); + } + + return dest; + } + + // PackedVector2Array + + public static unsafe Vector2[] PackedVector2Array_to_mono_array(godot_packed_vector2_array* p_array) + { + Vector2* buffer = (Vector2*)(*p_array)._ptr; + int size = *((int*)(*p_array)._ptr - 1); + int sizeInBytes = size * sizeof(Vector2); + var array = new Vector2[size]; + fixed (Vector2* dest = array) + Buffer.MemoryCopy(buffer, dest, sizeInBytes, sizeInBytes); + return array; + } + + public static unsafe godot_packed_vector2_array mono_array_to_PackedVector2Array(Span<Vector2> p_array) + { + if (p_array.IsEmpty) + return new godot_packed_vector2_array(); + fixed (Vector2* src = p_array) + return NativeFuncs.godotsharp_packed_vector2_array_new_mem_copy(src, p_array.Length); + } + + // PackedVector3Array + + public static unsafe Vector3[] PackedVector3Array_to_mono_array(godot_packed_vector3_array* p_array) + { + Vector3* buffer = (Vector3*)(*p_array)._ptr; + int size = *((int*)(*p_array)._ptr - 1); + int sizeInBytes = size * sizeof(Vector3); + var array = new Vector3[size]; + fixed (Vector3* dest = array) + Buffer.MemoryCopy(buffer, dest, sizeInBytes, sizeInBytes); + return array; + } + + public static unsafe godot_packed_vector3_array mono_array_to_PackedVector3Array(Span<Vector3> p_array) + { + if (p_array.IsEmpty) + return new godot_packed_vector3_array(); + fixed (Vector3* src = p_array) + return NativeFuncs.godotsharp_packed_vector3_array_new_mem_copy(src, p_array.Length); + } + + // PackedColorArray + + public static unsafe Color[] PackedColorArray_to_mono_array(godot_packed_color_array* p_array) + { + Color* buffer = (Color*)(*p_array)._ptr; + int size = *((int*)(*p_array)._ptr - 1); + int sizeInBytes = size * sizeof(Color); + var array = new Color[size]; + fixed (Color* dest = array) + Buffer.MemoryCopy(buffer, dest, sizeInBytes, sizeInBytes); + return array; + } + + public static unsafe godot_packed_color_array mono_array_to_PackedColorArray(Span<Color> p_array) + { + if (p_array.IsEmpty) + return new godot_packed_color_array(); + fixed (Color* src = p_array) + return NativeFuncs.godotsharp_packed_color_array_new_mem_copy(src, p_array.Length); + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs new file mode 100644 index 0000000000..2f1056219d --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs @@ -0,0 +1,346 @@ +using System; +using System.Runtime.InteropServices; + +// ReSharper disable InconsistentNaming + +namespace Godot.NativeInterop +{ +#if !NET + // This improves P/Invoke performance. + // The attribute is not available with .NET Core and it's not needed there. + [System.Security.SuppressUnmanagedCodeSecurity] +#endif + internal static unsafe partial class NativeFuncs + { + private const string GodotDllName = "__Internal"; + + // Custom functions + + [DllImport(GodotDllName)] + public static extern IntPtr godotsharp_method_bind_get_method(ref godot_string_name p_classname, char* p_methodname); + +#if NET + [DllImport(GodotDllName)] + public static extern delegate* unmanaged<IntPtr> godotsharp_get_class_constructor(ref godot_string_name p_classname); +#else + // Workaround until we switch to .NET 5/6 + [DllImport(GodotDllName)] + public static extern IntPtr godotsharp_get_class_constructor(ref godot_string_name p_classname); + + [DllImport(GodotDllName)] + public static extern IntPtr godotsharp_invoke_class_constructor(IntPtr p_creation_func); +#endif + + [DllImport(GodotDllName)] + public static extern IntPtr godotsharp_engine_get_singleton(godot_string* p_name); + + [DllImport(GodotDllName)] + public static extern void godotsharp_ref_destroy(ref godot_ref p_instance); + + [DllImport(GodotDllName)] + public static extern void godotsharp_string_name_new_from_string(godot_string_name* dest, godot_string* name); + + [DllImport(GodotDllName)] + public static extern void godotsharp_node_path_new_from_string(godot_node_path* dest, godot_string* name); + + [DllImport(GodotDllName)] + public static extern void godotsharp_string_name_as_string(godot_string* r_dest, godot_string_name* p_name); + + [DllImport(GodotDllName)] + public static extern void godotsharp_node_path_as_string(godot_string* r_dest, godot_node_path* p_np); + + [DllImport(GodotDllName)] + public static extern godot_packed_byte_array godotsharp_packed_byte_array_new_mem_copy(byte* p_src, int p_length); + + [DllImport(GodotDllName)] + public static extern godot_packed_int32_array godotsharp_packed_int32_array_new_mem_copy(int* p_src, int p_length); + + [DllImport(GodotDllName)] + public static extern godot_packed_int64_array godotsharp_packed_int64_array_new_mem_copy(long* p_src, int p_length); + + [DllImport(GodotDllName)] + public static extern godot_packed_float32_array godotsharp_packed_float32_array_new_mem_copy(float* p_src, int p_length); + + [DllImport(GodotDllName)] + public static extern godot_packed_float64_array godotsharp_packed_float64_array_new_mem_copy(double* p_src, int p_length); + + [DllImport(GodotDllName)] + public static extern godot_packed_vector2_array godotsharp_packed_vector2_array_new_mem_copy(Vector2* p_src, int p_length); + + [DllImport(GodotDllName)] + public static extern godot_packed_vector3_array godotsharp_packed_vector3_array_new_mem_copy(Vector3* p_src, int p_length); + + [DllImport(GodotDllName)] + public static extern godot_packed_color_array godotsharp_packed_color_array_new_mem_copy(Color* p_src, int p_length); + + [DllImport(GodotDllName)] + public static extern void godotsharp_packed_string_array_add(godot_packed_string_array* r_dest, godot_string* p_element); + + [DllImport(GodotDllName)] + public static extern void godotsharp_callable_new_with_delegate(IntPtr p_delegate_handle, godot_callable* r_callable); + + [DllImport(GodotDllName)] + public static extern bool godotsharp_callable_get_data_for_marshalling(godot_callable* p_callable, IntPtr* r_delegate_handle, IntPtr* r_object, godot_string_name* r_name); + + // GDNative functions + + // gdnative.h + + [DllImport(GodotDllName)] + public static extern void godotsharp_method_bind_ptrcall(IntPtr p_method_bind, IntPtr p_instance, void** p_args, void* p_ret); + + [DllImport(GodotDllName)] + public static extern godot_variant godotsharp_method_bind_call(IntPtr p_method_bind, IntPtr p_instance, godot_variant** p_args, int p_arg_count, godot_variant_call_error* p_call_error); + + // variant.h + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_string_name(godot_variant* r_dest, godot_string_name* p_s); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_node_path(godot_variant* r_dest, godot_node_path* p_np); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_object(godot_variant* r_dest, IntPtr p_obj); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_transform2d(godot_variant* r_dest, Transform2D* p_t2d); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_vector4(godot_variant* r_dest, Vector4* p_vec4); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_vector4i(godot_variant* r_dest, Vector4i* p_vec4i); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_basis(godot_variant* r_dest, Basis* p_basis); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_transform3d(godot_variant* r_dest, Transform3D* p_trans); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_projection(godot_variant* r_dest, Projection* p_proj); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_aabb(godot_variant* r_dest, AABB* p_aabb); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_dictionary(godot_variant* r_dest, godot_dictionary* p_dict); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_array(godot_variant* r_dest, godot_array* p_arr); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_packed_byte_array(godot_variant* r_dest, godot_packed_byte_array* p_pba); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_packed_int32_array(godot_variant* r_dest, godot_packed_int32_array* p_pia); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_packed_int64_array(godot_variant* r_dest, godot_packed_int64_array* p_pia); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_packed_float32_array(godot_variant* r_dest, godot_packed_float32_array* p_pra); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_packed_float64_array(godot_variant* r_dest, godot_packed_float64_array* p_pra); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_packed_string_array(godot_variant* r_dest, godot_packed_string_array* p_psa); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_packed_vector2_array(godot_variant* r_dest, godot_packed_vector2_array* p_pv2a); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_packed_vector3_array(godot_variant* r_dest, godot_packed_vector3_array* p_pv3a); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_packed_color_array(godot_variant* r_dest, godot_packed_color_array* p_pca); + + [DllImport(GodotDllName)] + public static extern bool godotsharp_variant_as_bool(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Int64 godotsharp_variant_as_int(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern double godotsharp_variant_as_float(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_string godotsharp_variant_as_string(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Vector2 godotsharp_variant_as_vector2(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Vector2i godotsharp_variant_as_vector2i(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Rect2 godotsharp_variant_as_rect2(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Rect2i godotsharp_variant_as_rect2i(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Vector3 godotsharp_variant_as_vector3(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Vector3i godotsharp_variant_as_vector3i(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Transform2D godotsharp_variant_as_transform2d(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Vector4 godotsharp_variant_as_vector4(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Vector4i godotsharp_variant_as_vector4i(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Plane godotsharp_variant_as_plane(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Quaternion godotsharp_variant_as_quaternion(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern AABB godotsharp_variant_as_aabb(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Basis godotsharp_variant_as_basis(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Transform3D godotsharp_variant_as_transform3d(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Projection godotsharp_variant_as_projection(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Color godotsharp_variant_as_color(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_string_name godotsharp_variant_as_string_name(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_node_path godotsharp_variant_as_node_path(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern RID godotsharp_variant_as_rid(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_callable godotsharp_variant_as_callable(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_signal godotsharp_variant_as_signal(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_dictionary godotsharp_variant_as_dictionary(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_array godotsharp_variant_as_array(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_packed_byte_array godotsharp_variant_as_packed_byte_array(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_packed_int32_array godotsharp_variant_as_packed_int32_array(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_packed_int64_array godotsharp_variant_as_packed_int64_array(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_packed_float32_array godotsharp_variant_as_packed_float32_array(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_packed_float64_array godotsharp_variant_as_packed_float64_array(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_packed_string_array godotsharp_variant_as_packed_string_array(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_packed_vector2_array godotsharp_variant_as_packed_vector2_array(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_packed_vector3_array godotsharp_variant_as_packed_vector3_array(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_packed_color_array godotsharp_variant_as_packed_color_array(godot_variant* p_self); + + // string.h + + [DllImport(GodotDllName)] + public static extern void godotsharp_string_new_with_utf16_chars(godot_string* r_dest, char* p_contents); + + // string_name.h + + [DllImport(GodotDllName)] + public static extern void godotsharp_string_name_new_copy(godot_string_name* r_dest, godot_string_name* p_src); + + // node_path.h + + [DllImport(GodotDllName)] + public static extern void godotsharp_node_path_new_copy(godot_node_path* r_dest, godot_node_path* p_src); + + // array.h + + [DllImport(GodotDllName)] + public static extern void godotsharp_array_new_copy(godot_array* r_dest, godot_array* p_src); + + // dictionary.h + + [DllImport(GodotDllName)] + public static extern void godotsharp_dictionary_new_copy(godot_dictionary* r_dest, godot_dictionary* p_src); + + // destroy functions + + [DllImport(GodotDllName)] + public static extern void godotsharp_packed_byte_array_destroy(ref godot_packed_byte_array p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_packed_int32_array_destroy(ref godot_packed_int32_array p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_packed_int64_array_destroy(ref godot_packed_int64_array p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_packed_float32_array_destroy(ref godot_packed_float32_array p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_packed_float64_array_destroy(ref godot_packed_float64_array p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_packed_string_array_destroy(ref godot_packed_string_array p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_packed_vector2_array_destroy(ref godot_packed_vector2_array p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_packed_vector3_array_destroy(ref godot_packed_vector3_array p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_packed_color_array_destroy(ref godot_packed_color_array p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_destroy(ref godot_variant p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_string_destroy(ref godot_string p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_string_name_destroy(ref godot_string_name p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_node_path_destroy(ref godot_node_path p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_signal_destroy(ref godot_signal p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_callable_destroy(ref godot_callable p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_array_destroy(ref godot_array p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_dictionary_destroy(ref godot_dictionary p_self); + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs new file mode 100644 index 0000000000..70df79c1de --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs @@ -0,0 +1,70 @@ +using System; +using System.Runtime.CompilerServices; + +// ReSharper disable InconsistentNaming + +namespace Godot.NativeInterop +{ + internal static unsafe partial class NativeFuncs + { + public static godot_string_name godotsharp_string_name_new_copy(godot_string_name* src) + { + godot_string_name ret; + godotsharp_string_name_new_copy(&ret, src); + return ret; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static godot_string_name godotsharp_string_name_new_copy(godot_string_name src) + => godotsharp_string_name_new_copy(&src); + + public static godot_node_path godotsharp_node_path_new_copy(godot_node_path* src) + { + godot_node_path ret; + godotsharp_node_path_new_copy(&ret, src); + return ret; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static godot_node_path godotsharp_node_path_new_copy(godot_node_path src) + => godotsharp_node_path_new_copy(&src); + + public static godot_array godotsharp_array_new_copy(godot_array* src) + { + godot_array ret; + godotsharp_array_new_copy(&ret, src); + return ret; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static godot_array godotsharp_array_new_copy(godot_array src) + => godotsharp_array_new_copy(&src); + + public static godot_dictionary godotsharp_dictionary_new_copy(godot_dictionary* src) + { + godot_dictionary ret; + godotsharp_dictionary_new_copy(&ret, src); + return ret; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static godot_dictionary godotsharp_dictionary_new_copy(godot_dictionary src) + => godotsharp_dictionary_new_copy(&src); + + public static godot_string_name godotsharp_string_name_new_from_string(string name) + { + godot_string_name ret; + using godot_string src = Marshaling.mono_string_to_godot(name); + godotsharp_string_name_new_from_string(&ret, &src); + return ret; + } + + public static godot_node_path godotsharp_node_path_new_from_string(string name) + { + godot_node_path ret; + using godot_string src = Marshaling.mono_string_to_godot(name); + godotsharp_node_path_new_from_string(&ret, &src); + return ret; + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantSpanHelpers.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantSpanHelpers.cs new file mode 100644 index 0000000000..2814f9d506 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantSpanHelpers.cs @@ -0,0 +1,33 @@ +using System; + +namespace Godot.NativeInterop +{ + internal ref struct VariantSpanDisposer + { + private readonly Span<godot_variant> _variantSpan; + + // IMPORTANT: The span element must be default initialized. + // Make sure call Clear() on the span if it was created with stackalloc. + public VariantSpanDisposer(Span<godot_variant> variantSpan) + { + _variantSpan = variantSpan; + } + + public void Dispose() + { + for (int i = 0; i < _variantSpan.Length; i++) + _variantSpan[i].Dispose(); + } + } + + internal static class VariantSpanExtensions + { + // Used to make sure we always initialize the span values to the default, + // as we need that in order to safely dispose all elements after. + public static Span<godot_variant> Cleared(this Span<godot_variant> span) + { + span.Clear(); + return span; + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs new file mode 100644 index 0000000000..67f9e23893 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs @@ -0,0 +1,335 @@ +using System; +using System.Runtime.CompilerServices; + +// ReSharper disable InconsistentNaming + +namespace Godot.NativeInterop +{ + internal static class VariantUtils + { + public static godot_variant CreateFromRID(RID from) + => new() {_type = Variant.Type.Rid, _data = {_m_rid = from}}; + + public static godot_variant CreateFromBool(bool from) + => new() {_type = Variant.Type.Bool, _data = {_bool = from}}; + + public static godot_variant CreateFromInt(long from) + => new() {_type = Variant.Type.Int, _data = {_int = from}}; + + public static godot_variant CreateFromInt(ulong from) + => new() {_type = Variant.Type.Int, _data = {_int = (long)from}}; + + public static godot_variant CreateFromFloat(double from) + => new() {_type = Variant.Type.Float, _data = {_float = from}}; + + public static godot_variant CreateFromVector2(Vector2 from) + => new() {_type = Variant.Type.Vector2, _data = {_m_vector2 = from}}; + + public static godot_variant CreateFromVector2i(Vector2i from) + => new() {_type = Variant.Type.Vector2i, _data = {_m_vector2i = from}}; + + public static godot_variant CreateFromVector3(Vector3 from) + => new() {_type = Variant.Type.Vector3, _data = {_m_vector3 = from}}; + + public static godot_variant CreateFromVector3i(Vector3i from) + => new() {_type = Variant.Type.Vector3i, _data = {_m_vector3i = from}}; + + public static godot_variant CreateFromRect2(Rect2 from) + => new() {_type = Variant.Type.Rect2, _data = {_m_rect2 = from}}; + + public static godot_variant CreateFromRect2i(Rect2i from) + => new() {_type = Variant.Type.Rect2i, _data = {_m_rect2i = from}}; + + public static godot_variant CreateFromQuaternion(Quaternion from) + => new() {_type = Variant.Type.Quaternion, _data = {_m_quaternion = from}}; + + public static godot_variant CreateFromColor(Color from) + => new() {_type = Variant.Type.Color, _data = {_m_color = from}}; + + public static godot_variant CreateFromPlane(Plane from) + => new() {_type = Variant.Type.Plane, _data = {_m_plane = from}}; + + public static unsafe godot_variant CreateFromTransform2D(Transform2D from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_transform2d(&ret, &from); + return ret; + } + + public static unsafe godot_variant CreateFromVector4(Vector4 from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_vector4(&ret, &from); + return ret; + } + + public static unsafe godot_variant CreateFromVector4i(Vector4i from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_vector4i(&ret, &from); + return ret; + } + + public static unsafe godot_variant CreateFromBasis(Basis from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_basis(&ret, &from); + return ret; + } + + public static unsafe godot_variant CreateFromTransform3D(Transform3D from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_transform3d(&ret, &from); + return ret; + } + + public static unsafe godot_variant CreateFromProjection(Projection from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_projection(&ret, &from); + return ret; + } + + public static unsafe godot_variant CreateFromAABB(AABB from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_aabb(&ret, &from); + return ret; + } + + // Explicit name to make it very clear + public static godot_variant CreateFromCallableTakingOwnershipOfDisposableValue(godot_callable from) + => new() {_type = Variant.Type.Callable, _data = {_m_callable = from}}; + + // Explicit name to make it very clear + public static godot_variant CreateFromSignalTakingOwnershipOfDisposableValue(godot_signal from) + => new() {_type = Variant.Type.Signal, _data = {_m_signal = from}}; + + // Explicit name to make it very clear + public static godot_variant CreateFromStringTakingOwnershipOfDisposableValue(godot_string from) + => new() {_type = Variant.Type.String, _data = {_m_string = from}}; + + public static unsafe godot_variant CreateFromPackedByteArray(godot_packed_byte_array* from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_packed_byte_array(&ret, from); + return ret; + } + + public static unsafe godot_variant CreateFromPackedInt32Array(godot_packed_int32_array* from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_packed_int32_array(&ret, from); + return ret; + } + + public static unsafe godot_variant CreateFromPackedInt64Array(godot_packed_int64_array* from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_packed_int64_array(&ret, from); + return ret; + } + + public static unsafe godot_variant CreateFromPackedFloat32Array(godot_packed_float32_array* from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_packed_float32_array(&ret, from); + return ret; + } + + public static unsafe godot_variant CreateFromPackedFloat64Array(godot_packed_float64_array* from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_packed_float64_array(&ret, from); + return ret; + } + + public static unsafe godot_variant CreateFromPackedStringArray(godot_packed_string_array* from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_packed_string_array(&ret, from); + return ret; + } + + public static unsafe godot_variant CreateFromPackedVector2Array(godot_packed_vector2_array* from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_packed_vector2_array(&ret, from); + return ret; + } + + public static unsafe godot_variant CreateFromPackedVector3Array(godot_packed_vector3_array* from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_packed_vector3_array(&ret, from); + return ret; + } + + public static unsafe godot_variant CreateFromPackedColorArray(godot_packed_color_array* from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_packed_color_array(&ret, from); + return ret; + } + + public static unsafe godot_variant CreateFromArray(godot_array* from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_array(&ret, from); + return ret; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe godot_variant CreateFromArray(godot_array from) + => CreateFromArray(&from); + + public static unsafe godot_variant CreateFromDictionary(godot_dictionary* from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_dictionary(&ret, from); + return ret; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe godot_variant CreateFromDictionary(godot_dictionary from) + => CreateFromDictionary(&from); + + public static unsafe godot_variant CreateFromStringName(ref godot_string_name arg1) + { + godot_variant ret; + godot_string_name src = arg1; + NativeFuncs.godotsharp_variant_new_string_name(&ret, &src); + return ret; + } + + public static unsafe godot_variant CreateFromNodePath(ref godot_node_path arg1) + { + godot_variant ret; + godot_node_path src = arg1; + NativeFuncs.godotsharp_variant_new_node_path(&ret, &src); + return ret; + } + + public static unsafe godot_variant CreateFromGodotObject(IntPtr from) + { + if (from == IntPtr.Zero) + return new godot_variant(); + godot_variant ret; + NativeFuncs.godotsharp_variant_new_object(&ret, from); + return ret; + } + + // We avoid the internal call if the stored type is the same we want. + + public static unsafe bool ConvertToBool(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Bool ? (*p_var)._data._bool : NativeFuncs.godotsharp_variant_as_bool(p_var); + + public static unsafe char ConvertToChar(godot_variant* p_var) + => (char)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var)); + + public static unsafe sbyte ConvertToInt8(godot_variant* p_var) + => (sbyte)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var)); + + public static unsafe Int16 ConvertToInt16(godot_variant* p_var) + => (Int16)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var)); + + public static unsafe Int32 ConvertToInt32(godot_variant* p_var) + => (Int32)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var)); + + public static unsafe Int64 ConvertToInt64(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var); + + public static unsafe byte ConvertToUInt8(godot_variant* p_var) + => (byte)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var)); + + public static unsafe UInt16 ConvertToUInt16(godot_variant* p_var) + => (UInt16)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var)); + + public static unsafe UInt32 ConvertToUInt32(godot_variant* p_var) + => (UInt32)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var)); + + public static unsafe UInt64 ConvertToUInt64(godot_variant* p_var) + => (UInt64)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var)); + + public static unsafe float ConvertToFloat32(godot_variant* p_var) + => (float)((*p_var)._type == Variant.Type.Float ? (*p_var)._data._float : NativeFuncs.godotsharp_variant_as_float(p_var)); + + public static unsafe double ConvertToFloat64(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Float ? (*p_var)._data._float : NativeFuncs.godotsharp_variant_as_float(p_var); + + public static unsafe Vector2 ConvertToVector2(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Vector2 ? (*p_var)._data._m_vector2 : NativeFuncs.godotsharp_variant_as_vector2(p_var); + + public static unsafe Vector2i ConvertToVector2i(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Vector2i ? (*p_var)._data._m_vector2i : NativeFuncs.godotsharp_variant_as_vector2i(p_var); + + public static unsafe Rect2 ConvertToRect2(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Rect2 ? (*p_var)._data._m_rect2 : NativeFuncs.godotsharp_variant_as_rect2(p_var); + + public static unsafe Rect2i ConvertToRect2i(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Rect2i ? (*p_var)._data._m_rect2i : NativeFuncs.godotsharp_variant_as_rect2i(p_var); + + public static unsafe Transform2D ConvertToTransform2D(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Transform2d ? *(*p_var)._data._transform2d : NativeFuncs.godotsharp_variant_as_transform2d(p_var); + + public static unsafe Vector3 ConvertToVector3(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Vector3 ? (*p_var)._data._m_vector3 : NativeFuncs.godotsharp_variant_as_vector3(p_var); + + public static unsafe Vector3i ConvertToVector3i(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Vector3i ? (*p_var)._data._m_vector3i : NativeFuncs.godotsharp_variant_as_vector3i(p_var); + + public static unsafe Vector4 ConvertToVector4(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Vector4 ? *(*p_var)._data._vector4 : NativeFuncs.godotsharp_variant_as_vector4(p_var); + + public static unsafe Vector4i ConvertToVector4i(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Vector4i ? *(*p_var)._data._vector4i : NativeFuncs.godotsharp_variant_as_vector4i(p_var); + + public static unsafe Basis ConvertToBasis(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Basis ? *(*p_var)._data._basis : NativeFuncs.godotsharp_variant_as_basis(p_var); + + public static unsafe Quaternion ConvertToQuaternion(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Quaternion ? (*p_var)._data._m_quaternion : NativeFuncs.godotsharp_variant_as_quaternion(p_var); + + public static unsafe Transform3D ConvertToTransform3D(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Transform3d ? *(*p_var)._data._transform3d : NativeFuncs.godotsharp_variant_as_transform3d(p_var); + + public static unsafe Projection ConvertToProjection(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Projection ? *(*p_var)._data._projection : NativeFuncs.godotsharp_variant_as_projection(p_var); + + public static unsafe AABB ConvertToAABB(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Aabb ? *(*p_var)._data._aabb : NativeFuncs.godotsharp_variant_as_aabb(p_var); + + public static unsafe Color ConvertToColor(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Color ? (*p_var)._data._m_color : NativeFuncs.godotsharp_variant_as_color(p_var); + + public static unsafe Plane ConvertToPlane(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Plane ? (*p_var)._data._m_plane : NativeFuncs.godotsharp_variant_as_plane(p_var); + + public static unsafe IntPtr ConvertToGodotObject(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Object ? (*p_var)._data._m_obj_data.obj : IntPtr.Zero; + + public static unsafe RID ConvertToRID(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Rid ? (*p_var)._data._m_rid : NativeFuncs.godotsharp_variant_as_rid(p_var); + + public static unsafe godot_string_name ConvertToStringName(godot_variant* p_var) + => (*p_var)._type == Variant.Type.StringName ? + NativeFuncs.godotsharp_string_name_new_copy(&(*p_var)._data._m_string_name) : + NativeFuncs.godotsharp_variant_as_string_name(p_var); + + public static unsafe godot_node_path ConvertToNodePath(godot_variant* p_var) + => (*p_var)._type == Variant.Type.NodePath ? + NativeFuncs.godotsharp_node_path_new_copy(&(*p_var)._data._m_node_path) : + NativeFuncs.godotsharp_variant_as_node_path(p_var); + + public static unsafe godot_array ConvertToArray(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Array ? + NativeFuncs.godotsharp_array_new_copy(&(*p_var)._data._m_array) : + NativeFuncs.godotsharp_variant_as_array(p_var); + + public static unsafe godot_dictionary ConvertToDictionary(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Dictionary ? + NativeFuncs.godotsharp_dictionary_new_copy(&(*p_var)._data._m_dictionary) : + NativeFuncs.godotsharp_variant_as_dictionary(p_var); + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs index 9ae01016cb..90e51e8a1a 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.CompilerServices; +using Godot.NativeInterop; namespace Godot { @@ -39,22 +40,9 @@ namespace Godot /// new NodePath("/root/MyAutoload"); // If you have an autoloaded node or scene. /// </code> /// </example> - public sealed partial class NodePath : IDisposable + public sealed class NodePath : IDisposable { - private bool _disposed = false; - - private IntPtr ptr; - - internal static IntPtr GetPtr(NodePath instance) - { - if (instance == null) - throw new NullReferenceException($"The instance of type {nameof(NodePath)} is null."); - - if (instance._disposed) - throw new ObjectDisposedException(instance.GetType().FullName); - - return instance.ptr; - } + internal godot_node_path NativeValue; ~NodePath() { @@ -70,29 +58,27 @@ namespace Godot GC.SuppressFinalize(this); } - private void Dispose(bool disposing) + public void Dispose(bool disposing) { - if (_disposed) - return; - - if (ptr != IntPtr.Zero) - { - godot_icall_NodePath_Dtor(ptr); - ptr = IntPtr.Zero; - } - - _disposed = true; + // Always dispose `NativeValue` even if disposing is true + NativeValue.Dispose(); } - internal NodePath(IntPtr ptr) + private NodePath(godot_node_path nativeValueToOwn) { - this.ptr = ptr; + NativeValue = nativeValueToOwn; } + // Explicit name to make it very clear + internal static NodePath CreateTakingOwnershipOfDisposableValue(godot_node_path nativeValueToOwn) + => new NodePath(nativeValueToOwn); + /// <summary> /// Constructs an empty <see cref="NodePath"/>. /// </summary> - public NodePath() : this(string.Empty) { } + public NodePath() + { + } /// <summary> /// Constructs a <see cref="NodePath"/> from a string <paramref name="path"/>, @@ -125,7 +111,8 @@ namespace Godot /// <param name="path">A string that represents a path in a scene tree.</param> public NodePath(string path) { - ptr = godot_icall_NodePath_Ctor(path); + if (!string.IsNullOrEmpty(path)) + NativeValue = NativeFuncs.godotsharp_node_path_new_from_string(path); } /// <summary> @@ -144,9 +131,16 @@ namespace Godot /// Converts this <see cref="NodePath"/> to a string. /// </summary> /// <returns>A string representation of this <see cref="NodePath"/>.</returns> - public override string ToString() + public override unsafe string ToString() { - return godot_icall_NodePath_operator_String(GetPtr(this)); + if (IsEmpty) + return string.Empty; + + godot_string dest; + godot_node_path src = NativeValue; + NativeFuncs.godotsharp_node_path_as_string(&dest, &src); + using (dest) + return Marshaling.mono_string_from_godot(&dest); } /// <summary> @@ -166,7 +160,9 @@ namespace Godot /// <returns>The <see cref="NodePath"/> as a pure property path.</returns> public NodePath GetAsPropertyPath() { - return new NodePath(godot_icall_NodePath_get_as_property_path(GetPtr(this))); + godot_node_path propertyPath = default; + godot_icall_NodePath_get_as_property_path(ref NativeValue, ref propertyPath); + return CreateTakingOwnershipOfDisposableValue(propertyPath); } /// <summary> @@ -181,7 +177,7 @@ namespace Godot /// <returns>The names concatenated with <c>/</c>.</returns> public string GetConcatenatedNames() { - return godot_icall_NodePath_get_concatenated_names(GetPtr(this)); + return godot_icall_NodePath_get_concatenated_names(ref NativeValue); } /// <summary> @@ -195,9 +191,9 @@ namespace Godot /// </code> /// </example> /// <returns>The subnames concatenated with <c>:</c>.</returns> - public string GetConcatenatedSubnames() + public string GetConcatenatedSubNames() { - return godot_icall_NodePath_get_concatenated_subnames(GetPtr(this)); + return godot_icall_NodePath_get_concatenated_subnames(ref NativeValue); } /// <summary> @@ -215,28 +211,28 @@ namespace Godot /// <returns>The name at the given index <paramref name="idx"/>.</returns> public string GetName(int idx) { - return godot_icall_NodePath_get_name(GetPtr(this), idx); + return godot_icall_NodePath_get_name(ref NativeValue, idx); } /// <summary> /// Gets the number of node names which make up the path. - /// Subnames (see <see cref="GetSubnameCount"/>) are not included. + /// Subnames (see <see cref="GetSubNameCount"/>) are not included. /// For example, <c>"Path2D/PathFollow2D/Sprite2D"</c> has 3 names. /// </summary> /// <returns>The number of node names which make up the path.</returns> public int GetNameCount() { - return godot_icall_NodePath_get_name_count(GetPtr(this)); + return godot_icall_NodePath_get_name_count(ref NativeValue); } /// <summary> - /// Gets the resource or property name indicated by <paramref name="idx"/> (0 to <see cref="GetSubnameCount"/>). + /// Gets the resource or property name indicated by <paramref name="idx"/> (0 to <see cref="GetSubNameCount"/>). /// </summary> /// <param name="idx">The subname index.</param> /// <returns>The subname at the given index <paramref name="idx"/>.</returns> - public string GetSubname(int idx) + public string GetSubName(int idx) { - return godot_icall_NodePath_get_subname(GetPtr(this), idx); + return godot_icall_NodePath_get_subname(ref NativeValue, idx); } /// <summary> @@ -245,9 +241,9 @@ namespace Godot /// For example, <c>"Path2D/PathFollow2D/Sprite2D:texture:load_path"</c> has 2 subnames. /// </summary> /// <returns>The number of subnames in the path.</returns> - public int GetSubnameCount() + public int GetSubNameCount() { - return godot_icall_NodePath_get_subname_count(GetPtr(this)); + return godot_icall_NodePath_get_subname_count(ref NativeValue); } /// <summary> @@ -259,52 +255,37 @@ namespace Godot /// <returns>If the <see cref="NodePath"/> is an absolute path.</returns> public bool IsAbsolute() { - return godot_icall_NodePath_is_absolute(GetPtr(this)); + return godot_icall_NodePath_is_absolute(ref NativeValue); } /// <summary> /// Returns <see langword="true"/> if the node path is empty. /// </summary> /// <returns>If the <see cref="NodePath"/> is empty.</returns> - public bool IsEmpty() - { - return godot_icall_NodePath_is_empty(GetPtr(this)); - } - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern IntPtr godot_icall_NodePath_Ctor(string path); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void godot_icall_NodePath_Dtor(IntPtr ptr); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string godot_icall_NodePath_operator_String(IntPtr ptr); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern IntPtr godot_icall_NodePath_get_as_property_path(IntPtr ptr); + public bool IsEmpty => godot_node_path.IsEmpty(in NativeValue); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string godot_icall_NodePath_get_concatenated_names(IntPtr ptr); + private static extern void godot_icall_NodePath_get_as_property_path(ref godot_node_path ptr, ref godot_node_path dest); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string godot_icall_NodePath_get_concatenated_subnames(IntPtr ptr); + private static extern string godot_icall_NodePath_get_concatenated_names(ref godot_node_path ptr); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string godot_icall_NodePath_get_name(IntPtr ptr, int arg1); + private static extern string godot_icall_NodePath_get_concatenated_subnames(ref godot_node_path ptr); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern int godot_icall_NodePath_get_name_count(IntPtr ptr); + private static extern string godot_icall_NodePath_get_name(ref godot_node_path ptr, int arg1); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string godot_icall_NodePath_get_subname(IntPtr ptr, int arg1); + private static extern int godot_icall_NodePath_get_name_count(ref godot_node_path ptr); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern int godot_icall_NodePath_get_subname_count(IntPtr ptr); + private static extern string godot_icall_NodePath_get_subname(ref godot_node_path ptr, int arg1); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern bool godot_icall_NodePath_is_absolute(IntPtr ptr); + private static extern int godot_icall_NodePath_get_subname_count(ref godot_node_path ptr); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern bool godot_icall_NodePath_is_empty(IntPtr ptr); + private static extern bool godot_icall_NodePath_is_absolute(ref godot_node_path ptr); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs index 746612477d..80d63581c1 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs @@ -7,38 +7,44 @@ namespace Godot { private bool _disposed = false; - private static StringName nativeName = "Object"; - - internal IntPtr ptr; - internal bool memoryOwn; + internal IntPtr NativePtr; + internal bool MemoryOwn; /// <summary> /// Constructs a new <see cref="Object"/>. /// </summary> public Object() : this(false) { - if (ptr == IntPtr.Zero) - ptr = godot_icall_Object_Ctor(this); + if (NativePtr == IntPtr.Zero) + { +#if NET + unsafe + { + ptr = NativeCtor(); + } +#else + NativePtr = _gd__invoke_class_constructor(NativeCtor); +#endif + NativeInterop.InteropUtils.TieManagedToUnmanaged(this, NativePtr); + } + _InitializeGodotScriptInstanceInternals(); } internal void _InitializeGodotScriptInstanceInternals() { - godot_icall_Object_ConnectEventSignals(ptr); + godot_icall_Object_ConnectEventSignals(NativePtr); } internal Object(bool memoryOwn) { - this.memoryOwn = memoryOwn; + this.MemoryOwn = memoryOwn; } /// <summary> /// The pointer to the native instance of this <see cref="Object"/>. /// </summary> - public IntPtr NativeInstance - { - get { return ptr; } - } + public IntPtr NativeInstance => NativePtr; internal static IntPtr GetPtr(Object instance) { @@ -48,7 +54,7 @@ namespace Godot if (instance._disposed) throw new ObjectDisposedException(instance.GetType().FullName); - return instance.ptr; + return instance.NativePtr; } ~Object() @@ -73,19 +79,19 @@ namespace Godot if (_disposed) return; - if (ptr != IntPtr.Zero) + if (NativePtr != IntPtr.Zero) { - if (memoryOwn) + if (MemoryOwn) { - memoryOwn = false; - godot_icall_RefCounted_Disposed(this, ptr, !disposing); + MemoryOwn = false; + godot_icall_RefCounted_Disposed(this, NativePtr, !disposing); } else { - godot_icall_Object_Disposed(this, ptr); + godot_icall_Object_Disposed(this, NativePtr); } - ptr = IntPtr.Zero; + this.NativePtr = IntPtr.Zero; } _disposed = true; @@ -137,13 +143,49 @@ namespace Godot /// </summary> public dynamic DynamicObject => new DynamicGodotObject(this); - internal static IntPtr __ClassDB_get_method(StringName type, string method) + internal static unsafe IntPtr ClassDB_get_method(StringName type, string method) + { + IntPtr methodBind; + fixed (char* methodChars = method) + { + methodBind = NativeInterop.NativeFuncs + .godotsharp_method_bind_get_method(ref type.NativeValue, methodChars); + } + + if (methodBind == IntPtr.Zero) + throw new NativeMethodBindNotFoundException(type + "." + method); + + return methodBind; + } + +#if NET + internal static unsafe delegate* unmanaged<IntPtr> _gd__ClassDB_get_constructor(StringName type) + { + // for some reason the '??' operator doesn't support 'delegate*' + var nativeConstructor = NativeInterop.NativeFuncs + .godotsharp_get_class_constructor(ref type.NativeValue); + + if (nativeConstructor == null) + throw new NativeConstructorNotFoundException(type); + + return nativeConstructor; + } +#else + internal static IntPtr ClassDB_get_constructor(StringName type) { - return godot_icall_Object_ClassDB_get_method(StringName.GetPtr(type), method); + // for some reason the '??' operator doesn't support 'delegate*' + var nativeConstructor = NativeInterop.NativeFuncs + .godotsharp_get_class_constructor(ref type.NativeValue); + + if (nativeConstructor == IntPtr.Zero) + throw new NativeConstructorNotFoundException(type); + + return nativeConstructor; } - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern IntPtr godot_icall_Object_Ctor(Object obj); + internal static IntPtr _gd__invoke_class_constructor(IntPtr ctorFuncPtr) + => NativeInterop.NativeFuncs.godotsharp_invoke_class_constructor(ctorFuncPtr); +#endif [MethodImpl(MethodImplOptions.InternalCall)] internal static extern void godot_icall_Object_Disposed(Object obj, IntPtr ptr); @@ -156,9 +198,5 @@ namespace Godot [MethodImpl(MethodImplOptions.InternalCall)] internal static extern string godot_icall_Object_ToString(IntPtr ptr); - - // Used by the generated API - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern IntPtr godot_icall_Object_ClassDB_get_method(IntPtr type, string method); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.exceptions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.exceptions.cs new file mode 100644 index 0000000000..eb2811c73d --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.exceptions.cs @@ -0,0 +1,135 @@ +using System; + +#nullable enable + +namespace Godot +{ + public partial class Object + { + public class NativeMemberNotFoundException : Exception + { + public NativeMemberNotFoundException() + { + } + + public NativeMemberNotFoundException(string? message) : base(message) + { + } + + public NativeMemberNotFoundException(string? message, Exception? innerException) + : base(message, innerException) + { + } + } + + public class NativeConstructorNotFoundException : NativeMemberNotFoundException + { + private readonly string? _nativeClassName; + + // ReSharper disable once InconsistentNaming + private const string Arg_NativeConstructorNotFoundException = "Unable to find the native constructor."; + + public NativeConstructorNotFoundException() + : base(Arg_NativeConstructorNotFoundException) + { + } + + public NativeConstructorNotFoundException(string? nativeClassName) + : this(Arg_NativeConstructorNotFoundException, nativeClassName) + { + } + + public NativeConstructorNotFoundException(string? message, Exception? innerException) + : base(message, innerException) + { + } + + public NativeConstructorNotFoundException(string? message, string? nativeClassName) + : base(message) + { + _nativeClassName = nativeClassName; + } + + public NativeConstructorNotFoundException(string? message, string? nativeClassName, Exception? innerException) + : base(message, innerException) + { + _nativeClassName = nativeClassName; + } + + public override string Message + { + get + { + string s = base.Message; + + if (string.IsNullOrEmpty(s)) + { + s = Arg_NativeConstructorNotFoundException; + } + + if (!string.IsNullOrEmpty(_nativeClassName)) + { + s += " " + string.Format("(Class '{0}')", _nativeClassName); + } + + return s; + } + } + } + + public class NativeMethodBindNotFoundException : NativeMemberNotFoundException + { + private readonly string? _nativeMethodName; + + // ReSharper disable once InconsistentNaming + private const string Arg_NativeMethodBindNotFoundException = "Unable to find the native method bind."; + + public NativeMethodBindNotFoundException() + : base(Arg_NativeMethodBindNotFoundException) + { + } + + public NativeMethodBindNotFoundException(string? nativeMethodName) + : this(Arg_NativeMethodBindNotFoundException, nativeMethodName) + { + } + + public NativeMethodBindNotFoundException(string? message, Exception? innerException) + : base(message, innerException) + { + } + + public NativeMethodBindNotFoundException(string? message, string? nativeMethodName) + : base(message) + { + _nativeMethodName = nativeMethodName; + } + + public NativeMethodBindNotFoundException(string? message, string? nativeMethodName, Exception? innerException) + : base(message, innerException) + { + _nativeMethodName = nativeMethodName; + } + + public override string Message + { + get + { + string s = base.Message; + + if (string.IsNullOrEmpty(s)) + { + s = Arg_NativeMethodBindNotFoundException; + } + + if (!string.IsNullOrEmpty(_nativeMethodName)) + { + s += " " + string.Format("(Method '{0}')", _nativeMethodName); + } + + return s; + } + } + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/RID.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/RID.cs index 1588869ec0..a31fef8360 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/RID.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/RID.cs @@ -1,5 +1,7 @@ using System; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Godot.NativeInterop; namespace Godot { @@ -9,99 +11,32 @@ namespace Godot /// resource by themselves. They are used by and with the low-level Server /// classes such as <see cref="RenderingServer"/>. /// </summary> - public sealed partial class RID : IDisposable + [StructLayout(LayoutKind.Sequential)] + public struct RID { - private bool _disposed = false; + private ulong _id; // Default is 0 - internal IntPtr ptr; - - internal static IntPtr GetPtr(RID instance) - { - if (instance == null) - throw new NullReferenceException($"The instance of type {nameof(RID)} is null."); - - if (instance._disposed) - throw new ObjectDisposedException(instance.GetType().FullName); - - return instance.ptr; - } - - ~RID() - { - Dispose(false); - } - - /// <summary> - /// Disposes of this <see cref="RID"/>. - /// </summary> - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - private void Dispose(bool disposing) - { - if (_disposed) - return; - - if (ptr != IntPtr.Zero) - { - godot_icall_RID_Dtor(ptr); - ptr = IntPtr.Zero; - } - - _disposed = true; - } - - internal RID(IntPtr ptr) + internal RID(ulong id) { - this.ptr = ptr; - } - - /// <summary> - /// The pointer to the native instance of this <see cref="RID"/>. - /// </summary> - public IntPtr NativeInstance - { - get { return ptr; } - } - - internal RID() - { - this.ptr = IntPtr.Zero; + _id = id; } /// <summary> /// Constructs a new <see cref="RID"/> for the given <see cref="Object"/> <paramref name="from"/>. /// </summary> public RID(Object from) - { - this.ptr = godot_icall_RID_Ctor(Object.GetPtr(from)); - } + => _id = from is Resource res ? res.GetRid()._id : default; /// <summary> /// Returns the ID of the referenced resource. /// </summary> /// <returns>The ID of the referenced resource.</returns> - public int GetId() - { - return godot_icall_RID_get_id(GetPtr(this)); - } + public ulong Id => _id; /// <summary> /// Converts this <see cref="RID"/> to a string. /// </summary> /// <returns>A string representation of this RID.</returns> - public override string ToString() => "[RID]"; - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern IntPtr godot_icall_RID_Ctor(IntPtr from); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_RID_Dtor(IntPtr ptr); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern int godot_icall_RID_get_id(IntPtr ptr); + public override string ToString() => $"RID({Id})"; } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs index 2ba0493002..e485207fb4 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.CompilerServices; +using Godot.NativeInterop; namespace Godot { @@ -11,34 +12,22 @@ namespace Godot public SignalAwaiter(Object source, StringName signal, Object target) { - godot_icall_SignalAwaiter_connect(Object.GetPtr(source), StringName.GetPtr(signal), Object.GetPtr(target), this); + godot_icall_SignalAwaiter_connect(Object.GetPtr(source), ref signal.NativeValue, Object.GetPtr(target), this); } [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern Error godot_icall_SignalAwaiter_connect(IntPtr source, IntPtr signal, IntPtr target, SignalAwaiter awaiter); + internal static extern Error godot_icall_SignalAwaiter_connect(IntPtr source, ref godot_string_name signal, IntPtr target, SignalAwaiter awaiter); - public bool IsCompleted - { - get - { - return _completed; - } - } + public bool IsCompleted => _completed; public void OnCompleted(Action action) { this._action = action; } - public object[] GetResult() - { - return _result; - } + public object[] GetResult() => _result; - public IAwaiter<object[]> GetAwaiter() - { - return this; - } + public IAwaiter<object[]> GetAwaiter() => this; internal void SignalCallback(object[] args) { diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs index a1f058ffe5..058c5447e2 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs @@ -214,7 +214,7 @@ namespace Godot /// <returns>The escaped string.</returns> public static string CEscape(this string instance) { - var sb = new StringBuilder(string.Copy(instance)); + var sb = new StringBuilder(instance); sb.Replace("\\", "\\\\"); sb.Replace("\a", "\\a"); @@ -239,7 +239,7 @@ namespace Godot /// <returns>The unescaped string.</returns> public static string CUnescape(this string instance) { - var sb = new StringBuilder(string.Copy(instance)); + var sb = new StringBuilder(instance); sb.Replace("\\a", "\a"); sb.Replace("\\b", "\b"); @@ -926,7 +926,7 @@ namespace Godot /// <returns>The escaped string.</returns> public static string JSONEscape(this string instance) { - var sb = new StringBuilder(string.Copy(instance)); + var sb = new StringBuilder(instance); sb.Replace("\\", "\\\\"); sb.Replace("\b", "\\b"); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs index b1d504410b..40d282eab4 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.CompilerServices; +using Godot.NativeInterop; namespace Godot { @@ -10,20 +11,9 @@ namespace Godot /// Comparing them is much faster than with regular strings, because only the pointers are compared, /// not the whole strings. /// </summary> - public sealed partial class StringName : IDisposable + public sealed class StringName : IDisposable { - private IntPtr ptr; - - internal static IntPtr GetPtr(StringName instance) - { - if (instance == null) - throw new NullReferenceException($"The instance of type {nameof(StringName)} is null."); - - if (instance.ptr == IntPtr.Zero) - throw new ObjectDisposedException(instance.GetType().FullName); - - return instance.ptr; - } + internal godot_string_name NativeValue; ~StringName() { @@ -39,35 +29,36 @@ namespace Godot GC.SuppressFinalize(this); } - private void Dispose(bool disposing) + public void Dispose(bool disposing) { - if (ptr != IntPtr.Zero) - { - godot_icall_StringName_Dtor(ptr); - ptr = IntPtr.Zero; - } + // Always dispose `NativeValue` even if disposing is true + NativeValue.Dispose(); } - internal StringName(IntPtr ptr) + private StringName(godot_string_name nativeValueToOwn) { - this.ptr = ptr; + NativeValue = nativeValueToOwn; } + // Explicit name to make it very clear + internal static StringName CreateTakingOwnershipOfDisposableValue(godot_string_name nativeValueToOwn) + => new StringName(nativeValueToOwn); + /// <summary> /// Constructs an empty <see cref="StringName"/>. /// </summary> public StringName() { - ptr = IntPtr.Zero; } /// <summary> /// Constructs a <see cref="StringName"/> from the given <paramref name="path"/> string. /// </summary> /// <param name="path">String to construct the <see cref="StringName"/> from.</param> - public StringName(string path) + public StringName(string name) { - ptr = path == null ? IntPtr.Zero : godot_icall_StringName_Ctor(path); + if (!string.IsNullOrEmpty(name)) + NativeValue = NativeFuncs.godotsharp_string_name_new_from_string(name); } /// <summary> @@ -86,30 +77,22 @@ namespace Godot /// Converts this <see cref="StringName"/> to a string. /// </summary> /// <returns>A string representation of this <see cref="StringName"/>.</returns> - public override string ToString() + public override unsafe string ToString() { - return ptr == IntPtr.Zero ? string.Empty : godot_icall_StringName_operator_String(GetPtr(this)); + if (IsEmpty) + return string.Empty; + + godot_string dest; + godot_string_name src = NativeValue; + NativeFuncs.godotsharp_string_name_as_string(&dest, &src); + using (dest) + return Marshaling.mono_string_from_godot(&dest); } /// <summary> /// Check whether this <see cref="StringName"/> is empty. /// </summary> /// <returns>If the <see cref="StringName"/> is empty.</returns> - public bool IsEmpty() - { - return ptr == IntPtr.Zero || godot_icall_StringName_is_empty(GetPtr(this)); - } - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern IntPtr godot_icall_StringName_Ctor(string path); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void godot_icall_StringName_Dtor(IntPtr ptr); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string godot_icall_StringName_operator_String(IntPtr ptr); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern bool godot_icall_StringName_is_empty(IntPtr ptr); + public bool IsEmpty => godot_string_name.IsEmpty(in NativeValue); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj index 4f55ce47e8..9683d8e812 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj @@ -7,11 +7,20 @@ <TargetFramework>netstandard2.1</TargetFramework> <DocumentationFile>$(OutputPath)/$(AssemblyName).xml</DocumentationFile> <EnableDefaultItems>false</EnableDefaultItems> + <AllowUnsafeBlocks>true</AllowUnsafeBlocks> + <LangVersion>9</LangVersion> + + <!-- Disabled temporarily as it pollutes the warnings, but we need to document public APIs. --> + <NoWarn>CS1591</NoWarn> </PropertyGroup> <PropertyGroup> <DefineConstants>$(DefineConstants);GODOT</DefineConstants> </PropertyGroup> <ItemGroup> + <PackageReference Include="ReflectionAnalyzers" Version="0.1.22-dev" PrivateAssets="all" IncludeAssets="runtime; build; native; contentfiles; analyzers" /> + <!--PackageReference Include="IDisposableAnalyzers" Version="3.4.13" PrivateAssets="all" IncludeAssets="runtime; build; native; contentfiles; analyzers" /--> + </ItemGroup> + <ItemGroup> <Compile Include="Core\AABB.cs" /> <Compile Include="Core\Array.cs" /> <Compile Include="Core\Attributes\AssemblyHasScriptsAttribute.cs" /> @@ -47,14 +56,22 @@ <Compile Include="Core\MarshalUtils.cs" /> <Compile Include="Core\Mathf.cs" /> <Compile Include="Core\MathfEx.cs" /> + <Compile Include="Core\NativeInterop\InteropUtils.cs" /> + <Compile Include="Core\NativeInterop\NativeFuncs.extended.cs" /> + <Compile Include="Core\NativeInterop\VariantSpanHelpers.cs" /> + <Compile Include="Core\NativeInterop\VariantUtils.cs" /> <Compile Include="Core\NodePath.cs" /> <Compile Include="Core\Object.base.cs" /> + <Compile Include="Core\Object.exceptions.cs" /> <Compile Include="Core\Plane.cs" /> <Compile Include="Core\Projection.cs" /> <Compile Include="Core\Quaternion.cs" /> <Compile Include="Core\Rect2.cs" /> <Compile Include="Core\Rect2i.cs" /> <Compile Include="Core\RID.cs" /> + <Compile Include="Core\NativeInterop\NativeFuncs.cs" /> + <Compile Include="Core\NativeInterop\InteropStructs.cs" /> + <Compile Include="Core\NativeInterop\Marshaling.cs" /> <Compile Include="Core\SignalInfo.cs" /> <Compile Include="Core\SignalAwaiter.cs" /> <Compile Include="Core\StringExtensions.cs" /> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj.DotSettings b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj.DotSettings new file mode 100644 index 0000000000..1add6cc77e --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj.DotSettings @@ -0,0 +1,5 @@ +<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> + <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=core/@EntryIndexedValue">True</s:Boolean> + <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=generated/@EntryIndexedValue">True</s:Boolean> + <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=generated_005Cgodotobjects/@EntryIndexedValue">True</s:Boolean> +</wpf:ResourceDictionary> diff --git a/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj b/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj index a8c4ba96b5..1082c74448 100644 --- a/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj @@ -7,6 +7,8 @@ <TargetFramework>netstandard2.1</TargetFramework> <DocumentationFile>$(OutputPath)/$(AssemblyName).xml</DocumentationFile> <EnableDefaultItems>false</EnableDefaultItems> + <AllowUnsafeBlocks>true</AllowUnsafeBlocks> + <LangVersion>9</LangVersion> </PropertyGroup> <PropertyGroup> <DefineConstants>$(DefineConstants);GODOT</DefineConstants> diff --git a/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj.DotSettings b/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj.DotSettings new file mode 100644 index 0000000000..c7ff6fd3ee --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj.DotSettings @@ -0,0 +1,4 @@ +<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> + <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=generated/@EntryIndexedValue">True</s:Boolean> + <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=generated_005Cgodotobjects/@EntryIndexedValue">True</s:Boolean> +</wpf:ResourceDictionary> diff --git a/modules/mono/glue/base_object_glue.cpp b/modules/mono/glue/base_object_glue.cpp index 7b9dbc87cf..05a8ef20f9 100644 --- a/modules/mono/glue/base_object_glue.cpp +++ b/modules/mono/glue/base_object_glue.cpp @@ -28,8 +28,6 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifdef MONO_GLUE_ENABLED - #include "core/object/class_db.h" #include "core/object/ref_counted.h" #include "core/string/string_name.h" @@ -43,12 +41,6 @@ #include "../signal_awaiter_utils.h" #include "arguments_vector.h" -Object *godot_icall_Object_Ctor(MonoObject *p_obj) { - Object *instance = memnew(Object); - GDMonoInternals::tie_managed_to_unmanaged(p_obj, instance); - return instance; -} - void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr) { #ifdef DEBUG_ENABLED CRASH_COND(p_ptr == nullptr); @@ -133,12 +125,6 @@ void godot_icall_Object_ConnectEventSignals(Object *p_ptr) { } } -MethodBind *godot_icall_Object_ClassDB_get_method(StringName *p_type, MonoString *p_method) { - StringName type = p_type ? *p_type : StringName(); - StringName method(GDMonoMarshal::mono_string_to_godot(p_method)); - return ClassDB::get_method(type, method); -} - MonoObject *godot_icall_Object_weakref(Object *p_ptr) { if (!p_ptr) { return nullptr; @@ -240,11 +226,9 @@ MonoString *godot_icall_Object_ToString(Object *p_ptr) { } void godot_register_object_icalls() { - GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_Ctor", godot_icall_Object_Ctor); GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_Disposed", godot_icall_Object_Disposed); GDMonoUtils::add_internal_call("Godot.Object::godot_icall_RefCounted_Disposed", godot_icall_RefCounted_Disposed); GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_ConnectEventSignals", godot_icall_Object_ConnectEventSignals); - GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_ClassDB_get_method", godot_icall_Object_ClassDB_get_method); GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_ToString", godot_icall_Object_ToString); GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_weakref", godot_icall_Object_weakref); GDMonoUtils::add_internal_call("Godot.SignalAwaiter::godot_icall_SignalAwaiter_connect", godot_icall_SignalAwaiter_connect); @@ -253,5 +237,3 @@ void godot_register_object_icalls() { GDMonoUtils::add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_GetMember", godot_icall_DynamicGodotObject_GetMember); GDMonoUtils::add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_SetMember", godot_icall_DynamicGodotObject_SetMember); } - -#endif // MONO_GLUE_ENABLED diff --git a/modules/mono/glue/collections_glue.cpp b/modules/mono/glue/collections_glue.cpp index 8a9f30459c..30adea60cc 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-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -28,8 +28,6 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifdef MONO_GLUE_ENABLED - #include <mono/metadata/exception.h> #include "core/variant/array.h" @@ -39,28 +37,17 @@ #include "../mono_gd/gd_mono_marshal.h" #include "../mono_gd/gd_mono_utils.h" -Array *godot_icall_Array_Ctor() { - return memnew(Array); -} - -void godot_icall_Array_Dtor(Array *ptr) { - memdelete(ptr); -} - -MonoObject *godot_icall_Array_At(Array *ptr, int32_t index) { - if (index < 0 || index >= ptr->size()) { - GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range()); - return nullptr; - } - return GDMonoMarshal::variant_to_mono_object(ptr->operator[](index)); +void godot_icall_Array_Ctor(Array *r_dest) { + memnew_placement(r_dest, Array); } -MonoObject *godot_icall_Array_At_Generic(Array *ptr, int32_t index, uint32_t type_encoding, GDMonoClass *type_class) { +void godot_icall_Array_At(Array *ptr, int32_t index, Variant *r_elem) { if (index < 0 || index >= ptr->size()) { GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range()); - return nullptr; + *r_elem = Variant(); + return; } - return GDMonoMarshal::variant_to_mono_object(ptr->operator[](index), ManagedType(type_encoding, type_class)); + *r_elem = ptr->operator[](index); } void godot_icall_Array_SetAt(Array *ptr, int32_t index, MonoObject *value) { @@ -104,29 +91,27 @@ void godot_icall_Array_CopyTo(Array *ptr, MonoArray *array, int32_t array_index) } } -Array *godot_icall_Array_Ctor_MonoArray(MonoArray *mono_array) { - Array *godot_array = memnew(Array); +void godot_icall_Array_Ctor_MonoArray(MonoArray *mono_array, Array *r_dest) { + memnew_placement(r_dest, Array); unsigned int count = mono_array_length(mono_array); - godot_array->resize(count); + r_dest->resize(count); for (unsigned int i = 0; i < count; i++) { MonoObject *item = mono_array_get(mono_array, MonoObject *, i); - godot_icall_Array_SetAt(godot_array, i, item); + godot_icall_Array_SetAt(r_dest, i, item); } - return godot_array; } -Array *godot_icall_Array_Duplicate(Array *ptr, MonoBoolean deep) { - return memnew(Array(ptr->duplicate(deep))); +void godot_icall_Array_Duplicate(Array *ptr, MonoBoolean deep, Array *r_dest) { + memnew_placement(r_dest, Array(ptr->duplicate(deep))); } -Array *godot_icall_Array_Concatenate(Array *left, Array *right) { +void godot_icall_Array_Concatenate(Array *left, Array *right, Array *r_dest) { int count = left->size() + right->size(); - Array *new_array = memnew(Array(left->duplicate(false))); - new_array->resize(count); + memnew_placement(r_dest, Array(left->duplicate(false))); + r_dest->resize(count); for (unsigned int i = 0; i < (unsigned int)right->size(); i++) { - new_array->operator[](i + left->size()) = right->operator[](i); + r_dest->operator[](i + left->size()) = right->operator[](i); } - return new_array; } int32_t godot_icall_Array_IndexOf(Array *ptr, MonoObject *item) { @@ -166,41 +151,15 @@ void godot_icall_Array_Shuffle(Array *ptr) { ptr->shuffle(); } -void godot_icall_Array_Generic_GetElementTypeInfo(MonoReflectionType *refltype, uint32_t *type_encoding, GDMonoClass **type_class) { - MonoType *elem_type = mono_reflection_type_get_type(refltype); - - *type_encoding = mono_type_get_type(elem_type); - MonoClass *type_class_raw = mono_class_from_mono_type(elem_type); - *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); -} - -void godot_icall_Dictionary_Dtor(Dictionary *ptr) { - memdelete(ptr); -} - -MonoObject *godot_icall_Dictionary_GetValue(Dictionary *ptr, MonoObject *key) { - Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key)); - if (ret == nullptr) { - MonoObject *exc = mono_object_new(mono_domain_get(), CACHED_CLASS(KeyNotFoundException)->get_mono_ptr()); -#ifdef DEBUG_ENABLED - CRASH_COND(!exc); -#endif - GDMonoUtils::runtime_object_init(exc, CACHED_CLASS(KeyNotFoundException)); - GDMonoUtils::set_pending_exception((MonoException *)exc); - return nullptr; - } - return GDMonoMarshal::variant_to_mono_object(ret); +void godot_icall_Dictionary_Ctor(Dictionary *r_dest) { + memnew_placement(r_dest, Dictionary); } -MonoObject *godot_icall_Dictionary_GetValue_Generic(Dictionary *ptr, MonoObject *key, uint32_t type_encoding, GDMonoClass *type_class) { +void godot_icall_Dictionary_GetValue(Dictionary *ptr, MonoObject *key, Variant *r_value) { Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key)); if (ret == nullptr) { MonoObject *exc = mono_object_new(mono_domain_get(), CACHED_CLASS(KeyNotFoundException)->get_mono_ptr()); @@ -209,42 +168,37 @@ MonoObject *godot_icall_Dictionary_GetValue_Generic(Dictionary *ptr, MonoObject #endif GDMonoUtils::runtime_object_init(exc, CACHED_CLASS(KeyNotFoundException)); GDMonoUtils::set_pending_exception((MonoException *)exc); - return nullptr; + *r_value = Variant(); + return; } - return GDMonoMarshal::variant_to_mono_object(ret, ManagedType(type_encoding, type_class)); + *r_value = *ret; } void godot_icall_Dictionary_SetValue(Dictionary *ptr, MonoObject *key, MonoObject *value) { ptr->operator[](GDMonoMarshal::mono_object_to_variant(key)) = GDMonoMarshal::mono_object_to_variant(value); } -Array *godot_icall_Dictionary_Keys(Dictionary *ptr) { - return memnew(Array(ptr->keys())); +void godot_icall_Dictionary_Keys(Dictionary *ptr, Array *r_dest) { + memnew_placement(r_dest, Array(ptr->keys())); } -Array *godot_icall_Dictionary_Values(Dictionary *ptr) { - return memnew(Array(ptr->values())); +void godot_icall_Dictionary_Values(Dictionary *ptr, Array *r_dest) { + memnew_placement(r_dest, Array(ptr->values())); } int32_t godot_icall_Dictionary_Count(Dictionary *ptr) { return ptr->size(); } -int32_t godot_icall_Dictionary_KeyValuePairs(Dictionary *ptr, Array **keys, Array **values) { - *keys = godot_icall_Dictionary_Keys(ptr); - *values = godot_icall_Dictionary_Values(ptr); - return godot_icall_Dictionary_Count(ptr); -} - -void godot_icall_Dictionary_KeyValuePairAt(Dictionary *ptr, int index, MonoObject **key, MonoObject **value) { - *key = GDMonoMarshal::variant_to_mono_object(ptr->get_key_at_index(index)); - *value = GDMonoMarshal::variant_to_mono_object(ptr->get_value_at_index(index)); +int32_t godot_icall_Dictionary_KeyValuePairs(Dictionary *ptr, Array *keys, Array *values) { + memnew_placement(keys, Array(ptr->keys())); + memnew_placement(values, Array(ptr->values())); + return ptr->size(); } -void godot_icall_Dictionary_KeyValuePairAt_Generic(Dictionary *ptr, int index, MonoObject **key, MonoObject **value, uint32_t value_type_encoding, GDMonoClass *value_type_class) { - ManagedType type(value_type_encoding, value_type_class); - *key = GDMonoMarshal::variant_to_mono_object(ptr->get_key_at_index(index)); - *value = GDMonoMarshal::variant_to_mono_object(ptr->get_value_at_index(index), type); +void godot_icall_Dictionary_KeyValuePairAt(Dictionary *ptr, int index, Variant *r_key, Variant *r_value) { + memnew_placement(r_key, Variant(ptr->get_key_at_index(index))); + memnew_placement(r_value, Variant(ptr->get_value_at_index(index))); } void godot_icall_Dictionary_Add(Dictionary *ptr, MonoObject *key, MonoObject *value) { @@ -271,8 +225,8 @@ MonoBoolean godot_icall_Dictionary_ContainsKey(Dictionary *ptr, MonoObject *key) return ptr->has(GDMonoMarshal::mono_object_to_variant(key)); } -Dictionary *godot_icall_Dictionary_Duplicate(Dictionary *ptr, MonoBoolean deep) { - return memnew(Dictionary(ptr->duplicate(deep))); +void godot_icall_Dictionary_Duplicate(Dictionary *ptr, MonoBoolean deep, Dictionary *r_dest) { + memnew_placement(r_dest, Dictionary(ptr->duplicate(deep))); } MonoBoolean godot_icall_Dictionary_RemoveKey(Dictionary *ptr, MonoObject *key) { @@ -292,34 +246,16 @@ MonoBoolean godot_icall_Dictionary_Remove(Dictionary *ptr, MonoObject *key, Mono return false; } -MonoBoolean godot_icall_Dictionary_TryGetValue(Dictionary *ptr, MonoObject *key, MonoObject **value) { - Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key)); - if (ret == nullptr) { - *value = nullptr; - return false; - } - *value = GDMonoMarshal::variant_to_mono_object(ret); - return true; -} - -MonoBoolean godot_icall_Dictionary_TryGetValue_Generic(Dictionary *ptr, MonoObject *key, MonoObject **value, uint32_t type_encoding, GDMonoClass *type_class) { +MonoBoolean godot_icall_Dictionary_TryGetValue(Dictionary *ptr, MonoObject *key, Variant *value) { Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key)); if (ret == nullptr) { - *value = nullptr; + *value = Variant(); return false; } - *value = GDMonoMarshal::variant_to_mono_object(ret, ManagedType(type_encoding, type_class)); + *value = *ret; return true; } -void godot_icall_Dictionary_Generic_GetValueTypeInfo(MonoReflectionType *refltype, uint32_t *type_encoding, GDMonoClass **type_class) { - MonoType *value_type = mono_reflection_type_get_type(refltype); - - *type_encoding = mono_type_get_type(value_type); - MonoClass *type_class_raw = mono_class_from_mono_type(value_type); - *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()); } @@ -327,9 +263,7 @@ MonoString *godot_icall_Dictionary_ToString(Dictionary *ptr) { void godot_register_collections_icalls() { GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Ctor", godot_icall_Array_Ctor); GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Ctor_MonoArray", godot_icall_Array_Ctor_MonoArray); - GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Dtor", godot_icall_Array_Dtor); GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_At", godot_icall_Array_At); - GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_At_Generic", godot_icall_Array_At_Generic); GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_SetAt", godot_icall_Array_SetAt); GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Count", godot_icall_Array_Count); GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Add", godot_icall_Array_Add); @@ -344,20 +278,16 @@ void godot_register_collections_icalls() { GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_RemoveAt", godot_icall_Array_RemoveAt); GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Resize", godot_icall_Array_Resize); GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Shuffle", godot_icall_Array_Shuffle); - GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Generic_GetElementTypeInfo", godot_icall_Array_Generic_GetElementTypeInfo); GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_ToString", godot_icall_Array_ToString); GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Ctor", godot_icall_Dictionary_Ctor); - GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Dtor", godot_icall_Dictionary_Dtor); GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_GetValue", godot_icall_Dictionary_GetValue); - GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_GetValue_Generic", godot_icall_Dictionary_GetValue_Generic); GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_SetValue", godot_icall_Dictionary_SetValue); GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Keys", godot_icall_Dictionary_Keys); GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Values", godot_icall_Dictionary_Values); GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Count", godot_icall_Dictionary_Count); GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_KeyValuePairs", godot_icall_Dictionary_KeyValuePairs); GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_KeyValuePairAt", godot_icall_Dictionary_KeyValuePairAt); - GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_KeyValuePairAt_Generic", godot_icall_Dictionary_KeyValuePairAt_Generic); GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Add", godot_icall_Dictionary_Add); GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Clear", godot_icall_Dictionary_Clear); GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Contains", godot_icall_Dictionary_Contains); @@ -366,9 +296,5 @@ void godot_register_collections_icalls() { GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_RemoveKey", godot_icall_Dictionary_RemoveKey); GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Remove", godot_icall_Dictionary_Remove); GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_TryGetValue", godot_icall_Dictionary_TryGetValue); - GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_TryGetValue_Generic", godot_icall_Dictionary_TryGetValue_Generic); - GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Generic_GetValueTypeInfo", godot_icall_Dictionary_Generic_GetValueTypeInfo); GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_ToString", godot_icall_Dictionary_ToString); } - -#endif // MONO_GLUE_ENABLED diff --git a/modules/mono/glue/gd_glue.cpp b/modules/mono/glue/gd_glue.cpp index 8b1c2b729e..b315831819 100644 --- a/modules/mono/glue/gd_glue.cpp +++ b/modules/mono/glue/gd_glue.cpp @@ -28,8 +28,6 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifdef MONO_GLUE_ENABLED - #include "core/io/marshalls.h" #include "core/os/os.h" #include "core/string/ustring.h" @@ -41,10 +39,9 @@ #include "../mono_gd/gd_mono_marshal.h" #include "../mono_gd/gd_mono_utils.h" -MonoObject *godot_icall_GD_bytes2var(MonoArray *p_bytes, MonoBoolean p_allow_objects) { +MonoObject *godot_icall_GD_bytes2var(PackedByteArray *p_bytes, MonoBoolean p_allow_objects) { Variant ret; - PackedByteArray varr = GDMonoMarshal::mono_array_to_PackedByteArray(p_bytes); - Error err = decode_variant(ret, varr.ptr(), varr.size(), nullptr, p_allow_objects); + Error err = decode_variant(ret, p_bytes->ptr(), p_bytes->size(), nullptr, p_allow_objects); if (err != OK) { ret = RTR("Not enough bytes for decoding bytes, or invalid format."); } @@ -285,18 +282,17 @@ void godot_icall_GD_pushwarning(MonoString *p_str) { WARN_PRINT(GDMonoMarshal::mono_string_to_godot(p_str)); } -MonoArray *godot_icall_GD_var2bytes(MonoObject *p_var, MonoBoolean p_full_objects) { +void godot_icall_GD_var2bytes(MonoObject *p_var, MonoBoolean p_full_objects, PackedByteArray *r_bytes) { + memnew_placement(r_bytes, PackedByteArray); + Variant var = GDMonoMarshal::mono_object_to_variant(p_var); - PackedByteArray barr; int len; Error err = encode_variant(var, nullptr, len, p_full_objects); - ERR_FAIL_COND_V_MSG(err != OK, nullptr, "Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID)."); + ERR_FAIL_COND_MSG(err != OK, "Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID)."); - barr.resize(len); - encode_variant(var, barr.ptrw(), len, p_full_objects); - - return GDMonoMarshal::PackedByteArray_to_mono_array(barr); + r_bytes->resize(len); + encode_variant(var, r_bytes->ptrw(), len, p_full_objects); } MonoString *godot_icall_GD_var2str(MonoObject *p_var) { @@ -344,5 +340,3 @@ void godot_register_gd_icalls() { // Dispatcher GDMonoUtils::add_internal_call("Godot.Dispatcher::godot_icall_DefaultGodotTaskScheduler", godot_icall_DefaultGodotTaskScheduler); } - -#endif // MONO_GLUE_ENABLED diff --git a/modules/mono/glue/glue_header.h b/modules/mono/glue/glue_header.h deleted file mode 100644 index f9ad1a9893..0000000000 --- a/modules/mono/glue/glue_header.h +++ /dev/null @@ -1,91 +0,0 @@ -/*************************************************************************/ -/* glue_header.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 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 GLUE_HEADER_H -#define GLUE_HEADER_H - -#ifdef MONO_GLUE_ENABLED - -#include "../mono_gd/gd_mono_marshal.h" - -void godot_register_collections_icalls(); -void godot_register_gd_icalls(); -void godot_register_string_name_icalls(); -void godot_register_nodepath_icalls(); -void godot_register_callable_icalls(); -void godot_register_object_icalls(); -void godot_register_rid_icalls(); -void godot_register_string_icalls(); -void godot_register_scene_tree_icalls(); - -/** - * Registers internal calls that were not generated. This function is called - * from the generated GodotSharpBindings::register_generated_icalls() function. - */ -void godot_register_glue_header_icalls() { - godot_register_collections_icalls(); - godot_register_gd_icalls(); - godot_register_string_name_icalls(); - godot_register_nodepath_icalls(); - godot_register_callable_icalls(); - godot_register_object_icalls(); - godot_register_rid_icalls(); - godot_register_string_icalls(); - godot_register_scene_tree_icalls(); -} - -// Used by the generated glue - -#include "core/config/engine.h" -#include "core/object/class_db.h" -#include "core/object/method_bind.h" -#include "core/object/ref_counted.h" -#include "core/string/node_path.h" -#include "core/string/ustring.h" -#include "core/typedefs.h" -#include "core/variant/array.h" -#include "core/variant/dictionary.h" - -#include "../mono_gd/gd_mono_class.h" -#include "../mono_gd/gd_mono_internals.h" -#include "../mono_gd/gd_mono_utils.h" - -#define GODOTSHARP_INSTANCE_OBJECT(m_instance, m_type) \ - static ClassDB::ClassInfo *ci = nullptr; \ - if (!ci) { \ - ci = ClassDB::classes.getptr(m_type); \ - } \ - Object *m_instance = ci->creation_func(); - -#include "arguments_vector.h" - -#endif // MONO_GLUE_ENABLED - -#endif // GLUE_HEADER_H diff --git a/modules/mono/glue/nodepath_glue.cpp b/modules/mono/glue/node_path_glue.cpp index 16e1509eb0..770ed31260 100644 --- a/modules/mono/glue/nodepath_glue.cpp +++ b/modules/mono/glue/node_path_glue.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* nodepath_glue.cpp */ +/* node_path_glue.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,26 +28,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifdef MONO_GLUE_ENABLED - #include "core/string/node_path.h" #include "core/string/ustring.h" #include "../mono_gd/gd_mono_marshal.h" -NodePath *godot_icall_NodePath_Ctor(MonoString *p_path) { - return memnew(NodePath(GDMonoMarshal::mono_string_to_godot(p_path))); -} - -void godot_icall_NodePath_Dtor(NodePath *p_ptr) { - ERR_FAIL_NULL(p_ptr); - memdelete(p_ptr); -} - -MonoString *godot_icall_NodePath_operator_String(NodePath *p_np) { - return GDMonoMarshal::mono_string_from_godot(p_np->operator String()); -} - MonoBoolean godot_icall_NodePath_is_absolute(NodePath *p_ptr) { return (MonoBoolean)p_ptr->is_absolute(); } @@ -76,18 +61,11 @@ MonoString *godot_icall_NodePath_get_concatenated_subnames(NodePath *p_ptr) { return GDMonoMarshal::mono_string_from_godot(p_ptr->get_concatenated_subnames()); } -NodePath *godot_icall_NodePath_get_as_property_path(NodePath *p_ptr) { - return memnew(NodePath(p_ptr->get_as_property_path())); +void godot_icall_NodePath_get_as_property_path(NodePath *p_ptr, NodePath *r_dest) { + *r_dest = p_ptr->get_as_property_path(); } -MonoBoolean godot_icall_NodePath_is_empty(NodePath *p_ptr) { - return (MonoBoolean)p_ptr->is_empty(); -} - -void godot_register_nodepath_icalls() { - GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_Ctor", godot_icall_NodePath_Ctor); - GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_Dtor", godot_icall_NodePath_Dtor); - GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_operator_String", godot_icall_NodePath_operator_String); +void godot_register_node_path_icalls() { GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_as_property_path", godot_icall_NodePath_get_as_property_path); GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_concatenated_names", godot_icall_NodePath_get_concatenated_names); GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_concatenated_subnames", godot_icall_NodePath_get_concatenated_subnames); @@ -96,7 +74,4 @@ void godot_register_nodepath_icalls() { GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_subname", godot_icall_NodePath_get_subname); GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_subname_count", godot_icall_NodePath_get_subname_count); GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_is_absolute", godot_icall_NodePath_is_absolute); - GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_is_empty", godot_icall_NodePath_is_empty); } - -#endif // MONO_GLUE_ENABLED diff --git a/modules/mono/glue/rid_glue.cpp b/modules/mono/glue/placeholder_glue.cpp index 3e09564539..edac231bb4 100644 --- a/modules/mono/glue/rid_glue.cpp +++ b/modules/mono/glue/placeholder_glue.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* rid_glue.cpp */ +/* placeholder_glue.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,37 +28,29 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifdef MONO_GLUE_ENABLED +#ifndef GLUE_HEADER_H +#define GLUE_HEADER_H -#include "core/io/resource.h" -#include "core/object/class_db.h" -#include "core/templates/rid.h" +#include "core/object/object.h" -#include "../mono_gd/gd_mono_marshal.h" +#include "../mono_gd/gd_mono_internals.h" +#include "../mono_gd/gd_mono_utils.h" -RID *godot_icall_RID_Ctor(Object *p_from) { - Resource *res_from = Object::cast_to<Resource>(p_from); - - if (res_from) { - return memnew(RID(res_from->get_rid())); - } - - return memnew(RID); -} - -void godot_icall_RID_Dtor(RID *p_ptr) { - ERR_FAIL_NULL(p_ptr); - memdelete(p_ptr); +MonoObject *godot_icall_InteropUtils_unmanaged_get_managed(Object *unmanaged) { + return GDMonoUtils::unmanaged_get_managed(unmanaged); } -uint32_t godot_icall_RID_get_id(RID *p_ptr) { - return p_ptr->get_id(); +void godot_icall_InteropUtils_tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) { + GDMonoInternals::tie_managed_to_unmanaged(managed, unmanaged); } -void godot_register_rid_icalls() { - GDMonoUtils::add_internal_call("Godot.RID::godot_icall_RID_Ctor", godot_icall_RID_Ctor); - GDMonoUtils::add_internal_call("Godot.RID::godot_icall_RID_Dtor", godot_icall_RID_Dtor); - GDMonoUtils::add_internal_call("Godot.RID::godot_icall_RID_get_id", godot_icall_RID_get_id); +void godot_register_placeholder_icalls() { + GDMonoUtils::add_internal_call( + "Godot.NativeInterop.InteropUtils::internal_unmanaged_get_managed", + godot_icall_InteropUtils_unmanaged_get_managed); + GDMonoUtils::add_internal_call( + "Godot.NativeInterop.InteropUtils::internal_tie_managed_to_unmanaged", + godot_icall_InteropUtils_tie_managed_to_unmanaged); } -#endif // MONO_GLUE_ENABLED +#endif // GLUE_HEADER_H diff --git a/modules/mono/glue/runtime_interop.cpp b/modules/mono/glue/runtime_interop.cpp new file mode 100644 index 0000000000..a9c47f8880 --- /dev/null +++ b/modules/mono/glue/runtime_interop.cpp @@ -0,0 +1,786 @@ +/*************************************************************************/ +/* runtime_interop.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 "core/config/engine.h" +#include "core/object/class_db.h" +#include "core/object/method_bind.h" +#include "core/string/string_name.h" + +#include "../interop_types.h" + +#include "modules/mono/managed_callable.h" +#include "modules/mono/signal_awaiter_utils.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +#define MAYBE_UNUSED [[maybe_unused]] +#else +#define MAYBE_UNUSED +#endif + +#ifdef __GNUC__ +#define GD_PINVOKE_EXPORT MAYBE_UNUSED __attribute__((visibility("default"))) +#elif defined(_WIN32) +#define GD_PINVOKE_EXPORT MAYBE_UNUSED __declspec(dllexport) +#else +#define GD_PINVOKE_EXPORT MAYBE_UNUSED +#endif + +typedef Object *(*godotsharp_class_creation_func)(); + +GD_PINVOKE_EXPORT MethodBind *godotsharp_method_bind_get_method(const StringName *p_classname, const char16_t *p_methodname) { + return ClassDB::get_method(*p_classname, StringName(String::utf16(p_methodname))); +} + +GD_PINVOKE_EXPORT godotsharp_class_creation_func godotsharp_get_class_constructor(const StringName *p_classname) { + ClassDB::ClassInfo *class_info = ClassDB::classes.getptr(*p_classname); + if (class_info) { + return class_info->creation_func; + } + return nullptr; +} + +GD_PINVOKE_EXPORT Object *godotsharp_invoke_class_constructor(godotsharp_class_creation_func p_creation_func) { + return p_creation_func(); +} + +GD_PINVOKE_EXPORT Object *godotsharp_engine_get_singleton(const String *p_name) { + return Engine::get_singleton()->get_singleton_object(*p_name); +} + +GD_PINVOKE_EXPORT void godotsharp_ref_destroy(Ref<RefCounted> *p_instance) { + p_instance->~Ref(); +} + +GD_PINVOKE_EXPORT void godotsharp_string_name_new_from_string(StringName *r_dest, const String *p_name) { + memnew_placement(r_dest, StringName(*p_name)); +} + +GD_PINVOKE_EXPORT void godotsharp_node_path_new_from_string(NodePath *r_dest, const String *p_name) { + memnew_placement(r_dest, NodePath(*p_name)); +} + +GD_PINVOKE_EXPORT void godotsharp_string_name_as_string(String *r_dest, const StringName *p_name) { + memnew_placement(r_dest, String(p_name->operator String())); +} + +GD_PINVOKE_EXPORT void godotsharp_node_path_as_string(String *r_dest, const NodePath *p_np) { + memnew_placement(r_dest, String(p_np->operator String())); +} + +GD_PINVOKE_EXPORT godot_packed_array godotsharp_packed_byte_array_new_mem_copy(const uint8_t *p_src, int32_t p_length) { + godot_packed_array ret; + memnew_placement(&ret, PackedByteArray); + PackedByteArray *array = reinterpret_cast<PackedByteArray *>(&ret); + array->resize(p_length); + uint8_t *dst = array->ptrw(); + memcpy(dst, p_src, p_length * sizeof(uint8_t)); + return ret; +} + +GD_PINVOKE_EXPORT godot_packed_array godotsharp_packed_int32_array_new_mem_copy(const int32_t *p_src, int32_t p_length) { + godot_packed_array ret; + memnew_placement(&ret, PackedInt32Array); + PackedInt32Array *array = reinterpret_cast<PackedInt32Array *>(&ret); + array->resize(p_length); + int32_t *dst = array->ptrw(); + memcpy(dst, p_src, p_length * sizeof(int32_t)); + return ret; +} + +GD_PINVOKE_EXPORT godot_packed_array godotsharp_packed_int64_array_new_mem_copy(const int64_t *p_src, int32_t p_length) { + godot_packed_array ret; + memnew_placement(&ret, PackedInt64Array); + PackedInt64Array *array = reinterpret_cast<PackedInt64Array *>(&ret); + array->resize(p_length); + int64_t *dst = array->ptrw(); + memcpy(dst, p_src, p_length * sizeof(int64_t)); + return ret; +} + +GD_PINVOKE_EXPORT godot_packed_array godotsharp_packed_float32_array_new_mem_copy(const float *p_src, int32_t p_length) { + godot_packed_array ret; + memnew_placement(&ret, PackedFloat32Array); + PackedFloat32Array *array = reinterpret_cast<PackedFloat32Array *>(&ret); + array->resize(p_length); + float *dst = array->ptrw(); + memcpy(dst, p_src, p_length * sizeof(float)); + return ret; +} + +GD_PINVOKE_EXPORT godot_packed_array godotsharp_packed_float64_array_new_mem_copy(const double *p_src, int32_t p_length) { + godot_packed_array ret; + memnew_placement(&ret, PackedFloat64Array); + PackedFloat64Array *array = reinterpret_cast<PackedFloat64Array *>(&ret); + array->resize(p_length); + double *dst = array->ptrw(); + memcpy(dst, p_src, p_length * sizeof(double)); + return ret; +} + +GD_PINVOKE_EXPORT godot_packed_array godotsharp_packed_vector2_array_new_mem_copy(const Vector2 *p_src, int32_t p_length) { + godot_packed_array ret; + memnew_placement(&ret, PackedVector2Array); + PackedVector2Array *array = reinterpret_cast<PackedVector2Array *>(&ret); + array->resize(p_length); + Vector2 *dst = array->ptrw(); + memcpy(dst, p_src, p_length * sizeof(Vector2)); + return ret; +} + +GD_PINVOKE_EXPORT godot_packed_array godotsharp_packed_vector3_array_new_mem_copy(const Vector3 *p_src, int32_t p_length) { + godot_packed_array ret; + memnew_placement(&ret, PackedVector3Array); + PackedVector3Array *array = reinterpret_cast<PackedVector3Array *>(&ret); + array->resize(p_length); + Vector3 *dst = array->ptrw(); + memcpy(dst, p_src, p_length * sizeof(Vector3)); + return ret; +} + +GD_PINVOKE_EXPORT godot_packed_array godotsharp_packed_color_array_new_mem_copy(const Color *p_src, int32_t p_length) { + godot_packed_array ret; + memnew_placement(&ret, PackedColorArray); + PackedColorArray *array = reinterpret_cast<PackedColorArray *>(&ret); + array->resize(p_length); + Color *dst = array->ptrw(); + memcpy(dst, p_src, p_length * sizeof(Color)); + return ret; +} + +GD_PINVOKE_EXPORT void godotsharp_packed_string_array_add(PackedStringArray *r_dest, const String *p_element) { + r_dest->append(*p_element); +} + +GD_PINVOKE_EXPORT void godotsharp_callable_new_with_delegate(void *p_delegate_handle, Callable *r_callable) { + // TODO: Use pooling for ManagedCallable instances. + CallableCustom *managed_callable = memnew(ManagedCallable(p_delegate_handle)); + *r_callable = Callable(managed_callable); +} + +GD_PINVOKE_EXPORT bool godotsharp_callable_get_data_for_marshalling(const Callable *p_callable, + void **r_delegate_handle, Object **r_object, StringName *r_name) { + if (p_callable->is_custom()) { + CallableCustom *custom = p_callable->get_custom(); + CallableCustom::CompareEqualFunc compare_equal_func = custom->get_compare_equal_func(); + + if (compare_equal_func == ManagedCallable::compare_equal_func_ptr) { + ManagedCallable *managed_callable = static_cast<ManagedCallable *>(custom); + *r_delegate_handle = managed_callable->get_delegate(); + *r_object = nullptr; + *r_name = StringName(); + return true; + } else if (compare_equal_func == SignalAwaiterCallable::compare_equal_func_ptr) { + SignalAwaiterCallable *signal_awaiter_callable = static_cast<SignalAwaiterCallable *>(custom); + *r_delegate_handle = nullptr; + *r_object = ObjectDB::get_instance(signal_awaiter_callable->get_object()); + *r_name = signal_awaiter_callable->get_signal(); + return true; + } else if (compare_equal_func == EventSignalCallable::compare_equal_func_ptr) { + EventSignalCallable *event_signal_callable = static_cast<EventSignalCallable *>(custom); + *r_delegate_handle = nullptr; + *r_object = ObjectDB::get_instance(event_signal_callable->get_object()); + *r_name = event_signal_callable->get_signal(); + return true; + } + + // Some other CallableCustom. We only support ManagedCallable. + *r_delegate_handle = nullptr; + *r_object = nullptr; + *r_name = StringName(); + return false; + } else { + *r_delegate_handle = nullptr; + *r_object = ObjectDB::get_instance(p_callable->get_object_id()); + *r_name = p_callable->get_method(); + return true; + } +} + +// GDNative functions + +// gdnative.h + +GD_PINVOKE_EXPORT void godotsharp_method_bind_ptrcall(MethodBind *p_method_bind, Object *p_instance, const void **p_args, void *p_ret) { + p_method_bind->ptrcall(p_instance, p_args, p_ret); +} + +GD_PINVOKE_EXPORT godot_variant godotsharp_method_bind_call(MethodBind *p_method_bind, Object *p_instance, const godot_variant **p_args, const int p_arg_count, Callable::CallError *p_call_error) { + godot_variant ret; + memnew_placement(&ret, Variant()); + + Variant *ret_val = (Variant *)&ret; + + *ret_val = p_method_bind->call(p_instance, (const Variant **)p_args, p_arg_count, *p_call_error); + + return ret; +} + +// variant.h + +GD_PINVOKE_EXPORT void godotsharp_variant_new_string_name(godot_variant *r_dest, const StringName *p_s) { + memnew_placement(r_dest, Variant(*p_s)); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_node_path(godot_variant *r_dest, const NodePath *p_np) { + memnew_placement(r_dest, Variant(*p_np)); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_object(godot_variant *r_dest, const Object *p_obj) { + memnew_placement(r_dest, Variant(p_obj)); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_transform2d(godot_variant *r_dest, const Transform2D *p_t2d) { + memnew_placement(r_dest, Variant(*p_t2d)); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_vector4(godot_variant *r_dest, const Vector4 *p_vec4) { + memnew_placement(r_dest, Variant(*p_vec4)); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_vector4i(godot_variant *r_dest, const Vector4i *p_vec4i) { + memnew_placement(r_dest, Variant(*p_vec4i)); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_basis(godot_variant *r_dest, const Basis *p_basis) { + memnew_placement(r_dest, Variant(*p_basis)); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_transform3d(godot_variant *r_dest, const Transform3D *p_trans) { + memnew_placement(r_dest, Variant(*p_trans)); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_projection(godot_variant *r_dest, const Projection *p_proj) { + memnew_placement(r_dest, Variant(*p_proj)); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_aabb(godot_variant *r_dest, const AABB *p_aabb) { + memnew_placement(r_dest, Variant(*p_aabb)); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_dictionary(godot_variant *r_dest, const Dictionary *p_dict) { + memnew_placement(r_dest, Variant(*p_dict)); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_array(godot_variant *r_dest, const Array *p_arr) { + memnew_placement(r_dest, Variant(*p_arr)); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_packed_byte_array(godot_variant *r_dest, const PackedByteArray *p_pba) { + memnew_placement(r_dest, Variant(*p_pba)); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_packed_int32_array(godot_variant *r_dest, const PackedInt32Array *p_pia) { + memnew_placement(r_dest, Variant(*p_pia)); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_packed_int64_array(godot_variant *r_dest, const PackedInt64Array *p_pia) { + memnew_placement(r_dest, Variant(*p_pia)); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_packed_float32_array(godot_variant *r_dest, const PackedFloat32Array *p_pra) { + memnew_placement(r_dest, Variant(*p_pra)); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_packed_float64_array(godot_variant *r_dest, const PackedFloat64Array *p_pra) { + memnew_placement(r_dest, Variant(*p_pra)); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_packed_string_array(godot_variant *r_dest, const PackedStringArray *p_psa) { + memnew_placement(r_dest, Variant(*p_psa)); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_packed_vector2_array(godot_variant *r_dest, const PackedVector2Array *p_pv2a) { + memnew_placement(r_dest, Variant(*p_pv2a)); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_packed_vector3_array(godot_variant *r_dest, const PackedVector3Array *p_pv3a) { + memnew_placement(r_dest, Variant(*p_pv3a)); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_packed_color_array(godot_variant *r_dest, const PackedColorArray *p_pca) { + memnew_placement(r_dest, Variant(*p_pca)); +} + +GD_PINVOKE_EXPORT bool godotsharp_variant_as_bool(const Variant *p_self) { + return p_self->operator bool(); +} + +GD_PINVOKE_EXPORT int64_t godotsharp_variant_as_int(const Variant *p_self) { + return p_self->operator int64_t(); +} + +GD_PINVOKE_EXPORT double godotsharp_variant_as_float(const Variant *p_self) { + return p_self->operator double(); +} + +GD_PINVOKE_EXPORT godot_string godotsharp_variant_as_string(const Variant *p_self) { + godot_string raw_dest; + String *dest = (String *)&raw_dest; + memnew_placement(dest, String(p_self->operator String())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_vector2 godotsharp_variant_as_vector2(const Variant *p_self) { + godot_vector2 raw_dest; + Vector2 *dest = (Vector2 *)&raw_dest; + memnew_placement(dest, Vector2(p_self->operator Vector2())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_vector2i godotsharp_variant_as_vector2i(const Variant *p_self) { + godot_vector2i raw_dest; + Vector2i *dest = (Vector2i *)&raw_dest; + memnew_placement(dest, Vector2i(p_self->operator Vector2i())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_rect2 godotsharp_variant_as_rect2(const Variant *p_self) { + godot_rect2 raw_dest; + Rect2 *dest = (Rect2 *)&raw_dest; + memnew_placement(dest, Rect2(p_self->operator Rect2())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_rect2i godotsharp_variant_as_rect2i(const Variant *p_self) { + godot_rect2i raw_dest; + Rect2i *dest = (Rect2i *)&raw_dest; + memnew_placement(dest, Rect2i(p_self->operator Rect2i())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_vector3 godotsharp_variant_as_vector3(const Variant *p_self) { + godot_vector3 raw_dest; + Vector3 *dest = (Vector3 *)&raw_dest; + memnew_placement(dest, Vector3(p_self->operator Vector3())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_vector3i godotsharp_variant_as_vector3i(const Variant *p_self) { + godot_vector3i raw_dest; + Vector3i *dest = (Vector3i *)&raw_dest; + memnew_placement(dest, Vector3i(p_self->operator Vector3i())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_transform2d godotsharp_variant_as_transform2d(const Variant *p_self) { + godot_transform2d raw_dest; + Transform2D *dest = (Transform2D *)&raw_dest; + memnew_placement(dest, Transform2D(p_self->operator Transform2D())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_vector4 godotsharp_variant_as_vector4(const Variant *p_self) { + godot_vector4 raw_dest; + Vector4 *dest = (Vector4 *)&raw_dest; + memnew_placement(dest, Vector4(p_self->operator Vector4())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_vector4i godotsharp_variant_as_vector4i(const Variant *p_self) { + godot_vector4i raw_dest; + Vector4i *dest = (Vector4i *)&raw_dest; + memnew_placement(dest, Vector4i(p_self->operator Vector4i())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_plane godotsharp_variant_as_plane(const Variant *p_self) { + godot_plane raw_dest; + Plane *dest = (Plane *)&raw_dest; + memnew_placement(dest, Plane(p_self->operator Plane())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_quaternion godotsharp_variant_as_quaternion(const Variant *p_self) { + godot_quaternion raw_dest; + Quaternion *dest = (Quaternion *)&raw_dest; + memnew_placement(dest, Quaternion(p_self->operator Quaternion())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_aabb godotsharp_variant_as_aabb(const Variant *p_self) { + godot_aabb raw_dest; + AABB *dest = (AABB *)&raw_dest; + memnew_placement(dest, AABB(p_self->operator ::AABB())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_basis godotsharp_variant_as_basis(const Variant *p_self) { + godot_basis raw_dest; + Basis *dest = (Basis *)&raw_dest; + memnew_placement(dest, Basis(p_self->operator Basis())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_transform3d godotsharp_variant_as_transform3d(const Variant *p_self) { + godot_transform3d raw_dest; + Transform3D *dest = (Transform3D *)&raw_dest; + memnew_placement(dest, Transform3D(p_self->operator Transform3D())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_projection godotsharp_variant_as_projection(const Variant *p_self) { + godot_projection raw_dest; + Projection *dest = (Projection *)&raw_dest; + memnew_placement(dest, Projection(p_self->operator Projection())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_color godotsharp_variant_as_color(const Variant *p_self) { + godot_color raw_dest; + Color *dest = (Color *)&raw_dest; + memnew_placement(dest, Color(p_self->operator Color())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_string_name godotsharp_variant_as_string_name(const Variant *p_self) { + godot_string_name raw_dest; + StringName *dest = (StringName *)&raw_dest; + memnew_placement(dest, StringName(p_self->operator StringName())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_node_path godotsharp_variant_as_node_path(const Variant *p_self) { + godot_node_path raw_dest; + NodePath *dest = (NodePath *)&raw_dest; + memnew_placement(dest, NodePath(p_self->operator NodePath())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_rid godotsharp_variant_as_rid(const Variant *p_self) { + godot_rid raw_dest; + RID *dest = (RID *)&raw_dest; + memnew_placement(dest, RID(p_self->operator ::RID())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_callable godotsharp_variant_as_callable(const Variant *p_self) { + godot_callable raw_dest; + Callable *dest = (Callable *)&raw_dest; + memnew_placement(dest, Callable(p_self->operator Callable())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_signal godotsharp_variant_as_signal(const Variant *p_self) { + godot_signal raw_dest; + Signal *dest = (Signal *)&raw_dest; + memnew_placement(dest, Signal(p_self->operator Signal())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_dictionary godotsharp_variant_as_dictionary(const Variant *p_self) { + godot_dictionary raw_dest; + Dictionary *dest = (Dictionary *)&raw_dest; + memnew_placement(dest, Dictionary(p_self->operator Dictionary())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_array godotsharp_variant_as_array(const Variant *p_self) { + godot_array raw_dest; + Array *dest = (Array *)&raw_dest; + memnew_placement(dest, Array(p_self->operator Array())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_packed_array godotsharp_variant_as_packed_byte_array(const Variant *p_self) { + godot_packed_array raw_dest; + PackedByteArray *dest = (PackedByteArray *)&raw_dest; + memnew_placement(dest, PackedByteArray(p_self->operator PackedByteArray())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_packed_array godotsharp_variant_as_packed_int32_array(const Variant *p_self) { + godot_packed_array raw_dest; + PackedInt32Array *dest = (PackedInt32Array *)&raw_dest; + memnew_placement(dest, PackedInt32Array(p_self->operator PackedInt32Array())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_packed_array godotsharp_variant_as_packed_int64_array(const Variant *p_self) { + godot_packed_array raw_dest; + PackedInt64Array *dest = (PackedInt64Array *)&raw_dest; + memnew_placement(dest, PackedInt64Array(p_self->operator PackedInt64Array())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_packed_array godotsharp_variant_as_packed_float32_array(const Variant *p_self) { + godot_packed_array raw_dest; + PackedFloat32Array *dest = (PackedFloat32Array *)&raw_dest; + memnew_placement(dest, PackedFloat32Array(p_self->operator PackedFloat32Array())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_packed_array godotsharp_variant_as_packed_float64_array(const Variant *p_self) { + godot_packed_array raw_dest; + PackedFloat64Array *dest = (PackedFloat64Array *)&raw_dest; + memnew_placement(dest, PackedFloat64Array(p_self->operator PackedFloat64Array())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_packed_array godotsharp_variant_as_packed_string_array(const Variant *p_self) { + godot_packed_array raw_dest; + PackedStringArray *dest = (PackedStringArray *)&raw_dest; + memnew_placement(dest, PackedStringArray(p_self->operator PackedStringArray())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_packed_array godotsharp_variant_as_packed_vector2_array(const Variant *p_self) { + godot_packed_array raw_dest; + PackedVector2Array *dest = (PackedVector2Array *)&raw_dest; + memnew_placement(dest, PackedVector2Array(p_self->operator PackedVector2Array())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_packed_array godotsharp_variant_as_packed_vector3_array(const Variant *p_self) { + godot_packed_array raw_dest; + PackedVector3Array *dest = (PackedVector3Array *)&raw_dest; + memnew_placement(dest, PackedVector3Array(p_self->operator PackedVector3Array())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_packed_array godotsharp_variant_as_packed_color_array(const Variant *p_self) { + godot_packed_array raw_dest; + PackedColorArray *dest = (PackedColorArray *)&raw_dest; + memnew_placement(dest, PackedColorArray(p_self->operator PackedColorArray())); + return raw_dest; +} + +// string.h + +GD_PINVOKE_EXPORT void godotsharp_string_new_with_utf16_chars(String *r_dest, const char16_t *p_contents) { + memnew_placement(r_dest, String()); + r_dest->parse_utf16(p_contents); +} + +// string_name.h + +GD_PINVOKE_EXPORT void godotsharp_string_name_new_copy(StringName *r_dest, const StringName *p_src) { + memnew_placement(r_dest, StringName(*p_src)); +} + +// node_path.h + +GD_PINVOKE_EXPORT void godotsharp_node_path_new_copy(NodePath *r_dest, const NodePath *p_src) { + memnew_placement(r_dest, NodePath(*p_src)); +} + +// array.h + +GD_PINVOKE_EXPORT void godotsharp_array_new_copy(Array *r_dest, const Array *p_src) { + memnew_placement(r_dest, Array(*p_src)); +} + +// dictionary.h + +GD_PINVOKE_EXPORT void godotsharp_dictionary_new_copy(Dictionary *r_dest, const Dictionary *p_src) { + memnew_placement(r_dest, Dictionary(*p_src)); +} + +// destroy functions + +GD_PINVOKE_EXPORT void godotsharp_packed_byte_array_destroy(PackedByteArray *p_self) { + p_self->~PackedByteArray(); +} + +GD_PINVOKE_EXPORT void godotsharp_packed_int32_array_destroy(PackedInt32Array *p_self) { + p_self->~PackedInt32Array(); +} + +GD_PINVOKE_EXPORT void godotsharp_packed_int64_array_destroy(PackedInt64Array *p_self) { + p_self->~PackedInt64Array(); +} + +GD_PINVOKE_EXPORT void godotsharp_packed_float32_array_destroy(PackedFloat32Array *p_self) { + p_self->~PackedFloat32Array(); +} + +GD_PINVOKE_EXPORT void godotsharp_packed_float64_array_destroy(PackedFloat64Array *p_self) { + p_self->~PackedFloat64Array(); +} + +GD_PINVOKE_EXPORT void godotsharp_packed_string_array_destroy(PackedStringArray *p_self) { + p_self->~PackedStringArray(); +} + +GD_PINVOKE_EXPORT void godotsharp_packed_vector2_array_destroy(PackedVector2Array *p_self) { + p_self->~PackedVector2Array(); +} + +GD_PINVOKE_EXPORT void godotsharp_packed_vector3_array_destroy(PackedVector3Array *p_self) { + p_self->~PackedVector3Array(); +} + +GD_PINVOKE_EXPORT void godotsharp_packed_color_array_destroy(PackedColorArray *p_self) { + p_self->~PackedColorArray(); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_destroy(Variant *p_self) { + p_self->~Variant(); +} + +GD_PINVOKE_EXPORT void godotsharp_string_destroy(String *p_self) { + p_self->~String(); +} + +GD_PINVOKE_EXPORT void godotsharp_string_name_destroy(StringName *p_self) { + p_self->~StringName(); +} + +GD_PINVOKE_EXPORT void godotsharp_node_path_destroy(NodePath *p_self) { + p_self->~NodePath(); +} + +GD_PINVOKE_EXPORT void godotsharp_signal_destroy(Signal *p_self) { + p_self->~Signal(); +} + +GD_PINVOKE_EXPORT void godotsharp_callable_destroy(Callable *p_self) { + p_self->~Callable(); +} + +GD_PINVOKE_EXPORT void godotsharp_array_destroy(Array *p_self) { + p_self->~Array(); +} + +GD_PINVOKE_EXPORT void godotsharp_dictionary_destroy(Dictionary *p_self) { + p_self->~Dictionary(); +} + +#ifdef __cplusplus +} +#endif + +// We need this to prevent the functions from being stripped. +void *godotsharp_pinvoke_funcs[101] = { + (void *)godotsharp_method_bind_get_method, + (void *)godotsharp_get_class_constructor, + (void *)godotsharp_invoke_class_constructor, + (void *)godotsharp_engine_get_singleton, + (void *)godotsharp_ref_destroy, + (void *)godotsharp_string_name_new_from_string, + (void *)godotsharp_node_path_new_from_string, + (void *)godotsharp_string_name_as_string, + (void *)godotsharp_node_path_as_string, + (void *)godotsharp_packed_byte_array_new_mem_copy, + (void *)godotsharp_packed_int32_array_new_mem_copy, + (void *)godotsharp_packed_int64_array_new_mem_copy, + (void *)godotsharp_packed_float32_array_new_mem_copy, + (void *)godotsharp_packed_float64_array_new_mem_copy, + (void *)godotsharp_packed_vector2_array_new_mem_copy, + (void *)godotsharp_packed_vector3_array_new_mem_copy, + (void *)godotsharp_packed_color_array_new_mem_copy, + (void *)godotsharp_packed_string_array_add, + (void *)godotsharp_callable_new_with_delegate, + (void *)godotsharp_callable_get_data_for_marshalling, + (void *)godotsharp_method_bind_ptrcall, + (void *)godotsharp_method_bind_call, + (void *)godotsharp_variant_new_string_name, + (void *)godotsharp_variant_new_node_path, + (void *)godotsharp_variant_new_object, + (void *)godotsharp_variant_new_transform2d, + (void *)godotsharp_variant_new_vector4, + (void *)godotsharp_variant_new_vector4i, + (void *)godotsharp_variant_new_basis, + (void *)godotsharp_variant_new_transform3d, + (void *)godotsharp_variant_new_projection, + (void *)godotsharp_variant_new_aabb, + (void *)godotsharp_variant_new_dictionary, + (void *)godotsharp_variant_new_array, + (void *)godotsharp_variant_new_packed_byte_array, + (void *)godotsharp_variant_new_packed_int32_array, + (void *)godotsharp_variant_new_packed_int64_array, + (void *)godotsharp_variant_new_packed_float32_array, + (void *)godotsharp_variant_new_packed_float64_array, + (void *)godotsharp_variant_new_packed_string_array, + (void *)godotsharp_variant_new_packed_vector2_array, + (void *)godotsharp_variant_new_packed_vector3_array, + (void *)godotsharp_variant_new_packed_color_array, + (void *)godotsharp_variant_as_bool, + (void *)godotsharp_variant_as_int, + (void *)godotsharp_variant_as_float, + (void *)godotsharp_variant_as_string, + (void *)godotsharp_variant_as_vector2, + (void *)godotsharp_variant_as_vector2i, + (void *)godotsharp_variant_as_rect2, + (void *)godotsharp_variant_as_rect2i, + (void *)godotsharp_variant_as_vector3, + (void *)godotsharp_variant_as_vector3i, + (void *)godotsharp_variant_as_transform2d, + (void *)godotsharp_variant_as_vector4, + (void *)godotsharp_variant_as_vector4i, + (void *)godotsharp_variant_as_plane, + (void *)godotsharp_variant_as_quaternion, + (void *)godotsharp_variant_as_aabb, + (void *)godotsharp_variant_as_basis, + (void *)godotsharp_variant_as_transform3d, + (void *)godotsharp_variant_as_projection, + (void *)godotsharp_variant_as_color, + (void *)godotsharp_variant_as_string_name, + (void *)godotsharp_variant_as_node_path, + (void *)godotsharp_variant_as_rid, + (void *)godotsharp_variant_as_callable, + (void *)godotsharp_variant_as_signal, + (void *)godotsharp_variant_as_dictionary, + (void *)godotsharp_variant_as_array, + (void *)godotsharp_variant_as_packed_byte_array, + (void *)godotsharp_variant_as_packed_int32_array, + (void *)godotsharp_variant_as_packed_int64_array, + (void *)godotsharp_variant_as_packed_float32_array, + (void *)godotsharp_variant_as_packed_float64_array, + (void *)godotsharp_variant_as_packed_string_array, + (void *)godotsharp_variant_as_packed_vector2_array, + (void *)godotsharp_variant_as_packed_vector3_array, + (void *)godotsharp_variant_as_packed_color_array, + (void *)godotsharp_string_new_with_utf16_chars, + (void *)godotsharp_string_name_new_copy, + (void *)godotsharp_node_path_new_copy, + (void *)godotsharp_array_new_copy, + (void *)godotsharp_dictionary_new_copy, + (void *)godotsharp_packed_byte_array_destroy, + (void *)godotsharp_packed_int32_array_destroy, + (void *)godotsharp_packed_int64_array_destroy, + (void *)godotsharp_packed_float32_array_destroy, + (void *)godotsharp_packed_float64_array_destroy, + (void *)godotsharp_packed_string_array_destroy, + (void *)godotsharp_packed_vector2_array_destroy, + (void *)godotsharp_packed_vector3_array_destroy, + (void *)godotsharp_packed_color_array_destroy, + (void *)godotsharp_variant_destroy, + (void *)godotsharp_string_destroy, + (void *)godotsharp_string_name_destroy, + (void *)godotsharp_node_path_destroy, + (void *)godotsharp_signal_destroy, + (void *)godotsharp_callable_destroy, + (void *)godotsharp_array_destroy, + (void *)godotsharp_dictionary_destroy +}; diff --git a/modules/mono/glue/scene_tree_glue.cpp b/modules/mono/glue/scene_tree_glue.cpp index c60e7c4869..55a46ad368 100644 --- a/modules/mono/glue/scene_tree_glue.cpp +++ b/modules/mono/glue/scene_tree_glue.cpp @@ -28,8 +28,6 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifdef MONO_GLUE_ENABLED - #include "core/object/class_db.h" #include "core/string/string_name.h" #include "core/variant/array.h" @@ -40,9 +38,10 @@ #include "../mono_gd/gd_mono_marshal.h" #include "../mono_gd/gd_mono_utils.h" -Array *godot_icall_SceneTree_get_nodes_in_group_Generic(SceneTree *ptr, StringName *group, MonoReflectionType *refltype) { +void godot_icall_SceneTree_get_nodes_in_group_Generic(SceneTree *ptr, StringName *group, MonoReflectionType *refltype, Array *r_dest) { + memnew_placement(r_dest, Array); + List<Node *> nodes; - Array ret; // Retrieve all the nodes in the group ptr->get_nodes_in_group(*group, &nodes); @@ -58,7 +57,7 @@ Array *godot_icall_SceneTree_get_nodes_in_group_Generic(SceneTree *ptr, StringNa StringName native_class_name = GDMonoUtils::get_native_godot_class_name(klass); for (int i = 0; i < nodes.size(); ++i) { if (ClassDB::is_parent_class(nodes[i]->get_class(), native_class_name)) { - ret.push_back(nodes[i]); + r_dest->push_back(nodes[i]); } } } else { @@ -69,18 +68,14 @@ Array *godot_icall_SceneTree_get_nodes_in_group_Generic(SceneTree *ptr, StringNa if (si != nullptr) { MonoObject *obj = si->get_mono_object(); if (obj != nullptr && mono_object_get_class(obj) == mono_class) { - ret.push_back(nodes[i]); + r_dest->push_back(nodes[i]); } } } } } - - return memnew(Array(ret)); } void godot_register_scene_tree_icalls() { GDMonoUtils::add_internal_call("Godot.SceneTree::godot_icall_SceneTree_get_nodes_in_group_Generic", godot_icall_SceneTree_get_nodes_in_group_Generic); } - -#endif // MONO_GLUE_ENABLED diff --git a/modules/mono/glue/string_glue.cpp b/modules/mono/glue/string_glue.cpp index fc6b13ceb3..fe1c0b5f8c 100644 --- a/modules/mono/glue/string_glue.cpp +++ b/modules/mono/glue/string_glue.cpp @@ -28,8 +28,6 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifdef MONO_GLUE_ENABLED - #include "core/string/ustring.h" #include "core/templates/vector.h" #include "core/variant/variant.h" @@ -81,5 +79,3 @@ void godot_register_string_icalls() { GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_sha256_text", godot_icall_String_sha256_text); GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_simplify_path", godot_icall_String_simplify_path); } - -#endif // MONO_GLUE_ENABLED diff --git a/modules/mono/glue/string_name_glue.cpp b/modules/mono/glue/string_name_glue.cpp deleted file mode 100644 index 46d15316ba..0000000000 --- a/modules/mono/glue/string_name_glue.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/*************************************************************************/ -/* string_name_glue.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 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. */ -/*************************************************************************/ - -#ifdef MONO_GLUE_ENABLED - -#include "core/string/string_name.h" -#include "core/string/ustring.h" - -#include "../mono_gd/gd_mono_marshal.h" - -StringName *godot_icall_StringName_Ctor(MonoString *p_path) { - return memnew(StringName(GDMonoMarshal::mono_string_to_godot(p_path))); -} - -void godot_icall_StringName_Dtor(StringName *p_ptr) { - ERR_FAIL_NULL(p_ptr); - memdelete(p_ptr); -} - -MonoString *godot_icall_StringName_operator_String(StringName *p_np) { - return GDMonoMarshal::mono_string_from_godot(p_np->operator String()); -} - -MonoBoolean godot_icall_StringName_is_empty(StringName *p_ptr) { - return (MonoBoolean)(*p_ptr == StringName()); -} - -void godot_register_string_name_icalls() { - GDMonoUtils::add_internal_call("Godot.StringName::godot_icall_StringName_Ctor", godot_icall_StringName_Ctor); - GDMonoUtils::add_internal_call("Godot.StringName::godot_icall_StringName_Dtor", godot_icall_StringName_Dtor); - GDMonoUtils::add_internal_call("Godot.StringName::godot_icall_StringName_operator_String", godot_icall_StringName_operator_String); - GDMonoUtils::add_internal_call("Godot.StringName::godot_icall_StringName_is_empty", godot_icall_StringName_is_empty); -} - -#endif // MONO_GLUE_ENABLED diff --git a/modules/mono/godotsharp_defs.h b/modules/mono/godotsharp_defs.h index e5f1abe8d7..a637e0be9b 100644 --- a/modules/mono/godotsharp_defs.h +++ b/modules/mono/godotsharp_defs.h @@ -34,8 +34,8 @@ #define BINDINGS_NAMESPACE "Godot" #define BINDINGS_NAMESPACE_COLLECTIONS BINDINGS_NAMESPACE ".Collections" #define BINDINGS_GLOBAL_SCOPE_CLASS "GD" -#define BINDINGS_PTR_FIELD "ptr" -#define BINDINGS_NATIVE_NAME_FIELD "nativeName" +#define BINDINGS_PTR_FIELD "NativePtr" +#define BINDINGS_NATIVE_NAME_FIELD "NativeName" #define API_SOLUTION_NAME "GodotSharp" #define CORE_API_ASSEMBLY_NAME "GodotSharp" #define EDITOR_API_ASSEMBLY_NAME "GodotSharpEditor" diff --git a/modules/mono/interop_types.h b/modules/mono/interop_types.h new file mode 100644 index 0000000000..6942a91559 --- /dev/null +++ b/modules/mono/interop_types.h @@ -0,0 +1,208 @@ +/*************************************************************************/ +/* interop_types.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 INTEROP_TYPES_H +#define INTEROP_TYPES_H + +#include "core/math/math_defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdbool.h> +#include <stdint.h> + +// This is taken from the old GDNative, which was removed. + +#define GODOT_VARIANT_SIZE (sizeof(real_t) * 4 + sizeof(int64_t)) + +typedef struct { + uint8_t _dont_touch_that[GODOT_VARIANT_SIZE]; +} godot_variant; + +#define GODOT_ARRAY_SIZE sizeof(void *) + +typedef struct { + uint8_t _dont_touch_that[GODOT_ARRAY_SIZE]; +} godot_array; + +#define GODOT_DICTIONARY_SIZE sizeof(void *) + +typedef struct { + uint8_t _dont_touch_that[GODOT_DICTIONARY_SIZE]; +} godot_dictionary; + +#define GODOT_STRING_SIZE sizeof(void *) + +typedef struct { + uint8_t _dont_touch_that[GODOT_STRING_SIZE]; +} godot_string; + +#define GODOT_STRING_NAME_SIZE sizeof(void *) + +typedef struct { + uint8_t _dont_touch_that[GODOT_STRING_NAME_SIZE]; +} godot_string_name; + +#define GODOT_PACKED_ARRAY_SIZE (2 * sizeof(void *)) + +typedef struct { + uint8_t _dont_touch_that[GODOT_PACKED_ARRAY_SIZE]; +} godot_packed_array; + +#define GODOT_VECTOR2_SIZE (sizeof(real_t) * 2) + +typedef struct { + uint8_t _dont_touch_that[GODOT_VECTOR2_SIZE]; +} godot_vector2; + +#define GODOT_VECTOR2I_SIZE (sizeof(int32_t) * 2) + +typedef struct { + uint8_t _dont_touch_that[GODOT_VECTOR2I_SIZE]; +} godot_vector2i; + +#define GODOT_RECT2_SIZE (sizeof(real_t) * 4) + +typedef struct godot_rect2 { + uint8_t _dont_touch_that[GODOT_RECT2_SIZE]; +} godot_rect2; + +#define GODOT_RECT2I_SIZE (sizeof(int32_t) * 4) + +typedef struct godot_rect2i { + uint8_t _dont_touch_that[GODOT_RECT2I_SIZE]; +} godot_rect2i; + +#define GODOT_VECTOR3_SIZE (sizeof(real_t) * 3) + +typedef struct { + uint8_t _dont_touch_that[GODOT_VECTOR3_SIZE]; +} godot_vector3; + +#define GODOT_VECTOR3I_SIZE (sizeof(int32_t) * 3) + +typedef struct { + uint8_t _dont_touch_that[GODOT_VECTOR3I_SIZE]; +} godot_vector3i; + +#define GODOT_TRANSFORM2D_SIZE (sizeof(real_t) * 6) + +typedef struct { + uint8_t _dont_touch_that[GODOT_TRANSFORM2D_SIZE]; +} godot_transform2d; + +#define GODOT_VECTOR4_SIZE (sizeof(real_t) * 4) + +typedef struct { + uint8_t _dont_touch_that[GODOT_VECTOR4_SIZE]; +} godot_vector4; + +#define GODOT_VECTOR4I_SIZE (sizeof(int32_t) * 4) + +typedef struct { + uint8_t _dont_touch_that[GODOT_VECTOR4I_SIZE]; +} godot_vector4i; + +#define GODOT_PLANE_SIZE (sizeof(real_t) * 4) + +typedef struct { + uint8_t _dont_touch_that[GODOT_PLANE_SIZE]; +} godot_plane; + +#define GODOT_QUATERNION_SIZE (sizeof(real_t) * 4) + +typedef struct { + uint8_t _dont_touch_that[GODOT_QUATERNION_SIZE]; +} godot_quaternion; + +#define GODOT_AABB_SIZE (sizeof(real_t) * 6) + +typedef struct { + uint8_t _dont_touch_that[GODOT_AABB_SIZE]; +} godot_aabb; + +#define GODOT_BASIS_SIZE (sizeof(real_t) * 9) + +typedef struct { + uint8_t _dont_touch_that[GODOT_BASIS_SIZE]; +} godot_basis; + +#define GODOT_TRANSFORM3D_SIZE (sizeof(real_t) * 12) + +typedef struct { + uint8_t _dont_touch_that[GODOT_TRANSFORM3D_SIZE]; +} godot_transform3d; + +#define GODOT_PROJECTION_SIZE (sizeof(real_t) * 4 * 4) + +typedef struct { + uint8_t _dont_touch_that[GODOT_PROJECTION_SIZE]; +} godot_projection; + +// Colors should always use 32-bit floats, so don't use real_t here. +#define GODOT_COLOR_SIZE (sizeof(float) * 4) + +typedef struct { + uint8_t _dont_touch_that[GODOT_COLOR_SIZE]; +} godot_color; + +#define GODOT_NODE_PATH_SIZE sizeof(void *) + +typedef struct { + uint8_t _dont_touch_that[GODOT_NODE_PATH_SIZE]; +} godot_node_path; + +#define GODOT_RID_SIZE sizeof(uint64_t) + +typedef struct { + uint8_t _dont_touch_that[GODOT_RID_SIZE]; +} godot_rid; + +// Alignment hardcoded in `core/variant/callable.h`. +#define GODOT_CALLABLE_SIZE (16) + +typedef struct { + uint8_t _dont_touch_that[GODOT_CALLABLE_SIZE]; +} godot_callable; + +// Alignment hardcoded in `core/variant/callable.h`. +#define GODOT_SIGNAL_SIZE (16) + +typedef struct { + uint8_t _dont_touch_that[GODOT_SIGNAL_SIZE]; +} godot_signal; + +#ifdef __cplusplus +} +#endif + +#endif // INTEROP_TYPES_H diff --git a/modules/mono/managed_callable.cpp b/modules/mono/managed_callable.cpp index c159bb9eea..72b353d8c1 100644 --- a/modules/mono/managed_callable.cpp +++ b/modules/mono/managed_callable.cpp @@ -31,6 +31,7 @@ #include "managed_callable.h" #include "csharp_script.h" +#include "mono_gd/gd_mono_cache.h" #include "mono_gd/gd_mono_marshal.h" #include "mono_gd/gd_mono_utils.h" @@ -44,18 +45,20 @@ bool ManagedCallable::compare_equal(const CallableCustom *p_a, const CallableCus const ManagedCallable *a = static_cast<const ManagedCallable *>(p_a); const ManagedCallable *b = static_cast<const ManagedCallable *>(p_b); - MonoDelegate *delegate_a = (MonoDelegate *)a->delegate_handle.get_target(); - MonoDelegate *delegate_b = (MonoDelegate *)b->delegate_handle.get_target(); - - if (!delegate_a || !delegate_b) { - if (!delegate_a && !delegate_b) { + if (!a->delegate_handle || !b->delegate_handle) { + if (!a->delegate_handle && !b->delegate_handle) { return true; } return false; } // Call Delegate's 'Equals' - return GDMonoUtils::mono_delegate_equal(delegate_a, delegate_b); + MonoException *exc = nullptr; + MonoBoolean res = CACHED_METHOD_THUNK(DelegateUtils, DelegateEquals) + .invoke(a->delegate_handle, + b->delegate_handle, &exc); + UNHANDLED_EXCEPTION(exc); + return (bool)res; } bool ManagedCallable::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) { @@ -66,8 +69,7 @@ bool ManagedCallable::compare_less(const CallableCustom *p_a, const CallableCust } uint32_t ManagedCallable::hash() const { - uint32_t hash = delegate_invoke->get_name().hash(); - return hash_murmur3_one_64(delegate_handle.handle, hash); + return hash_murmur3_one_64((uint64_t)delegate_handle); } String ManagedCallable::get_as_text() const { @@ -91,41 +93,34 @@ void ManagedCallable::call(const Variant **p_arguments, int p_argcount, Variant r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; // Can't find anything better r_return_value = Variant(); -#ifdef GD_MONO_HOT_RELOAD - // Lost during hot-reload - ERR_FAIL_NULL(delegate_invoke); - ERR_FAIL_COND(delegate_handle.is_released()); -#endif - - ERR_FAIL_COND(delegate_invoke->get_parameters_count() < p_argcount); - - MonoObject *delegate = delegate_handle.get_target(); - MonoException *exc = nullptr; - MonoObject *ret = delegate_invoke->invoke(delegate, p_arguments, &exc); + CACHED_METHOD_THUNK(DelegateUtils, InvokeWithVariantArgs) + .invoke(delegate_handle, p_arguments, + p_argcount, &r_return_value, &exc); if (exc) { GDMonoUtils::set_pending_exception(exc); } else { - r_return_value = GDMonoMarshal::mono_object_to_variant(ret); r_call_error.error = Callable::CallError::CALL_OK; } } -void ManagedCallable::set_delegate(MonoDelegate *p_delegate) { - delegate_handle = MonoGCHandleData::new_strong_handle((MonoObject *)p_delegate); - MonoMethod *delegate_invoke_raw = mono_get_delegate_invoke(mono_object_get_class((MonoObject *)p_delegate)); - const StringName &delegate_invoke_name = CSharpLanguage::get_singleton()->get_string_names().delegate_invoke_method_name; - delegate_invoke = memnew(GDMonoMethod(delegate_invoke_name, delegate_invoke_raw)); // TODO: Use pooling for this GDMonoMethod instances -} +void ManagedCallable::release_delegate_handle() { + if (delegate_handle) { + MonoException *exc = nullptr; + CACHED_METHOD_THUNK(DelegateUtils, FreeGCHandle).invoke(delegate_handle, &exc); -ManagedCallable::ManagedCallable(MonoDelegate *p_delegate) { -#ifdef DEBUG_ENABLED - CRASH_COND(p_delegate == nullptr); -#endif + if (exc) { + GDMonoUtils::debug_print_unhandled_exception(exc); + } - set_delegate(p_delegate); + delegate_handle = nullptr; + } +} +// Why you do this clang-format... +/* clang-format off */ +ManagedCallable::ManagedCallable(void *p_delegate_handle) : delegate_handle(p_delegate_handle) { #ifdef GD_MONO_HOT_RELOAD { MutexLock lock(instances_mutex); @@ -133,6 +128,7 @@ ManagedCallable::ManagedCallable(MonoDelegate *p_delegate) { } #endif } +/* clang-format on */ ManagedCallable::~ManagedCallable() { #ifdef GD_MONO_HOT_RELOAD @@ -143,5 +139,5 @@ ManagedCallable::~ManagedCallable() { } #endif - delegate_handle.release(); + release_delegate_handle(); } diff --git a/modules/mono/managed_callable.h b/modules/mono/managed_callable.h index 11bee6cf60..56c11e7e45 100644 --- a/modules/mono/managed_callable.h +++ b/modules/mono/managed_callable.h @@ -42,8 +42,7 @@ class ManagedCallable : public CallableCustom { friend class CSharpLanguage; - MonoGCHandleData delegate_handle; - GDMonoMethod *delegate_invoke = nullptr; + void *delegate_handle = nullptr; #ifdef GD_MONO_HOT_RELOAD SelfList<ManagedCallable> self_instance = this; @@ -60,9 +59,7 @@ public: ObjectID get_object() const override; void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override; - _FORCE_INLINE_ MonoDelegate *get_delegate() { return (MonoDelegate *)delegate_handle.get_target(); } - - void set_delegate(MonoDelegate *p_delegate); + _FORCE_INLINE_ void *get_delegate() const { return delegate_handle; } static bool compare_equal(const CallableCustom *p_a, const CallableCustom *p_b); static bool compare_less(const CallableCustom *p_a, const CallableCustom *p_b); @@ -70,7 +67,9 @@ public: static constexpr CompareEqualFunc compare_equal_func_ptr = &ManagedCallable::compare_equal; static constexpr CompareEqualFunc compare_less_func_ptr = &ManagedCallable::compare_less; - ManagedCallable(MonoDelegate *p_delegate); + void release_delegate_handle(); + + ManagedCallable(void *p_delegate_handle); ~ManagedCallable(); }; diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index d3d3bb2bef..eeaec30d73 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -408,10 +408,6 @@ void GDMono::initialize() { } void GDMono::initialize_load_assemblies() { -#ifndef MONO_GLUE_ENABLED - CRASH_NOW_MSG("Mono: This binary was built with 'mono_glue=no'; cannot load assemblies."); -#endif - // Load assemblies. The API and tools assemblies are required, // the application is aborted if these assemblies cannot be loaded. @@ -446,59 +442,34 @@ bool GDMono::_are_api_assemblies_out_of_sync() { return out_of_sync; } -namespace GodotSharpBindings { -#ifdef MONO_GLUE_ENABLED - -uint64_t get_core_api_hash(); -#ifdef TOOLS_ENABLED -uint64_t get_editor_api_hash(); -#endif -uint32_t get_bindings_version(); -uint32_t get_cs_glue_version(); - -void register_generated_icalls(); - -#else - -uint64_t get_core_api_hash() { - GD_UNREACHABLE(); -} -#ifdef TOOLS_ENABLED -uint64_t get_editor_api_hash() { - GD_UNREACHABLE(); -} -#endif -uint32_t get_bindings_version() { - GD_UNREACHABLE(); -} - -uint32_t get_cs_glue_version() { - GD_UNREACHABLE(); -} - -void register_generated_icalls() { - /* Fine, just do nothing */ -} - -#endif // MONO_GLUE_ENABLED -} // namespace GodotSharpBindings +void godot_register_collections_icalls(); +void godot_register_gd_icalls(); +void godot_register_node_path_icalls(); +void godot_register_object_icalls(); +void godot_register_rid_icalls(); +void godot_register_string_icalls(); +void godot_register_scene_tree_icalls(); +void godot_register_placeholder_icalls(); void GDMono::_register_internal_calls() { - GodotSharpBindings::register_generated_icalls(); + // Registers internal calls that were not generated. + godot_register_collections_icalls(); + godot_register_gd_icalls(); + godot_register_node_path_icalls(); + godot_register_object_icalls(); + godot_register_string_icalls(); + godot_register_scene_tree_icalls(); + godot_register_placeholder_icalls(); } void GDMono::_init_godot_api_hashes() { -#if defined(MONO_GLUE_ENABLED) && defined(DEBUG_METHODS_ENABLED) - if (get_api_core_hash() != GodotSharpBindings::get_core_api_hash()) { - ERR_PRINT("Mono: Core API hash mismatch."); - } +#ifdef DEBUG_METHODS_ENABLED + get_api_core_hash(); #ifdef TOOLS_ENABLED - if (get_api_editor_hash() != GodotSharpBindings::get_editor_api_hash()) { - ERR_PRINT("Mono: Editor API hash mismatch."); - } + get_api_editor_hash(); #endif // TOOLS_ENABLED -#endif // MONO_GLUE_ENABLED && DEBUG_METHODS_ENABLED +#endif // DEBUG_METHODS_ENABLED } void GDMono::_init_exception_policy() { @@ -601,16 +572,6 @@ ApiAssemblyInfo::Version ApiAssemblyInfo::Version::get_from_loaded_assembly(GDMo if (api_hash_field) { api_assembly_version.godot_api_hash = GDMonoMarshal::unbox<uint64_t>(api_hash_field->get_value(nullptr)); } - - GDMonoField *binds_ver_field = nativecalls_klass->get_field("bindings_version"); - if (binds_ver_field) { - api_assembly_version.bindings_version = GDMonoMarshal::unbox<uint32_t>(binds_ver_field->get_value(nullptr)); - } - - GDMonoField *cs_glue_ver_field = nativecalls_klass->get_field("cs_glue_version"); - if (cs_glue_ver_field) { - api_assembly_version.cs_glue_version = GDMonoMarshal::unbox<uint32_t>(cs_glue_ver_field->get_value(nullptr)); - } } return api_assembly_version; @@ -698,12 +659,8 @@ static bool try_get_cached_api_hash_for(const String &p_api_assemblies_dir, bool return false; } - r_out_of_sync = GodotSharpBindings::get_bindings_version() != (uint32_t)cfg->get_value("core", "bindings_version") || - GodotSharpBindings::get_cs_glue_version() != (uint32_t)cfg->get_value("core", "cs_glue_version") || - GodotSharpBindings::get_bindings_version() != (uint32_t)cfg->get_value("editor", "bindings_version") || - GodotSharpBindings::get_cs_glue_version() != (uint32_t)cfg->get_value("editor", "cs_glue_version") || - GodotSharpBindings::get_core_api_hash() != (uint64_t)cfg->get_value("core", "api_hash") || - GodotSharpBindings::get_editor_api_hash() != (uint64_t)cfg->get_value("editor", "api_hash"); + r_out_of_sync = GDMono::get_singleton()->get_api_core_hash() != (uint64_t)cfg->get_value("core", "api_hash") || + GDMono::get_singleton()->get_api_editor_hash() != (uint64_t)cfg->get_value("editor", "api_hash"); return true; } @@ -719,14 +676,9 @@ static void create_cached_api_hash_for(const String &p_api_assemblies_dir) { cfg->set_value("core", "modified_time", FileAccess::get_modified_time(core_api_assembly_path)); cfg->set_value("editor", "modified_time", FileAccess::get_modified_time(editor_api_assembly_path)); - cfg->set_value("core", "bindings_version", GodotSharpBindings::get_bindings_version()); - cfg->set_value("core", "cs_glue_version", GodotSharpBindings::get_cs_glue_version()); - cfg->set_value("editor", "bindings_version", GodotSharpBindings::get_bindings_version()); - cfg->set_value("editor", "cs_glue_version", GodotSharpBindings::get_cs_glue_version()); - // This assumes the prebuilt api assemblies we copied to the project are not out of sync - cfg->set_value("core", "api_hash", GodotSharpBindings::get_core_api_hash()); - cfg->set_value("editor", "api_hash", GodotSharpBindings::get_editor_api_hash()); + cfg->set_value("core", "api_hash", GDMono::get_singleton()->get_api_core_hash()); + cfg->set_value("editor", "api_hash", GDMono::get_singleton()->get_api_editor_hash()); Error err = cfg->save(cached_api_hash_path); ERR_FAIL_COND(err != OK); @@ -764,7 +716,7 @@ String GDMono::update_api_assemblies_from_prebuilt(const String &p_config, const bool api_assemblies_out_of_sync = false; if (p_core_api_out_of_sync && p_editor_api_out_of_sync) { - api_assemblies_out_of_sync = p_core_api_out_of_sync || p_editor_api_out_of_sync; + api_assemblies_out_of_sync = *p_core_api_out_of_sync || *p_editor_api_out_of_sync; } else if (FileAccess::exists(core_assembly_path) && FileAccess::exists(editor_assembly_path)) { // Determine if they're out of sync if (!try_get_cached_api_hash_for(dst_assemblies_dir, api_assemblies_out_of_sync)) { @@ -824,14 +776,16 @@ bool GDMono::_load_core_api_assembly(LoadedApiAssembly &r_loaded_api_assembly, c bool success = load_assembly(CORE_API_ASSEMBLY_NAME, &r_loaded_api_assembly.assembly, p_refonly); #endif +#ifdef DEBUG_METHODS_ENABLED if (success) { ApiAssemblyInfo::Version api_assembly_ver = ApiAssemblyInfo::Version::get_from_loaded_assembly(r_loaded_api_assembly.assembly, ApiAssemblyInfo::API_CORE); - r_loaded_api_assembly.out_of_sync = GodotSharpBindings::get_core_api_hash() != api_assembly_ver.godot_api_hash || - GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version || - GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version; + r_loaded_api_assembly.out_of_sync = get_api_core_hash() != api_assembly_ver.godot_api_hash; } else { r_loaded_api_assembly.out_of_sync = false; } +#else + r_loaded_api_assembly.out_of_sync = false; +#endif return success; } @@ -854,14 +808,16 @@ bool GDMono::_load_editor_api_assembly(LoadedApiAssembly &r_loaded_api_assembly, bool success = FileAccess::exists(assembly_path) && load_assembly_from(EDITOR_API_ASSEMBLY_NAME, assembly_path, &r_loaded_api_assembly.assembly, p_refonly); +#ifdef DEBUG_METHODS_ENABLED if (success) { ApiAssemblyInfo::Version api_assembly_ver = ApiAssemblyInfo::Version::get_from_loaded_assembly(r_loaded_api_assembly.assembly, ApiAssemblyInfo::API_EDITOR); - r_loaded_api_assembly.out_of_sync = GodotSharpBindings::get_editor_api_hash() != api_assembly_ver.godot_api_hash || - GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version || - GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version; + r_loaded_api_assembly.out_of_sync = get_api_editor_hash() != api_assembly_ver.godot_api_hash; } else { r_loaded_api_assembly.out_of_sync = false; } +#else + r_loaded_api_assembly.out_of_sync = false; +#endif return success; } @@ -1091,13 +1047,13 @@ Error GDMono::_unload_scripts_domain() { Error GDMono::reload_scripts_domain() { ERR_FAIL_COND_V(!runtime_initialized, ERR_BUG); + CSharpLanguage::get_singleton()->_on_scripts_domain_about_to_unload(); + if (scripts_domain) { Error domain_unload_err = _unload_scripts_domain(); ERR_FAIL_COND_V_MSG(domain_unload_err != OK, domain_unload_err, "Mono: Failed to unload scripts domain."); } - CSharpLanguage::get_singleton()->_on_scripts_domain_unloaded(); - Error domain_load_err = _load_scripts_domain(); ERR_FAIL_COND_V_MSG(domain_load_err != OK, domain_load_err, "Mono: Failed to load scripts domain."); diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h index 51fd0f8483..bc871fe750 100644 --- a/modules/mono/mono_gd/gd_mono.h +++ b/modules/mono/mono_gd/gd_mono.h @@ -49,23 +49,15 @@ enum Type { struct Version { uint64_t godot_api_hash = 0; - uint32_t bindings_version = 0; - uint32_t cs_glue_version = 0; bool operator==(const Version &p_other) const { - return godot_api_hash == p_other.godot_api_hash && - bindings_version == p_other.bindings_version && - cs_glue_version == p_other.cs_glue_version; + return godot_api_hash == p_other.godot_api_hash; } Version() {} - Version(uint64_t p_godot_api_hash, - uint32_t p_bindings_version, - uint32_t p_cs_glue_version) : - godot_api_hash(p_godot_api_hash), - bindings_version(p_bindings_version), - cs_glue_version(p_cs_glue_version) { + Version(uint64_t p_godot_api_hash) : + godot_api_hash(p_godot_api_hash) { } static Version get_from_loaded_assembly(GDMonoAssembly *p_api_assembly, Type p_api_type); diff --git a/modules/mono/mono_gd/gd_mono_cache.cpp b/modules/mono/mono_gd/gd_mono_cache.cpp index 69d8c7edc9..08660e3701 100644 --- a/modules/mono/mono_gd/gd_mono_cache.cpp +++ b/modules/mono/mono_gd/gd_mono_cache.cpp @@ -48,8 +48,6 @@ CachedData cached_data; } #define CACHE_CLASS_AND_CHECK(m_class, m_val) CACHE_AND_CHECK(cached_data.class_##m_class, m_val) -#define CACHE_NS_CLASS_AND_CHECK(m_ns, m_class, m_val) CACHE_AND_CHECK(cached_data.class_##m_ns##_##m_class, m_val) -#define CACHE_RAW_MONO_CLASS_AND_CHECK(m_class, m_val) CACHE_AND_CHECK(cached_data.rawclass_##m_class, m_val) #define CACHE_FIELD_AND_CHECK(m_class, m_field, m_val) CACHE_AND_CHECK(cached_data.field_##m_class##_##m_field, m_val) #define CACHE_METHOD_AND_CHECK(m_class, m_method, m_val) CACHE_AND_CHECK(cached_data.method_##m_class##_##m_method, m_val) #define CACHE_PROPERTY_AND_CHECK(m_class, m_property, m_val) CACHE_AND_CHECK(cached_data.property_##m_class##_##m_property, m_val) @@ -68,23 +66,7 @@ void CachedData::clear_corlib_cache() { corlib_cache_updated = false; class_MonoObject = nullptr; - class_bool = nullptr; - class_int8_t = nullptr; - class_int16_t = nullptr; - class_int32_t = nullptr; - class_int64_t = nullptr; - class_uint8_t = nullptr; - class_uint16_t = nullptr; - class_uint32_t = nullptr; - class_uint64_t = nullptr; - class_float = nullptr; - class_double = nullptr; class_String = nullptr; - class_IntPtr = nullptr; - - class_System_Collections_IEnumerable = nullptr; - class_System_Collections_ICollection = nullptr; - class_System_Collections_IDictionary = nullptr; #ifdef DEBUG_ENABLED class_System_Diagnostics_StackTrace = nullptr; @@ -99,38 +81,12 @@ void CachedData::clear_corlib_cache() { void CachedData::clear_godot_api_cache() { godot_api_cache_updated = false; - rawclass_Dictionary = nullptr; - - class_Vector2 = nullptr; - class_Vector2i = nullptr; - class_Rect2 = nullptr; - class_Rect2i = nullptr; - class_Transform2D = nullptr; - class_Vector3 = nullptr; - class_Vector3i = nullptr; - class_Vector4 = nullptr; - class_Vector4i = nullptr; - class_Basis = nullptr; - class_Quaternion = nullptr; - class_Transform3D = nullptr; - class_Projection = nullptr; - class_AABB = nullptr; - class_Color = nullptr; - class_Plane = nullptr; - class_StringName = nullptr; - class_NodePath = nullptr; - class_RID = nullptr; class_GodotObject = nullptr; class_GodotResource = nullptr; class_Node = nullptr; class_Control = nullptr; - class_Node3D = nullptr; - class_WeakRef = nullptr; class_Callable = nullptr; class_SignalInfo = nullptr; - class_Array = nullptr; - class_Dictionary = nullptr; - class_MarshalUtils = nullptr; class_ISerializationListener = nullptr; #ifdef DEBUG_ENABLED @@ -157,68 +113,39 @@ void CachedData::clear_godot_api_cache() { field_AssemblyHasScriptsAttribute_scriptTypes = nullptr; field_GodotObject_ptr = nullptr; - field_StringName_ptr = nullptr; - field_NodePath_ptr = nullptr; - field_Image_ptr = nullptr; - field_RID_ptr = nullptr; methodthunk_GodotObject_Dispose.nullify(); - methodthunk_Array_GetPtr.nullify(); - methodthunk_Dictionary_GetPtr.nullify(); methodthunk_SignalAwaiter_SignalCallback.nullify(); methodthunk_GodotTaskScheduler_Activate.nullify(); methodthunk_Delegate_Equals.nullify(); + methodthunk_DelegateUtils_TrySerializeDelegateWithGCHandle.nullify(); + methodthunk_DelegateUtils_TryDeserializeDelegateWithGCHandle.nullify(); methodthunk_DelegateUtils_TrySerializeDelegate.nullify(); methodthunk_DelegateUtils_TryDeserializeDelegate.nullify(); + methodthunk_DelegateUtils_InvokeWithVariantArgs.nullify(); + methodthunk_DelegateUtils_DelegateEquals.nullify(); + methodthunk_DelegateUtils_FreeGCHandle.nullify(); - // Start of MarshalUtils methods - - methodthunk_MarshalUtils_TypeIsGenericArray.nullify(); - methodthunk_MarshalUtils_TypeIsGenericDictionary.nullify(); - methodthunk_MarshalUtils_TypeIsSystemGenericList.nullify(); - methodthunk_MarshalUtils_TypeIsSystemGenericDictionary.nullify(); - methodthunk_MarshalUtils_TypeIsGenericIEnumerable.nullify(); - methodthunk_MarshalUtils_TypeIsGenericICollection.nullify(); - methodthunk_MarshalUtils_TypeIsGenericIDictionary.nullify(); - methodthunk_MarshalUtils_TypeHasFlagsAttribute.nullify(); - - methodthunk_MarshalUtils_GetGenericTypeDefinition.nullify(); - - methodthunk_MarshalUtils_ArrayGetElementType.nullify(); - methodthunk_MarshalUtils_DictionaryGetKeyValueTypes.nullify(); + methodthunk_Marshaling_managed_to_variant_type.nullify(); + methodthunk_Marshaling_try_get_array_element_type.nullify(); + methodthunk_Marshaling_variant_to_mono_object_of_type.nullify(); + methodthunk_Marshaling_variant_to_mono_object.nullify(); + methodthunk_Marshaling_mono_object_to_variant_out.nullify(); - methodthunk_MarshalUtils_MakeGenericArrayType.nullify(); - methodthunk_MarshalUtils_MakeGenericDictionaryType.nullify(); + methodthunk_Marshaling_SetFieldValue.nullify(); - // End of MarshalUtils methods + methodthunk_MarshalUtils_TypeHasFlagsAttribute.nullify(); task_scheduler_handle = Ref<MonoGCHandleRef>(); } #define GODOT_API_CLASS(m_class) (GDMono::get_singleton()->get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, #m_class)) -#define GODOT_API_NS_CLASS(m_ns, m_class) (GDMono::get_singleton()->get_core_api_assembly()->get_class(m_ns, #m_class)) void update_corlib_cache() { CACHE_CLASS_AND_CHECK(MonoObject, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_object_class())); - CACHE_CLASS_AND_CHECK(bool, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_boolean_class())); - CACHE_CLASS_AND_CHECK(int8_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_sbyte_class())); - CACHE_CLASS_AND_CHECK(int16_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_int16_class())); - CACHE_CLASS_AND_CHECK(int32_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_int32_class())); - CACHE_CLASS_AND_CHECK(int64_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_int64_class())); - CACHE_CLASS_AND_CHECK(uint8_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_byte_class())); - CACHE_CLASS_AND_CHECK(uint16_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_uint16_class())); - CACHE_CLASS_AND_CHECK(uint32_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_uint32_class())); - CACHE_CLASS_AND_CHECK(uint64_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_uint64_class())); - CACHE_CLASS_AND_CHECK(float, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_single_class())); - CACHE_CLASS_AND_CHECK(double, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_double_class())); CACHE_CLASS_AND_CHECK(String, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_string_class())); - CACHE_CLASS_AND_CHECK(IntPtr, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_intptr_class())); - - CACHE_CLASS_AND_CHECK(System_Collections_IEnumerable, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections", "IEnumerable")); - CACHE_CLASS_AND_CHECK(System_Collections_ICollection, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections", "ICollection")); - 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")); @@ -235,36 +162,12 @@ void update_corlib_cache() { } void update_godot_api_cache() { - CACHE_CLASS_AND_CHECK(Vector2, GODOT_API_CLASS(Vector2)); - CACHE_CLASS_AND_CHECK(Vector2i, GODOT_API_CLASS(Vector2i)); - CACHE_CLASS_AND_CHECK(Rect2, GODOT_API_CLASS(Rect2)); - CACHE_CLASS_AND_CHECK(Rect2i, GODOT_API_CLASS(Rect2i)); - CACHE_CLASS_AND_CHECK(Transform2D, GODOT_API_CLASS(Transform2D)); - CACHE_CLASS_AND_CHECK(Vector3, GODOT_API_CLASS(Vector3)); - CACHE_CLASS_AND_CHECK(Vector3i, GODOT_API_CLASS(Vector3i)); - CACHE_CLASS_AND_CHECK(Vector4, GODOT_API_CLASS(Vector4)); - CACHE_CLASS_AND_CHECK(Vector4i, GODOT_API_CLASS(Vector4i)); - CACHE_CLASS_AND_CHECK(Basis, GODOT_API_CLASS(Basis)); - CACHE_CLASS_AND_CHECK(Quaternion, GODOT_API_CLASS(Quaternion)); - CACHE_CLASS_AND_CHECK(Transform3D, GODOT_API_CLASS(Transform3D)); - CACHE_CLASS_AND_CHECK(Projection, GODOT_API_CLASS(Projection)); - CACHE_CLASS_AND_CHECK(AABB, GODOT_API_CLASS(AABB)); - CACHE_CLASS_AND_CHECK(Color, GODOT_API_CLASS(Color)); - CACHE_CLASS_AND_CHECK(Plane, GODOT_API_CLASS(Plane)); - CACHE_CLASS_AND_CHECK(StringName, GODOT_API_CLASS(StringName)); - CACHE_CLASS_AND_CHECK(NodePath, GODOT_API_CLASS(NodePath)); - CACHE_CLASS_AND_CHECK(RID, GODOT_API_CLASS(RID)); CACHE_CLASS_AND_CHECK(GodotObject, GODOT_API_CLASS(Object)); CACHE_CLASS_AND_CHECK(GodotResource, GODOT_API_CLASS(Resource)); CACHE_CLASS_AND_CHECK(Node, GODOT_API_CLASS(Node)); CACHE_CLASS_AND_CHECK(Control, GODOT_API_CLASS(Control)); - CACHE_CLASS_AND_CHECK(Node3D, GODOT_API_CLASS(Node3D)); - CACHE_CLASS_AND_CHECK(WeakRef, GODOT_API_CLASS(WeakRef)); CACHE_CLASS_AND_CHECK(Callable, GODOT_API_CLASS(Callable)); CACHE_CLASS_AND_CHECK(SignalInfo, GODOT_API_CLASS(SignalInfo)); - CACHE_CLASS_AND_CHECK(Array, GODOT_API_NS_CLASS(BINDINGS_NAMESPACE_COLLECTIONS, Array)); - CACHE_CLASS_AND_CHECK(Dictionary, GODOT_API_NS_CLASS(BINDINGS_NAMESPACE_COLLECTIONS, Dictionary)); - CACHE_CLASS_AND_CHECK(MarshalUtils, GODOT_API_CLASS(MarshalUtils)); CACHE_CLASS_AND_CHECK(ISerializationListener, GODOT_API_CLASS(ISerializationListener)); #ifdef DEBUG_ENABLED @@ -291,40 +194,41 @@ void update_godot_api_cache() { CACHE_FIELD_AND_CHECK(AssemblyHasScriptsAttribute, scriptTypes, CACHED_CLASS(AssemblyHasScriptsAttribute)->get_field("scriptTypes")); CACHE_FIELD_AND_CHECK(GodotObject, ptr, CACHED_CLASS(GodotObject)->get_field(BINDINGS_PTR_FIELD)); - CACHE_FIELD_AND_CHECK(StringName, ptr, CACHED_CLASS(StringName)->get_field(BINDINGS_PTR_FIELD)); - CACHE_FIELD_AND_CHECK(NodePath, ptr, CACHED_CLASS(NodePath)->get_field(BINDINGS_PTR_FIELD)); - CACHE_FIELD_AND_CHECK(RID, ptr, CACHED_CLASS(RID)->get_field(BINDINGS_PTR_FIELD)); CACHE_METHOD_THUNK_AND_CHECK(GodotObject, Dispose, CACHED_CLASS(GodotObject)->get_method("Dispose", 0)); - CACHE_METHOD_THUNK_AND_CHECK(Array, GetPtr, GODOT_API_NS_CLASS(BINDINGS_NAMESPACE_COLLECTIONS, Array)->get_method("GetPtr", 0)); - CACHE_METHOD_THUNK_AND_CHECK(Dictionary, GetPtr, GODOT_API_NS_CLASS(BINDINGS_NAMESPACE_COLLECTIONS, Dictionary)->get_method("GetPtr", 0)); CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, SignalCallback, GODOT_API_CLASS(SignalAwaiter)->get_method("SignalCallback", 1)); CACHE_METHOD_THUNK_AND_CHECK(GodotTaskScheduler, Activate, GODOT_API_CLASS(GodotTaskScheduler)->get_method("Activate", 0)); + CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, TrySerializeDelegateWithGCHandle, GODOT_API_CLASS(DelegateUtils)->get_method("TrySerializeDelegateWithGCHandle", 2)); + CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, TryDeserializeDelegateWithGCHandle, GODOT_API_CLASS(DelegateUtils)->get_method("TryDeserializeDelegateWithGCHandle", 2)); CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, TrySerializeDelegate, GODOT_API_CLASS(DelegateUtils)->get_method("TrySerializeDelegate", 2)); CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, TryDeserializeDelegate, GODOT_API_CLASS(DelegateUtils)->get_method("TryDeserializeDelegate", 2)); + CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, InvokeWithVariantArgs, GODOT_API_CLASS(DelegateUtils)->get_method("InvokeWithVariantArgs", 4)); + CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, DelegateEquals, GODOT_API_CLASS(DelegateUtils)->get_method("DelegateEquals", 2)); + CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, FreeGCHandle, GODOT_API_CLASS(DelegateUtils)->get_method("FreeGCHandle", 1)); + + GDMonoClass *gd_mono_marshal_class = GDMono::get_singleton()->get_core_api_assembly()->get_class( + "Godot.NativeInterop", "Marshaling"); + + ERR_FAIL_COND_MSG(gd_mono_marshal_class == nullptr, + "Mono Cache: Class `Godot.NativeInterop.Marshaling` not found."); + + CACHE_METHOD_THUNK_AND_CHECK(Marshaling, managed_to_variant_type, + gd_mono_marshal_class->get_method("managed_to_variant_type", 2)); + CACHE_METHOD_THUNK_AND_CHECK(Marshaling, try_get_array_element_type, + gd_mono_marshal_class->get_method("try_get_array_element_type", 2)); + CACHE_METHOD_THUNK_AND_CHECK(Marshaling, variant_to_mono_object_of_type, + gd_mono_marshal_class->get_method("variant_to_mono_object_of_type", 2)); + CACHE_METHOD_THUNK_AND_CHECK(Marshaling, variant_to_mono_object, + gd_mono_marshal_class->get_method("variant_to_mono_object", 1)); + CACHE_METHOD_THUNK_AND_CHECK(Marshaling, mono_object_to_variant_out, + gd_mono_marshal_class->get_method("mono_object_to_variant_out", 3)); + + CACHE_METHOD_THUNK_AND_CHECK(Marshaling, SetFieldValue, + gd_mono_marshal_class->get_method("SetFieldValue", 3)); - // Start of MarshalUtils methods - - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericArray, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericArray", 1)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericDictionary, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericDictionary", 1)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsSystemGenericList, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsSystemGenericList", 1)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsSystemGenericDictionary, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsSystemGenericDictionary", 1)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericIEnumerable, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericIEnumerable", 1)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericICollection, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericICollection", 1)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericIDictionary, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericIDictionary", 1)); CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeHasFlagsAttribute, GODOT_API_CLASS(MarshalUtils)->get_method("TypeHasFlagsAttribute", 1)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GetGenericTypeDefinition, GODOT_API_CLASS(MarshalUtils)->get_method("GetGenericTypeDefinition", 2)); - - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, ArrayGetElementType, GODOT_API_CLASS(MarshalUtils)->get_method("ArrayGetElementType", 2)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, DictionaryGetKeyValueTypes, GODOT_API_CLASS(MarshalUtils)->get_method("DictionaryGetKeyValueTypes", 3)); - - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, MakeGenericArrayType, GODOT_API_CLASS(MarshalUtils)->get_method("MakeGenericArrayType", 1)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, MakeGenericDictionaryType, GODOT_API_CLASS(MarshalUtils)->get_method("MakeGenericDictionaryType", 2)); - - // End of MarshalUtils methods - #ifdef DEBUG_ENABLED CACHE_METHOD_THUNK_AND_CHECK(DebuggingUtils, GetStackFrameInfo, GODOT_API_CLASS(DebuggingUtils)->get_method("GetStackFrameInfo", 4)); #endif diff --git a/modules/mono/mono_gd/gd_mono_cache.h b/modules/mono/mono_gd/gd_mono_cache.h index e9cc26899e..0db32b5885 100644 --- a/modules/mono/mono_gd/gd_mono_cache.h +++ b/modules/mono/mono_gd/gd_mono_cache.h @@ -42,23 +42,7 @@ struct CachedData { // Let's use the no-namespace format for these too GDMonoClass *class_MonoObject = nullptr; // object - GDMonoClass *class_bool = nullptr; // bool - GDMonoClass *class_int8_t = nullptr; // sbyte - GDMonoClass *class_int16_t = nullptr; // short - GDMonoClass *class_int32_t = nullptr; // int - GDMonoClass *class_int64_t = nullptr; // long - GDMonoClass *class_uint8_t = nullptr; // byte - GDMonoClass *class_uint16_t = nullptr; // ushort - GDMonoClass *class_uint32_t = nullptr; // uint - GDMonoClass *class_uint64_t = nullptr; // ulong - GDMonoClass *class_float = nullptr; // float - GDMonoClass *class_double = nullptr; // double GDMonoClass *class_String = nullptr; // string - GDMonoClass *class_IntPtr = nullptr; // System.IntPtr - - GDMonoClass *class_System_Collections_IEnumerable = nullptr; - GDMonoClass *class_System_Collections_ICollection = nullptr; - GDMonoClass *class_System_Collections_IDictionary = nullptr; #ifdef DEBUG_ENABLED GDMonoClass *class_System_Diagnostics_StackTrace = nullptr; @@ -68,40 +52,14 @@ struct CachedData { #endif GDMonoClass *class_KeyNotFoundException = nullptr; - - MonoClass *rawclass_Dictionary = nullptr; // ----------------------------------------------- - GDMonoClass *class_Vector2 = nullptr; - GDMonoClass *class_Vector2i = nullptr; - GDMonoClass *class_Rect2 = nullptr; - GDMonoClass *class_Rect2i = nullptr; - GDMonoClass *class_Transform2D = nullptr; - GDMonoClass *class_Vector3 = nullptr; - GDMonoClass *class_Vector3i = nullptr; - GDMonoClass *class_Vector4 = nullptr; - GDMonoClass *class_Vector4i = nullptr; - GDMonoClass *class_Basis = nullptr; - GDMonoClass *class_Quaternion = nullptr; - GDMonoClass *class_Transform3D = nullptr; - GDMonoClass *class_Projection = nullptr; - GDMonoClass *class_AABB = nullptr; - GDMonoClass *class_Color = nullptr; - GDMonoClass *class_Plane = nullptr; - GDMonoClass *class_StringName = nullptr; - GDMonoClass *class_NodePath = nullptr; - GDMonoClass *class_RID = nullptr; GDMonoClass *class_GodotObject = nullptr; GDMonoClass *class_GodotResource = nullptr; GDMonoClass *class_Node = nullptr; GDMonoClass *class_Control = nullptr; - GDMonoClass *class_Node3D = nullptr; - GDMonoClass *class_WeakRef = nullptr; GDMonoClass *class_Callable = nullptr; GDMonoClass *class_SignalInfo = nullptr; - GDMonoClass *class_Array = nullptr; - GDMonoClass *class_Dictionary = nullptr; - GDMonoClass *class_MarshalUtils = nullptr; GDMonoClass *class_ISerializationListener = nullptr; #ifdef DEBUG_ENABLED @@ -127,43 +85,34 @@ struct CachedData { GDMonoField *field_AssemblyHasScriptsAttribute_requiresLookup = nullptr; GDMonoField *field_AssemblyHasScriptsAttribute_scriptTypes = nullptr; + GDMonoField *field_GodotObject_ptr = nullptr; - GDMonoField *field_StringName_ptr = nullptr; - GDMonoField *field_NodePath_ptr = nullptr; - GDMonoField *field_Image_ptr = nullptr; - GDMonoField *field_RID_ptr = nullptr; GDMonoMethodThunk<MonoObject *> methodthunk_GodotObject_Dispose; - GDMonoMethodThunkR<Array *, MonoObject *> methodthunk_Array_GetPtr; - GDMonoMethodThunkR<Dictionary *, MonoObject *> methodthunk_Dictionary_GetPtr; GDMonoMethodThunk<MonoObject *, MonoArray *> methodthunk_SignalAwaiter_SignalCallback; GDMonoMethodThunk<MonoObject *> methodthunk_GodotTaskScheduler_Activate; GDMonoMethodThunkR<MonoBoolean, MonoObject *, MonoObject *> methodthunk_Delegate_Equals; + GDMonoMethodThunkR<MonoBoolean, void *, MonoObject *> methodthunk_DelegateUtils_TrySerializeDelegateWithGCHandle; + GDMonoMethodThunkR<MonoBoolean, MonoObject *, void **> methodthunk_DelegateUtils_TryDeserializeDelegateWithGCHandle; + GDMonoMethodThunkR<MonoBoolean, MonoDelegate *, MonoObject *> methodthunk_DelegateUtils_TrySerializeDelegate; GDMonoMethodThunkR<MonoBoolean, MonoObject *, MonoDelegate **> methodthunk_DelegateUtils_TryDeserializeDelegate; - // Start of MarshalUtils methods - - GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericArray; - GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericDictionary; - GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsSystemGenericList; - GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsSystemGenericDictionary; - GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericIEnumerable; - GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericICollection; - GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericIDictionary; - GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeHasFlagsAttribute; - - GDMonoMethodThunk<MonoReflectionType *, MonoReflectionType **> methodthunk_MarshalUtils_GetGenericTypeDefinition; + GDMonoMethodThunk<void *, const Variant **, uint32_t, const Variant *> methodthunk_DelegateUtils_InvokeWithVariantArgs; + GDMonoMethodThunkR<MonoBoolean, void *, void *> methodthunk_DelegateUtils_DelegateEquals; + GDMonoMethodThunk<void *> methodthunk_DelegateUtils_FreeGCHandle; - GDMonoMethodThunk<MonoReflectionType *, MonoReflectionType **> methodthunk_MarshalUtils_ArrayGetElementType; - GDMonoMethodThunk<MonoReflectionType *, MonoReflectionType **, MonoReflectionType **> methodthunk_MarshalUtils_DictionaryGetKeyValueTypes; + GDMonoMethodThunkR<int32_t, MonoReflectionType *, MonoBoolean *> methodthunk_Marshaling_managed_to_variant_type; + GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *, MonoReflectionType **> methodthunk_Marshaling_try_get_array_element_type; + GDMonoMethodThunkR<MonoObject *, const Variant *, MonoReflectionType *> methodthunk_Marshaling_variant_to_mono_object_of_type; + GDMonoMethodThunkR<MonoObject *, const Variant *> methodthunk_Marshaling_variant_to_mono_object; + GDMonoMethodThunk<MonoObject *, MonoBoolean, Variant *> methodthunk_Marshaling_mono_object_to_variant_out; - GDMonoMethodThunkR<MonoReflectionType *, MonoReflectionType *> methodthunk_MarshalUtils_MakeGenericArrayType; - GDMonoMethodThunkR<MonoReflectionType *, MonoReflectionType *, MonoReflectionType *> methodthunk_MarshalUtils_MakeGenericDictionaryType; + GDMonoMethodThunk<MonoReflectionField *, MonoObject *, const Variant *> methodthunk_Marshaling_SetFieldValue; - // End of MarshalUtils methods + GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeHasFlagsAttribute; Ref<MonoGCHandleRef> task_scheduler_handle; @@ -184,10 +133,6 @@ extern CachedData cached_data; void update_corlib_cache(); void update_godot_api_cache(); -inline void clear_corlib_cache() { - cached_data.clear_corlib_cache(); -} - inline void clear_godot_api_cache() { cached_data.clear_godot_api_cache(); } @@ -195,7 +140,6 @@ inline void clear_godot_api_cache() { #define CACHED_CLASS(m_class) (GDMonoCache::cached_data.class_##m_class) #define CACHED_CLASS_RAW(m_class) (GDMonoCache::cached_data.class_##m_class->get_mono_ptr()) -#define CACHED_RAW_MONO_CLASS(m_class) (GDMonoCache::cached_data.rawclass_##m_class) #define CACHED_FIELD(m_class, m_field) (GDMonoCache::cached_data.field_##m_class##_##m_field) #define CACHED_METHOD(m_class, m_method) (GDMonoCache::cached_data.method_##m_class##_##m_method) #define CACHED_METHOD_THUNK(m_class, m_method) (GDMonoCache::cached_data.methodthunk_##m_class##_##m_method) diff --git a/modules/mono/mono_gd/gd_mono_class.cpp b/modules/mono/mono_gd/gd_mono_class.cpp index 51c5aa3542..24b46d2ce8 100644 --- a/modules/mono/mono_gd/gd_mono_class.cpp +++ b/modules/mono/mono_gd/gd_mono_class.cpp @@ -462,18 +462,11 @@ const Vector<GDMonoClass *> &GDMonoClass::get_all_delegates() { return delegates_list; } - // If the class is generic we must use the generic type definition. - MonoClass *klass = mono_class; - if (mono_type_get_type(get_mono_type()) == MONO_TYPE_GENERICINST) { - MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), get_mono_type()); - GDMonoUtils::Marshal::get_generic_type_definition(reftype, &reftype); - MonoType *type = mono_reflection_type_get_type(reftype); - klass = mono_class_from_mono_type(type); - } + // NOTE: Temporarily reverted d28be4d5808947606b8189ae1b2900b8fd2925cf, while we move code to C# void *iter = nullptr; MonoClass *raw_class = nullptr; - while ((raw_class = mono_class_get_nested_types(klass, &iter)) != nullptr) { + while ((raw_class = mono_class_get_nested_types(mono_class, &iter)) != nullptr) { if (mono_class_is_delegate(raw_class)) { StringName name = String::utf8(mono_class_get_name(raw_class)); diff --git a/modules/mono/mono_gd/gd_mono_field.cpp b/modules/mono/mono_gd/gd_mono_field.cpp index cb025fc67a..6beeca79c3 100644 --- a/modules/mono/mono_gd/gd_mono_field.cpp +++ b/modules/mono/mono_gd/gd_mono_field.cpp @@ -46,421 +46,14 @@ void GDMonoField::set_value_raw(MonoObject *p_object, void *p_ptr) { } void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_value) { - switch (type.type_encoding) { - case MONO_TYPE_BOOLEAN: { - MonoBoolean val = p_value.operator bool(); - mono_field_set_value(p_object, mono_field, &val); - } break; - case MONO_TYPE_CHAR: { - int16_t val = p_value.operator unsigned short(); - mono_field_set_value(p_object, mono_field, &val); - } break; - case MONO_TYPE_I1: { - int8_t val = p_value.operator signed char(); - mono_field_set_value(p_object, mono_field, &val); - } break; - case MONO_TYPE_I2: { - int16_t val = p_value.operator signed short(); - mono_field_set_value(p_object, mono_field, &val); - } break; - case MONO_TYPE_I4: { - int32_t val = p_value.operator signed int(); - mono_field_set_value(p_object, mono_field, &val); - } break; - case MONO_TYPE_I8: { - int64_t val = p_value.operator int64_t(); - mono_field_set_value(p_object, mono_field, &val); - } break; - case MONO_TYPE_U1: { - uint8_t val = p_value.operator unsigned char(); - mono_field_set_value(p_object, mono_field, &val); - } break; - case MONO_TYPE_U2: { - uint16_t val = p_value.operator unsigned short(); - mono_field_set_value(p_object, mono_field, &val); - } break; - case MONO_TYPE_U4: { - uint32_t val = p_value.operator unsigned int(); - mono_field_set_value(p_object, mono_field, &val); - } break; - case MONO_TYPE_U8: { - uint64_t val = p_value.operator uint64_t(); - mono_field_set_value(p_object, mono_field, &val); - } break; - case MONO_TYPE_R4: { - float val = p_value.operator float(); - mono_field_set_value(p_object, mono_field, &val); - } break; - case MONO_TYPE_R8: { - double val = p_value.operator double(); - mono_field_set_value(p_object, mono_field, &val); - } break; - case MONO_TYPE_VALUETYPE: { - GDMonoClass *tclass = type.type_class; + MonoReflectionField *reflfield = mono_field_get_object(mono_domain_get(), owner->get_mono_ptr(), mono_field); - if (tclass == CACHED_CLASS(Vector2)) { - GDMonoMarshal::M_Vector2 from = MARSHALLED_OUT(Vector2, p_value.operator ::Vector2()); - mono_field_set_value(p_object, mono_field, &from); - break; - } + MonoException *exc = nullptr; + CACHED_METHOD_THUNK(Marshaling, SetFieldValue) + .invoke(reflfield, p_object, &p_value, &exc); - if (tclass == CACHED_CLASS(Vector2i)) { - GDMonoMarshal::M_Vector2i from = MARSHALLED_OUT(Vector2i, p_value.operator ::Vector2i()); - mono_field_set_value(p_object, mono_field, &from); - break; - } - - if (tclass == CACHED_CLASS(Rect2)) { - GDMonoMarshal::M_Rect2 from = MARSHALLED_OUT(Rect2, p_value.operator ::Rect2()); - mono_field_set_value(p_object, mono_field, &from); - break; - } - - if (tclass == CACHED_CLASS(Rect2i)) { - GDMonoMarshal::M_Rect2i from = MARSHALLED_OUT(Rect2i, p_value.operator ::Rect2i()); - mono_field_set_value(p_object, mono_field, &from); - break; - } - - if (tclass == CACHED_CLASS(Transform2D)) { - GDMonoMarshal::M_Transform2D from = MARSHALLED_OUT(Transform2D, p_value.operator ::Transform2D()); - mono_field_set_value(p_object, mono_field, &from); - break; - } - - if (tclass == CACHED_CLASS(Vector3)) { - GDMonoMarshal::M_Vector3 from = MARSHALLED_OUT(Vector3, p_value.operator ::Vector3()); - mono_field_set_value(p_object, mono_field, &from); - break; - } - - if (tclass == CACHED_CLASS(Vector3i)) { - GDMonoMarshal::M_Vector3i from = MARSHALLED_OUT(Vector3i, p_value.operator ::Vector3i()); - mono_field_set_value(p_object, mono_field, &from); - break; - } - - if (tclass == CACHED_CLASS(Vector4)) { - GDMonoMarshal::M_Vector4 from = MARSHALLED_OUT(Vector4, p_value.operator ::Vector4()); - mono_field_set_value(p_object, mono_field, &from); - break; - } - - if (tclass == CACHED_CLASS(Vector4i)) { - GDMonoMarshal::M_Vector4i from = MARSHALLED_OUT(Vector4i, p_value.operator ::Vector4i()); - mono_field_set_value(p_object, mono_field, &from); - break; - } - - if (tclass == CACHED_CLASS(Basis)) { - GDMonoMarshal::M_Basis from = MARSHALLED_OUT(Basis, p_value.operator ::Basis()); - mono_field_set_value(p_object, mono_field, &from); - break; - } - - if (tclass == CACHED_CLASS(Quaternion)) { - GDMonoMarshal::M_Quaternion from = MARSHALLED_OUT(Quaternion, p_value.operator ::Quaternion()); - mono_field_set_value(p_object, mono_field, &from); - break; - } - - if (tclass == CACHED_CLASS(Transform3D)) { - GDMonoMarshal::M_Transform3D from = MARSHALLED_OUT(Transform3D, p_value.operator ::Transform3D()); - mono_field_set_value(p_object, mono_field, &from); - break; - } - - if (tclass == CACHED_CLASS(Projection)) { - GDMonoMarshal::M_Projection from = MARSHALLED_OUT(Projection, p_value.operator ::Projection()); - mono_field_set_value(p_object, mono_field, &from); - break; - } - - if (tclass == CACHED_CLASS(AABB)) { - GDMonoMarshal::M_AABB from = MARSHALLED_OUT(AABB, p_value.operator ::AABB()); - mono_field_set_value(p_object, mono_field, &from); - break; - } - - if (tclass == CACHED_CLASS(Color)) { - GDMonoMarshal::M_Color from = MARSHALLED_OUT(Color, p_value.operator ::Color()); - mono_field_set_value(p_object, mono_field, &from); - break; - } - - if (tclass == CACHED_CLASS(Plane)) { - GDMonoMarshal::M_Plane from = MARSHALLED_OUT(Plane, p_value.operator ::Plane()); - mono_field_set_value(p_object, mono_field, &from); - break; - } - - if (tclass == CACHED_CLASS(Callable)) { - GDMonoMarshal::M_Callable val = GDMonoMarshal::callable_to_managed(p_value.operator Callable()); - mono_field_set_value(p_object, mono_field, &val); - break; - } - - if (tclass == CACHED_CLASS(SignalInfo)) { - GDMonoMarshal::M_SignalInfo val = GDMonoMarshal::signal_info_to_managed(p_value.operator Signal()); - mono_field_set_value(p_object, mono_field, &val); - break; - } - - if (mono_class_is_enum(tclass->get_mono_ptr())) { - MonoType *enum_basetype = mono_class_enum_basetype(tclass->get_mono_ptr()); - switch (mono_type_get_type(enum_basetype)) { - case MONO_TYPE_BOOLEAN: { - MonoBoolean val = p_value.operator bool(); - mono_field_set_value(p_object, mono_field, &val); - break; - } - case MONO_TYPE_CHAR: { - uint16_t val = p_value.operator unsigned short(); - mono_field_set_value(p_object, mono_field, &val); - break; - } - case MONO_TYPE_I1: { - int8_t val = p_value.operator signed char(); - mono_field_set_value(p_object, mono_field, &val); - break; - } - case MONO_TYPE_I2: { - int16_t val = p_value.operator signed short(); - mono_field_set_value(p_object, mono_field, &val); - break; - } - case MONO_TYPE_I4: { - int32_t val = p_value.operator signed int(); - mono_field_set_value(p_object, mono_field, &val); - break; - } - case MONO_TYPE_I8: { - int64_t val = p_value.operator int64_t(); - mono_field_set_value(p_object, mono_field, &val); - break; - } - case MONO_TYPE_U1: { - uint8_t val = p_value.operator unsigned char(); - mono_field_set_value(p_object, mono_field, &val); - break; - } - case MONO_TYPE_U2: { - uint16_t val = p_value.operator unsigned short(); - mono_field_set_value(p_object, mono_field, &val); - break; - } - case MONO_TYPE_U4: { - uint32_t val = p_value.operator unsigned int(); - mono_field_set_value(p_object, mono_field, &val); - break; - } - case MONO_TYPE_U8: { - uint64_t val = p_value.operator uint64_t(); - mono_field_set_value(p_object, mono_field, &val); - break; - } - default: { - ERR_FAIL_MSG("Attempted to convert Variant to a managed enum value of unmarshallable base type."); - } - } - - break; - } - - ERR_FAIL_MSG("Attempted to set the value of a field of unmarshallable type: '" + tclass->get_name() + "'."); - } break; - case MONO_TYPE_STRING: { - if (p_value.get_type() == Variant::NIL) { - // Otherwise, Variant -> String would return the string "Null" - MonoString *mono_string = nullptr; - mono_field_set_value(p_object, mono_field, mono_string); - } else { - MonoString *mono_string = GDMonoMarshal::mono_string_from_godot(p_value); - mono_field_set_value(p_object, mono_field, mono_string); - } - } break; - case MONO_TYPE_ARRAY: - case MONO_TYPE_SZARRAY: { - MonoArray *managed = GDMonoMarshal::variant_to_mono_array(p_value, type.type_class); - if (likely(managed != nullptr)) { - mono_field_set_value(p_object, mono_field, managed); - } - } break; - case MONO_TYPE_CLASS: { - MonoObject *managed = GDMonoMarshal::variant_to_mono_object_of_class(p_value, type.type_class); - if (likely(managed != nullptr)) { - mono_field_set_value(p_object, mono_field, managed); - } - } break; - case MONO_TYPE_GENERICINST: { - MonoObject *managed = GDMonoMarshal::variant_to_mono_object_of_genericinst(p_value, type.type_class); - if (likely(managed != nullptr)) { - mono_field_set_value(p_object, mono_field, managed); - } - } break; - case MONO_TYPE_OBJECT: { - // Variant - switch (p_value.get_type()) { - case Variant::BOOL: { - MonoBoolean val = p_value.operator bool(); - mono_field_set_value(p_object, mono_field, &val); - } break; - case Variant::INT: { - int32_t val = p_value.operator signed int(); - mono_field_set_value(p_object, mono_field, &val); - } break; - case Variant::FLOAT: { -#ifdef REAL_T_IS_DOUBLE - double val = p_value.operator double(); - mono_field_set_value(p_object, mono_field, &val); -#else - float val = p_value.operator float(); - mono_field_set_value(p_object, mono_field, &val); -#endif - } break; - case Variant::STRING: { - MonoString *mono_string = GDMonoMarshal::mono_string_from_godot(p_value); - mono_field_set_value(p_object, mono_field, mono_string); - } break; - case Variant::VECTOR2: { - GDMonoMarshal::M_Vector2 from = MARSHALLED_OUT(Vector2, p_value.operator ::Vector2()); - mono_field_set_value(p_object, mono_field, &from); - } break; - case Variant::VECTOR2I: { - GDMonoMarshal::M_Vector2i from = MARSHALLED_OUT(Vector2i, p_value.operator ::Vector2i()); - mono_field_set_value(p_object, mono_field, &from); - } break; - case Variant::RECT2: { - GDMonoMarshal::M_Rect2 from = MARSHALLED_OUT(Rect2, p_value.operator ::Rect2()); - mono_field_set_value(p_object, mono_field, &from); - } break; - case Variant::RECT2I: { - GDMonoMarshal::M_Rect2i from = MARSHALLED_OUT(Rect2i, p_value.operator ::Rect2i()); - mono_field_set_value(p_object, mono_field, &from); - } break; - case Variant::VECTOR3: { - GDMonoMarshal::M_Vector3 from = MARSHALLED_OUT(Vector3, p_value.operator ::Vector3()); - mono_field_set_value(p_object, mono_field, &from); - } break; - case Variant::VECTOR3I: { - GDMonoMarshal::M_Vector3i from = MARSHALLED_OUT(Vector3i, p_value.operator ::Vector3i()); - mono_field_set_value(p_object, mono_field, &from); - } break; - case Variant::VECTOR4: { - GDMonoMarshal::M_Vector4 from = MARSHALLED_OUT(Vector4, p_value.operator ::Vector4()); - mono_field_set_value(p_object, mono_field, &from); - } break; - case Variant::VECTOR4I: { - GDMonoMarshal::M_Vector4i from = MARSHALLED_OUT(Vector4i, p_value.operator ::Vector4i()); - mono_field_set_value(p_object, mono_field, &from); - } break; - case Variant::TRANSFORM2D: { - GDMonoMarshal::M_Transform2D from = MARSHALLED_OUT(Transform2D, p_value.operator ::Transform2D()); - mono_field_set_value(p_object, mono_field, &from); - } break; - case Variant::PLANE: { - GDMonoMarshal::M_Plane from = MARSHALLED_OUT(Plane, p_value.operator ::Plane()); - mono_field_set_value(p_object, mono_field, &from); - } break; - case Variant::QUATERNION: { - GDMonoMarshal::M_Quaternion from = MARSHALLED_OUT(Quaternion, p_value.operator ::Quaternion()); - mono_field_set_value(p_object, mono_field, &from); - } break; - case Variant::AABB: { - GDMonoMarshal::M_AABB from = MARSHALLED_OUT(AABB, p_value.operator ::AABB()); - mono_field_set_value(p_object, mono_field, &from); - } break; - case Variant::BASIS: { - GDMonoMarshal::M_Basis from = MARSHALLED_OUT(Basis, p_value.operator ::Basis()); - mono_field_set_value(p_object, mono_field, &from); - } break; - case Variant::TRANSFORM3D: { - GDMonoMarshal::M_Transform3D from = MARSHALLED_OUT(Transform3D, p_value.operator ::Transform3D()); - mono_field_set_value(p_object, mono_field, &from); - } break; - case Variant::PROJECTION: { - GDMonoMarshal::M_Projection from = MARSHALLED_OUT(Projection, p_value.operator ::Projection()); - mono_field_set_value(p_object, mono_field, &from); - } break; - case Variant::COLOR: { - GDMonoMarshal::M_Color from = MARSHALLED_OUT(Color, p_value.operator ::Color()); - mono_field_set_value(p_object, mono_field, &from); - } break; - case Variant::STRING_NAME: { - MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator StringName()); - mono_field_set_value(p_object, mono_field, managed); - } break; - case Variant::NODE_PATH: { - MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator NodePath()); - mono_field_set_value(p_object, mono_field, managed); - } break; - case Variant::RID: { - MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator ::RID()); - mono_field_set_value(p_object, mono_field, managed); - } break; - case Variant::OBJECT: { - MonoObject *managed = GDMonoUtils::unmanaged_get_managed(p_value.operator Object *()); - mono_field_set_value(p_object, mono_field, managed); - } break; - case Variant::CALLABLE: { - GDMonoMarshal::M_Callable val = GDMonoMarshal::callable_to_managed(p_value.operator Callable()); - mono_field_set_value(p_object, mono_field, &val); - } break; - case Variant::SIGNAL: { - GDMonoMarshal::M_SignalInfo val = GDMonoMarshal::signal_info_to_managed(p_value.operator Signal()); - mono_field_set_value(p_object, mono_field, &val); - } break; - case Variant::DICTIONARY: { - MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary)); - mono_field_set_value(p_object, mono_field, managed); - } break; - case Variant::ARRAY: { - MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array)); - mono_field_set_value(p_object, mono_field, managed); - } break; - case Variant::PACKED_BYTE_ARRAY: { - MonoArray *managed = GDMonoMarshal::PackedByteArray_to_mono_array(p_value.operator ::PackedByteArray()); - mono_field_set_value(p_object, mono_field, managed); - } break; - case Variant::PACKED_INT32_ARRAY: { - MonoArray *managed = GDMonoMarshal::PackedInt32Array_to_mono_array(p_value.operator ::PackedInt32Array()); - mono_field_set_value(p_object, mono_field, managed); - } break; - case Variant::PACKED_INT64_ARRAY: { - MonoArray *managed = GDMonoMarshal::PackedInt64Array_to_mono_array(p_value.operator ::PackedInt64Array()); - mono_field_set_value(p_object, mono_field, managed); - } break; - case Variant::PACKED_FLOAT32_ARRAY: { - MonoArray *managed = GDMonoMarshal::PackedFloat32Array_to_mono_array(p_value.operator ::PackedFloat32Array()); - mono_field_set_value(p_object, mono_field, managed); - } break; - case Variant::PACKED_FLOAT64_ARRAY: { - MonoArray *managed = GDMonoMarshal::PackedFloat64Array_to_mono_array(p_value.operator ::PackedFloat64Array()); - mono_field_set_value(p_object, mono_field, managed); - } break; - case Variant::PACKED_STRING_ARRAY: { - MonoArray *managed = GDMonoMarshal::PackedStringArray_to_mono_array(p_value.operator ::PackedStringArray()); - mono_field_set_value(p_object, mono_field, managed); - } break; - case Variant::PACKED_VECTOR2_ARRAY: { - MonoArray *managed = GDMonoMarshal::PackedVector2Array_to_mono_array(p_value.operator ::PackedVector2Array()); - mono_field_set_value(p_object, mono_field, managed); - } break; - case Variant::PACKED_VECTOR3_ARRAY: { - MonoArray *managed = GDMonoMarshal::PackedVector3Array_to_mono_array(p_value.operator ::PackedVector3Array()); - mono_field_set_value(p_object, mono_field, managed); - } break; - case Variant::PACKED_COLOR_ARRAY: { - MonoArray *managed = GDMonoMarshal::PackedColorArray_to_mono_array(p_value.operator ::PackedColorArray()); - mono_field_set_value(p_object, mono_field, managed); - } break; - default: - break; - } - } break; - default: { - ERR_PRINT("Attempted to set the value of a field of unexpected type encoding: " + itos(type.type_encoding) + "."); - } break; + if (exc) { + GDMonoUtils::debug_print_unhandled_exception(exc); } } diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp index a860442764..ef6a008a25 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-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -37,1282 +37,104 @@ namespace GDMonoMarshal { -Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_variant) { - switch (p_type.type_encoding) { - case MONO_TYPE_BOOLEAN: - return Variant::BOOL; - - case MONO_TYPE_I1: - return Variant::INT; - case MONO_TYPE_I2: - return Variant::INT; - case MONO_TYPE_I4: - return Variant::INT; - case MONO_TYPE_I8: - return Variant::INT; - - case MONO_TYPE_U1: - return Variant::INT; - case MONO_TYPE_U2: - return Variant::INT; - case MONO_TYPE_U4: - return Variant::INT; - case MONO_TYPE_U8: - return Variant::INT; - - case MONO_TYPE_R4: - return Variant::FLOAT; - case MONO_TYPE_R8: - return Variant::FLOAT; - - case MONO_TYPE_STRING: { - return Variant::STRING; - } break; - - case MONO_TYPE_VALUETYPE: { - GDMonoClass *vtclass = p_type.type_class; - - if (vtclass == CACHED_CLASS(Vector2)) { - return Variant::VECTOR2; - } - - if (vtclass == CACHED_CLASS(Vector2i)) { - return Variant::VECTOR2I; - } - - if (vtclass == CACHED_CLASS(Rect2)) { - return Variant::RECT2; - } - - if (vtclass == CACHED_CLASS(Rect2i)) { - return Variant::RECT2I; - } - - if (vtclass == CACHED_CLASS(Transform2D)) { - return Variant::TRANSFORM2D; - } - - if (vtclass == CACHED_CLASS(Vector3)) { - return Variant::VECTOR3; - } - - if (vtclass == CACHED_CLASS(Vector3i)) { - return Variant::VECTOR3I; - } - if (vtclass == CACHED_CLASS(Vector4)) { - return Variant::VECTOR4; - } - - if (vtclass == CACHED_CLASS(Vector4i)) { - return Variant::VECTOR4I; - } - - if (vtclass == CACHED_CLASS(Basis)) { - return Variant::BASIS; - } - - if (vtclass == CACHED_CLASS(Quaternion)) { - return Variant::QUATERNION; - } - - if (vtclass == CACHED_CLASS(Transform3D)) { - return Variant::TRANSFORM3D; - } - if (vtclass == CACHED_CLASS(Projection)) { - return Variant::PROJECTION; - } - if (vtclass == CACHED_CLASS(AABB)) { - return Variant::AABB; - } - - if (vtclass == CACHED_CLASS(Color)) { - return Variant::COLOR; - } - - if (vtclass == CACHED_CLASS(Plane)) { - return Variant::PLANE; - } - - if (vtclass == CACHED_CLASS(Callable)) { - return Variant::CALLABLE; - } - - if (vtclass == CACHED_CLASS(SignalInfo)) { - return Variant::SIGNAL; - } - - if (mono_class_is_enum(vtclass->get_mono_ptr())) { - return Variant::INT; - } - } break; - - case MONO_TYPE_ARRAY: - case MONO_TYPE_SZARRAY: { - MonoClass *elem_class = mono_class_get_element_class(p_type.type_class->get_mono_ptr()); - - if (elem_class == CACHED_CLASS_RAW(MonoObject)) { - return Variant::ARRAY; - } - - if (elem_class == CACHED_CLASS_RAW(uint8_t)) { - return Variant::PACKED_BYTE_ARRAY; - } - - if (elem_class == CACHED_CLASS_RAW(int32_t)) { - return Variant::PACKED_INT32_ARRAY; - } - - if (elem_class == CACHED_CLASS_RAW(int64_t)) { - return Variant::PACKED_INT64_ARRAY; - } - - if (elem_class == CACHED_CLASS_RAW(float)) { - return Variant::PACKED_FLOAT32_ARRAY; - } - - if (elem_class == CACHED_CLASS_RAW(double)) { - return Variant::PACKED_FLOAT64_ARRAY; - } - - if (elem_class == CACHED_CLASS_RAW(String)) { - return Variant::PACKED_STRING_ARRAY; - } - - if (elem_class == CACHED_CLASS_RAW(Vector2)) { - return Variant::PACKED_VECTOR2_ARRAY; - } - - if (elem_class == CACHED_CLASS_RAW(Vector3)) { - return Variant::PACKED_VECTOR3_ARRAY; - } - - if (elem_class == CACHED_CLASS_RAW(Color)) { - return Variant::PACKED_COLOR_ARRAY; - } - - if (elem_class == CACHED_CLASS_RAW(StringName)) { - return Variant::ARRAY; - } - - if (elem_class == CACHED_CLASS_RAW(NodePath)) { - return Variant::ARRAY; - } - - if (elem_class == CACHED_CLASS_RAW(RID)) { - return Variant::ARRAY; - } - - if (mono_class_is_enum(elem_class)) { - return Variant::ARRAY; - } - - GDMonoClass *array_type_class = GDMono::get_singleton()->get_class(elem_class); - if (CACHED_CLASS(GodotObject)->is_assignable_from(array_type_class)) { - return Variant::ARRAY; - } - } break; - - case MONO_TYPE_CLASS: { - GDMonoClass *type_class = p_type.type_class; +// TODO: Those are just temporary until the code that needs them is moved to C# - // GodotObject - if (CACHED_CLASS(GodotObject)->is_assignable_from(type_class)) { - return Variant::OBJECT; - } - - if (CACHED_CLASS(StringName) == type_class) { - return Variant::STRING_NAME; - } - - if (CACHED_CLASS(NodePath) == type_class) { - return Variant::NODE_PATH; - } - - if (CACHED_CLASS(RID) == type_class) { - return Variant::RID; - } - - if (CACHED_CLASS(Dictionary) == type_class) { - return Variant::DICTIONARY; - } - - if (CACHED_CLASS(Array) == type_class) { - return Variant::ARRAY; - } - - // IDictionary - if (p_type.type_class == CACHED_CLASS(System_Collections_IDictionary)) { - return Variant::DICTIONARY; - } - - // ICollection or IEnumerable - if (p_type.type_class == CACHED_CLASS(System_Collections_ICollection) || - p_type.type_class == CACHED_CLASS(System_Collections_IEnumerable)) { - return Variant::ARRAY; - } - } break; - - case MONO_TYPE_OBJECT: { - if (r_nil_is_variant) { - *r_nil_is_variant = true; - } - return Variant::NIL; - } break; - - case MONO_TYPE_GENERICINST: { - MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type()); - - // Godot.Collections.Dictionary<TKey, TValue> - if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) { - return Variant::DICTIONARY; - } - - // Godot.Collections.Array<T> - if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) { - return Variant::ARRAY; - } - - // System.Collections.Generic.Dictionary<TKey, TValue> - if (GDMonoUtils::Marshal::type_is_system_generic_dictionary(reftype)) { - return Variant::DICTIONARY; - } - - // System.Collections.Generic.List<T> - if (GDMonoUtils::Marshal::type_is_system_generic_list(reftype)) { - return Variant::ARRAY; - } - - // IDictionary<TKey, TValue> - if (GDMonoUtils::Marshal::type_is_generic_idictionary(reftype)) { - return Variant::DICTIONARY; - } +Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_variant) { + if (p_type.type_encoding == MONO_TYPE_VOID) { + return Variant::NIL; + } - // ICollection<T> or IEnumerable<T> - if (GDMonoUtils::Marshal::type_is_generic_icollection(reftype) || GDMonoUtils::Marshal::type_is_generic_ienumerable(reftype)) { - return Variant::ARRAY; - } + MonoReflectionType *refltype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type()); + MonoBoolean nil_is_variant = false; - // GodotObject - GDMonoClass *type_class = p_type.type_class; - if (CACHED_CLASS(GodotObject)->is_assignable_from(type_class)) { - return Variant::OBJECT; - } - } break; + MonoException *exc = nullptr; + int32_t ret = CACHED_METHOD_THUNK(Marshaling, managed_to_variant_type) + .invoke(refltype, &nil_is_variant, &exc); - default: { - } break; + if (exc) { + GDMonoUtils::debug_print_unhandled_exception(exc); + return Variant::NIL; } if (r_nil_is_variant) { - *r_nil_is_variant = false; + *r_nil_is_variant = (bool)nil_is_variant; } - // Unknown - return Variant::NIL; + return (Variant::Type)ret; } bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_elem_type) { - switch (p_array_type.type_encoding) { - case MONO_TYPE_ARRAY: - case MONO_TYPE_SZARRAY: { - MonoClass *elem_class = mono_class_get_element_class(p_array_type.type_class->get_mono_ptr()); - r_elem_type = ManagedType::from_class(elem_class); - return true; - } break; - case MONO_TYPE_GENERICINST: { - MonoReflectionType *array_reftype = mono_type_get_object(mono_domain_get(), p_array_type.type_class->get_mono_type()); - - if (GDMonoUtils::Marshal::type_is_generic_array(array_reftype) || - GDMonoUtils::Marshal::type_is_system_generic_list(array_reftype) || - GDMonoUtils::Marshal::type_is_generic_icollection(array_reftype) || - GDMonoUtils::Marshal::type_is_generic_ienumerable(array_reftype)) { - MonoReflectionType *elem_reftype; - - GDMonoUtils::Marshal::array_get_element_type(array_reftype, &elem_reftype); - - r_elem_type = ManagedType::from_reftype(elem_reftype); - return true; - } - } break; - default: { - } break; - } - - return false; -} - -MonoString *variant_to_mono_string(const Variant &p_var) { - if (p_var.get_type() == Variant::NIL) { - return nullptr; // Otherwise, Variant -> String would return the string "Null" - } - return mono_string_from_godot(p_var.operator String()); -} - -MonoArray *variant_to_mono_array(const Variant &p_var, GDMonoClass *p_type_class) { - MonoArrayType *array_type = mono_type_get_array_type(p_type_class->get_mono_type()); - - if (array_type->eklass == CACHED_CLASS_RAW(MonoObject)) { - return Array_to_mono_array(p_var.operator Array()); - } - - if (array_type->eklass == CACHED_CLASS_RAW(uint8_t)) { - return PackedByteArray_to_mono_array(p_var.operator PackedByteArray()); - } - - if (array_type->eklass == CACHED_CLASS_RAW(int32_t)) { - return PackedInt32Array_to_mono_array(p_var.operator PackedInt32Array()); - } - - if (array_type->eklass == CACHED_CLASS_RAW(int64_t)) { - return PackedInt64Array_to_mono_array(p_var.operator PackedInt64Array()); - } - - if (array_type->eklass == CACHED_CLASS_RAW(float)) { - return PackedFloat32Array_to_mono_array(p_var.operator PackedFloat32Array()); - } - - if (array_type->eklass == CACHED_CLASS_RAW(double)) { - return PackedFloat64Array_to_mono_array(p_var.operator PackedFloat64Array()); - } - - if (array_type->eklass == CACHED_CLASS_RAW(String)) { - return PackedStringArray_to_mono_array(p_var.operator PackedStringArray()); - } - - if (array_type->eklass == CACHED_CLASS_RAW(Vector2)) { - return PackedVector2Array_to_mono_array(p_var.operator PackedVector2Array()); - } - - if (array_type->eklass == CACHED_CLASS_RAW(Vector3)) { - return PackedVector3Array_to_mono_array(p_var.operator PackedVector3Array()); - } - - if (array_type->eklass == CACHED_CLASS_RAW(Color)) { - return PackedColorArray_to_mono_array(p_var.operator PackedColorArray()); - } - - if (array_type->eklass == CACHED_CLASS_RAW(StringName)) { - return Array_to_mono_array(p_var.operator Array()); - } - - if (array_type->eklass == CACHED_CLASS_RAW(NodePath)) { - return Array_to_mono_array(p_var.operator Array()); - } - - if (array_type->eklass == CACHED_CLASS_RAW(RID)) { - return Array_to_mono_array(p_var.operator Array()); - } - - if (mono_class_is_assignable_from(CACHED_CLASS(GodotObject)->get_mono_ptr(), array_type->eklass)) { - return Array_to_mono_array(p_var.operator ::Array(), array_type->eklass); - } + MonoReflectionType *array_refltype = mono_type_get_object(mono_domain_get(), p_array_type.type_class->get_mono_type()); + MonoReflectionType *elem_refltype = nullptr; - ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to array of unsupported element type:" + GDMonoClass::get_full_name(array_type->eklass) + "'."); -} - -MonoObject *variant_to_mono_object_of_class(const Variant &p_var, GDMonoClass *p_type_class) { - // GodotObject - if (CACHED_CLASS(GodotObject)->is_assignable_from(p_type_class)) { - return GDMonoUtils::unmanaged_get_managed(p_var.operator Object *()); - } - - if (CACHED_CLASS(StringName) == p_type_class) { - return GDMonoUtils::create_managed_from(p_var.operator StringName()); - } - - if (CACHED_CLASS(NodePath) == p_type_class) { - return GDMonoUtils::create_managed_from(p_var.operator NodePath()); - } - - if (CACHED_CLASS(RID) == p_type_class) { - return GDMonoUtils::create_managed_from(p_var.operator ::RID()); - } - - // Godot.Collections.Dictionary or IDictionary - if (CACHED_CLASS(Dictionary) == p_type_class || CACHED_CLASS(System_Collections_IDictionary) == p_type_class) { - return GDMonoUtils::create_managed_from(p_var.operator Dictionary(), CACHED_CLASS(Dictionary)); - } + MonoException *exc = nullptr; + MonoBoolean ret = CACHED_METHOD_THUNK(Marshaling, try_get_array_element_type) + .invoke(array_refltype, &elem_refltype, &exc); - // Godot.Collections.Array or ICollection or IEnumerable - if (CACHED_CLASS(Array) == p_type_class || - CACHED_CLASS(System_Collections_ICollection) == p_type_class || - CACHED_CLASS(System_Collections_IEnumerable) == p_type_class) { - return GDMonoUtils::create_managed_from(p_var.operator Array(), CACHED_CLASS(Array)); + if (exc) { + GDMonoUtils::debug_print_unhandled_exception(exc); + return Variant::NIL; } - ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported type: '" + p_type_class->get_full_name() + "'."); + r_elem_type = ManagedType::from_reftype(elem_refltype); + return ret; } -MonoObject *variant_to_mono_object_of_genericinst(const Variant &p_var, GDMonoClass *p_type_class) { - MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type_class->get_mono_type()); - - // Godot.Collections.Dictionary<TKey, TValue> - if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) { - return GDMonoUtils::create_managed_from(p_var.operator Dictionary(), p_type_class); - } - - // Godot.Collections.Array<T> - if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) { - return GDMonoUtils::create_managed_from(p_var.operator Array(), p_type_class); - } - - // System.Collections.Generic.Dictionary<TKey, TValue> - if (GDMonoUtils::Marshal::type_is_system_generic_dictionary(reftype)) { - MonoReflectionType *key_reftype = nullptr; - MonoReflectionType *value_reftype = nullptr; - GDMonoUtils::Marshal::dictionary_get_key_value_types(reftype, &key_reftype, &value_reftype); - return Dictionary_to_system_generic_dict(p_var.operator Dictionary(), p_type_class, key_reftype, value_reftype); - } +MonoObject *variant_to_mono_object_of_type(const Variant &p_var, const ManagedType &p_type) { + MonoReflectionType *refltype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type()); - // System.Collections.Generic.List<T> - if (GDMonoUtils::Marshal::type_is_system_generic_list(reftype)) { - MonoReflectionType *elem_reftype = nullptr; - GDMonoUtils::Marshal::array_get_element_type(reftype, &elem_reftype); - return Array_to_system_generic_list(p_var.operator Array(), p_type_class, elem_reftype); - } - - // IDictionary<TKey, TValue> - if (GDMonoUtils::Marshal::type_is_generic_idictionary(reftype)) { - MonoReflectionType *key_reftype; - MonoReflectionType *value_reftype; - GDMonoUtils::Marshal::dictionary_get_key_value_types(reftype, &key_reftype, &value_reftype); - GDMonoClass *godot_dict_class = GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype); - - return GDMonoUtils::create_managed_from(p_var.operator Dictionary(), godot_dict_class); - } - - // ICollection<T> or IEnumerable<T> - if (GDMonoUtils::Marshal::type_is_generic_icollection(reftype) || GDMonoUtils::Marshal::type_is_generic_ienumerable(reftype)) { - MonoReflectionType *elem_reftype; - GDMonoUtils::Marshal::array_get_element_type(reftype, &elem_reftype); - GDMonoClass *godot_array_class = GDMonoUtils::Marshal::make_generic_array_type(elem_reftype); - - return GDMonoUtils::create_managed_from(p_var.operator Array(), godot_array_class); - } + MonoException *exc = nullptr; + MonoObject *ret = CACHED_METHOD_THUNK(Marshaling, variant_to_mono_object_of_type) + .invoke(&p_var, refltype, &exc); - // GodotObject - if (CACHED_CLASS(GodotObject)->is_assignable_from(p_type_class)) { - return GDMonoUtils::unmanaged_get_managed(p_var.operator Object *()); + if (exc) { + GDMonoUtils::debug_print_unhandled_exception(exc); + return nullptr; } - ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported generic type: '" + p_type_class->get_full_name() + "'."); + return ret; } MonoObject *variant_to_mono_object(const Variant &p_var) { - // Variant - switch (p_var.get_type()) { - case Variant::BOOL: { - MonoBoolean val = p_var.operator bool(); - return BOX_BOOLEAN(val); - } - case Variant::INT: { - int64_t val = p_var.operator int64_t(); - return BOX_INT64(val); - } - case Variant::FLOAT: { -#ifdef REAL_T_IS_DOUBLE - double val = p_var.operator double(); - return BOX_DOUBLE(val); -#else - float val = p_var.operator float(); - return BOX_FLOAT(val); -#endif - } - case Variant::STRING: - return (MonoObject *)mono_string_from_godot(p_var.operator String()); - case Variant::VECTOR2: { - GDMonoMarshal::M_Vector2 from = MARSHALLED_OUT(Vector2, p_var.operator ::Vector2()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector2), &from); - } - case Variant::VECTOR2I: { - GDMonoMarshal::M_Vector2i from = MARSHALLED_OUT(Vector2i, p_var.operator ::Vector2i()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector2i), &from); - } - case Variant::RECT2: { - GDMonoMarshal::M_Rect2 from = MARSHALLED_OUT(Rect2, p_var.operator ::Rect2()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Rect2), &from); - } - case Variant::RECT2I: { - GDMonoMarshal::M_Rect2i from = MARSHALLED_OUT(Rect2i, p_var.operator ::Rect2i()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Rect2i), &from); - } - case Variant::VECTOR3: { - GDMonoMarshal::M_Vector3 from = MARSHALLED_OUT(Vector3, p_var.operator ::Vector3()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector3), &from); - } - case Variant::VECTOR3I: { - GDMonoMarshal::M_Vector3i from = MARSHALLED_OUT(Vector3i, p_var.operator ::Vector3i()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector3i), &from); - } - case Variant::TRANSFORM2D: { - GDMonoMarshal::M_Transform2D from = MARSHALLED_OUT(Transform2D, p_var.operator ::Transform2D()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform2D), &from); - } - case Variant::VECTOR4: { - GDMonoMarshal::M_Vector4 from = MARSHALLED_OUT(Vector4, p_var.operator ::Vector4()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector4), &from); - } - case Variant::VECTOR4I: { - GDMonoMarshal::M_Vector4i from = MARSHALLED_OUT(Vector4i, p_var.operator ::Vector4i()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector4i), &from); - } - case Variant::PLANE: { - GDMonoMarshal::M_Plane from = MARSHALLED_OUT(Plane, p_var.operator ::Plane()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Plane), &from); - } - case Variant::QUATERNION: { - GDMonoMarshal::M_Quaternion from = MARSHALLED_OUT(Quaternion, p_var.operator ::Quaternion()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Quaternion), &from); - } - case Variant::AABB: { - GDMonoMarshal::M_AABB from = MARSHALLED_OUT(AABB, p_var.operator ::AABB()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(AABB), &from); - } - case Variant::BASIS: { - GDMonoMarshal::M_Basis from = MARSHALLED_OUT(Basis, p_var.operator ::Basis()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Basis), &from); - } - case Variant::TRANSFORM3D: { - GDMonoMarshal::M_Transform3D from = MARSHALLED_OUT(Transform3D, p_var.operator ::Transform3D()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform3D), &from); - } - case Variant::PROJECTION: { - GDMonoMarshal::M_Projection from = MARSHALLED_OUT(Projection, p_var.operator ::Projection()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Projection), &from); - } - case Variant::COLOR: { - GDMonoMarshal::M_Color from = MARSHALLED_OUT(Color, p_var.operator ::Color()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Color), &from); - } - case Variant::STRING_NAME: - return GDMonoUtils::create_managed_from(p_var.operator StringName()); - case Variant::NODE_PATH: - return GDMonoUtils::create_managed_from(p_var.operator NodePath()); - case Variant::RID: - return GDMonoUtils::create_managed_from(p_var.operator ::RID()); - case Variant::OBJECT: - return GDMonoUtils::unmanaged_get_managed(p_var.operator Object *()); - case Variant::CALLABLE: { - GDMonoMarshal::M_Callable from = GDMonoMarshal::callable_to_managed(p_var.operator Callable()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Callable), &from); - } - case Variant::SIGNAL: { - GDMonoMarshal::M_SignalInfo from = GDMonoMarshal::signal_info_to_managed(p_var.operator Signal()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(SignalInfo), &from); - } - case Variant::DICTIONARY: - return GDMonoUtils::create_managed_from(p_var.operator Dictionary(), CACHED_CLASS(Dictionary)); - case Variant::ARRAY: - return GDMonoUtils::create_managed_from(p_var.operator Array(), CACHED_CLASS(Array)); - case Variant::PACKED_BYTE_ARRAY: - return (MonoObject *)PackedByteArray_to_mono_array(p_var.operator PackedByteArray()); - case Variant::PACKED_INT32_ARRAY: - return (MonoObject *)PackedInt32Array_to_mono_array(p_var.operator PackedInt32Array()); - case Variant::PACKED_INT64_ARRAY: - return (MonoObject *)PackedInt64Array_to_mono_array(p_var.operator PackedInt64Array()); - case Variant::PACKED_FLOAT32_ARRAY: - return (MonoObject *)PackedFloat32Array_to_mono_array(p_var.operator PackedFloat32Array()); - case Variant::PACKED_FLOAT64_ARRAY: - return (MonoObject *)PackedFloat64Array_to_mono_array(p_var.operator PackedFloat64Array()); - case Variant::PACKED_STRING_ARRAY: - return (MonoObject *)PackedStringArray_to_mono_array(p_var.operator PackedStringArray()); - case Variant::PACKED_VECTOR2_ARRAY: - return (MonoObject *)PackedVector2Array_to_mono_array(p_var.operator PackedVector2Array()); - case Variant::PACKED_VECTOR3_ARRAY: - return (MonoObject *)PackedVector3Array_to_mono_array(p_var.operator PackedVector3Array()); - case Variant::PACKED_COLOR_ARRAY: - return (MonoObject *)PackedColorArray_to_mono_array(p_var.operator PackedColorArray()); - default: - return nullptr; - } -} - -size_t variant_get_managed_unboxed_size(const ManagedType &p_type) { - // This method prints no errors for unsupported types. It's called on all methods, not only - // those that end up being invoked with Variant parameters. - - // For MonoObject* we return 0, as it doesn't need to be stored. - constexpr size_t zero_for_mono_object = 0; - - switch (p_type.type_encoding) { - case MONO_TYPE_BOOLEAN: - return sizeof(MonoBoolean); - case MONO_TYPE_CHAR: - return sizeof(uint16_t); - case MONO_TYPE_I1: - return sizeof(int8_t); - case MONO_TYPE_I2: - return sizeof(int16_t); - case MONO_TYPE_I4: - return sizeof(int32_t); - case MONO_TYPE_I8: - return sizeof(int64_t); - case MONO_TYPE_U1: - return sizeof(uint8_t); - case MONO_TYPE_U2: - return sizeof(uint16_t); - case MONO_TYPE_U4: - return sizeof(uint32_t); - case MONO_TYPE_U8: - return sizeof(uint64_t); - case MONO_TYPE_R4: - return sizeof(float); - case MONO_TYPE_R8: - return sizeof(double); - case MONO_TYPE_VALUETYPE: { - GDMonoClass *vtclass = p_type.type_class; - -#define RETURN_CHECK_FOR_STRUCT(m_struct) \ - if (vtclass == CACHED_CLASS(m_struct)) { \ - return sizeof(M_##m_struct); \ - } - - RETURN_CHECK_FOR_STRUCT(Vector2); - RETURN_CHECK_FOR_STRUCT(Vector2i); - RETURN_CHECK_FOR_STRUCT(Rect2); - RETURN_CHECK_FOR_STRUCT(Rect2i); - RETURN_CHECK_FOR_STRUCT(Transform2D); - RETURN_CHECK_FOR_STRUCT(Vector3); - RETURN_CHECK_FOR_STRUCT(Vector3i); - RETURN_CHECK_FOR_STRUCT(Basis); - RETURN_CHECK_FOR_STRUCT(Quaternion); - RETURN_CHECK_FOR_STRUCT(Transform3D); - RETURN_CHECK_FOR_STRUCT(AABB); - RETURN_CHECK_FOR_STRUCT(Color); - RETURN_CHECK_FOR_STRUCT(Plane); - RETURN_CHECK_FOR_STRUCT(Callable); - RETURN_CHECK_FOR_STRUCT(SignalInfo); - -#undef RETURN_CHECK_FOR_STRUCT - - if (mono_class_is_enum(vtclass->get_mono_ptr())) { - MonoType *enum_basetype = mono_class_enum_basetype(vtclass->get_mono_ptr()); - switch (mono_type_get_type(enum_basetype)) { - case MONO_TYPE_BOOLEAN: - return sizeof(MonoBoolean); - case MONO_TYPE_CHAR: - return sizeof(uint16_t); - case MONO_TYPE_I1: - return sizeof(int8_t); - case MONO_TYPE_I2: - return sizeof(int16_t); - case MONO_TYPE_I4: - return sizeof(int32_t); - case MONO_TYPE_I8: - return sizeof(int64_t); - case MONO_TYPE_U1: - return sizeof(uint8_t); - case MONO_TYPE_U2: - return sizeof(uint16_t); - case MONO_TYPE_U4: - return sizeof(uint32_t); - case MONO_TYPE_U8: - return sizeof(uint64_t); - default: { - // Enum with unsupported base type. We return nullptr MonoObject* on error. - return zero_for_mono_object; - } - } - } - - // Enum with unsupported value type. We return nullptr MonoObject* on error. - } break; - case MONO_TYPE_STRING: - return zero_for_mono_object; - case MONO_TYPE_ARRAY: - case MONO_TYPE_SZARRAY: - case MONO_TYPE_CLASS: - case MONO_TYPE_GENERICINST: - return zero_for_mono_object; - case MONO_TYPE_OBJECT: - return zero_for_mono_object; - } - - // Unsupported type encoding. We return nullptr MonoObject* on error. - return zero_for_mono_object; -} - -void *variant_to_managed_unboxed(const Variant &p_var, const ManagedType &p_type, void *r_buffer, unsigned int &r_offset) { -#define RETURN_TYPE_VAL(m_type, m_val) \ - *reinterpret_cast<m_type *>(r_buffer) = m_val; \ - r_offset += sizeof(m_type); \ - return r_buffer; - - switch (p_type.type_encoding) { - case MONO_TYPE_BOOLEAN: - RETURN_TYPE_VAL(MonoBoolean, (MonoBoolean)p_var.operator bool()); - case MONO_TYPE_CHAR: - RETURN_TYPE_VAL(uint16_t, p_var.operator unsigned short()); - case MONO_TYPE_I1: - RETURN_TYPE_VAL(int8_t, p_var.operator signed char()); - case MONO_TYPE_I2: - RETURN_TYPE_VAL(int16_t, p_var.operator signed short()); - case MONO_TYPE_I4: - RETURN_TYPE_VAL(int32_t, p_var.operator signed int()); - case MONO_TYPE_I8: - RETURN_TYPE_VAL(int64_t, p_var.operator int64_t()); - case MONO_TYPE_U1: - RETURN_TYPE_VAL(uint8_t, p_var.operator unsigned char()); - case MONO_TYPE_U2: - RETURN_TYPE_VAL(uint16_t, p_var.operator unsigned short()); - case MONO_TYPE_U4: - RETURN_TYPE_VAL(uint32_t, p_var.operator unsigned int()); - case MONO_TYPE_U8: - RETURN_TYPE_VAL(uint64_t, p_var.operator uint64_t()); - case MONO_TYPE_R4: - RETURN_TYPE_VAL(float, p_var.operator float()); - case MONO_TYPE_R8: - RETURN_TYPE_VAL(double, p_var.operator double()); - case MONO_TYPE_VALUETYPE: { - GDMonoClass *vtclass = p_type.type_class; - -#define RETURN_CHECK_FOR_STRUCT(m_struct) \ - if (vtclass == CACHED_CLASS(m_struct)) { \ - GDMonoMarshal::M_##m_struct from = MARSHALLED_OUT(m_struct, p_var.operator ::m_struct()); \ - RETURN_TYPE_VAL(M_##m_struct, from); \ - } - - RETURN_CHECK_FOR_STRUCT(Vector2); - RETURN_CHECK_FOR_STRUCT(Vector2i); - RETURN_CHECK_FOR_STRUCT(Rect2); - RETURN_CHECK_FOR_STRUCT(Rect2i); - RETURN_CHECK_FOR_STRUCT(Transform2D); - RETURN_CHECK_FOR_STRUCT(Vector3); - RETURN_CHECK_FOR_STRUCT(Vector3i); - RETURN_CHECK_FOR_STRUCT(Basis); - RETURN_CHECK_FOR_STRUCT(Quaternion); - RETURN_CHECK_FOR_STRUCT(Transform3D); - RETURN_CHECK_FOR_STRUCT(AABB); - RETURN_CHECK_FOR_STRUCT(Color); - RETURN_CHECK_FOR_STRUCT(Plane); - -#undef RETURN_CHECK_FOR_STRUCT - - if (vtclass == CACHED_CLASS(Callable)) { - GDMonoMarshal::M_Callable from = GDMonoMarshal::callable_to_managed(p_var.operator Callable()); - RETURN_TYPE_VAL(M_Callable, from); - } - - if (vtclass == CACHED_CLASS(SignalInfo)) { - GDMonoMarshal::M_SignalInfo from = GDMonoMarshal::signal_info_to_managed(p_var.operator Signal()); - RETURN_TYPE_VAL(M_SignalInfo, from); - } - - if (mono_class_is_enum(vtclass->get_mono_ptr())) { - MonoType *enum_basetype = mono_class_enum_basetype(vtclass->get_mono_ptr()); - switch (mono_type_get_type(enum_basetype)) { - case MONO_TYPE_BOOLEAN: { - MonoBoolean val = p_var.operator bool(); - RETURN_TYPE_VAL(MonoBoolean, val); - } - case MONO_TYPE_CHAR: { - uint16_t val = p_var.operator unsigned short(); - RETURN_TYPE_VAL(uint16_t, val); - } - case MONO_TYPE_I1: { - int8_t val = p_var.operator signed char(); - RETURN_TYPE_VAL(int8_t, val); - } - case MONO_TYPE_I2: { - int16_t val = p_var.operator signed short(); - RETURN_TYPE_VAL(int16_t, val); - } - case MONO_TYPE_I4: { - int32_t val = p_var.operator signed int(); - RETURN_TYPE_VAL(int32_t, val); - } - case MONO_TYPE_I8: { - int64_t val = p_var.operator int64_t(); - RETURN_TYPE_VAL(int64_t, val); - } - case MONO_TYPE_U1: { - uint8_t val = p_var.operator unsigned char(); - RETURN_TYPE_VAL(uint8_t, val); - } - case MONO_TYPE_U2: { - uint16_t val = p_var.operator unsigned short(); - RETURN_TYPE_VAL(uint16_t, val); - } - case MONO_TYPE_U4: { - uint32_t val = p_var.operator unsigned int(); - RETURN_TYPE_VAL(uint32_t, val); - } - case MONO_TYPE_U8: { - uint64_t val = p_var.operator uint64_t(); - RETURN_TYPE_VAL(uint64_t, val); - } - default: { - ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to enum value of unsupported base type: '" + GDMonoClass::get_full_name(mono_class_from_mono_type(enum_basetype)) + "'."); - } - } - } - - ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported value type: '" + p_type.type_class->get_full_name() + "'."); - } break; -#undef RETURN_TYPE_VAL - case MONO_TYPE_STRING: - return variant_to_mono_string(p_var); - case MONO_TYPE_ARRAY: - case MONO_TYPE_SZARRAY: - return variant_to_mono_array(p_var, p_type.type_class); - case MONO_TYPE_CLASS: - return variant_to_mono_object_of_class(p_var, p_type.type_class); - case MONO_TYPE_GENERICINST: - return variant_to_mono_object_of_genericinst(p_var, p_type.type_class); - case MONO_TYPE_OBJECT: - return variant_to_mono_object(p_var); - } - - ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported type with encoding: " + itos(p_type.type_encoding) + "."); -} - -MonoObject *variant_to_mono_object(const Variant &p_var, const ManagedType &p_type) { - switch (p_type.type_encoding) { - case MONO_TYPE_BOOLEAN: { - MonoBoolean val = p_var.operator bool(); - return BOX_BOOLEAN(val); - } - case MONO_TYPE_CHAR: { - uint16_t val = p_var.operator unsigned short(); - return BOX_UINT16(val); - } - case MONO_TYPE_I1: { - int8_t val = p_var.operator signed char(); - return BOX_INT8(val); - } - case MONO_TYPE_I2: { - int16_t val = p_var.operator signed short(); - return BOX_INT16(val); - } - case MONO_TYPE_I4: { - int32_t val = p_var.operator signed int(); - return BOX_INT32(val); - } - case MONO_TYPE_I8: { - int64_t val = p_var.operator int64_t(); - return BOX_INT64(val); - } - case MONO_TYPE_U1: { - uint8_t val = p_var.operator unsigned char(); - return BOX_UINT8(val); - } - case MONO_TYPE_U2: { - uint16_t val = p_var.operator unsigned short(); - return BOX_UINT16(val); - } - case MONO_TYPE_U4: { - uint32_t val = p_var.operator unsigned int(); - return BOX_UINT32(val); - } - case MONO_TYPE_U8: { - uint64_t val = p_var.operator uint64_t(); - return BOX_UINT64(val); - } - case MONO_TYPE_R4: { - float val = p_var.operator float(); - return BOX_FLOAT(val); - } - case MONO_TYPE_R8: { - double val = p_var.operator double(); - return BOX_DOUBLE(val); - } - case MONO_TYPE_VALUETYPE: { - GDMonoClass *vtclass = p_type.type_class; - -#define RETURN_CHECK_FOR_STRUCT(m_struct) \ - if (vtclass == CACHED_CLASS(m_struct)) { \ - GDMonoMarshal::M_##m_struct from = MARSHALLED_OUT(m_struct, p_var.operator ::m_struct()); \ - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(m_struct), &from); \ - } - - RETURN_CHECK_FOR_STRUCT(Vector2); - RETURN_CHECK_FOR_STRUCT(Vector2i); - RETURN_CHECK_FOR_STRUCT(Rect2); - RETURN_CHECK_FOR_STRUCT(Rect2i); - RETURN_CHECK_FOR_STRUCT(Transform2D); - RETURN_CHECK_FOR_STRUCT(Vector3); - RETURN_CHECK_FOR_STRUCT(Vector3i); - RETURN_CHECK_FOR_STRUCT(Basis); - RETURN_CHECK_FOR_STRUCT(Quaternion); - RETURN_CHECK_FOR_STRUCT(Transform3D); - RETURN_CHECK_FOR_STRUCT(AABB); - RETURN_CHECK_FOR_STRUCT(Color); - RETURN_CHECK_FOR_STRUCT(Plane); - -#undef RETURN_CHECK_FOR_STRUCT - - if (vtclass == CACHED_CLASS(Callable)) { - GDMonoMarshal::M_Callable from = GDMonoMarshal::callable_to_managed(p_var.operator Callable()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Callable), &from); - } - - if (vtclass == CACHED_CLASS(SignalInfo)) { - GDMonoMarshal::M_SignalInfo from = GDMonoMarshal::signal_info_to_managed(p_var.operator Signal()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(SignalInfo), &from); - } - - if (mono_class_is_enum(vtclass->get_mono_ptr())) { - MonoType *enum_basetype = mono_class_enum_basetype(vtclass->get_mono_ptr()); - MonoClass *enum_baseclass = mono_class_from_mono_type(enum_basetype); - switch (mono_type_get_type(enum_basetype)) { - case MONO_TYPE_BOOLEAN: { - MonoBoolean val = p_var.operator bool(); - return BOX_ENUM(enum_baseclass, val); - } - case MONO_TYPE_CHAR: { - uint16_t val = p_var.operator unsigned short(); - return BOX_ENUM(enum_baseclass, val); - } - case MONO_TYPE_I1: { - int8_t val = p_var.operator signed char(); - return BOX_ENUM(enum_baseclass, val); - } - case MONO_TYPE_I2: { - int16_t val = p_var.operator signed short(); - return BOX_ENUM(enum_baseclass, val); - } - case MONO_TYPE_I4: { - int32_t val = p_var.operator signed int(); - return BOX_ENUM(enum_baseclass, val); - } - case MONO_TYPE_I8: { - int64_t val = p_var.operator int64_t(); - return BOX_ENUM(enum_baseclass, val); - } - case MONO_TYPE_U1: { - uint8_t val = p_var.operator unsigned char(); - return BOX_ENUM(enum_baseclass, val); - } - case MONO_TYPE_U2: { - uint16_t val = p_var.operator unsigned short(); - return BOX_ENUM(enum_baseclass, val); - } - case MONO_TYPE_U4: { - uint32_t val = p_var.operator unsigned int(); - return BOX_ENUM(enum_baseclass, val); - } - case MONO_TYPE_U8: { - uint64_t val = p_var.operator uint64_t(); - return BOX_ENUM(enum_baseclass, val); - } - default: { - ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to enum value of unsupported base type: '" + GDMonoClass::get_full_name(enum_baseclass) + "'."); - } - } - } - - ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported value type: '" + p_type.type_class->get_full_name() + "'."); - } break; - case MONO_TYPE_STRING: - return (MonoObject *)variant_to_mono_string(p_var); - case MONO_TYPE_ARRAY: - case MONO_TYPE_SZARRAY: - return (MonoObject *)variant_to_mono_array(p_var, p_type.type_class); - case MONO_TYPE_CLASS: - return variant_to_mono_object_of_class(p_var, p_type.type_class); - case MONO_TYPE_GENERICINST: - return variant_to_mono_object_of_genericinst(p_var, p_type.type_class); - case MONO_TYPE_OBJECT: - return variant_to_mono_object(p_var); - } - - ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported type with encoding: " + itos(p_type.type_encoding) + "."); -} - -Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type, bool p_fail_with_err = true) { - ERR_FAIL_COND_V(!p_type.type_class, Variant()); - -#ifdef DEBUG_ENABLED - CRASH_COND_MSG(p_type.type_encoding == MONO_TYPE_OBJECT, "Type of object should be known."); -#endif - - switch (p_type.type_encoding) { - case MONO_TYPE_BOOLEAN: - return (bool)unbox<MonoBoolean>(p_obj); - case MONO_TYPE_CHAR: - return unbox<uint16_t>(p_obj); - case MONO_TYPE_I1: - return unbox<int8_t>(p_obj); - case MONO_TYPE_I2: - return unbox<int16_t>(p_obj); - case MONO_TYPE_I4: - return unbox<int32_t>(p_obj); - case MONO_TYPE_I8: - return unbox<int64_t>(p_obj); - case MONO_TYPE_U1: - return unbox<uint8_t>(p_obj); - case MONO_TYPE_U2: - return unbox<uint16_t>(p_obj); - case MONO_TYPE_U4: - return unbox<uint32_t>(p_obj); - case MONO_TYPE_U8: - return unbox<uint64_t>(p_obj); - case MONO_TYPE_R4: - return unbox<float>(p_obj); - case MONO_TYPE_R8: - return unbox<double>(p_obj); - case MONO_TYPE_VALUETYPE: { - GDMonoClass *vtclass = p_type.type_class; - - if (vtclass == CACHED_CLASS(Vector2)) { - return MARSHALLED_IN(Vector2, unbox_addr<GDMonoMarshal::M_Vector2>(p_obj)); - } - - if (vtclass == CACHED_CLASS(Vector2i)) { - return MARSHALLED_IN(Vector2i, unbox_addr<GDMonoMarshal::M_Vector2i>(p_obj)); - } - - if (vtclass == CACHED_CLASS(Rect2)) { - return MARSHALLED_IN(Rect2, unbox_addr<GDMonoMarshal::M_Rect2>(p_obj)); - } - - if (vtclass == CACHED_CLASS(Rect2i)) { - return MARSHALLED_IN(Rect2i, unbox_addr<GDMonoMarshal::M_Rect2i>(p_obj)); - } - - if (vtclass == CACHED_CLASS(Transform2D)) { - return MARSHALLED_IN(Transform2D, unbox_addr<GDMonoMarshal::M_Transform2D>(p_obj)); - } - - if (vtclass == CACHED_CLASS(Vector3)) { - return MARSHALLED_IN(Vector3, unbox_addr<GDMonoMarshal::M_Vector3>(p_obj)); - } - - if (vtclass == CACHED_CLASS(Vector3i)) { - return MARSHALLED_IN(Vector3i, unbox_addr<GDMonoMarshal::M_Vector3i>(p_obj)); - } - - if (vtclass == CACHED_CLASS(Basis)) { - return MARSHALLED_IN(Basis, unbox_addr<GDMonoMarshal::M_Basis>(p_obj)); - } - - if (vtclass == CACHED_CLASS(Quaternion)) { - return MARSHALLED_IN(Quaternion, unbox_addr<GDMonoMarshal::M_Quaternion>(p_obj)); - } - - if (vtclass == CACHED_CLASS(Transform3D)) { - return MARSHALLED_IN(Transform3D, unbox_addr<GDMonoMarshal::M_Transform3D>(p_obj)); - } - - if (vtclass == CACHED_CLASS(AABB)) { - return MARSHALLED_IN(AABB, unbox_addr<GDMonoMarshal::M_AABB>(p_obj)); - } - - if (vtclass == CACHED_CLASS(Color)) { - return MARSHALLED_IN(Color, unbox_addr<GDMonoMarshal::M_Color>(p_obj)); - } - - if (vtclass == CACHED_CLASS(Plane)) { - return MARSHALLED_IN(Plane, unbox_addr<GDMonoMarshal::M_Plane>(p_obj)); - } - - if (vtclass == CACHED_CLASS(Callable)) { - return managed_to_callable(unbox<GDMonoMarshal::M_Callable>(p_obj)); - } - - if (vtclass == CACHED_CLASS(SignalInfo)) { - return managed_to_signal_info(unbox<GDMonoMarshal::M_SignalInfo>(p_obj)); - } - - if (mono_class_is_enum(vtclass->get_mono_ptr())) { - return unbox<int32_t>(p_obj); - } - } break; - case MONO_TYPE_STRING: { - if (p_obj == nullptr) { - return Variant(); // NIL - } - return mono_string_to_godot_not_null((MonoString *)p_obj); - } break; - case MONO_TYPE_ARRAY: - case MONO_TYPE_SZARRAY: { - MonoArrayType *array_type = mono_type_get_array_type(p_type.type_class->get_mono_type()); - - if (array_type->eklass == CACHED_CLASS_RAW(MonoObject)) { - return mono_array_to_Array((MonoArray *)p_obj); - } - - if (array_type->eklass == CACHED_CLASS_RAW(uint8_t)) { - return mono_array_to_PackedByteArray((MonoArray *)p_obj); - } - - if (array_type->eklass == CACHED_CLASS_RAW(int32_t)) { - return mono_array_to_PackedInt32Array((MonoArray *)p_obj); - } - - if (array_type->eklass == CACHED_CLASS_RAW(int64_t)) { - return mono_array_to_PackedInt64Array((MonoArray *)p_obj); - } - - if (array_type->eklass == CACHED_CLASS_RAW(float)) { - return mono_array_to_PackedFloat32Array((MonoArray *)p_obj); - } - - if (array_type->eklass == CACHED_CLASS_RAW(double)) { - return mono_array_to_PackedFloat64Array((MonoArray *)p_obj); - } - - if (array_type->eklass == CACHED_CLASS_RAW(String)) { - return mono_array_to_PackedStringArray((MonoArray *)p_obj); - } - - if (array_type->eklass == CACHED_CLASS_RAW(Vector2)) { - return mono_array_to_PackedVector2Array((MonoArray *)p_obj); - } - - if (array_type->eklass == CACHED_CLASS_RAW(Vector3)) { - return mono_array_to_PackedVector3Array((MonoArray *)p_obj); - } - - if (array_type->eklass == CACHED_CLASS_RAW(Color)) { - return mono_array_to_PackedColorArray((MonoArray *)p_obj); - } - - if (array_type->eklass == CACHED_CLASS_RAW(StringName)) { - return mono_array_to_Array((MonoArray *)p_obj); - } - - if (array_type->eklass == CACHED_CLASS_RAW(NodePath)) { - return mono_array_to_Array((MonoArray *)p_obj); - } - - if (array_type->eklass == CACHED_CLASS_RAW(RID)) { - return mono_array_to_Array((MonoArray *)p_obj); - } - - GDMonoClass *array_type_class = GDMono::get_singleton()->get_class(array_type->eklass); - if (CACHED_CLASS(GodotObject)->is_assignable_from(array_type_class)) { - return mono_array_to_Array((MonoArray *)p_obj); - } - - if (p_fail_with_err) { - ERR_FAIL_V_MSG(Variant(), "Attempted to convert a managed array of unmarshallable element type to Variant."); - } else { - return Variant(); - } - } break; - case MONO_TYPE_CLASS: { - GDMonoClass *type_class = p_type.type_class; - - // GodotObject - if (CACHED_CLASS(GodotObject)->is_assignable_from(type_class)) { - Object *ptr = unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(p_obj)); - if (ptr != nullptr) { - RefCounted *rc = Object::cast_to<RefCounted>(ptr); - return rc ? Variant(Ref<RefCounted>(rc)) : Variant(ptr); - } - return Variant(); - } - - if (CACHED_CLASS(StringName) == type_class) { - StringName *ptr = unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(p_obj)); - return ptr ? Variant(*ptr) : Variant(); - } - - if (CACHED_CLASS(NodePath) == type_class) { - NodePath *ptr = unbox<NodePath *>(CACHED_FIELD(NodePath, ptr)->get_value(p_obj)); - return ptr ? Variant(*ptr) : Variant(); - } - - if (CACHED_CLASS(RID) == type_class) { - RID *ptr = unbox<RID *>(CACHED_FIELD(RID, ptr)->get_value(p_obj)); - return ptr ? Variant(*ptr) : Variant(); - } - - // Godot.Collections.Dictionary - if (CACHED_CLASS(Dictionary) == type_class) { - MonoException *exc = nullptr; - Dictionary *ptr = CACHED_METHOD_THUNK(Dictionary, GetPtr).invoke(p_obj, &exc); - UNHANDLED_EXCEPTION(exc); - return ptr ? Variant(*ptr) : Variant(); - } - - // Godot.Collections.Array - if (CACHED_CLASS(Array) == type_class) { - MonoException *exc = nullptr; - Array *ptr = CACHED_METHOD_THUNK(Array, GetPtr).invoke(p_obj, &exc); - UNHANDLED_EXCEPTION(exc); - return ptr ? Variant(*ptr) : Variant(); - } - } break; - case MONO_TYPE_GENERICINST: { - MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type()); - - // Godot.Collections.Dictionary<TKey, TValue> - if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) { - MonoException *exc = nullptr; - MonoObject *ret = p_type.type_class->get_method("GetPtr")->invoke(p_obj, &exc); - UNHANDLED_EXCEPTION(exc); - return *unbox<Dictionary *>(ret); - } - - // Godot.Collections.Array<T> - if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) { - MonoException *exc = nullptr; - MonoObject *ret = p_type.type_class->get_method("GetPtr")->invoke(p_obj, &exc); - UNHANDLED_EXCEPTION(exc); - return *unbox<Array *>(ret); - } - - // System.Collections.Generic.Dictionary<TKey, TValue> - if (GDMonoUtils::Marshal::type_is_system_generic_dictionary(reftype)) { - MonoReflectionType *key_reftype = nullptr; - MonoReflectionType *value_reftype = nullptr; - GDMonoUtils::Marshal::dictionary_get_key_value_types(reftype, &key_reftype, &value_reftype); - return system_generic_dict_to_Dictionary(p_obj, p_type.type_class, key_reftype, value_reftype); - } - - // System.Collections.Generic.List<T> - if (GDMonoUtils::Marshal::type_is_system_generic_list(reftype)) { - MonoReflectionType *elem_reftype = nullptr; - GDMonoUtils::Marshal::array_get_element_type(reftype, &elem_reftype); - return system_generic_list_to_Array_variant(p_obj, p_type.type_class, elem_reftype); - } + MonoException *exc = nullptr; + MonoObject *ret = CACHED_METHOD_THUNK(Marshaling, variant_to_mono_object) + .invoke(&p_var, &exc); - // GodotObject - GDMonoClass *type_class = p_type.type_class; - if (CACHED_CLASS(GodotObject)->is_assignable_from(type_class)) { - Object *ptr = unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(p_obj)); - if (ptr != nullptr) { - RefCounted *rc = Object::cast_to<RefCounted>(ptr); - return rc ? Variant(Ref<RefCounted>(rc)) : Variant(ptr); - } - return Variant(); - } - } break; + if (exc) { + GDMonoUtils::debug_print_unhandled_exception(exc); + return nullptr; } - if (p_fail_with_err) { - ERR_FAIL_V_MSG(Variant(), "Attempted to convert an unmarshallable managed type to Variant. Name: '" + p_type.type_class->get_name() + "' Encoding: " + itos(p_type.type_encoding) + "."); - } else { - return Variant(); - } + return ret; } -Variant mono_object_to_variant(MonoObject *p_obj) { +static Variant mono_object_to_variant_impl(MonoObject *p_obj, bool p_fail_with_err) { if (!p_obj) { return Variant(); } - ManagedType type = ManagedType::from_class(mono_object_get_class(p_obj)); + MonoBoolean fail_with_error = p_fail_with_err; - return mono_object_to_variant_impl(p_obj, type); -} + Variant ret; -Variant mono_object_to_variant(MonoObject *p_obj, const ManagedType &p_type) { - if (!p_obj) { + MonoException *exc = nullptr; + CACHED_METHOD_THUNK(Marshaling, mono_object_to_variant_out) + .invoke(p_obj, fail_with_error, &ret, &exc); + + if (exc) { + GDMonoUtils::debug_print_unhandled_exception(exc); return Variant(); } - return mono_object_to_variant_impl(p_obj, p_type); + return ret; } -Variant mono_object_to_variant_no_err(MonoObject *p_obj, const ManagedType &p_type) { - if (!p_obj) { - return Variant(); - } +Variant mono_object_to_variant(MonoObject *p_obj) { + return mono_object_to_variant_impl(p_obj, /* fail_with_err: */ true); +} - return mono_object_to_variant_impl(p_obj, p_type, /* fail_with_err: */ false); +Variant mono_object_to_variant_no_err(MonoObject *p_obj) { + return mono_object_to_variant_impl(p_obj, /* fail_with_err: */ false); } String mono_object_to_variant_string(MonoObject *p_obj, MonoException **r_exc) { @@ -1320,8 +142,7 @@ String mono_object_to_variant_string(MonoObject *p_obj, MonoException **r_exc) { return String("null"); } - ManagedType type = ManagedType::from_class(mono_object_get_class(p_obj)); - Variant var = GDMonoMarshal::mono_object_to_variant_no_err(p_obj, type); + Variant var = GDMonoMarshal::mono_object_to_variant_no_err(p_obj); if (var.get_type() == Variant::NIL) { // `&& p_obj != nullptr` but omitted because always true // Cannot convert MonoObject* to Variant; fallback to 'ToString()'. @@ -1341,90 +162,6 @@ String mono_object_to_variant_string(MonoObject *p_obj, MonoException **r_exc) { } } -MonoObject *Dictionary_to_system_generic_dict(const Dictionary &p_dict, GDMonoClass *p_class, MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype) { - String ctor_desc = ":.ctor(System.Collections.Generic.IDictionary`2<" + GDMonoUtils::get_type_desc(p_key_reftype) + - ", " + GDMonoUtils::get_type_desc(p_value_reftype) + ">)"; - GDMonoMethod *ctor = p_class->get_method_with_desc(ctor_desc, true); - CRASH_COND(ctor == nullptr); - - MonoObject *mono_object = mono_object_new(mono_domain_get(), p_class->get_mono_ptr()); - ERR_FAIL_NULL_V(mono_object, nullptr); - - GDMonoClass *godot_dict_class = GDMonoUtils::Marshal::make_generic_dictionary_type(p_key_reftype, p_value_reftype); - MonoObject *godot_dict = GDMonoUtils::create_managed_from(p_dict, godot_dict_class); - - void *ctor_args[1] = { godot_dict }; - - MonoException *exc = nullptr; - ctor->invoke_raw(mono_object, ctor_args, &exc); - UNHANDLED_EXCEPTION(exc); - - return mono_object; -} - -Dictionary system_generic_dict_to_Dictionary(MonoObject *p_obj, [[maybe_unused]] GDMonoClass *p_class, MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype) { - GDMonoClass *godot_dict_class = GDMonoUtils::Marshal::make_generic_dictionary_type(p_key_reftype, p_value_reftype); - String ctor_desc = ":.ctor(System.Collections.Generic.IDictionary`2<" + GDMonoUtils::get_type_desc(p_key_reftype) + - ", " + GDMonoUtils::get_type_desc(p_value_reftype) + ">)"; - GDMonoMethod *godot_dict_ctor = godot_dict_class->get_method_with_desc(ctor_desc, true); - CRASH_COND(godot_dict_ctor == nullptr); - - MonoObject *godot_dict = mono_object_new(mono_domain_get(), godot_dict_class->get_mono_ptr()); - ERR_FAIL_NULL_V(godot_dict, Dictionary()); - - void *ctor_args[1] = { p_obj }; - - MonoException *exc = nullptr; - godot_dict_ctor->invoke_raw(godot_dict, ctor_args, &exc); - UNHANDLED_EXCEPTION(exc); - - exc = nullptr; - MonoObject *ret = godot_dict_class->get_method("GetPtr")->invoke(godot_dict, &exc); - UNHANDLED_EXCEPTION(exc); - - return *unbox<Dictionary *>(ret); -} - -MonoObject *Array_to_system_generic_list(const Array &p_array, GDMonoClass *p_class, MonoReflectionType *p_elem_reftype) { - MonoType *elem_type = mono_reflection_type_get_type(p_elem_reftype); - - String ctor_desc = ":.ctor(System.Collections.Generic.IEnumerable`1<" + GDMonoUtils::get_type_desc(elem_type) + ">)"; - GDMonoMethod *ctor = p_class->get_method_with_desc(ctor_desc, true); - CRASH_COND(ctor == nullptr); - - MonoObject *mono_object = mono_object_new(mono_domain_get(), p_class->get_mono_ptr()); - ERR_FAIL_NULL_V(mono_object, nullptr); - - GDMonoClass *godot_array_class = GDMonoUtils::Marshal::make_generic_array_type(p_elem_reftype); - MonoObject *godot_array = GDMonoUtils::create_managed_from(p_array, godot_array_class); - - void *ctor_args[1] = { godot_array }; - - MonoException *exc = nullptr; - ctor->invoke_raw(mono_object, ctor_args, &exc); - UNHANDLED_EXCEPTION(exc); - - return mono_object; -} - -Variant system_generic_list_to_Array_variant(MonoObject *p_obj, GDMonoClass *p_class, [[maybe_unused]] MonoReflectionType *p_elem_reftype) { - GDMonoMethod *to_array = p_class->get_method("ToArray", 0); - CRASH_COND(to_array == nullptr); - - MonoException *exc = nullptr; - MonoObject *array = to_array->invoke_raw(p_obj, nullptr, &exc); - UNHANDLED_EXCEPTION(exc); - - ERR_FAIL_NULL_V(array, Variant()); - - ManagedType type = ManagedType::from_class(mono_object_get_class(array)); - - bool result_is_array = type.type_encoding != MONO_TYPE_SZARRAY && type.type_encoding != MONO_TYPE_ARRAY; - ERR_FAIL_COND_V(result_is_array, Variant()); - - return mono_object_to_variant(array, type); -} - MonoArray *Array_to_mono_array(const Array &p_array) { int length = p_array.size(); MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), length); @@ -1437,18 +174,6 @@ MonoArray *Array_to_mono_array(const Array &p_array) { return ret; } -MonoArray *Array_to_mono_array(const Array &p_array, MonoClass *p_array_type_class) { - int length = p_array.size(); - MonoArray *ret = mono_array_new(mono_domain_get(), p_array_type_class, length); - - for (int i = 0; i < length; i++) { - MonoObject *boxed = variant_to_mono_object(p_array[i]); - mono_array_setref(ret, i, boxed); - } - - return ret; -} - Array mono_array_to_Array(MonoArray *p_array) { Array ret; if (!p_array) { @@ -1465,141 +190,6 @@ Array mono_array_to_Array(MonoArray *p_array) { return ret; } -MonoArray *PackedInt32Array_to_mono_array(const PackedInt32Array &p_array) { - const int32_t *src = p_array.ptr(); - int length = p_array.size(); - - MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(int32_t), length); - - int32_t *dst = mono_array_addr(ret, int32_t, 0); - memcpy(dst, src, length * sizeof(int32_t)); - - return ret; -} - -PackedInt32Array mono_array_to_PackedInt32Array(MonoArray *p_array) { - PackedInt32Array ret; - if (!p_array) { - return ret; - } - int length = mono_array_length(p_array); - ret.resize(length); - int32_t *dst = ret.ptrw(); - - const int32_t *src = mono_array_addr(p_array, int32_t, 0); - memcpy(dst, src, length * sizeof(int32_t)); - - return ret; -} - -MonoArray *PackedInt64Array_to_mono_array(const PackedInt64Array &p_array) { - const int64_t *src = p_array.ptr(); - int length = p_array.size(); - - MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(int64_t), length); - - int64_t *dst = mono_array_addr(ret, int64_t, 0); - memcpy(dst, src, length * sizeof(int64_t)); - - return ret; -} - -PackedInt64Array mono_array_to_PackedInt64Array(MonoArray *p_array) { - PackedInt64Array ret; - if (!p_array) { - return ret; - } - int length = mono_array_length(p_array); - ret.resize(length); - int64_t *dst = ret.ptrw(); - - const int64_t *src = mono_array_addr(p_array, int64_t, 0); - memcpy(dst, src, length * sizeof(int64_t)); - - return ret; -} - -MonoArray *PackedByteArray_to_mono_array(const PackedByteArray &p_array) { - const uint8_t *src = p_array.ptr(); - int length = p_array.size(); - - MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(uint8_t), length); - - uint8_t *dst = mono_array_addr(ret, uint8_t, 0); - memcpy(dst, src, length * sizeof(uint8_t)); - - return ret; -} - -PackedByteArray mono_array_to_PackedByteArray(MonoArray *p_array) { - PackedByteArray ret; - if (!p_array) { - return ret; - } - int length = mono_array_length(p_array); - ret.resize(length); - uint8_t *dst = ret.ptrw(); - - const uint8_t *src = mono_array_addr(p_array, uint8_t, 0); - memcpy(dst, src, length * sizeof(uint8_t)); - - return ret; -} - -MonoArray *PackedFloat32Array_to_mono_array(const PackedFloat32Array &p_array) { - const float *src = p_array.ptr(); - int length = p_array.size(); - - MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(float), length); - - float *dst = mono_array_addr(ret, float, 0); - memcpy(dst, src, length * sizeof(float)); - - return ret; -} - -PackedFloat32Array mono_array_to_PackedFloat32Array(MonoArray *p_array) { - PackedFloat32Array ret; - if (!p_array) { - return ret; - } - int length = mono_array_length(p_array); - ret.resize(length); - float *dst = ret.ptrw(); - - const float *src = mono_array_addr(p_array, float, 0); - memcpy(dst, src, length * sizeof(float)); - - return ret; -} - -MonoArray *PackedFloat64Array_to_mono_array(const PackedFloat64Array &p_array) { - const double *src = p_array.ptr(); - int length = p_array.size(); - - MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(double), length); - - double *dst = mono_array_addr(ret, double, 0); - memcpy(dst, src, length * sizeof(double)); - - return ret; -} - -PackedFloat64Array mono_array_to_PackedFloat64Array(MonoArray *p_array) { - PackedFloat64Array ret; - if (!p_array) { - return ret; - } - int length = mono_array_length(p_array); - ret.resize(length); - double *dst = ret.ptrw(); - - const double *src = mono_array_addr(p_array, double, 0); - memcpy(dst, src, length * sizeof(double)); - - return ret; -} - MonoArray *PackedStringArray_to_mono_array(const PackedStringArray &p_array) { const String *r = p_array.ptr(); int length = p_array.size(); @@ -1613,212 +203,4 @@ MonoArray *PackedStringArray_to_mono_array(const PackedStringArray &p_array) { return ret; } - -PackedStringArray mono_array_to_PackedStringArray(MonoArray *p_array) { - PackedStringArray ret; - if (!p_array) { - return ret; - } - int length = mono_array_length(p_array); - ret.resize(length); - String *w = ret.ptrw(); - - for (int i = 0; i < length; i++) { - MonoString *elem = mono_array_get(p_array, MonoString *, i); - w[i] = mono_string_to_godot(elem); - } - - return ret; -} - -MonoArray *PackedColorArray_to_mono_array(const PackedColorArray &p_array) { - const Color *src = p_array.ptr(); - int length = p_array.size(); - - MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Color), length); - - if constexpr (InteropLayout::MATCHES_Color) { - Color *dst = mono_array_addr(ret, Color, 0); - memcpy(dst, src, length * sizeof(Color)); - } else { - for (int i = 0; i < length; i++) { - M_Color *raw = (M_Color *)mono_array_addr_with_size(ret, sizeof(M_Color), i); - *raw = MARSHALLED_OUT(Color, src[i]); - } - } - - return ret; -} - -PackedColorArray mono_array_to_PackedColorArray(MonoArray *p_array) { - PackedColorArray ret; - if (!p_array) { - return ret; - } - int length = mono_array_length(p_array); - ret.resize(length); - Color *dst = ret.ptrw(); - - if constexpr (InteropLayout::MATCHES_Color) { - const Color *src = mono_array_addr(p_array, Color, 0); - memcpy(dst, src, length * sizeof(Color)); - } else { - for (int i = 0; i < length; i++) { - dst[i] = MARSHALLED_IN(Color, (M_Color *)mono_array_addr_with_size(p_array, sizeof(M_Color), i)); - } - } - - return ret; -} - -MonoArray *PackedVector2Array_to_mono_array(const PackedVector2Array &p_array) { - const Vector2 *src = p_array.ptr(); - int length = p_array.size(); - - MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Vector2), length); - - if constexpr (InteropLayout::MATCHES_Vector2) { - Vector2 *dst = mono_array_addr(ret, Vector2, 0); - memcpy(dst, src, length * sizeof(Vector2)); - } else { - for (int i = 0; i < length; i++) { - M_Vector2 *raw = (M_Vector2 *)mono_array_addr_with_size(ret, sizeof(M_Vector2), i); - *raw = MARSHALLED_OUT(Vector2, src[i]); - } - } - - return ret; -} - -PackedVector2Array mono_array_to_PackedVector2Array(MonoArray *p_array) { - PackedVector2Array ret; - if (!p_array) { - return ret; - } - int length = mono_array_length(p_array); - ret.resize(length); - Vector2 *dst = ret.ptrw(); - - if constexpr (InteropLayout::MATCHES_Vector2) { - const Vector2 *src = mono_array_addr(p_array, Vector2, 0); - memcpy(dst, src, length * sizeof(Vector2)); - } else { - for (int i = 0; i < length; i++) { - dst[i] = MARSHALLED_IN(Vector2, (M_Vector2 *)mono_array_addr_with_size(p_array, sizeof(M_Vector2), i)); - } - } - - return ret; -} - -MonoArray *PackedVector3Array_to_mono_array(const PackedVector3Array &p_array) { - const Vector3 *src = p_array.ptr(); - int length = p_array.size(); - - MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Vector3), length); - - if constexpr (InteropLayout::MATCHES_Vector3) { - Vector3 *dst = mono_array_addr(ret, Vector3, 0); - memcpy(dst, src, length * sizeof(Vector3)); - } else { - for (int i = 0; i < length; i++) { - M_Vector3 *raw = (M_Vector3 *)mono_array_addr_with_size(ret, sizeof(M_Vector3), i); - *raw = MARSHALLED_OUT(Vector3, src[i]); - } - } - - return ret; -} - -PackedVector3Array mono_array_to_PackedVector3Array(MonoArray *p_array) { - PackedVector3Array ret; - if (!p_array) { - return ret; - } - int length = mono_array_length(p_array); - ret.resize(length); - Vector3 *dst = ret.ptrw(); - - if constexpr (InteropLayout::MATCHES_Vector3) { - const Vector3 *src = mono_array_addr(p_array, Vector3, 0); - memcpy(dst, src, length * sizeof(Vector3)); - } else { - for (int i = 0; i < length; i++) { - dst[i] = MARSHALLED_IN(Vector3, (M_Vector3 *)mono_array_addr_with_size(p_array, sizeof(M_Vector3), i)); - } - } - - return ret; -} - -Callable managed_to_callable(const M_Callable &p_managed_callable) { - if (p_managed_callable.delegate) { - // TODO: Use pooling for ManagedCallable instances. - CallableCustom *managed_callable = memnew(ManagedCallable(p_managed_callable.delegate)); - return Callable(managed_callable); - } else { - Object *target = p_managed_callable.target - ? unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(p_managed_callable.target)) - : nullptr; - StringName *method_ptr = p_managed_callable.method_string_name - ? unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(p_managed_callable.method_string_name)) - : nullptr; - StringName method = method_ptr ? *method_ptr : StringName(); - return Callable(target, method); - } -} - -M_Callable callable_to_managed(const Callable &p_callable) { - if (p_callable.is_custom()) { - CallableCustom *custom = p_callable.get_custom(); - CallableCustom::CompareEqualFunc compare_equal_func = custom->get_compare_equal_func(); - - if (compare_equal_func == ManagedCallable::compare_equal_func_ptr) { - ManagedCallable *managed_callable = static_cast<ManagedCallable *>(custom); - return { - nullptr, nullptr, - managed_callable->get_delegate() - }; - } else if (compare_equal_func == SignalAwaiterCallable::compare_equal_func_ptr) { - SignalAwaiterCallable *signal_awaiter_callable = static_cast<SignalAwaiterCallable *>(custom); - return { - GDMonoUtils::unmanaged_get_managed(ObjectDB::get_instance(signal_awaiter_callable->get_object())), - GDMonoUtils::create_managed_from(signal_awaiter_callable->get_signal()), - nullptr - }; - } else if (compare_equal_func == EventSignalCallable::compare_equal_func_ptr) { - EventSignalCallable *event_signal_callable = static_cast<EventSignalCallable *>(custom); - return { - GDMonoUtils::unmanaged_get_managed(ObjectDB::get_instance(event_signal_callable->get_object())), - GDMonoUtils::create_managed_from(event_signal_callable->get_signal()), - nullptr - }; - } - - // Some other CallableCustom. We only support ManagedCallable. - return { nullptr, nullptr, nullptr }; - } else { - MonoObject *target_managed = GDMonoUtils::unmanaged_get_managed(p_callable.get_object()); - MonoObject *method_string_name_managed = GDMonoUtils::create_managed_from(p_callable.get_method()); - return { target_managed, method_string_name_managed, nullptr }; - } -} - -Signal managed_to_signal_info(const M_SignalInfo &p_managed_signal) { - Object *owner = p_managed_signal.owner - ? unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(p_managed_signal.owner)) - : nullptr; - StringName *name_ptr = p_managed_signal.name_string_name - ? unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(p_managed_signal.name_string_name)) - : nullptr; - StringName name = name_ptr ? *name_ptr : StringName(); - return Signal(owner, name); -} - -M_SignalInfo signal_info_to_managed(const Signal &p_signal) { - Object *owner = p_signal.get_object(); - MonoObject *owner_managed = GDMonoUtils::unmanaged_get_managed(owner); - MonoObject *name_string_name_managed = GDMonoUtils::create_managed_from(p_signal.get_name()); - return { owner_managed, name_string_name_managed }; -} } // namespace GDMonoMarshal diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h index 51f11ab18a..16683de51a 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-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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,7 +33,6 @@ #include "core/variant/variant.h" -#include "../managed_callable.h" #include "gd_mono.h" #include "gd_mono_utils.h" @@ -44,25 +43,6 @@ T unbox(MonoObject *p_obj) { return *(T *)mono_object_unbox(p_obj); } -template <typename T> -T *unbox_addr(MonoObject *p_obj) { - return (T *)mono_object_unbox(p_obj); -} - -#define BOX_DOUBLE(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(double), &x) -#define BOX_FLOAT(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(float), &x) -#define BOX_INT64(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(int64_t), &x) -#define BOX_INT32(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(int32_t), &x) -#define BOX_INT16(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(int16_t), &x) -#define BOX_INT8(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(int8_t), &x) -#define BOX_UINT64(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(uint64_t), &x) -#define BOX_UINT32(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(uint32_t), &x) -#define BOX_UINT16(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(uint16_t), &x) -#define BOX_UINT8(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(uint8_t), &x) -#define BOX_BOOLEAN(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(bool), &x) -#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, bool *r_nil_is_variant = nullptr); bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_elem_type); @@ -90,516 +70,37 @@ _FORCE_INLINE_ MonoString *mono_string_from_godot(const String &p_string) { // Variant -size_t variant_get_managed_unboxed_size(const ManagedType &p_type); -void *variant_to_managed_unboxed(const Variant &p_var, const ManagedType &p_type, void *r_buffer, unsigned int &r_offset); -MonoObject *variant_to_mono_object(const Variant &p_var, const ManagedType &p_type); +MonoObject *variant_to_mono_object_of_type(const Variant &p_var, const ManagedType &p_type); MonoObject *variant_to_mono_object(const Variant &p_var); -MonoArray *variant_to_mono_array(const Variant &p_var, GDMonoClass *p_type_class); -MonoObject *variant_to_mono_object_of_class(const Variant &p_var, GDMonoClass *p_type_class); -MonoObject *variant_to_mono_object_of_genericinst(const Variant &p_var, GDMonoClass *p_type_class); -MonoString *variant_to_mono_string(const Variant &p_var); // These overloads were added to avoid passing a `const Variant *` to the `const Variant &` // parameter. That would result in the `Variant(bool)` copy constructor being called as // pointers are implicitly converted to bool. Implicit conversions are f-ing evil. -_FORCE_INLINE_ void *variant_to_managed_unboxed(const Variant *p_var, const ManagedType &p_type, void *r_buffer, unsigned int &r_offset) { - return variant_to_managed_unboxed(*p_var, p_type, r_buffer, r_offset); -} -_FORCE_INLINE_ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_type) { - return variant_to_mono_object(*p_var, p_type); +_FORCE_INLINE_ MonoObject *variant_to_mono_object_of_type(const Variant *p_var, const ManagedType &p_type) { + return variant_to_mono_object_of_type(*p_var, p_type); } _FORCE_INLINE_ MonoObject *variant_to_mono_object(const Variant *p_var) { return variant_to_mono_object(*p_var); } -_FORCE_INLINE_ MonoArray *variant_to_mono_array(const Variant *p_var, GDMonoClass *p_type_class) { - return variant_to_mono_array(*p_var, p_type_class); -} -_FORCE_INLINE_ MonoObject *variant_to_mono_object_of_class(const Variant *p_var, GDMonoClass *p_type_class) { - return variant_to_mono_object_of_class(*p_var, p_type_class); -} -_FORCE_INLINE_ MonoObject *variant_to_mono_object_of_genericinst(const Variant *p_var, GDMonoClass *p_type_class) { - return variant_to_mono_object_of_genericinst(*p_var, p_type_class); -} -_FORCE_INLINE_ MonoString *variant_to_mono_string(const Variant *p_var) { - return variant_to_mono_string(*p_var); -} Variant mono_object_to_variant(MonoObject *p_obj); -Variant mono_object_to_variant(MonoObject *p_obj, const ManagedType &p_type); -Variant mono_object_to_variant_no_err(MonoObject *p_obj, const ManagedType &p_type); +Variant mono_object_to_variant_no_err(MonoObject *p_obj); /// Tries to convert the MonoObject* to Variant and then convert the Variant to String. /// If the MonoObject* cannot be converted to Variant, then 'ToString()' is called instead. String mono_object_to_variant_string(MonoObject *p_obj, MonoException **r_exc); -// System.Collections.Generic - -MonoObject *Dictionary_to_system_generic_dict(const Dictionary &p_dict, GDMonoClass *p_class, MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype); -Dictionary system_generic_dict_to_Dictionary(MonoObject *p_obj, GDMonoClass *p_class, MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype); - -MonoObject *Array_to_system_generic_list(const Array &p_array, GDMonoClass *p_class, MonoReflectionType *p_elem_reftype); -Variant system_generic_list_to_Array_variant(MonoObject *p_obj, GDMonoClass *p_class, MonoReflectionType *p_elem_reftype); - // Array MonoArray *Array_to_mono_array(const Array &p_array); -MonoArray *Array_to_mono_array(const Array &p_array, MonoClass *p_array_type_class); Array mono_array_to_Array(MonoArray *p_array); -// PackedInt32Array - -MonoArray *PackedInt32Array_to_mono_array(const PackedInt32Array &p_array); -PackedInt32Array mono_array_to_PackedInt32Array(MonoArray *p_array); - -// PackedInt64Array - -MonoArray *PackedInt64Array_to_mono_array(const PackedInt64Array &p_array); -PackedInt64Array mono_array_to_PackedInt64Array(MonoArray *p_array); - -// PackedByteArray - -MonoArray *PackedByteArray_to_mono_array(const PackedByteArray &p_array); -PackedByteArray mono_array_to_PackedByteArray(MonoArray *p_array); - -// PackedFloat32Array - -MonoArray *PackedFloat32Array_to_mono_array(const PackedFloat32Array &p_array); -PackedFloat32Array mono_array_to_PackedFloat32Array(MonoArray *p_array); - -// PackedFloat64Array - -MonoArray *PackedFloat64Array_to_mono_array(const PackedFloat64Array &p_array); -PackedFloat64Array mono_array_to_PackedFloat64Array(MonoArray *p_array); - // PackedStringArray MonoArray *PackedStringArray_to_mono_array(const PackedStringArray &p_array); -PackedStringArray mono_array_to_PackedStringArray(MonoArray *p_array); - -// PackedColorArray - -MonoArray *PackedColorArray_to_mono_array(const PackedColorArray &p_array); -PackedColorArray mono_array_to_PackedColorArray(MonoArray *p_array); - -// PackedVector2Array - -MonoArray *PackedVector2Array_to_mono_array(const PackedVector2Array &p_array); -PackedVector2Array mono_array_to_PackedVector2Array(MonoArray *p_array); - -// PackedVector3Array - -MonoArray *PackedVector3Array_to_mono_array(const PackedVector3Array &p_array); -PackedVector3Array mono_array_to_PackedVector3Array(MonoArray *p_array); - -#pragma pack(push, 1) - -struct M_Callable { - MonoObject *target = nullptr; - MonoObject *method_string_name = nullptr; - MonoDelegate *delegate = nullptr; -}; - -struct M_SignalInfo { - MonoObject *owner = nullptr; - MonoObject *name_string_name = nullptr; -}; - -#pragma pack(pop) - -// Callable -Callable managed_to_callable(const M_Callable &p_managed_callable); -M_Callable callable_to_managed(const Callable &p_callable); - -// SignalInfo -Signal managed_to_signal_info(const M_SignalInfo &p_managed_signal); -M_SignalInfo signal_info_to_managed(const Signal &p_signal); - -// Structures - -namespace InteropLayout { - -enum { - MATCHES_int = (sizeof(int32_t) == sizeof(uint32_t)), - - MATCHES_float = (sizeof(float) == sizeof(uint32_t)), - - MATCHES_double = (sizeof(double) == sizeof(uint64_t)), - -#ifdef REAL_T_IS_DOUBLE - MATCHES_real_t = (sizeof(real_t) == sizeof(uint64_t)), -#else - MATCHES_real_t = (sizeof(real_t) == sizeof(uint32_t)), -#endif - - MATCHES_Vector2 = (MATCHES_real_t && (sizeof(Vector2) == (sizeof(real_t) * 2)) && - offsetof(Vector2, x) == (sizeof(real_t) * 0) && - offsetof(Vector2, y) == (sizeof(real_t) * 1)), - - MATCHES_Vector2i = (MATCHES_int && (sizeof(Vector2i) == (sizeof(int32_t) * 2)) && - offsetof(Vector2i, x) == (sizeof(int32_t) * 0) && - offsetof(Vector2i, y) == (sizeof(int32_t) * 1)), - - MATCHES_Rect2 = (MATCHES_Vector2 && (sizeof(Rect2) == (sizeof(Vector2) * 2)) && - offsetof(Rect2, position) == (sizeof(Vector2) * 0) && - offsetof(Rect2, size) == (sizeof(Vector2) * 1)), - - MATCHES_Rect2i = (MATCHES_Vector2i && (sizeof(Rect2i) == (sizeof(Vector2i) * 2)) && - offsetof(Rect2i, position) == (sizeof(Vector2i) * 0) && - offsetof(Rect2i, size) == (sizeof(Vector2i) * 1)), - - MATCHES_Transform2D = (MATCHES_Vector2 && (sizeof(Transform2D) == (sizeof(Vector2) * 3))), // No field offset required, it stores an array - - MATCHES_Vector3 = (MATCHES_real_t && (sizeof(Vector3) == (sizeof(real_t) * 3)) && - offsetof(Vector3, x) == (sizeof(real_t) * 0) && - offsetof(Vector3, y) == (sizeof(real_t) * 1) && - offsetof(Vector3, z) == (sizeof(real_t) * 2)), - - MATCHES_Vector4 = (MATCHES_real_t && (sizeof(Vector4) == (sizeof(real_t) * 4)) && - offsetof(Vector4, x) == (sizeof(real_t) * 0) && - offsetof(Vector4, y) == (sizeof(real_t) * 1) && - offsetof(Vector4, z) == (sizeof(real_t) * 2) && - offsetof(Vector4, w) == (sizeof(real_t) * 3)), - - MATCHES_Vector4i = (MATCHES_int && (sizeof(Vector4i) == (sizeof(int32_t) * 4)) && - offsetof(Vector4i, x) == (sizeof(int32_t) * 0) && - offsetof(Vector4i, y) == (sizeof(int32_t) * 1) && - offsetof(Vector4i, z) == (sizeof(int32_t) * 2) && - offsetof(Vector4i, w) == (sizeof(int32_t) * 3)), - - MATCHES_Vector3i = (MATCHES_int && (sizeof(Vector3i) == (sizeof(int32_t) * 3)) && - offsetof(Vector3i, x) == (sizeof(int32_t) * 0) && - offsetof(Vector3i, y) == (sizeof(int32_t) * 1) && - offsetof(Vector3i, z) == (sizeof(int32_t) * 2)), - - MATCHES_Basis = (MATCHES_Vector3 && (sizeof(Basis) == (sizeof(Vector3) * 3))), // No field offset required, it stores an array - - MATCHES_Quaternion = (MATCHES_real_t && (sizeof(Quaternion) == (sizeof(real_t) * 4)) && - offsetof(Quaternion, x) == (sizeof(real_t) * 0) && - offsetof(Quaternion, y) == (sizeof(real_t) * 1) && - offsetof(Quaternion, z) == (sizeof(real_t) * 2) && - offsetof(Quaternion, w) == (sizeof(real_t) * 3)), - - MATCHES_Transform3D = (MATCHES_Basis && MATCHES_Vector3 && (sizeof(Transform3D) == (sizeof(Basis) + sizeof(Vector3))) && - offsetof(Transform3D, basis) == 0 && - offsetof(Transform3D, origin) == sizeof(Basis)), - - MATCHES_Projection = (MATCHES_Vector4 && (sizeof(Projection) == (sizeof(Vector4) * 4))), - - MATCHES_AABB = (MATCHES_Vector3 && (sizeof(AABB) == (sizeof(Vector3) * 2)) && - offsetof(AABB, position) == (sizeof(Vector3) * 0) && - offsetof(AABB, size) == (sizeof(Vector3) * 1)), - - MATCHES_Color = (MATCHES_float && (sizeof(Color) == (sizeof(float) * 4)) && - offsetof(Color, r) == (sizeof(float) * 0) && - offsetof(Color, g) == (sizeof(float) * 1) && - offsetof(Color, b) == (sizeof(float) * 2) && - offsetof(Color, a) == (sizeof(float) * 3)), - - MATCHES_Plane = (MATCHES_Vector3 && MATCHES_real_t && (sizeof(Plane) == (sizeof(Vector3) + sizeof(real_t))) && - offsetof(Plane, normal) == 0 && - offsetof(Plane, d) == sizeof(Vector3)) -}; - -// In the future we may force this if we want to ref return these structs -#ifdef GD_MONO_FORCE_INTEROP_STRUCT_COPY -/* clang-format off */ -static_assert(MATCHES_Vector2 && MATCHES_Rect2 && MATCHES_Transform2D && MATCHES_Vector3 && MATCHES_Vector4 && - MATCHES_Basis && MATCHES_Quaternion && MATCHES_Transform3D && MATCHES_Projection && MATCHES_AABB && MATCHES_Color && - MATCHES_Plane && MATCHES_Vector2i && MATCHES_Rect2i && MATCHES_Vector3i && MATCHES_Vector4i); -/* clang-format on */ -#endif -} // namespace InteropLayout - -#pragma pack(push, 1) - -struct M_Vector2 { - real_t x, y; - - static _FORCE_INLINE_ Vector2 convert_to(const M_Vector2 &p_from) { - return Vector2(p_from.x, p_from.y); - } - - static _FORCE_INLINE_ M_Vector2 convert_from(const Vector2 &p_from) { - M_Vector2 ret = { p_from.x, p_from.y }; - return ret; - } -}; - -struct M_Vector2i { - int32_t x, y; - - static _FORCE_INLINE_ Vector2i convert_to(const M_Vector2i &p_from) { - return Vector2i(p_from.x, p_from.y); - } - - static _FORCE_INLINE_ M_Vector2i convert_from(const Vector2i &p_from) { - M_Vector2i ret = { p_from.x, p_from.y }; - return ret; - } -}; - -struct M_Rect2 { - M_Vector2 position; - M_Vector2 size; - - static _FORCE_INLINE_ Rect2 convert_to(const M_Rect2 &p_from) { - return Rect2(M_Vector2::convert_to(p_from.position), - M_Vector2::convert_to(p_from.size)); - } - - static _FORCE_INLINE_ M_Rect2 convert_from(const Rect2 &p_from) { - M_Rect2 ret = { M_Vector2::convert_from(p_from.position), M_Vector2::convert_from(p_from.size) }; - return ret; - } -}; - -struct M_Rect2i { - M_Vector2i position; - M_Vector2i size; - - static _FORCE_INLINE_ Rect2i convert_to(const M_Rect2i &p_from) { - return Rect2i(M_Vector2i::convert_to(p_from.position), - M_Vector2i::convert_to(p_from.size)); - } - - static _FORCE_INLINE_ M_Rect2i convert_from(const Rect2i &p_from) { - M_Rect2i ret = { M_Vector2i::convert_from(p_from.position), M_Vector2i::convert_from(p_from.size) }; - return ret; - } -}; - -struct M_Transform2D { - M_Vector2 elements[3]; - - static _FORCE_INLINE_ Transform2D convert_to(const M_Transform2D &p_from) { - return Transform2D(p_from.elements[0].x, p_from.elements[0].y, - p_from.elements[1].x, p_from.elements[1].y, - p_from.elements[2].x, p_from.elements[2].y); - } - - static _FORCE_INLINE_ M_Transform2D convert_from(const Transform2D &p_from) { - M_Transform2D ret = { - M_Vector2::convert_from(p_from.columns[0]), - M_Vector2::convert_from(p_from.columns[1]), - M_Vector2::convert_from(p_from.columns[2]) - }; - return ret; - } -}; - -struct M_Vector3 { - real_t x, y, z; - - static _FORCE_INLINE_ Vector3 convert_to(const M_Vector3 &p_from) { - return Vector3(p_from.x, p_from.y, p_from.z); - } - - static _FORCE_INLINE_ M_Vector3 convert_from(const Vector3 &p_from) { - M_Vector3 ret = { p_from.x, p_from.y, p_from.z }; - return ret; - } -}; - -struct M_Vector3i { - int32_t x, y, z; - - static _FORCE_INLINE_ Vector3i convert_to(const M_Vector3i &p_from) { - return Vector3i(p_from.x, p_from.y, p_from.z); - } - - static _FORCE_INLINE_ M_Vector3i convert_from(const Vector3i &p_from) { - M_Vector3i ret = { p_from.x, p_from.y, p_from.z }; - return ret; - } -}; - -struct M_Vector4 { - real_t x, y, z, w; - - static _FORCE_INLINE_ Vector4 convert_to(const M_Vector4 &p_from) { - return Vector4(p_from.x, p_from.y, p_from.z, p_from.w); - } - - static _FORCE_INLINE_ M_Vector4 convert_from(const Vector4 &p_from) { - M_Vector4 ret = { p_from.x, p_from.y, p_from.z, p_from.w }; - return ret; - } -}; - -struct M_Vector4i { - int32_t x, y, z, w; - - static _FORCE_INLINE_ Vector4i convert_to(const M_Vector4i &p_from) { - return Vector4i(p_from.x, p_from.y, p_from.z, p_from.w); - } - - static _FORCE_INLINE_ M_Vector4i convert_from(const Vector4i &p_from) { - M_Vector4i ret = { p_from.x, p_from.y, p_from.z, p_from.w }; - return ret; - } -}; - -struct M_Basis { - M_Vector3 elements[3]; - - static _FORCE_INLINE_ Basis convert_to(const M_Basis &p_from) { - return Basis(M_Vector3::convert_to(p_from.elements[0]), - M_Vector3::convert_to(p_from.elements[1]), - M_Vector3::convert_to(p_from.elements[2])); - } - - static _FORCE_INLINE_ M_Basis convert_from(const Basis &p_from) { - M_Basis ret = { - M_Vector3::convert_from(p_from.rows[0]), - M_Vector3::convert_from(p_from.rows[1]), - M_Vector3::convert_from(p_from.rows[2]) - }; - return ret; - } -}; - -struct M_Quaternion { - real_t x, y, z, w; - - static _FORCE_INLINE_ Quaternion convert_to(const M_Quaternion &p_from) { - return Quaternion(p_from.x, p_from.y, p_from.z, p_from.w); - } - - static _FORCE_INLINE_ M_Quaternion convert_from(const Quaternion &p_from) { - M_Quaternion ret = { p_from.x, p_from.y, p_from.z, p_from.w }; - return ret; - } -}; - -struct M_Transform3D { - M_Basis basis; - M_Vector3 origin; - - static _FORCE_INLINE_ Transform3D convert_to(const M_Transform3D &p_from) { - return Transform3D(M_Basis::convert_to(p_from.basis), M_Vector3::convert_to(p_from.origin)); - } - - static _FORCE_INLINE_ M_Transform3D convert_from(const Transform3D &p_from) { - M_Transform3D ret = { M_Basis::convert_from(p_from.basis), M_Vector3::convert_from(p_from.origin) }; - return ret; - } -}; - -struct M_Projection { - M_Vector4 vec1; - M_Vector4 vec2; - M_Vector4 vec3; - M_Vector4 vec4; - - static _FORCE_INLINE_ Projection convert_to(const M_Projection &p_from) { - return Projection(M_Vector4::convert_to(p_from.vec1), M_Vector4::convert_to(p_from.vec2), M_Vector4::convert_to(p_from.vec3), M_Vector4::convert_to(p_from.vec4)); - } - - static _FORCE_INLINE_ M_Projection convert_from(const Projection &p_from) { - M_Projection ret = { M_Vector4::convert_from(p_from.matrix[0]), M_Vector4::convert_from(p_from.matrix[1]), M_Vector4::convert_from(p_from.matrix[2]), M_Vector4::convert_from(p_from.matrix[3]) }; - return ret; - } -}; - -struct M_AABB { - M_Vector3 position; - M_Vector3 size; - - static _FORCE_INLINE_ AABB convert_to(const M_AABB &p_from) { - return AABB(M_Vector3::convert_to(p_from.position), M_Vector3::convert_to(p_from.size)); - } - - static _FORCE_INLINE_ M_AABB convert_from(const AABB &p_from) { - M_AABB ret = { M_Vector3::convert_from(p_from.position), M_Vector3::convert_from(p_from.size) }; - return ret; - } -}; - -struct M_Color { - float r, g, b, a; - - static _FORCE_INLINE_ Color convert_to(const M_Color &p_from) { - return Color(p_from.r, p_from.g, p_from.b, p_from.a); - } - - static _FORCE_INLINE_ M_Color convert_from(const Color &p_from) { - M_Color ret = { p_from.r, p_from.g, p_from.b, p_from.a }; - return ret; - } -}; - -struct M_Plane { - M_Vector3 normal; - real_t d; - - static _FORCE_INLINE_ Plane convert_to(const M_Plane &p_from) { - return Plane(M_Vector3::convert_to(p_from.normal), p_from.d); - } - - static _FORCE_INLINE_ M_Plane convert_from(const Plane &p_from) { - M_Plane ret = { M_Vector3::convert_from(p_from.normal), p_from.d }; - return ret; - } -}; - -#pragma pack(pop) - -#define DECL_TYPE_MARSHAL_TEMPLATES(m_type) \ - template <int> \ - _FORCE_INLINE_ m_type marshalled_in_##m_type##_impl(const M_##m_type *p_from); \ - \ - template <> \ - _FORCE_INLINE_ m_type marshalled_in_##m_type##_impl<0>(const M_##m_type *p_from) { \ - return M_##m_type::convert_to(*p_from); \ - } \ - \ - template <> \ - _FORCE_INLINE_ m_type marshalled_in_##m_type##_impl<1>(const M_##m_type *p_from) { \ - return *reinterpret_cast<const m_type *>(p_from); \ - } \ - \ - _FORCE_INLINE_ m_type marshalled_in_##m_type(const M_##m_type *p_from) { \ - return marshalled_in_##m_type##_impl<InteropLayout::MATCHES_##m_type>(p_from); \ - } \ - \ - template <int> \ - _FORCE_INLINE_ M_##m_type marshalled_out_##m_type##_impl(const m_type &p_from); \ - \ - template <> \ - _FORCE_INLINE_ M_##m_type marshalled_out_##m_type##_impl<0>(const m_type &p_from) { \ - return M_##m_type::convert_from(p_from); \ - } \ - \ - template <> \ - _FORCE_INLINE_ M_##m_type marshalled_out_##m_type##_impl<1>(const m_type &p_from) { \ - return *reinterpret_cast<const M_##m_type *>(&p_from); \ - } \ - \ - _FORCE_INLINE_ M_##m_type marshalled_out_##m_type(const m_type &p_from) { \ - return marshalled_out_##m_type##_impl<InteropLayout::MATCHES_##m_type>(p_from); \ - } - -DECL_TYPE_MARSHAL_TEMPLATES(Vector2) -DECL_TYPE_MARSHAL_TEMPLATES(Vector2i) -DECL_TYPE_MARSHAL_TEMPLATES(Rect2) -DECL_TYPE_MARSHAL_TEMPLATES(Rect2i) -DECL_TYPE_MARSHAL_TEMPLATES(Transform2D) -DECL_TYPE_MARSHAL_TEMPLATES(Vector3) -DECL_TYPE_MARSHAL_TEMPLATES(Vector3i) -DECL_TYPE_MARSHAL_TEMPLATES(Basis) -DECL_TYPE_MARSHAL_TEMPLATES(Vector4) -DECL_TYPE_MARSHAL_TEMPLATES(Vector4i) -DECL_TYPE_MARSHAL_TEMPLATES(Quaternion) -DECL_TYPE_MARSHAL_TEMPLATES(Transform3D) -DECL_TYPE_MARSHAL_TEMPLATES(Projection) -DECL_TYPE_MARSHAL_TEMPLATES(AABB) -DECL_TYPE_MARSHAL_TEMPLATES(Color) -DECL_TYPE_MARSHAL_TEMPLATES(Plane) -#define MARSHALLED_IN(m_type, m_from_ptr) (GDMonoMarshal::marshalled_in_##m_type(m_from_ptr)) -#define MARSHALLED_OUT(m_type, m_from) (GDMonoMarshal::marshalled_out_##m_type(m_from)) } // namespace GDMonoMarshal #endif // GD_MONO_MARSHAL_H diff --git a/modules/mono/mono_gd/gd_mono_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp index 6734b44783..04f6005338 100644 --- a/modules/mono/mono_gd/gd_mono_method.cpp +++ b/modules/mono/mono_gd/gd_mono_method.cpp @@ -75,10 +75,6 @@ void GDMonoMethod::_update_signature(MonoMethodSignature *p_method_sig) { // clear the cache method_info_fetched = false; method_info = MethodInfo(); - - for (int i = 0; i < params_count; i++) { - params_buffer_size += GDMonoMarshal::variant_get_managed_unboxed_size(param_types[i]); - } } GDMonoClass *GDMonoMethod::get_enclosing_class() const { @@ -111,15 +107,14 @@ MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, MonoObject *ret; if (params_count > 0) { - void **params = (void **)alloca(params_count * sizeof(void *)); - uint8_t *buffer = (uint8_t *)alloca(params_buffer_size); - unsigned int offset = 0; + MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), get_parameters_count()); for (int i = 0; i < params_count; i++) { - params[i] = GDMonoMarshal::variant_to_managed_unboxed(p_params[i], param_types[i], buffer + offset, offset); + MonoObject *boxed_param = GDMonoMarshal::variant_to_mono_object_of_type(p_params[i], param_types[i]); + mono_array_setref(params, i, boxed_param); } - ret = GDMonoUtils::runtime_invoke(mono_method, p_object, params, &exc); + ret = GDMonoUtils::runtime_invoke_array(mono_method, p_object, params, &exc); } else { ret = GDMonoUtils::runtime_invoke(mono_method, p_object, nullptr, &exc); } diff --git a/modules/mono/mono_gd/gd_mono_method.h b/modules/mono/mono_gd/gd_mono_method.h index be11ef5bfe..5398f34103 100644 --- a/modules/mono/mono_gd/gd_mono_method.h +++ b/modules/mono/mono_gd/gd_mono_method.h @@ -39,7 +39,6 @@ class GDMonoMethod : public IMonoClassMember { StringName name; uint16_t params_count; - unsigned int params_buffer_size = 0; ManagedType return_type; Vector<ManagedType> param_types; diff --git a/modules/mono/mono_gd/gd_mono_property.cpp b/modules/mono/mono_gd/gd_mono_property.cpp index c9775ae9cb..7cbf5be151 100644 --- a/modules/mono/mono_gd/gd_mono_property.cpp +++ b/modules/mono/mono_gd/gd_mono_property.cpp @@ -65,8 +65,6 @@ GDMonoProperty::GDMonoProperty(MonoProperty *p_mono_property, GDMonoClass *p_own type.type_class = GDMono::get_singleton()->get_class(param_type_class); } - param_buffer_size = GDMonoMarshal::variant_get_managed_unboxed_size(type); - attrs_fetched = false; attributes = nullptr; } @@ -150,19 +148,16 @@ bool GDMonoProperty::has_setter() { } void GDMonoProperty::set_value_from_variant(MonoObject *p_object, const Variant &p_value, MonoException **r_exc) { - uint8_t *buffer = (uint8_t *)alloca(param_buffer_size); - unsigned int offset = 0; - - void *params[1] = { - GDMonoMarshal::variant_to_managed_unboxed(p_value, type, buffer, offset) - }; + MonoMethod *set_method = mono_property_get_set_method(mono_property); + ERR_FAIL_COND(set_method == nullptr); -#ifdef DEBUG_ENABLED - CRASH_COND(offset != param_buffer_size); -#endif + // Temporary solution, while moving code to C# + MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), 1); + MonoObject *boxed_param = GDMonoMarshal::variant_to_mono_object_of_type(p_value, type); + mono_array_setref(params, 0, boxed_param); MonoException *exc = nullptr; - GDMonoUtils::property_set_value(mono_property, p_object, params, &exc); + GDMonoUtils::runtime_invoke_array(set_method, p_object, params, &exc); if (exc) { if (r_exc) { *r_exc = exc; diff --git a/modules/mono/mono_gd/gd_mono_property.h b/modules/mono/mono_gd/gd_mono_property.h index 6fc681aeb5..885ea8f011 100644 --- a/modules/mono/mono_gd/gd_mono_property.h +++ b/modules/mono/mono_gd/gd_mono_property.h @@ -45,8 +45,6 @@ class GDMonoProperty : public IMonoClassMember { bool attrs_fetched; MonoCustomAttrInfo *attributes = nullptr; - unsigned int param_buffer_size; - public: virtual GDMonoClass *get_enclosing_class() const final { return owner; } diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp index 1983d6ebe2..e240381112 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-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -170,13 +170,6 @@ void runtime_object_init(MonoObject *p_this_obj, GDMonoClass *p_class, MonoExcep ctor->invoke_raw(p_this_obj, nullptr, r_exc); } -bool mono_delegate_equal(MonoDelegate *p_a, MonoDelegate *p_b) { - MonoException *exc = nullptr; - MonoBoolean res = CACHED_METHOD_THUNK(Delegate, Equals).invoke((MonoObject *)p_a, (MonoObject *)p_b, &exc); - UNHANDLED_EXCEPTION(exc); - return (bool)res; -} - GDMonoClass *get_object_class(MonoObject *p_object) { return GDMono::get_singleton()->get_class(mono_object_get_class(p_object)); } @@ -239,102 +232,6 @@ MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringNa return mono_object; } -MonoObject *create_managed_from(const StringName &p_from) { - MonoObject *mono_object = mono_object_new(mono_domain_get(), CACHED_CLASS_RAW(StringName)); - ERR_FAIL_NULL_V(mono_object, nullptr); - - // Construct - GDMonoUtils::runtime_object_init(mono_object, CACHED_CLASS(StringName)); - - CACHED_FIELD(StringName, ptr)->set_value_raw(mono_object, memnew(StringName(p_from))); - - return mono_object; -} - -MonoObject *create_managed_from(const NodePath &p_from) { - MonoObject *mono_object = mono_object_new(mono_domain_get(), CACHED_CLASS_RAW(NodePath)); - ERR_FAIL_NULL_V(mono_object, nullptr); - - // Construct - GDMonoUtils::runtime_object_init(mono_object, CACHED_CLASS(NodePath)); - - CACHED_FIELD(NodePath, ptr)->set_value_raw(mono_object, memnew(NodePath(p_from))); - - return mono_object; -} - -MonoObject *create_managed_from(const RID &p_from) { - MonoObject *mono_object = mono_object_new(mono_domain_get(), CACHED_CLASS_RAW(RID)); - ERR_FAIL_NULL_V(mono_object, nullptr); - - // Construct - GDMonoUtils::runtime_object_init(mono_object, CACHED_CLASS(RID)); - - CACHED_FIELD(RID, ptr)->set_value_raw(mono_object, memnew(RID(p_from))); - - return mono_object; -} - -MonoObject *create_managed_from(const Array &p_from, GDMonoClass *p_class) { - MonoObject *mono_object = mono_object_new(mono_domain_get(), p_class->get_mono_ptr()); - ERR_FAIL_NULL_V(mono_object, nullptr); - - // Search constructor that takes a pointer as parameter - MonoMethod *m; - void *iter = nullptr; - while ((m = mono_class_get_methods(p_class->get_mono_ptr(), &iter))) { - if (strcmp(mono_method_get_name(m), ".ctor") == 0) { - MonoMethodSignature *sig = mono_method_signature(m); - void *front = nullptr; - if (mono_signature_get_param_count(sig) == 1 && - mono_class_from_mono_type(mono_signature_get_params(sig, &front)) == CACHED_CLASS(IntPtr)->get_mono_ptr()) { - break; - } - } - } - - CRASH_COND(m == nullptr); - - Array *new_array = memnew(Array(p_from)); - void *args[1] = { &new_array }; - - MonoException *exc = nullptr; - GDMonoUtils::runtime_invoke(m, mono_object, args, &exc); - UNHANDLED_EXCEPTION(exc); - - return mono_object; -} - -MonoObject *create_managed_from(const Dictionary &p_from, GDMonoClass *p_class) { - MonoObject *mono_object = mono_object_new(mono_domain_get(), p_class->get_mono_ptr()); - ERR_FAIL_NULL_V(mono_object, nullptr); - - // Search constructor that takes a pointer as parameter - MonoMethod *m; - void *iter = nullptr; - while ((m = mono_class_get_methods(p_class->get_mono_ptr(), &iter))) { - if (strcmp(mono_method_get_name(m), ".ctor") == 0) { - MonoMethodSignature *sig = mono_method_signature(m); - void *front = nullptr; - if (mono_signature_get_param_count(sig) == 1 && - mono_class_from_mono_type(mono_signature_get_params(sig, &front)) == CACHED_CLASS(IntPtr)->get_mono_ptr()) { - break; - } - } - } - - CRASH_COND(m == nullptr); - - Dictionary *new_dict = memnew(Dictionary(p_from)); - void *args[1] = { &new_dict }; - - MonoException *exc = nullptr; - GDMonoUtils::runtime_invoke(m, mono_object, args, &exc); - UNHANDLED_EXCEPTION(exc); - - return mono_object; -} - MonoDomain *create_domain(const String &p_friendly_name) { print_verbose("Mono: Creating domain '" + p_friendly_name + "'..."); @@ -487,6 +384,13 @@ MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, M return ret; } +MonoObject *runtime_invoke_array(MonoMethod *p_method, void *p_obj, MonoArray *p_params, MonoException **r_exc) { + GD_MONO_BEGIN_RUNTIME_INVOKE; + MonoObject *ret = mono_runtime_invoke_array(p_method, p_obj, p_params, (MonoObject **)r_exc); + GD_MONO_END_RUNTIME_INVOKE; + return ret; +} + MonoString *object_to_string(MonoObject *p_obj, MonoException **r_exc) { GD_MONO_BEGIN_RUNTIME_INVOKE; MonoString *ret = mono_object_to_string(p_obj, (MonoObject **)r_exc); @@ -558,62 +462,6 @@ namespace Marshal { { return m_ret; } #endif -bool type_is_generic_array(MonoReflectionType *p_reftype) { - NO_GLUE_RET(false); - MonoException *exc = nullptr; - MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray).invoke(p_reftype, &exc); - UNHANDLED_EXCEPTION(exc); - return (bool)res; -} - -bool type_is_generic_dictionary(MonoReflectionType *p_reftype) { - NO_GLUE_RET(false); - MonoException *exc = nullptr; - MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary).invoke(p_reftype, &exc); - UNHANDLED_EXCEPTION(exc); - return (bool)res; -} - -bool type_is_system_generic_list(MonoReflectionType *p_reftype) { - NO_GLUE_RET(false); - MonoException *exc = nullptr; - MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsSystemGenericList).invoke(p_reftype, &exc); - UNHANDLED_EXCEPTION(exc); - return (bool)res; -} - -bool type_is_system_generic_dictionary(MonoReflectionType *p_reftype) { - NO_GLUE_RET(false); - MonoException *exc = nullptr; - MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsSystemGenericDictionary).invoke(p_reftype, &exc); - UNHANDLED_EXCEPTION(exc); - return (bool)res; -} - -bool type_is_generic_ienumerable(MonoReflectionType *p_reftype) { - NO_GLUE_RET(false); - MonoException *exc = nullptr; - MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericIEnumerable).invoke(p_reftype, &exc); - UNHANDLED_EXCEPTION(exc); - return (bool)res; -} - -bool type_is_generic_icollection(MonoReflectionType *p_reftype) { - NO_GLUE_RET(false); - MonoException *exc = nullptr; - MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericICollection).invoke(p_reftype, &exc); - UNHANDLED_EXCEPTION(exc); - return (bool)res; -} - -bool type_is_generic_idictionary(MonoReflectionType *p_reftype) { - NO_GLUE_RET(false); - MonoException *exc = nullptr; - MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericIDictionary).invoke(p_reftype, &exc); - UNHANDLED_EXCEPTION(exc); - return (bool)res; -} - bool type_has_flags_attribute(MonoReflectionType *p_reftype) { NO_GLUE_RET(false); MonoException *exc = nullptr; @@ -622,39 +470,6 @@ bool type_has_flags_attribute(MonoReflectionType *p_reftype) { return (bool)res; } -void get_generic_type_definition(MonoReflectionType *p_reftype, MonoReflectionType **r_generic_reftype) { - MonoException *exc = nullptr; - CACHED_METHOD_THUNK(MarshalUtils, GetGenericTypeDefinition).invoke(p_reftype, r_generic_reftype, &exc); - UNHANDLED_EXCEPTION(exc); -} - -void array_get_element_type(MonoReflectionType *p_array_reftype, MonoReflectionType **r_elem_reftype) { - MonoException *exc = nullptr; - CACHED_METHOD_THUNK(MarshalUtils, ArrayGetElementType).invoke(p_array_reftype, r_elem_reftype, &exc); - UNHANDLED_EXCEPTION(exc); -} - -void dictionary_get_key_value_types(MonoReflectionType *p_dict_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype) { - MonoException *exc = nullptr; - CACHED_METHOD_THUNK(MarshalUtils, DictionaryGetKeyValueTypes).invoke(p_dict_reftype, r_key_reftype, r_value_reftype, &exc); - UNHANDLED_EXCEPTION(exc); -} - -GDMonoClass *make_generic_array_type(MonoReflectionType *p_elem_reftype) { - NO_GLUE_RET(nullptr); - MonoException *exc = nullptr; - MonoReflectionType *reftype = CACHED_METHOD_THUNK(MarshalUtils, MakeGenericArrayType).invoke(p_elem_reftype, &exc); - UNHANDLED_EXCEPTION(exc); - return GDMono::get_singleton()->get_class(mono_class_from_mono_type(mono_reflection_type_get_type(reftype))); -} - -GDMonoClass *make_generic_dictionary_type(MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype) { - NO_GLUE_RET(nullptr); - MonoException *exc = nullptr; - MonoReflectionType *reftype = CACHED_METHOD_THUNK(MarshalUtils, MakeGenericDictionaryType).invoke(p_key_reftype, p_value_reftype, &exc); - UNHANDLED_EXCEPTION(exc); - return GDMono::get_singleton()->get_class(mono_class_from_mono_type(mono_reflection_type_get_type(reftype))); -} } // namespace Marshal ScopeThreadAttach::ScopeThreadAttach() { @@ -671,7 +486,6 @@ ScopeThreadAttach::~ScopeThreadAttach() { StringName get_native_godot_class_name(GDMonoClass *p_class) { MonoObject *native_name_obj = p_class->get_field(BINDINGS_NATIVE_NAME_FIELD)->get_value(nullptr); - StringName *ptr = GDMonoMarshal::unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(native_name_obj)); - return ptr ? *ptr : StringName(); + return (StringName)GDMonoMarshal::mono_object_to_variant(native_name_obj); } } // namespace GDMonoUtils diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h index 300cacfa4b..ee1be979e7 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-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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,23 +53,7 @@ namespace GDMonoUtils { namespace Marshal { - -bool type_is_generic_array(MonoReflectionType *p_reftype); -bool type_is_generic_dictionary(MonoReflectionType *p_reftype); -bool type_is_system_generic_list(MonoReflectionType *p_reftype); -bool type_is_system_generic_dictionary(MonoReflectionType *p_reftype); -bool type_is_generic_ienumerable(MonoReflectionType *p_reftype); -bool type_is_generic_icollection(MonoReflectionType *p_reftype); -bool type_is_generic_idictionary(MonoReflectionType *p_reftype); bool type_has_flags_attribute(MonoReflectionType *p_reftype); - -void get_generic_type_definition(MonoReflectionType *p_reftype, MonoReflectionType **r_generic_reftype); - -void array_get_element_type(MonoReflectionType *p_array_reftype, MonoReflectionType **r_elem_reftype); -void dictionary_get_key_value_types(MonoReflectionType *p_dict_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype); - -GDMonoClass *make_generic_array_type(MonoReflectionType *p_elem_reftype); -GDMonoClass *make_generic_dictionary_type(MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype); } // namespace Marshal _FORCE_INLINE_ void hash_combine(uint32_t &p_hash, const uint32_t &p_with_hash) { @@ -97,20 +81,12 @@ void free_gchandle(uint32_t p_gchandle); void runtime_object_init(MonoObject *p_this_obj, GDMonoClass *p_class, MonoException **r_exc = nullptr); -bool mono_delegate_equal(MonoDelegate *p_a, MonoDelegate *p_b); - GDMonoClass *get_object_class(MonoObject *p_object); GDMonoClass *type_get_proxy_class(const StringName &p_type); GDMonoClass *get_class_native_base(GDMonoClass *p_class); MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringName &p_native, Object *p_object); -MonoObject *create_managed_from(const StringName &p_from); -MonoObject *create_managed_from(const NodePath &p_from); -MonoObject *create_managed_from(const RID &p_from); -MonoObject *create_managed_from(const Array &p_from, GDMonoClass *p_class); -MonoObject *create_managed_from(const Dictionary &p_from, GDMonoClass *p_class); - MonoDomain *create_domain(const String &p_friendly_name); String get_type_desc(MonoType *p_type); @@ -141,6 +117,7 @@ _FORCE_INLINE_ int &get_runtime_invoke_count_ref() { } MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, MonoException **r_exc); +MonoObject *runtime_invoke_array(MonoMethod *p_method, void *p_obj, MonoArray *p_params, MonoException **r_exc); MonoString *object_to_string(MonoObject *p_obj, MonoException **r_exc); diff --git a/modules/mono/signal_awaiter_utils.cpp b/modules/mono/signal_awaiter_utils.cpp index 437c4ca54a..f26459554c 100644 --- a/modules/mono/signal_awaiter_utils.cpp +++ b/modules/mono/signal_awaiter_utils.cpp @@ -92,6 +92,10 @@ ObjectID SignalAwaiterCallable::get_object() const { return target_id; } +StringName SignalAwaiterCallable::get_signal() const { + return signal; +} + void SignalAwaiterCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const { r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; // Can't find anything better r_return_value = Variant(); diff --git a/modules/mono/signal_awaiter_utils.h b/modules/mono/signal_awaiter_utils.h index 532aa3e327..4f4986baf3 100644 --- a/modules/mono/signal_awaiter_utils.h +++ b/modules/mono/signal_awaiter_utils.h @@ -38,7 +38,12 @@ Error gd_mono_connect_signal_awaiter(Object *p_source, const StringName &p_signal, Object *p_target, MonoObject *p_awaiter); -class SignalAwaiterCallable : public CallableCustom { +class BaseSignalCallable : public CallableCustom { +public: + virtual StringName get_signal() const = 0; +}; + +class SignalAwaiterCallable : public BaseSignalCallable { ObjectID target_id; MonoGCHandleData awaiter_handle; StringName signal; @@ -59,7 +64,7 @@ public: ObjectID get_object() const override; - _FORCE_INLINE_ StringName get_signal() const { return signal; } + StringName get_signal() const override; void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override; @@ -67,7 +72,7 @@ public: ~SignalAwaiterCallable(); }; -class EventSignalCallable : public CallableCustom { +class EventSignalCallable : public BaseSignalCallable { Object *owner = nullptr; const CSharpScript::EventSignal *event_signal; @@ -87,7 +92,7 @@ public: ObjectID get_object() const override; - StringName get_signal() const; + StringName get_signal() const override; void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override; diff --git a/modules/mono/utils/string_utils.cpp b/modules/mono/utils/string_utils.cpp index 975f2d8332..b0f94310b8 100644 --- a/modules/mono/utils/string_utils.cpp +++ b/modules/mono/utils/string_utils.cpp @@ -65,7 +65,7 @@ int sfind(const String &p_text, int p_from) { break; case 1: { char32_t c = src[read_pos]; - found = src[read_pos] == 's' || (c >= '0' && c <= '4'); + found = src[read_pos] == 's' || (c >= '0' && c <= '5'); break; } default: @@ -86,32 +86,13 @@ int sfind(const String &p_text, int p_from) { } } // namespace -String sformat(const String &p_text, const Variant &p1, const Variant &p2, const Variant &p3, const Variant &p4, const Variant &p5) { +String sformat(const String &p_text, const String &p1, const String &p2, + const String &p3, const String &p4, const String &p5, const String &p6) { if (p_text.length() < 2) { return p_text; } - Array args; - - if (p1.get_type() != Variant::NIL) { - args.push_back(p1); - - if (p2.get_type() != Variant::NIL) { - args.push_back(p2); - - if (p3.get_type() != Variant::NIL) { - args.push_back(p3); - - if (p4.get_type() != Variant::NIL) { - args.push_back(p4); - - if (p5.get_type() != Variant::NIL) { - args.push_back(p5); - } - } - } - } - } + String args[6] = { p1, p2, p3, p4, p5, p6 }; String new_string; @@ -125,7 +106,7 @@ String sformat(const String &p_text, const Variant &p1, const Variant &p2, const int req_index = (c == 's' ? findex++ : c - '0'); new_string += p_text.substr(search_from, result - search_from); - new_string += args[req_index].operator String(); + new_string += args[req_index]; search_from = result + 2; } diff --git a/modules/mono/utils/string_utils.h b/modules/mono/utils/string_utils.h index fa4c5e89f4..b00dd9dde8 100644 --- a/modules/mono/utils/string_utils.h +++ b/modules/mono/utils/string_utils.h @@ -36,7 +36,8 @@ #include <stdarg.h> -String sformat(const String &p_text, const Variant &p1 = Variant(), const Variant &p2 = Variant(), const Variant &p3 = Variant(), const Variant &p4 = Variant(), const Variant &p5 = Variant()); +String sformat(const String &p_text, const String &p1 = String(), const String &p2 = String(), + const String &p3 = String(), const String &p4 = String(), const String &p5 = String(), const String &p6 = String()); #ifdef TOOLS_ENABLED bool is_csharp_keyword(const String &p_name); |