From 483071716e15616557f268833264e1b89abd7d92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Rold=C3=A1n=20Etcheverry?= Date: Mon, 3 May 2021 15:21:06 +0200 Subject: [PATCH] C#: Move marshaling logic and generated glue to C# We will be progressively moving most code to C#. The plan is to only use Mono's embedding APIs to set things at launch. This will make it much easier to later support CoreCLR too which doesn't have rich embedding APIs. Additionally the code in C# is more maintainable and makes it easier to implement new features, e.g.: runtime codegen which we could use to avoid using reflection for marshaling everytime a field, property or method is accessed. SOME NOTES ON INTEROP We make the same assumptions as GDNative about the size of the Godot structures we use. We take it a bit further by also assuming the layout of fields in some cases, which is riskier but let's us squeeze out some performance by avoiding unnecessary managed to native calls. Code that deals with native structs is less safe than before as there's no RAII and copy constructors in C#. It's like using the GDNative C API directly. One has to take special care to free values they own. Perhaps we could use roslyn analyzers to check this, but I don't know any that uses attributes to determine what's owned or borrowed. As to why we maily use pointers for native structs instead of ref/out: - AFAIK (and confirmed with a benchmark) ref/out are pinned during P/Invoke calls and that has a cost. - Native struct fields can't be ref/out in the first place. - A `using` local can't be passed as ref/out, only `in`. Calling a method or property on an `in` value makes a silent copy, so we want to avoid `in`. REGARDING THE BUILD SYSTEM There's no longer a `mono_glue=yes/no` SCons options. We no longer need to build with `mono_glue=no`, generate the glue and then build again with `mono_glue=yes`. We build only once and generate the glue (which is in C# now). However, SCons no longer builds the C# projects for us. Instead one must run `build_assemblies.py`, e.g.: ```sh %godot_src_root%/modules/mono/build_scripts/build_assemblies.py \ --godot-output-dir=%godot_src_root%/bin \ --godot-target=release_debug` ``` We could turn this into a custom build target, but I don't know how to do that with SCons (it's possible with Meson). OTHER NOTES Most of the moved code doesn't follow the C# naming convention and still has the word Mono in the names despite no longer dealing with Mono's embedding APIs. This is just temporary while transitioning, to make it easier to understand what was moved where. --- modules/mono/SCsub | 33 +- .../mono/build_scripts/api_solution_build.py | 80 - .../mono/build_scripts/build_assemblies.py | 301 +++ .../mono/build_scripts/gen_cs_glue_version.py | 20 - .../mono/build_scripts/godot_net_sdk_build.py | 55 - .../mono/build_scripts/godot_tools_build.py | 38 - .../mono/build_scripts/solution_builder.py | 145 -- modules/mono/config.py | 3 +- modules/mono/csharp_script.cpp | 41 +- modules/mono/csharp_script.h | 2 +- modules/mono/editor/bindings_generator.cpp | 1435 ++++++-------- modules/mono/editor/bindings_generator.h | 157 +- modules/mono/editor/editor_internal_calls.cpp | 1 - .../GodotSharp/GodotSharp.sln.DotSettings | 7 + .../glue/GodotSharp/GodotSharp/Core/Array.cs | 279 ++- .../glue/GodotSharp/GodotSharp/Core/Basis.cs | 2 +- .../GodotSharp/Core/DelegateUtils.cs | 59 +- .../GodotSharp/GodotSharp/Core/Dictionary.cs | 288 ++- .../Core/Extensions/SceneTreeExtensions.cs | 7 +- .../glue/GodotSharp/GodotSharp/Core/GD.cs | 63 +- .../GodotSharp/Core/MarshalUtils.cs | 62 - .../Core/NativeInterop/InteropStructs.cs | 428 +++++ .../Core/NativeInterop/InteropUtils.cs | 32 + .../Core/NativeInterop/Marshaling.cs | 1340 +++++++++++++ .../Core/NativeInterop/NativeFuncs.cs | 328 ++++ .../NativeInterop/NativeFuncs.extended.cs | 70 + .../Core/NativeInterop/VariantSpanHelpers.cs | 33 + .../Core/NativeInterop/VariantUtils.cs | 305 +++ .../GodotSharp/GodotSharp/Core/NodePath.cs | 111 +- .../GodotSharp/GodotSharp/Core/Object.base.cs | 100 +- .../GodotSharp/Core/Object.exceptions.cs | 135 ++ .../glue/GodotSharp/GodotSharp/Core/RID.cs | 79 +- .../GodotSharp/Core/SignalAwaiter.cs | 39 +- .../GodotSharp/Core/StringExtensions.cs | 6 +- .../GodotSharp/GodotSharp/Core/StringName.cs | 67 +- .../GodotSharp/GodotSharp/GodotSharp.csproj | 18 +- .../GodotSharp/GodotSharp.csproj.DotSettings | 5 + .../GodotSharpEditor/GodotSharpEditor.csproj | 2 + .../GodotSharpEditor.csproj.DotSettings | 4 + modules/mono/glue/base_object_glue.cpp | 18 - modules/mono/glue/collections_glue.cpp | 141 +- modules/mono/glue/gd_glue.cpp | 22 +- modules/mono/glue/glue_header.h | 84 - .../{nodepath_glue.cpp => node_path_glue.cpp} | 33 +- .../{rid_glue.cpp => placeholder_glue.cpp} | 43 +- modules/mono/glue/runtime_interop.cpp | 649 +++++++ modules/mono/glue/scene_tree_glue.cpp | 15 +- modules/mono/glue/string_glue.cpp | 4 - modules/mono/glue/string_name_glue.cpp | 62 - modules/mono/godotsharp_defs.h | 4 +- modules/mono/managed_callable.cpp | 63 +- modules/mono/managed_callable.h | 11 +- modules/mono/mono_gd/gd_mono.cpp | 118 +- modules/mono/mono_gd/gd_mono.h | 14 +- modules/mono/mono_gd/gd_mono_cache.cpp | 155 +- modules/mono/mono_gd/gd_mono_cache.h | 79 +- modules/mono/mono_gd/gd_mono_field.cpp | 389 +--- modules/mono/mono_gd/gd_mono_marshal.cpp | 1666 +---------------- modules/mono/mono_gd/gd_mono_marshal.h | 448 +---- modules/mono/mono_gd/gd_mono_method.cpp | 13 +- modules/mono/mono_gd/gd_mono_method.h | 1 - modules/mono/mono_gd/gd_mono_utils.cpp | 216 +-- modules/mono/mono_gd/gd_mono_utils.h | 26 +- modules/mono/signal_awaiter_utils.cpp | 4 + modules/mono/signal_awaiter_utils.h | 13 +- modules/mono/utils/string_utils.cpp | 29 +- modules/mono/utils/string_utils.h | 3 +- 67 files changed, 5178 insertions(+), 5325 deletions(-) delete mode 100644 modules/mono/build_scripts/api_solution_build.py create mode 100755 modules/mono/build_scripts/build_assemblies.py delete mode 100644 modules/mono/build_scripts/gen_cs_glue_version.py delete mode 100644 modules/mono/build_scripts/godot_net_sdk_build.py delete mode 100644 modules/mono/build_scripts/godot_tools_build.py delete mode 100644 modules/mono/build_scripts/solution_builder.py create mode 100644 modules/mono/glue/GodotSharp/GodotSharp.sln.DotSettings delete mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantSpanHelpers.cs create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/Object.exceptions.cs create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj.DotSettings create mode 100644 modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj.DotSettings delete mode 100644 modules/mono/glue/glue_header.h rename modules/mono/glue/{nodepath_glue.cpp => node_path_glue.cpp} (76%) rename modules/mono/glue/{rid_glue.cpp => placeholder_glue.cpp} (70%) create mode 100644 modules/mono/glue/runtime_interop.cpp delete mode 100644 modules/mono/glue/string_name_glue.cpp diff --git a/modules/mono/SCsub b/modules/mono/SCsub index 3b94949470..8f8cb4c74f 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") @@ -60,3 +29,5 @@ if env["platform"] in ["osx", "iphone"]: if env["tools"]: env_mono.add_source_files(env.modules_sources, "editor/*.cpp") + +env_mono.Prepend(CPPPATH=["#modules/gdnative/include/"]) 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..bfb437ab6b --- /dev/null +++ b/modules/mono/build_scripts/build_assemblies.py @@ -0,0 +1,301 @@ +#!/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, godot_target, 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 godot_target == "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("--godot-target", choices=["debug", "release_debug", "release"], type=str, required=True) + 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_target, args.godot_platform) + 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 4c851a2989..3b216a23b2 100644 --- a/modules/mono/config.py +++ b/modules/mono/config.py @@ -2,7 +2,7 @@ supported_platforms = ["windows", "osx", "linuxbsd", "server", "android", "haiku def can_build(env, platform): - return True + return env["module_gdnative_enabled"] def configure(env): @@ -36,7 +36,6 @@ def configure(env): ) ) envvars.Add(BoolVariable("mono_static", "Statically link Mono", default_mono_static)) - envvars.Add(BoolVariable("mono_glue", "Build with the Mono glue sources", True)) envvars.Add(BoolVariable("build_cil", "Build C# solutions", True)) envvars.Add( BoolVariable("copy_mono_root", "Make a copy of the Mono installation directory to bundle with the editor", True) diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 1f7f1390ea..0d9b02b2e2 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -54,6 +54,7 @@ #include "editor/editor_internal_calls.h" #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" @@ -105,6 +106,9 @@ Error CSharpLanguage::execute_file(const String &p_path) { return OK; } +extern void *godotsharp_pinvoke_funcs[95]; +[[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")) { @@ -115,20 +119,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 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(); @@ -844,13 +846,13 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { for (SelfList *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); @@ -1169,10 +1171,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); @@ -1181,7 +1184,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"); } @@ -1309,7 +1312,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 &E : script_bindings) { CSharpScriptBinding &script_binding = E.value; script_binding.gchandle.release(); @@ -1322,8 +1325,7 @@ void CSharpLanguage::_on_scripts_domain_unloaded() { for (SelfList *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 @@ -1657,7 +1659,7 @@ bool CSharpInstance::set(const StringName &p_name, const Variant &p_value) { GDMonoProperty *property = top->get_property(p_name); if (property) { - property->set_value(mono_object, GDMonoMarshal::variant_to_mono_object(p_value, property->get_type())); + property->set_value(mono_object, GDMonoMarshal::variant_to_mono_object_of_type(p_value, property->get_type())); return true; } @@ -1794,7 +1796,8 @@ void CSharpInstance::get_event_signals_state_for_reloading(Listclass_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. @@ -714,44 +745,25 @@ 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,IntPtr"; - - im_sig += "IntPtr " CS_PARAM_INSTANCE; + String im_unique_sig = get_ret_unique_sig(return_type) + ",CallMethodBind,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++; } @@ -762,7 +774,14 @@ 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.return_type = imethod.return_type; + + for (const List::Element *F = imethod.arguments.front(); F; F = F->next()) { + im_icall.argument_types.push_back(F->get().type); + } List::Element *match = method_icalls.find(im_icall); @@ -776,6 +795,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) { @@ -791,7 +812,7 @@ void BindingsGenerator::_generate_array_extensions(StringBuilder &p_output) { p_output.append(INDENT2 "/// The " #m_type " array check.\n"); \ p_output.append(INDENT2 "/// Whether or not the array is empty.\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); @@ -803,7 +824,7 @@ void BindingsGenerator::_generate_array_extensions(StringBuilder &p_output) { p_output.append(INDENT2 "/// The delimiter to use between items.\n"); \ p_output.append(INDENT2 "/// A single string with all items.\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); @@ -814,7 +835,7 @@ void BindingsGenerator::_generate_array_extensions(StringBuilder &p_output) { p_output.append(INDENT2 "/// The " #m_type " array to convert.\n"); \ p_output.append(INDENT2 "/// A single string with all items.\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); @@ -904,12 +925,12 @@ 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); } p_output.append("\n" INDENT1 "public enum "); p_output.append(enum_proxy_name); - 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) { @@ -1016,41 +1037,37 @@ 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"); \ + 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; + } } - 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); - } - -#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"); @@ -1062,6 +1079,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("\n" " \n"); @@ -1125,40 +1144,38 @@ 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(String::num_uint64(ClassDB::get_api_hash(ClassDB::API_EDITOR)) + ";\n"); cs_icalls_content.append("\n"); -#define ADD_INTERNAL_CALL(m_icall) \ - if (m_icall.editor_only) { \ - cs_icalls_content.append(INDENT2 "[MethodImpl(MethodImplOptions.InternalCall)]\n"); \ - cs_icalls_content.append(INDENT2 "internal 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"); \ + 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; + } } - 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); - } - -#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_EDITOR ".cs"); @@ -1170,6 +1187,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("\n" " \n"); @@ -1253,12 +1272,8 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str CRASH_COND(itype.is_singleton); } - List &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 @@ -1396,53 +1411,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). + + // Add native name static field + + output.append(MEMBER_BEGIN "private static readonly StringName " BINDINGS_NATIVE_NAME_FIELD " = \""); output.append(itype.name); output.append("\";\n"); - output.append(INDENT2 "internal static IntPtr " BINDINGS_PTR_FIELD " = "); - output.append(itype.api_type == ClassDB::API_EDITOR ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS); - output.append("." ICALL_PREFIX); - output.append(itype.name); - output.append(SINGLETON_ICALL_SUFFIX "();\n"); - } else if (is_derived_type) { - // Add member fields - - output.append(MEMBER_BEGIN "private static 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"); + // Add native constructor static field - // 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 - output.append(MEMBER_BEGIN "internal "); - output.append(itype.proxy_name); - output.append("() {}\n"); + 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 " + << 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"; } - // 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"); + 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("(bool " CS_PARAM_MEMORYOWN ") : base(" CS_PARAM_MEMORYOWN ") {}\n"); + } } int method_bind_count = 0; @@ -1458,22 +1516,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 */); @@ -1572,7 +1614,7 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte p_output.append(prop_itype->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" @@ -1580,7 +1622,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 + "("); @@ -1609,7 +1651,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) { @@ -1637,7 +1679,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 + "'."); @@ -1648,10 +1691,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 + ", "; icall_params += sformat(p_itype.cs_in, "this"); @@ -1661,7 +1705,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 + "'."); @@ -1715,24 +1760,20 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf String arg_in = iarg.name; arg_in += "_in"; - cs_in_statements += arg_type->cs_type; - cs_in_statements += " "; - cs_in_statements += arg_in; - cs_in_statements += " = "; - cs_in_statements += iarg.name; + cs_in_statements << INDENT3 << arg_type->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_type->cs_type; @@ -1742,8 +1783,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); @@ -1756,16 +1796,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()) { @@ -1818,6 +1867,10 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf p_output.append("virtual "); } + if (cs_in_is_unsafe) { + p_output.append("unsafe "); + } + p_output.append(return_type->cs_type + " "); p_output.append(p_imethod.proxy_name + "("); p_output.append(arguments_sig + ")\n" OPEN_BLOCK_L2); @@ -1826,11 +1879,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_type->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 @@ -1839,7 +1890,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("\""); @@ -1863,15 +1914,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_type->cs_type, return_type->im_type_out)); + p_output.append(sformat(return_type->cs_out, im_call, icall_params, + return_type->cs_type, return_type->c_type_out, String(), INDENT3)); p_output.append("\n"); } @@ -1889,7 +1941,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 + "'."); @@ -1955,7 +2008,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); @@ -1972,21 +2026,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); @@ -1997,177 +2051,200 @@ 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); +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; - 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."); + const TypeInterface *return_type = _get_type_or_null(p_icall.return_type); + ERR_FAIL_NULL_V(return_type, ERR_BUG); // Return type not found - StringBuilder output; + StringBuilder c_func_sig; + StringBuilder c_in_statements; + StringBuilder c_args_var_content; - 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"); + c_func_sig << "IntPtr " CS_PARAM_METHODBIND ", IntPtr " CS_PARAM_INSTANCE; - generated_icall_funcs.clear(); + // Get arguments information + int i = 0; + 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 - for (OrderedHashMap::Element type_elem = obj_types.front(); type_elem; type_elem = type_elem.next()) { - const TypeInterface &itype = type_elem.get(); + String c_param_name = "arg" + itos(i + 1); - bool is_derived_type = itype.base_name != StringName(); + if (p_icall.is_vararg) { + if (i < p_icall.get_arguments_count() - 1) { + string c_in_vararg = arg_type->c_in_vararg; - 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); - } + if (arg_type->is_object_type) { + c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromGodotObject(%1);\n"; + } - List &custom_icalls = itype.api_type == ClassDB::API_EDITOR ? editor_custom_icalls : core_custom_icalls; + ERR_FAIL_COND_V_MSG(c_in_vararg.is_empty(), ERR_BUG, + "VarArg support not implemented for parameter type: " + arg_type->name); - 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); - } - - output.append("Object* "); - output.append(singleton_icall_name); - output.append("() " OPEN_BLOCK "\treturn Engine::get_singleton()->get_singleton_object(\""); - output.append(itype.proxy_name); - output.append("\");\n" CLOSE_BLOCK "\n"); - } - - if (is_derived_type && itype.is_instantiable) { - 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("Object* "); - output.append(ctor_method); - output.append("(MonoObject* obj) " OPEN_BLOCK - "\t" C_MACRO_OBJECT_CONSTRUCT "(instance, \""); - output.append(itype.name); - output.append("\");\n" - "\t" C_METHOD_TIE_MANAGED_TO_UNMANAGED "(obj, instance);\n" - "\treturn instance;\n" CLOSE_BLOCK "\n"); - } - } - - output.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"); + 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 (internal_call.editor_only) { - output.append("#ifdef TOOLS_ENABLED\n"); - tools_sequence = true; + if (i > 0) { + 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, + String(), String(), String(), INDENT3); + } + c_args_var_content << sformat(arg_type->c_arg_in, c_param_name); + } + + c_func_sig << ", " << arg_type->c_type_in << " " << c_param_name; + + i++; + } + + String icall_method = p_icall.name; + + // Generate icall function + + 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 (!ret_void && (!p_icall.is_vararg || return_type->cname != name_cache.type_Variant)) { + String ptrcall_return_type; + String initialization; + + if (return_type->is_object_type) { + ptrcall_return_type = return_type->is_ref_counted ? "godot_ref" : return_type->c_type; + initialization = return_type->is_ref_counted ? " = default" : " = IntPtr.Zero"; + } else { + ptrcall_return_type = return_type->c_type; + } + + r_output << INDENT3; + + if (return_type->is_ref_counted || return_type->c_type_is_disposable_struct) { + r_output << "using "; + + if (initialization.is_empty()) { + initialization = " = default"; } } - ADD_INTERNAL_CALL_REGISTRATION(internal_call); + r_output << ptrcall_return_type << " " C_LOCAL_RET << initialization << ";\n"; } - if (tools_sequence) { - tools_sequence = false; - output.append("#endif\n"); - } + r_output << INDENT3 "if (" CS_PARAM_INSTANCE " == IntPtr.Zero)\n" + << INDENT4 "throw new ArgumentNullException(nameof(" CS_PARAM_INSTANCE "));\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"); + String argc_str = itos(p_icall.get_arguments_count()); - for (const InternalCall &internal_call : method_icalls) { - if (tools_sequence) { - if (!internal_call.editor_only) { - tools_sequence = false; - output.append("#endif\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"; + + // MethodBind Call + r_output << base_indent; + + // 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) { + if (return_type->cname != name_cache.type_Variant) { + r_output << "using godot_variant " << C_LOCAL_VARARG_RET " = "; + } else { + r_output << "using godot_variant " << C_LOCAL_RET " = "; + } + } + + r_output << C_CLASS_NATIVE_FUNCS ".godotsharp_method_bind_call(" + << CS_PARAM_METHODBIND ", " CS_PARAM_INSTANCE ", " + << (p_icall.get_arguments_count() ? "(godot_variant**)" C_LOCAL_PTRCALL_ARGS : "null") + << ", total_length, &vcall_error);\n"; + + if (!ret_void) { + if (return_type->cname != name_cache.type_Variant) { + // 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 { - if (internal_call.editor_only) { - output.append("#ifdef TOOLS_ENABLED\n"); - tools_sequence = true; - } + // MethodBind PtrCall + r_output << base_indent << C_CLASS_NATIVE_FUNCS ".godotsharp_method_bind_ptrcall(" + << CS_PARAM_METHODBIND ", " CS_PARAM_INSTANCE ", " + << (p_icall.get_arguments_count() ? C_LOCAL_PTRCALL_ARGS : "null") + << ", " << (!ret_void ? "&" C_LOCAL_RET ");\n" : "null);\n"); } - ADD_INTERNAL_CALL_REGISTRATION(internal_call); + // Return statement + + if (!ret_void) { + if (return_type->c_out.is_empty()) { + r_output << base_indent << "return " C_LOCAL_RET ";\n"; + } else { + 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 varargs_span = vararg_length <= 5 ?\n" + << INDENT4 "stackalloc godot_variant[vararg_length].Cleared() :\n" + << INDENT4 "new godot_variant[vararg_length];\n"; + + r_output << INDENT3 "Span " C_LOCAL_PTRCALL_ARGS "_span = total_length <= 10 ?\n" + << INDENT4 "stackalloc IntPtr[total_length] :\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(); + + 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; + + 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); } - 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"); + r_output << CLOSE_BLOCK_L2; return OK; } -uint32_t BindingsGenerator::get_version() { - return BINDINGS_GENERATOR_VERSION; -} - Error BindingsGenerator::_save_file(const String &p_path, const StringBuilder &p_content) { FileAccessRef file = FileAccess::open(p_path, FileAccess::WRITE); @@ -2179,199 +2256,6 @@ Error BindingsGenerator::_save_file(const String &p_path, const StringBuilder &p 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 ", " + 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); - - 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); - } - } else { - if (i > 0) { - 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_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; - - i++; - } - - if (return_type->ret_as_byref_arg) { - c_func_sig += ", "; - c_func_sig += return_type->c_type_in; - c_func_sig += " "; - c_func_sig += "arg_ret"; - - i++; - } - - const Map::Element *match = method_icalls_map.find(&p_imethod); - ERR_FAIL_NULL_V(match, ERR_BUG); - - const InternalCall *im_icall = match->value(); - String icall_method = im_icall->name; - - if (!generated_icall_funcs.find(im_icall)) { - generated_icall_funcs.push_back(im_icall); - - if (im_icall->editor_only) { - p_output.append("#ifdef TOOLS_ENABLED\n"); - } - - // Generate icall function - - 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 (!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 (return_type->is_object_type) { - ptrcall_return_type = return_type->is_ref_counted ? "Ref" : return_type->c_type; - initialization = return_type->is_ref_counted ? "" : " = nullptr"; - } else { - ptrcall_return_type = return_type->c_type; - } - - p_output.append("\t" + ptrcall_return_type); - p_output.append(" " C_LOCAL_RET); - p_output.append(initialization + ";\n"); - - String fail_ret = return_type->c_type_out.ends_with("*") && !return_type->ret_as_byref_arg ? "nullptr" : return_type->c_type_out + "()"; - - 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 { - p_output.append("\tERR_FAIL_NULL(" CS_PARAM_INSTANCE ");\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 varargs(vararg_length);\n" - "\tArgumentsVector " 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"); - } - } - - if (p_imethod.is_vararg) { - p_output.append("\tCallable::CallError vcall_error;\n\t"); - - if (!ret_void) { - // See the comment on the C_LOCAL_VARARG_RET declaration - if (return_type->cname != name_cache.type_Variant) { - p_output.append(C_LOCAL_VARARG_RET " = "); - } else { - p_output.append(C_LOCAL_RET " = "); - } - } - - p_output.append(CS_PARAM_METHODBIND "->call(" CS_PARAM_INSTANCE ", "); - p_output.append(p_imethod.arguments.size() ? C_LOCAL_PTRCALL_ARGS ".ptr()" : "nullptr"); - p_output.append(", 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"); - } - } - } else { - p_output.append("\t" CS_PARAM_METHODBIND "->ptrcall(" CS_PARAM_INSTANCE ", "); - p_output.append(p_imethod.arguments.size() ? C_LOCAL_PTRCALL_ARGS ", " : "nullptr, "); - p_output.append(!ret_void ? "&" C_LOCAL_RET ");\n" : "nullptr);\n"); - } - - if (!ret_void) { - 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")); - } else { - p_output.append(sformat(return_type->c_out, return_type->c_type_out, C_LOCAL_RET, return_type->name)); - } - } - - p_output.append(CLOSE_BLOCK "\n"); - - if (im_icall->editor_only) { - p_output.append("#endif // TOOLS_ENABLED\n"); - } - } - - return OK; -} - const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_null(const TypeReference &p_typeref) { const Map::Element *builtin_type_match = builtin_types.find(p_typeref.cname); @@ -2401,27 +2285,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() + "'."); - - const Map::Element *match = placeholder_types.find(p_typeref.cname); - - if (match) { - return &match->get(); - } - - TypeInterface placeholder; - TypeInterface::create_placeholder_type(placeholder, p_typeref.cname); - - return &placeholder_types.insert(placeholder.cname, placeholder)->get(); -} - StringName BindingsGenerator::_get_int_type_name_from_meta(GodotTypeInfo::Metadata p_meta) { switch (p_meta) { case GodotTypeInfo::METADATA_INT_IS_INT8: @@ -2589,18 +2452,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.c_type = "Object*"; - 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; + + 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.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 = "Object"; // Populate properties @@ -3074,7 +2943,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 { }"; @@ -3130,11 +2999,21 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL; } break; case Variant::CALLABLE: + ERR_FAIL_COND_V_MSG(r_iarg.type.cname != name_cache.type_Callable, false, + "Parameter of type '" + String(r_iarg.type.cname) + "' cannot have a default value of type '" + String(name_cache.type_Callable) + "'."); + 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 = "default"; + break; case Variant::SIGNAL: - CRASH_NOW_MSG("Parameter of type '" + String(r_iarg.type.cname) + "' cannot have a default value."); + ERR_FAIL_COND_V_MSG(r_iarg.type.cname != name_cache.type_Signal, false, + "Parameter of type '" + String(r_iarg.type.cname) + "' cannot have a default value of type '" + String(name_cache.type_Signal) + "'."); + 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 = "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; } @@ -3150,20 +3029,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) @@ -3184,51 +3057,48 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { // bool itype = TypeInterface::create_value_type(String("bool")); - { - // MonoBoolean <---> bool - itype.c_in = "\t%0 %1_in = (%0)%1;\n"; - 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 @@ -3236,18 +3106,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 @@ -3255,15 +3124,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); } @@ -3272,15 +3138,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 @@ -3288,17 +3154,16 @@ 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"; builtin_types.insert(itype.cname, itype); // NodePath @@ -3306,15 +3171,15 @@ 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; builtin_types.insert(itype.cname, itype); // RID @@ -3322,15 +3187,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 @@ -3338,29 +3199,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 @@ -3368,19 +3226,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) @@ -3388,46 +3242,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 @@ -3436,15 +3288,14 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.name = "Array"; itype.cname = itype.name; itype.proxy_name = itype.name; - 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 " + itype.cs_type + "(%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; builtin_types.insert(itype.cname, itype); // Dictionary @@ -3452,15 +3303,14 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.name = "Dictionary"; itype.cname = itype.name; itype.proxy_name = itype.name; - 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 " + itype.cs_type + "(%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; builtin_types.insert(itype.cname, itype); // void (fictitious type to represent the return type of methods that do not return anything) @@ -3468,12 +3318,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); } @@ -3597,21 +3445,18 @@ void BindingsGenerator::_initialize() { // Generate internal calls (after populating type interfaces and global constants) - core_custom_icalls.clear(); - editor_custom_icalls.clear(); - for (OrderedHashMap::Element E = obj_types.front(); E; E = E.next()) { - _generate_method_icalls(E.get()); + const TypeInterface &itype = E.get(); + 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); @@ -3620,43 +3465,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."); - } + CRASH_COND(glue_dir_path.is_empty()); - 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 (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 (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."); - } + 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."); } } +static void cleanup_and_exit_godot() { + // Exit once done + Main::cleanup(true); + ::exit(0); +} + void BindingsGenerator::handle_cmdline_args(const List &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::Element *elem = p_cmdline_args.front(); - while (elem && options_left) { + while (elem) { if (elem->get() == generate_all_glue_option) { const List::Element *path_elem = elem->next(); @@ -3665,48 +3492,20 @@ void BindingsGenerator::handle_cmdline_args(const List &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; + // Exit once done with invalid command line arguments + cleanup_and_exit_godot(); } - --options_left; - } else if (elem->get() == generate_cs_glue_option) { - const List::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; - } - - --options_left; - } else if (elem->get() == generate_cpp_glue_option) { - const List::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 a649181b20..207bb4a0ef 100644 --- a/modules/mono/editor/bindings_generator.h +++ b/modules/mono/editor/bindings_generator.h @@ -218,6 +218,11 @@ class BindingsGenerator { bool is_singleton = false; bool is_ref_counted = false; + /** + * Determines whether the + */ + bool c_type_is_disposable_struct = false; + /** * Used only by Object-derived types. * Determines if this type is not abstract (incomplete). @@ -232,32 +237,35 @@ 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, @@ -281,7 +289,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; @@ -320,6 +329,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. @@ -328,7 +338,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; @@ -338,16 +350,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 constants; @@ -402,8 +404,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]; } @@ -437,65 +439,26 @@ 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; + bool is_vararg; + TypeReference return_type; + List argument_types; + + _FORCE_INLINE_ int get_arguments_count() const { return argument_types.size(); } + InternalCall() {} - InternalCall(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 = false; - } - - 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()) { - 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; } @@ -510,7 +473,6 @@ class BindingsGenerator { OrderedHashMap obj_types; - Map placeholder_types; Map builtin_types; Map enum_types; @@ -518,13 +480,9 @@ class BindingsGenerator { List global_constants; List method_icalls; + /// Stores the unique internal calls from [method_icalls] that are assigned to each method. Map method_icalls_map; - List generated_icall_funcs; - - List core_custom_icalls; - List editor_custom_icalls; - Map> blacklisted_methods; void _initialize_blacklisted_methods(); @@ -536,6 +494,8 @@ class BindingsGenerator { StringName type_Object = StaticCString::create("Object"); StringName type_RefCounted = StaticCString::create("RefCounted"); StringName type_RID = StaticCString::create("RID"); + StringName type_Callable = StaticCString::create("Callable"); + StringName type_Signal = StaticCString::create("Signal"); StringName type_String = StaticCString::create("String"); StringName type_StringName = StaticCString::create("StringName"); StringName type_NodePath = StaticCString::create("NodePath"); @@ -601,17 +561,6 @@ class BindingsGenerator { NameCache name_cache; - const List::Element *find_icall_by_name(const String &p_name, const List &p_list) { - const List::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 &p_constants) const { for (const ConstantInterface &E : p_constants) { if (E.name == p_name) { @@ -622,10 +571,9 @@ 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"; @@ -634,15 +582,27 @@ class BindingsGenerator { 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"; + } + + return p_type->name; + } + String bbcode_to_xml(const String &p_bbcode, const TypeInterface *p_itype); int _determine_enum_prefix(const EnumInterface &p_ienum); void _apply_prefix_to_enum_constants(EnumInterface &p_ienum, int p_prefix_length); - 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); StringName _get_int_type_name_from_meta(GodotTypeInfo::Metadata p_meta); StringName _get_float_type_name_from_meta(GodotTypeInfo::Metadata p_meta); @@ -661,11 +621,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; @@ -676,15 +636,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 &p_cmdline_args); BindingsGenerator() { diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp index 6692a6efec..4e9ee61689 100644 --- a/modules/mono/editor/editor_internal_calls.cpp +++ b/modules/mono/editor/editor_internal_calls.cpp @@ -43,7 +43,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/osx_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 @@ + + GC + True + True + True + True + True diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs index f52a767018..92ace4798c 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 { - 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; - } - } - /// /// 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 or . /// - public class Array : IList, IDisposable + public sealed class Array : IList, IDisposable { - ArraySafeHandle safeHandle; - bool disposed = false; + internal godot_array NativeValue; /// /// Constructs a new empty . /// public Array() { - safeHandle = new ArraySafeHandle(godot_icall_Array_Ctor()); + godot_icall_Array_Ctor(out NativeValue); } /// @@ -58,6 +39,7 @@ namespace Godot.Collections Add(element); } + // TODO: This must be removed. Lots of silent mistakes as it takes pretty much anything. /// /// Constructs a new from the given objects. /// @@ -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() + /// + /// Disposes of this . + /// + 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(); } /// @@ -97,7 +91,9 @@ namespace Godot.Collections /// A new Godot Array. 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); } /// @@ -107,7 +103,7 @@ namespace Godot.Collections /// if successful, or an error code. public Error Resize(int newSize) { - return godot_icall_Array_Resize(GetPtr(), newSize); + return godot_icall_Array_Resize(ref NativeValue, newSize); } /// @@ -115,7 +111,7 @@ namespace Godot.Collections /// public void Shuffle() { - godot_icall_Array_Shuffle(GetPtr()); + godot_icall_Array_Shuffle(ref NativeValue); } /// @@ -126,26 +122,9 @@ namespace Godot.Collections /// A new Godot Array with the contents of both arrays. public static Array operator +(Array left, Array right) { - return new Array(godot_icall_Array_Concatenate(left.GetPtr(), right.GetPtr())); - } - - // IDisposable - - /// - /// Disposes of this . - /// - 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 /// The object at the given index. 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); } /// @@ -170,19 +157,19 @@ namespace Godot.Collections /// /// The object to add. /// The new size after adding the object. - public int Add(object value) => godot_icall_Array_Add(GetPtr(), value); + public int Add(object value) => godot_icall_Array_Add(ref NativeValue, value); /// /// Checks if this contains the given object. /// /// The item to look for. /// Whether or not this array contains the given object. - public bool Contains(object value) => godot_icall_Array_Contains(GetPtr(), value); + public bool Contains(object value) => godot_icall_Array_Contains(ref NativeValue, value); /// /// Erases all items from this . /// - public void Clear() => godot_icall_Array_Clear(GetPtr()); + public void Clear() => godot_icall_Array_Clear(ref NativeValue); /// /// Searches this for an object @@ -190,7 +177,7 @@ namespace Godot.Collections /// /// The object to search for. /// The index of the object, or -1 if not found. - public int IndexOf(object value) => godot_icall_Array_IndexOf(GetPtr(), value); + public int IndexOf(object value) => godot_icall_Array_IndexOf(ref NativeValue, value); /// /// Inserts a new object at a given position in the array. @@ -200,20 +187,20 @@ namespace Godot.Collections /// /// The index to insert at. /// The object to insert. - 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); /// /// Removes the first occurrence of the specified value /// from this . /// /// The value to remove. - public void Remove(object value) => godot_icall_Array_Remove(GetPtr(), value); + public void Remove(object value) => godot_icall_Array_Remove(ref NativeValue, value); /// /// Removes an element from this by index. /// /// The index of the element to remove. - 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. /// /// The number of elements. - 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 /// A string representation of this array. public override string ToString() { - return godot_icall_Array_ToString(GetPtr()); + return godot_icall_Array_ToString(ref NativeValue); } [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static IntPtr godot_icall_Array_Ctor(); + internal static extern void godot_icall_Array_Ctor(out godot_array dest); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static IntPtr godot_icall_Array_Ctor_MonoArray(System.Array array); + internal static extern void godot_icall_Array_Ctor_MonoArray(System.Array array, out godot_array dest); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_Array_Dtor(IntPtr ptr); + internal static extern void godot_icall_Array_At(ref godot_array ptr, int index, out godot_variant elem); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static object godot_icall_Array_At(IntPtr ptr, int index); + internal static extern void godot_icall_Array_SetAt(ref godot_array ptr, int index, object value); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static object godot_icall_Array_At_Generic(IntPtr ptr, int index, int elemTypeEncoding, IntPtr elemTypeClass); + internal static extern int godot_icall_Array_Count(ref godot_array ptr); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_Array_SetAt(IntPtr ptr, int index, object value); + internal static extern int godot_icall_Array_Add(ref godot_array ptr, object item); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static int godot_icall_Array_Count(IntPtr ptr); + internal static extern void godot_icall_Array_Clear(ref godot_array ptr); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static int godot_icall_Array_Add(IntPtr ptr, object item); + internal static extern void godot_icall_Array_Concatenate(ref godot_array left, ref godot_array right, out godot_array dest); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_Array_Clear(IntPtr ptr); + internal static extern bool godot_icall_Array_Contains(ref godot_array ptr, object item); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static IntPtr godot_icall_Array_Concatenate(IntPtr left, IntPtr right); + internal static extern void godot_icall_Array_CopyTo(ref godot_array ptr, System.Array array, int arrayIndex); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static bool godot_icall_Array_Contains(IntPtr ptr, object item); + internal static extern void godot_icall_Array_Duplicate(ref godot_array ptr, bool deep, out godot_array dest); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_Array_CopyTo(IntPtr ptr, System.Array array, int arrayIndex); + internal static extern int godot_icall_Array_IndexOf(ref godot_array ptr, object item); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static IntPtr godot_icall_Array_Duplicate(IntPtr ptr, bool deep); + internal static extern void godot_icall_Array_Insert(ref godot_array ptr, int index, object item); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static int godot_icall_Array_IndexOf(IntPtr ptr, object item); + internal static extern bool godot_icall_Array_Remove(ref godot_array ptr, object item); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_Array_Insert(IntPtr ptr, int index, object item); + internal static extern void godot_icall_Array_RemoveAt(ref godot_array ptr, int index); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static bool godot_icall_Array_Remove(IntPtr ptr, object item); + internal static extern Error godot_icall_Array_Resize(ref godot_array ptr, int newSize); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_Array_RemoveAt(IntPtr ptr, int index); + internal static extern Error godot_icall_Array_Shuffle(ref godot_array ptr); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static Error godot_icall_Array_Resize(IntPtr ptr, int newSize); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static Error godot_icall_Array_Shuffle(IntPtr ptr); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_Array_Generic_GetElementTypeInfo(Type elemType, out int elemTypeEncoding, out IntPtr elemTypeClass); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static string godot_icall_Array_ToString(IntPtr ptr); + internal static extern string godot_icall_Array_ToString(ref godot_array ptr); } + internal interface IGenericGodotArray + { + Array UnderlyingArray { get; } + Type TypeOfElements { get; } + } + + // TODO: Now we should be able to avoid boxing /// /// Typed wrapper around Godot's Array class, an array of Variant /// typed elements allocated in the engine in C++. Useful when @@ -342,24 +327,27 @@ namespace Godot.Collections /// such as arrays or . /// /// The type of the array. - public class Array : IList, ICollection, IEnumerable + [SuppressMessage("ReSharper", "RedundantExtendsListEntry")] + public sealed class Array : IList, ICollection, IEnumerable, IGenericGodotArray { - Array objectArray; + private readonly Array _underlyingArray; - internal static int elemTypeEncoding; - internal static IntPtr 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 - static Array() - { - Array.godot_icall_Array_Generic_GetElementTypeInfo(typeof(T), out elemTypeEncoding, out elemTypeClass); - } + Array IGenericGodotArray.UnderlyingArray => _underlyingArray; + Type IGenericGodotArray.TypeOfElements => TypeOfElements; /// /// Constructs a new empty . /// public Array() { - objectArray = new Array(); + _underlyingArray = new Array(); } /// @@ -372,7 +360,7 @@ namespace Godot.Collections if (collection == null) throw new NullReferenceException($"Parameter '{nameof(collection)} cannot be null.'"); - objectArray = new Array(collection); + _underlyingArray = new Array(collection); } /// @@ -386,7 +374,8 @@ namespace Godot.Collections { throw new NullReferenceException($"Parameter '{nameof(array)} cannot be null.'"); } - objectArray = new Array(array); + + _underlyingArray = new Array(array); } /// @@ -395,23 +384,12 @@ namespace Godot.Collections /// The untyped array to construct from. public Array(Array array) { - objectArray = array; + _underlyingArray = array; } - internal Array(IntPtr handle) - { - objectArray = new Array(handle); - } - - internal Array(ArraySafeHandle handle) - { - objectArray = new Array(handle); - } - - internal IntPtr GetPtr() - { - return objectArray.GetPtr(); - } + // Explicit name to make it very clear + internal static Array CreateTakingOwnershipOfDisposableValue(godot_array nativeValueToOwn) + => new Array(Array.CreateTakingOwnershipOfDisposableValue(nativeValueToOwn)); /// /// Converts this typed to an untyped . @@ -419,7 +397,7 @@ namespace Godot.Collections /// The typed array to convert. public static explicit operator Array(Array from) { - return from.objectArray; + return from._underlyingArray; } /// @@ -429,7 +407,7 @@ namespace Godot.Collections /// A new Godot Array. public Array Duplicate(bool deep = false) { - return new Array(objectArray.Duplicate(deep)); + return new Array(_underlyingArray.Duplicate(deep)); } /// @@ -439,7 +417,7 @@ namespace Godot.Collections /// if successful, or an error code. public Error Resize(int newSize) { - return objectArray.Resize(newSize); + return _underlyingArray.Resize(newSize); } /// @@ -447,7 +425,7 @@ namespace Godot.Collections /// public void Shuffle() { - objectArray.Shuffle(); + _underlyingArray.Shuffle(); } /// @@ -458,7 +436,7 @@ namespace Godot.Collections /// A new Godot Array with the contents of both arrays. public static Array operator +(Array left, Array right) { - return new Array(left.objectArray + right.objectArray); + return new Array(left._underlyingArray + right._underlyingArray); } // IList @@ -469,8 +447,16 @@ namespace Godot.Collections /// The value at the given index. 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; } /// @@ -481,7 +467,7 @@ namespace Godot.Collections /// The index of the item, or -1 if not found. public int IndexOf(T item) { - return objectArray.IndexOf(item); + return _underlyingArray.IndexOf(item); } /// @@ -494,7 +480,7 @@ namespace Godot.Collections /// The item to insert. public void Insert(int index, T item) { - objectArray.Insert(index, item); + _underlyingArray.Insert(index, item); } /// @@ -503,7 +489,7 @@ namespace Godot.Collections /// The index of the element to remove. public void RemoveAt(int index) { - objectArray.RemoveAt(index); + _underlyingArray.RemoveAt(index); } // ICollection @@ -513,10 +499,7 @@ namespace Godot.Collections /// This is also known as the size or length of the array. /// /// The number of elements. - public int Count - { - get { return objectArray.Count; } - } + public int Count => _underlyingArray.Count; bool ICollection.IsReadOnly => false; @@ -528,7 +511,7 @@ namespace Godot.Collections /// The new size after adding the item. public void Add(T item) { - objectArray.Add(item); + _underlyingArray.Add(item); } /// @@ -536,7 +519,7 @@ namespace Godot.Collections /// public void Clear() { - objectArray.Clear(); + _underlyingArray.Clear(); } /// @@ -546,7 +529,7 @@ namespace Godot.Collections /// Whether or not this array contains the given item. public bool Contains(T item) { - return objectArray.Contains(item); + return _underlyingArray.Contains(item); } /// @@ -563,17 +546,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 +566,7 @@ namespace Godot.Collections /// A bool indicating success or failure. public bool Remove(T item) { - return Array.godot_icall_Array_Remove(GetPtr(), item); + return Array.godot_icall_Array_Remove(ref _underlyingArray.NativeValue, item); } // IEnumerable @@ -597,23 +577,20 @@ namespace Godot.Collections /// An enumerator. public IEnumerator 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(); /// /// Converts this to a string. /// /// A string representation of this array. - public override string ToString() => objectArray.ToString(); + public override string ToString() => _underlyingArray.ToString(); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs index 968f853c2d..3034323d9e 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs @@ -727,7 +727,7 @@ namespace Godot /// (in the YXZ convention: when *composing*, first Y, then X, and Z last), /// given in the vector format as (X angle, Y angle, Z angle). /// - /// Consider using the constructor instead, which + /// Consider using the constructor instead, which /// uses a quaternion instead of Euler angles. /// /// The Euler angles to create the basis from. 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 61a34bfc87..344acd65e0 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs @@ -2,48 +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 { - 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; - } - } - /// /// Wrapper around Godot's Dictionary class, a dictionary of Variant /// typed elements allocated in the engine in C++. Useful when /// interfacing with the engine. /// - public class Dictionary : + public sealed class Dictionary : IDictionary, IDisposable { - DictionarySafeHandle safeHandle; - bool disposed = false; + internal godot_dictionary NativeValue; /// /// Constructs a new empty . /// public Dictionary() { - safeHandle = new DictionarySafeHandle(godot_icall_Dictionary_Ctor()); + godot_icall_Dictionary_Ctor(out NativeValue); } /// @@ -60,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); } /// @@ -83,16 +59,14 @@ namespace Godot.Collections /// public void Dispose() { - if (disposed) - return; + Dispose(true); + GC.SuppressFinalize(this); + } - if (safeHandle != null) - { - safeHandle.Dispose(); - safeHandle = null; - } - - disposed = true; + public void Dispose(bool disposing) + { + // Always dispose `NativeValue` even if disposing is true + NativeValue.Dispose(); } /// @@ -102,7 +76,9 @@ namespace Godot.Collections /// A new Godot Dictionary. 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 @@ -114,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); } } @@ -126,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); } @@ -149,8 +129,16 @@ namespace Godot.Collections /// The object at the given . 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); } /// @@ -159,19 +147,19 @@ namespace Godot.Collections /// /// The key at which to add the object. /// The object to add. - 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); /// /// Erases all items from this . /// - public void Clear() => godot_icall_Dictionary_Clear(GetPtr()); + public void Clear() => godot_icall_Dictionary_Clear(ref NativeValue); /// /// Checks if this contains the given key. /// /// The key to look for. /// Whether or not this dictionary contains the given key. - public bool Contains(object key) => godot_icall_Dictionary_ContainsKey(GetPtr(), key); + public bool Contains(object key) => godot_icall_Dictionary_ContainsKey(ref NativeValue, key); /// /// Gets an enumerator for this . @@ -183,7 +171,7 @@ namespace Godot.Collections /// Removes an element from this by key. /// /// The key of the element to remove. - public void Remove(object key) => godot_icall_Dictionary_RemoveKey(GetPtr(), key); + public void Remove(object key) => godot_icall_Dictionary_RemoveKey(ref NativeValue, key); // ICollection @@ -196,7 +184,7 @@ namespace Godot.Collections /// This is also known as the size or length of the dictionary. /// /// The number of elements. - public int Count => godot_icall_Dictionary_Count(GetPtr()); + public int Count => godot_icall_Dictionary_Count(ref NativeValue); /// /// Copies the elements of this to the given @@ -260,7 +248,7 @@ namespace Godot.Collections private void UpdateEntry() { dirty = false; - godot_icall_Dictionary_KeyValuePairAt(dictionary.GetPtr(), index, out object key, out object value); + godot_icall_Dictionary_KeyValuePairAt(ref dictionary.NativeValue, index, out object key, out object value); entry = new DictionaryEntry(key, value); } @@ -288,73 +276,70 @@ namespace Godot.Collections /// A string representation of this dictionary. public override string ToString() { - return godot_icall_Dictionary_ToString(GetPtr()); + return godot_icall_Dictionary_ToString(ref NativeValue); } [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static IntPtr godot_icall_Dictionary_Ctor(); + internal static extern void godot_icall_Dictionary_Ctor(out godot_dictionary dest); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_Dictionary_Dtor(IntPtr ptr); + internal static extern void godot_icall_Dictionary_GetValue(ref godot_dictionary ptr, object key, out godot_variant value); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static object godot_icall_Dictionary_GetValue(IntPtr ptr, object key); + internal static extern void godot_icall_Dictionary_SetValue(ref godot_dictionary ptr, object key, object value); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static object godot_icall_Dictionary_GetValue_Generic(IntPtr ptr, object key, int valTypeEncoding, IntPtr valTypeClass); + internal static extern void godot_icall_Dictionary_Keys(ref godot_dictionary ptr, out godot_array dest); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_Dictionary_SetValue(IntPtr ptr, object key, object value); + internal static extern void godot_icall_Dictionary_Values(ref godot_dictionary ptr, out godot_array dest); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static IntPtr godot_icall_Dictionary_Keys(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 extern static IntPtr godot_icall_Dictionary_Values(IntPtr ptr); + internal static extern void godot_icall_Dictionary_KeyValuePairAt(ref godot_dictionary ptr, int index, out object key, out object value); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static int godot_icall_Dictionary_Count(IntPtr ptr); + internal static extern void godot_icall_Dictionary_Add(ref godot_dictionary ptr, object key, object value); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static int godot_icall_Dictionary_KeyValuePairs(IntPtr ptr, out IntPtr keys, out IntPtr values); + internal static extern int godot_icall_Dictionary_Count(ref godot_dictionary ptr); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_Dictionary_KeyValuePairAt(IntPtr ptr, int index, out object key, out object value); + internal static extern void godot_icall_Dictionary_Clear(ref godot_dictionary ptr); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static 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 extern static 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 extern static 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 extern static 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 extern static 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 extern static 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 extern static bool godot_icall_Dictionary_Remove(IntPtr ptr, object key, object value); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static bool godot_icall_Dictionary_TryGetValue(IntPtr ptr, object key, out object value); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static bool godot_icall_Dictionary_TryGetValue_Generic(IntPtr ptr, object key, out object value, int valTypeEncoding, IntPtr valTypeClass); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_Dictionary_Generic_GetValueTypeInfo(Type valueType, out int valTypeEncoding, out IntPtr valTypeClass); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static string godot_icall_Dictionary_ToString(IntPtr ptr); + internal static extern string godot_icall_Dictionary_ToString(ref godot_dictionary ptr); } + internal interface IGenericGodotDictionary + { + Dictionary UnderlyingDictionary { get; } + Type TypeOfKeys { get; } + Type TypeOfValues { get; } + } + + // TODO: Now we should be able to avoid boxing + /// /// Typed wrapper around Godot's Dictionary class, a dictionary of Variant /// typed elements allocated in the engine in C++. Useful when @@ -363,25 +348,30 @@ namespace Godot.Collections /// /// The type of the dictionary's keys. /// The type of the dictionary's values. - public class Dictionary : - IDictionary + public sealed class Dictionary : + IDictionary, IGenericGodotDictionary { - private readonly Dictionary objectDict; + private readonly Dictionary _underlyingDict; - internal static int valTypeEncoding; - internal static IntPtr 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); - static Dictionary() - { - Dictionary.godot_icall_Dictionary_Generic_GetValueTypeInfo(typeof(TValue), out valTypeEncoding, out valTypeClass); - } + private static readonly Type TypeOfValues = typeof(TValue); + // ReSharper restore StaticMemberInGenericType + + Dictionary IGenericGodotDictionary.UnderlyingDictionary => _underlyingDict; + Type IGenericGodotDictionary.TypeOfKeys => TypeOfKeys; + Type IGenericGodotDictionary.TypeOfValues => TypeOfValues; /// /// Constructs a new empty . /// public Dictionary() { - objectDict = new Dictionary(); + _underlyingDict = new Dictionary(); } /// @@ -391,19 +381,13 @@ namespace Godot.Collections /// A new Godot Dictionary. public Dictionary(IDictionary 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 entry in dictionary) - { - Dictionary.godot_icall_Dictionary_Add(godotDictionaryPtr, entry.Key, entry.Value); - } + Add(entry.Key, entry.Value); } /// @@ -413,18 +397,12 @@ namespace Godot.Collections /// A new Godot Dictionary. 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 CreateTakingOwnershipOfDisposableValue(godot_dictionary nativeValueToOwn) + => new Dictionary(Dictionary.CreateTakingOwnershipOfDisposableValue(nativeValueToOwn)); /// /// Converts this typed to an untyped . @@ -432,12 +410,7 @@ namespace Godot.Collections /// The typed dictionary to convert. public static explicit operator Dictionary(Dictionary from) { - return from.objectDict; - } - - internal IntPtr GetPtr() - { - return objectDict.GetPtr(); + return from._underlyingDict; } /// @@ -447,7 +420,7 @@ namespace Godot.Collections /// A new Godot Dictionary. public Dictionary Duplicate(bool deep = false) { - return new Dictionary(objectDict.Duplicate(deep)); + return new Dictionary(_underlyingDict.Duplicate(deep)); } // IDictionary @@ -458,8 +431,16 @@ namespace Godot.Collections /// The value at the given . 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; } /// @@ -469,8 +450,9 @@ namespace Godot.Collections { get { - IntPtr handle = Dictionary.godot_icall_Dictionary_Keys(objectDict.GetPtr()); - return new Array(new ArraySafeHandle(handle)); + godot_array keyArray; + Dictionary.godot_icall_Dictionary_Keys(ref _underlyingDict.NativeValue, out keyArray); + return Array.CreateTakingOwnershipOfDisposableValue(keyArray); } } @@ -481,14 +463,15 @@ namespace Godot.Collections { get { - IntPtr handle = Dictionary.godot_icall_Dictionary_Values(objectDict.GetPtr()); - return new Array(new ArraySafeHandle(handle)); + godot_array valuesArray; + Dictionary.godot_icall_Dictionary_Values(ref _underlyingDict.NativeValue, out valuesArray); + return Array.CreateTakingOwnershipOfDisposableValue(valuesArray); } } private KeyValuePair GetKeyValuePair(int index) { - Dictionary.godot_icall_Dictionary_KeyValuePairAt(GetPtr(), index, out object key, out object value); + Dictionary.godot_icall_Dictionary_KeyValuePairAt(ref _underlyingDict.NativeValue, index, out object key, out object value); return new KeyValuePair((TKey)key, (TValue)value); } @@ -500,7 +483,7 @@ namespace Godot.Collections /// The object to add. public void Add(TKey key, TValue value) { - objectDict.Add(key, value); + _underlyingDict.Add(key, value); } /// @@ -510,7 +493,7 @@ namespace Godot.Collections /// Whether or not this dictionary contains the given key. public bool ContainsKey(TKey key) { - return objectDict.Contains(key); + return _underlyingDict.Contains(key); } /// @@ -519,7 +502,7 @@ namespace Godot.Collections /// The key of the element to remove. public bool Remove(TKey key) { - return Dictionary.godot_icall_Dictionary_RemoveKey(GetPtr(), key); + return Dictionary.godot_icall_Dictionary_RemoveKey(ref _underlyingDict.NativeValue, key); } /// @@ -530,8 +513,18 @@ namespace Godot.Collections /// If an object was found for the given . 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 +535,13 @@ namespace Godot.Collections /// This is also known as the size or length of the dictionary. /// /// The number of elements. - public int Count - { - get { return objectDict.Count; } - } + public int Count => _underlyingDict.Count; bool ICollection>.IsReadOnly => false; void ICollection>.Add(KeyValuePair item) { - objectDict.Add(item.Key, item.Value); + _underlyingDict.Add(item.Key, item.Value); } /// @@ -559,12 +549,12 @@ namespace Godot.Collections /// public void Clear() { - objectDict.Clear(); + _underlyingDict.Clear(); } bool ICollection>.Contains(KeyValuePair item) { - return objectDict.Contains(new KeyValuePair(item.Key, item.Value)); + return _underlyingDict.Contains(new KeyValuePair(item.Key, item.Value)); } /// @@ -595,8 +585,7 @@ namespace Godot.Collections bool ICollection>.Remove(KeyValuePair 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> @@ -613,15 +602,12 @@ namespace Godot.Collections } } - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); /// /// Converts this to a string. /// /// A string representation of this dictionary. - 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 20b11a48dd..5ba46d6bd8 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 { @@ -8,10 +9,12 @@ namespace Godot { public Array GetNodesInGroup(StringName group) where T : class { - return new Array(godot_icall_SceneTree_get_nodes_in_group_Generic(Object.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.CreateTakingOwnershipOfDisposableValue(array); } [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static 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 71d0593916..cd0e0d858a 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; // TODO: Add comments describing what this class does. It is not obvious. @@ -13,9 +14,10 @@ namespace Godot { public static partial class GD { - 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); } public static object Convert(object what, Variant.Type type) @@ -180,12 +182,17 @@ namespace Godot public static bool TypeExists(StringName type) { - return godot_icall_GD_type_exists(StringName.GetPtr(type)); + return godot_icall_GD_type_exists(ref type.NativeValue); } - 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); + } } public static string Var2Str(object var) @@ -199,73 +206,73 @@ namespace Godot } [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static 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 extern static object godot_icall_GD_convert(object what, Variant.Type type); + internal static extern object godot_icall_GD_convert(object what, Variant.Type type); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static int godot_icall_GD_hash(object var); + internal static extern int godot_icall_GD_hash(object var); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static Object godot_icall_GD_instance_from_id(ulong instanceId); + internal static extern Object godot_icall_GD_instance_from_id(ulong instanceId); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_GD_print(object[] what); + internal static extern void godot_icall_GD_print(object[] what); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_GD_printerr(object[] what); + internal static extern void godot_icall_GD_printerr(object[] what); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_GD_printraw(object[] what); + internal static extern void godot_icall_GD_printraw(object[] what); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_GD_prints(object[] what); + internal static extern void godot_icall_GD_prints(object[] what); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_GD_printt(object[] what); + internal static extern void godot_icall_GD_printt(object[] what); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static float godot_icall_GD_randf(); + internal static extern float godot_icall_GD_randf(); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static uint godot_icall_GD_randi(); + internal static extern uint godot_icall_GD_randi(); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_GD_randomize(); + internal static extern void godot_icall_GD_randomize(); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static double godot_icall_GD_randf_range(double from, double to); + internal static extern double godot_icall_GD_randf_range(double from, double to); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static int godot_icall_GD_randi_range(int from, int to); + internal static extern int godot_icall_GD_randi_range(int from, int to); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static uint godot_icall_GD_rand_seed(ulong seed, out ulong newSeed); + internal static extern uint godot_icall_GD_rand_seed(ulong seed, out ulong newSeed); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_GD_seed(ulong seed); + internal static extern void godot_icall_GD_seed(ulong seed); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static string godot_icall_GD_str(object[] what); + internal static extern string godot_icall_GD_str(object[] what); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static object godot_icall_GD_str2var(string str); + internal static extern object godot_icall_GD_str2var(string str); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static 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 extern static 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 extern static string godot_icall_GD_var2str(object var); + internal static extern string godot_icall_GD_var2str(object var); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_GD_pusherror(string type); + internal static extern void godot_icall_GD_pusherror(string type); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_GD_pushwarning(string type); + internal static extern void godot_icall_GD_pushwarning(string type); [MethodImpl(MethodImplOptions.InternalCall)] private static extern Variant.Type godot_icall_TypeToVariantType(Type type); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs deleted file mode 100644 index f508211f68..0000000000 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Godot -{ - static class MarshalUtils - { - /// - /// Returns if the generic type definition of - /// is ; otherwise returns . - /// - /// - /// is not a generic type. That is, IsGenericType returns false. - /// - static bool TypeIsGenericArray(Type type) => - type.GetGenericTypeDefinition() == typeof(Godot.Collections.Array<>); - - /// - /// Returns if the generic type definition of - /// is ; otherwise returns . - /// - /// - /// is not a generic type. That is, IsGenericType returns false. - /// - static bool TypeIsGenericDictionary(Type type) => - type.GetGenericTypeDefinition() == typeof(Godot.Collections.Dictionary<,>); - - static bool TypeIsSystemGenericList(Type type) => - type.GetGenericTypeDefinition() == typeof(System.Collections.Generic.List<>); - - static bool TypeIsSystemGenericDictionary(Type type) => - type.GetGenericTypeDefinition() == typeof(System.Collections.Generic.Dictionary<,>); - - static bool TypeIsGenericIEnumerable(Type type) => type.GetGenericTypeDefinition() == typeof(IEnumerable<>); - - static bool TypeIsGenericICollection(Type type) => type.GetGenericTypeDefinition() == typeof(ICollection<>); - - static bool TypeIsGenericIDictionary(Type type) => type.GetGenericTypeDefinition() == typeof(IDictionary<,>); - - static void ArrayGetElementType(Type arrayType, out Type elementType) - { - elementType = arrayType.GetGenericArguments()[0]; - } - - static void DictionaryGetKeyValueTypes(Type dictionaryType, out Type keyType, out Type valueType) - { - var genericArgs = dictionaryType.GetGenericArguments(); - keyType = genericArgs[0]; - valueType = genericArgs[1]; - } - - static Type MakeGenericArrayType(Type elemType) - { - return typeof(Godot.Collections.Array<>).MakeGenericType(elemType); - } - - static Type MakeGenericDictionaryType(Type keyType, Type valueType) - { - return typeof(Godot.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..b36206f2ee --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs @@ -0,0 +1,428 @@ +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; + Godot.Variant.Type expected; + } + + [StructLayout(LayoutKind.Explicit)] + // ReSharper disable once InconsistentNaming + internal struct godot_variant : IDisposable + { + [FieldOffset(0)] public Godot.Variant.Type _type; + + // There's padding here + + [FieldOffset(8)] internal godot_variant_data _data; + + [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)] 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..4dfe48fe7b --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs @@ -0,0 +1,1340 @@ +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(Basis)) + return Variant.Type.Basis; + + if (type == typeof(Quaternion)) + return Variant.Type.Quaternion; + + if (type == typeof(Transform3D)) + return Variant.Type.Transform3d; + + 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.RawArray; + + if (type == typeof(Int32[])) + return Variant.Type.Int32Array; + + if (type == typeof(Int64[])) + return Variant.Type.Int64Array; + + if (type == typeof(float[])) + return Variant.Type.Float32Array; + + if (type == typeof(double[])) + return Variant.Type.Float64Array; + + if (type == typeof(string[])) + return Variant.Type.StringArray; + + if (type == typeof(Vector2[])) + return Variant.Type.Vector2Array; + + if (type == typeof(Vector3[])) + return Variant.Type.Vector3Array; + + if (type == typeof(Color[])) + return Variant.Type.ColorArray; + + 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 Basis @basis: + return VariantUtils.CreateFromBasis(@basis); + case Quaternion @quaternion: + return VariantUtils.CreateFromQuaternion(@quaternion); + case Transform3D @transform3d: + return VariantUtils.CreateFromTransform3D(@transform3d); + 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 IDictionaryToGenericGodotDictionary + (IDictionary 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(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(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.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.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.RawArray: + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_byte_array(p_var); + return PackedByteArray_to_mono_array(&packedArray); + } + case Variant.Type.Int32Array: + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_int32_array(p_var); + return PackedInt32Array_to_mono_array(&packedArray); + } + case Variant.Type.Int64Array: + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_int64_array(p_var); + return PackedInt64Array_to_mono_array(&packedArray); + } + case Variant.Type.Float32Array: + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_float32_array(p_var); + return PackedFloat32Array_to_mono_array(&packedArray); + } + case Variant.Type.Float64Array: + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_float64_array(p_var); + return PackedFloat64Array_to_mono_array(&packedArray); + } + case Variant.Type.StringArray: + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_string_array(p_var); + return PackedStringArray_to_mono_array(&packedArray); + } + case Variant.Type.Vector2Array: + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector2_array(p_var); + return PackedVector2Array_to_mono_array(&packedArray); + } + case Variant.Type.Vector3Array: + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector3_array(p_var); + return PackedVector3Array_to_mono_array(&packedArray); + } + case Variant.Type.ColorArray: + { + 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 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 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 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 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 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 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 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 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 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 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..2b0ff62816 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs @@ -0,0 +1,328 @@ +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 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_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_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 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 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 _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 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 Cleared(this Span 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..03373152a8 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs @@ -0,0 +1,305 @@ +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 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 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 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 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 4ecc55f94e..d0d6606936 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs @@ -1,24 +1,12 @@ using System; using System.Runtime.CompilerServices; +using Godot.NativeInterop; namespace Godot { - 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() { @@ -31,112 +19,105 @@ 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; } - public NodePath() : this(string.Empty) {} + // Explicit name to make it very clear + internal static NodePath CreateTakingOwnershipOfDisposableValue(godot_node_path nativeValueToOwn) + => new NodePath(nativeValueToOwn); + + public NodePath() + { + } public NodePath(string path) { - ptr = godot_icall_NodePath_Ctor(path); + if (!string.IsNullOrEmpty(path)) + NativeValue = NativeFuncs.godotsharp_node_path_new_from_string(path); } public static implicit operator NodePath(string from) => new NodePath(from); public static implicit operator string(NodePath from) => from.ToString(); - 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); } 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); } - public string GetConcatenatedSubnames() + public string GetConcatenatedSubNames() { - return godot_icall_NodePath_get_concatenated_subnames(GetPtr(this)); + return godot_icall_NodePath_get_concatenated_subnames(ref NativeValue); } public string GetName(int idx) { - return godot_icall_NodePath_get_name(GetPtr(this), idx); + return godot_icall_NodePath_get_name(ref NativeValue, idx); } public int GetNameCount() { - return godot_icall_NodePath_get_name_count(GetPtr(this)); + return godot_icall_NodePath_get_name_count(ref NativeValue); } - 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); } - public int GetSubnameCount() + public int GetSubNameCount() { - return godot_icall_NodePath_get_subname_count(GetPtr(this)); + return godot_icall_NodePath_get_subname_count(ref NativeValue); } public bool IsAbsolute() { - return godot_icall_NodePath_is_absolute(GetPtr(this)); + return godot_icall_NodePath_is_absolute(ref NativeValue); } - public bool IsEmpty() - { - return godot_icall_NodePath_is_empty(GetPtr(this)); - } + public bool IsEmpty => godot_node_path.IsEmpty(in NativeValue); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern IntPtr godot_icall_NodePath_Ctor(string path); + 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 void godot_icall_NodePath_Dtor(IntPtr ptr); + private static extern string godot_icall_NodePath_get_concatenated_subnames(ref godot_node_path ptr); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string godot_icall_NodePath_operator_String(IntPtr ptr); + private static extern string godot_icall_NodePath_get_name(ref godot_node_path ptr, int arg1); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern IntPtr godot_icall_NodePath_get_as_property_path(IntPtr ptr); + private static extern int godot_icall_NodePath_get_name_count(ref godot_node_path ptr); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string godot_icall_NodePath_get_concatenated_subnames(IntPtr ptr); + private static extern string godot_icall_NodePath_get_subname(ref godot_node_path ptr, int arg1); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string godot_icall_NodePath_get_name(IntPtr ptr, int arg1); + private static extern int godot_icall_NodePath_get_subname_count(ref godot_node_path ptr); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern int godot_icall_NodePath_get_name_count(IntPtr ptr); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string godot_icall_NodePath_get_subname(IntPtr ptr, int arg1); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern int godot_icall_NodePath_get_subname_count(IntPtr ptr); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern bool godot_icall_NodePath_is_absolute(IntPtr 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 d486d79557..8587748d03 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs @@ -5,44 +5,50 @@ namespace Godot { public partial class Object : IDisposable { - private bool disposed = false; + private bool _disposed = false; - private static StringName nativeName = "Object"; - - internal IntPtr ptr; - internal bool memoryOwn; + internal IntPtr NativePtr; + internal bool MemoryOwn; 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; } - public IntPtr NativeInstance - { - get { return ptr; } - } + public IntPtr NativeInstance => NativePtr; internal static IntPtr GetPtr(Object instance) { if (instance == null) return IntPtr.Zero; - if (instance.disposed) + if (instance._disposed) throw new ObjectDisposedException(instance.GetType().FullName); - return instance.ptr; + return instance.NativePtr; } ~Object() @@ -58,25 +64,25 @@ namespace Godot protected virtual void Dispose(bool disposing) { - if (disposed) + 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); } - this.ptr = IntPtr.Zero; + this.NativePtr = IntPtr.Zero; } - disposed = true; + _disposed = true; } public override string ToString() @@ -117,13 +123,49 @@ namespace Godot /// 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) { - return godot_icall_Object_ClassDB_get_method(StringName.GetPtr(type), 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; } - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern IntPtr godot_icall_Object_Ctor(Object obj); +#if NET + internal static unsafe delegate* unmanaged _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) + { + // 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; + } + + 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); @@ -136,9 +178,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 94761531b1..bc86c53dec 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/RID.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/RID.cs @@ -1,84 +1,25 @@ using System; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Godot.NativeInterop; namespace Godot { - 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) + internal RID(ulong id) { - 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); - } - - 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) - { - this.ptr = ptr; - } - - public IntPtr NativeInstance - { - get { return ptr; } - } - - internal RID() - { - this.ptr = IntPtr.Zero; + _id = id; } public RID(Object from) - { - this.ptr = godot_icall_RID_Ctor(Object.GetPtr(from)); - } + => _id = from is Resource res ? res.GetRid()._id : default; - public int GetId() - { - return godot_icall_RID_get_id(RID.GetPtr(this)); - } + public ulong Id => _id; - public override string ToString() => "[RID]"; - - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static IntPtr godot_icall_RID_Ctor(IntPtr from); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_RID_Dtor(IntPtr ptr); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static 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 4dc630238b..e9a6eb6aad 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs @@ -1,53 +1,42 @@ using System; using System.Runtime.CompilerServices; +using Godot.NativeInterop; namespace Godot { public class SignalAwaiter : IAwaiter, IAwaitable { - private bool completed; - private object[] result; - private Action action; + private bool _completed; + private object[] _result; + private Action _action; 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 extern static 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; + this._action = action; } - public object[] GetResult() - { - return result; - } + public object[] GetResult() => _result; - public IAwaiter GetAwaiter() - { - return this; - } + public IAwaiter GetAwaiter() => this; internal void SignalCallback(object[] args) { - completed = true; - result = args; + _completed = true; + _result = args; - if (action != null) + if (_action != null) { - action(); + _action(); } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs index d9665cbf2b..6009173843 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs @@ -192,7 +192,7 @@ namespace Godot /// 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"); @@ -215,7 +215,7 @@ namespace Godot /// 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"); @@ -732,7 +732,7 @@ namespace Godot /// 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 7700b6d4ed..5cebbff153 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs @@ -1,22 +1,12 @@ using System; using System.Runtime.CompilerServices; +using Godot.NativeInterop; namespace Godot { - 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() { @@ -29,54 +19,47 @@ 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); + public StringName() { - ptr = IntPtr.Zero; } - 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); } public static implicit operator StringName(string from) => new StringName(from); public static implicit operator string(StringName from) => from.ToString(); - 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); } - 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 1fcfe74c86..6cd0539ac4 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj @@ -7,10 +7,19 @@ netstandard2.1 $(OutputPath)/$(AssemblyName).xml false + true + 9 + + + CS1591 $(DefineConstants);GODOT + + + + @@ -44,16 +53,23 @@ - + + + + + + + + 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 @@ + + True + True + True + 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 @@ netstandard2.1 $(OutputPath)/$(AssemblyName).xml false + true + 9 $(DefineConstants);GODOT 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 @@ + + True + True + diff --git a/modules/mono/glue/base_object_glue.cpp b/modules/mono/glue/base_object_glue.cpp index 6c5503a3bd..40886ddbea 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 86976de244..09072cce67 100644 --- a/modules/mono/glue/collections_glue.cpp +++ b/modules/mono/glue/collections_glue.cpp @@ -28,8 +28,6 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifdef MONO_GLUE_ENABLED - #include #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_Ctor(Array *r_dest) { + memnew_placement(r_dest, Array); } -void godot_icall_Array_Dtor(Array *ptr) { - memdelete(ptr); -} - -MonoObject *godot_icall_Array_At(Array *ptr, int32_t index) { +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)); -} - -MonoObject *godot_icall_Array_At_Generic(Array *ptr, int32_t index, uint32_t type_encoding, GDMonoClass *type_class) { - if (index < 0 || index >= ptr->size()) { - GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range()); - return nullptr; - } - 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,27 +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_Ctor(Dictionary *r_dest) { + memnew_placement(r_dest, Dictionary); } -void godot_icall_Dictionary_Dtor(Dictionary *ptr) { - memdelete(ptr); -} - -MonoObject *godot_icall_Dictionary_GetValue(Dictionary *ptr, MonoObject *key) { +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()); @@ -195,35 +168,22 @@ MonoObject *godot_icall_Dictionary_GetValue(Dictionary *ptr, MonoObject *key) { #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); -} - -MonoObject *godot_icall_Dictionary_GetValue_Generic(Dictionary *ptr, MonoObject *key, uint32_t type_encoding, GDMonoClass *type_class) { - Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key)); - if (ret == 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, 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) { @@ -231,16 +191,14 @@ int32_t godot_icall_Dictionary_Count(Dictionary *ptr) { } 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); + memnew_placement(*keys, Array(ptr->keys())); + memnew_placement(*values, Array(ptr->values())); + return ptr->size(); } void godot_icall_Dictionary_KeyValuePairAt(Dictionary *ptr, int index, MonoObject **key, MonoObject **value) { - Array *keys = godot_icall_Dictionary_Keys(ptr); - Array *values = godot_icall_Dictionary_Values(ptr); - *key = GDMonoMarshal::variant_to_mono_object(keys->get(index)); - *value = GDMonoMarshal::variant_to_mono_object(values->get(index)); + *key = GDMonoMarshal::variant_to_mono_object(ptr->get_key_at_index(index)); + *value = GDMonoMarshal::variant_to_mono_object(ptr->get_value_at_index(index)); } void godot_icall_Dictionary_Add(Dictionary *ptr, MonoObject *key, MonoObject *value) { @@ -267,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) { @@ -288,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) { +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); + *value = ret; return true; } -MonoBoolean godot_icall_Dictionary_TryGetValue_Generic(Dictionary *ptr, MonoObject *key, MonoObject **value, uint32_t type_encoding, GDMonoClass *type_class) { - Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key)); - if (ret == nullptr) { - *value = nullptr; - return false; - } - *value = GDMonoMarshal::variant_to_mono_object(ret, ManagedType(type_encoding, type_class)); - 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()); } @@ -323,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); @@ -340,13 +278,10 @@ 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); @@ -361,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 a2ff868f65..9cd422ed58 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."); } @@ -260,18 +257,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) { @@ -317,5 +313,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 eed3bd2167..0000000000 --- a/modules/mono/glue/glue_header.h +++ /dev/null @@ -1,84 +0,0 @@ -/*************************************************************************/ -/* glue_header.h */ -/*************************************************************************/ -/* 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. */ -/*************************************************************************/ - -#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_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_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 diff --git a/modules/mono/glue/nodepath_glue.cpp b/modules/mono/glue/node_path_glue.cpp similarity index 76% rename from modules/mono/glue/nodepath_glue.cpp rename to modules/mono/glue/node_path_glue.cpp index 4ddb94e1a8..56962ea9da 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(); } @@ -72,18 +57,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_subnames", godot_icall_NodePath_get_concatenated_subnames); GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_name", godot_icall_NodePath_get_name); @@ -91,7 +69,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 similarity index 70% rename from modules/mono/glue/rid_glue.cpp rename to modules/mono/glue/placeholder_glue.cpp index f464e63a81..27be3fbbab 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,24 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifdef MONO_GLUE_ENABLED +#include "core/object/object.h" -#include "core/io/resource.h" -#include "core/object/class_db.h" -#include "core/templates/rid.h" +#include "../mono_gd/gd_mono_internals.h" +#include "../mono_gd/gd_mono_utils.h" -#include "../mono_gd/gd_mono_marshal.h" - -RID *godot_icall_RID_Ctor(Object *p_from) { - Resource *res_from = Object::cast_to(p_from); - - if (res_from) { - return memnew(RID(res_from->get_rid())); - } - - return memnew(RID); +MonoObject *godot_icall_InteropUtils_unmanaged_get_managed(Object *unmanaged) { + return GDMonoUtils::unmanaged_get_managed(unmanaged); } -void godot_icall_RID_Dtor(RID *p_ptr) { - ERR_FAIL_NULL(p_ptr); - memdelete(p_ptr); +void godot_icall_InteropUtils_tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) { + GDMonoInternals::tie_managed_to_unmanaged(managed, unmanaged); } -uint32_t godot_icall_RID_get_id(RID *p_ptr) { - return p_ptr->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); } - -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); -} - -#endif // MONO_GLUE_ENABLED diff --git a/modules/mono/glue/runtime_interop.cpp b/modules/mono/glue/runtime_interop.cpp new file mode 100644 index 0000000000..aa6f36d898 --- /dev/null +++ b/modules/mono/glue/runtime_interop.cpp @@ -0,0 +1,649 @@ +/*************************************************************************/ +/* 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 + +#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 *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_byte_array godotsharp_packed_byte_array_new_mem_copy(const uint8_t *p_src, int32_t p_length) { + godot_packed_byte_array ret; + memnew_placement(&ret, PackedByteArray); + PackedByteArray *array = reinterpret_cast(&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_int32_array godotsharp_packed_int32_array_new_mem_copy(const int32_t *p_src, int32_t p_length) { + godot_packed_int32_array ret; + memnew_placement(&ret, PackedInt32Array); + PackedInt32Array *array = reinterpret_cast(&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_int64_array godotsharp_packed_int64_array_new_mem_copy(const int64_t *p_src, int32_t p_length) { + godot_packed_int64_array ret; + memnew_placement(&ret, PackedInt64Array); + PackedInt64Array *array = reinterpret_cast(&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_float32_array godotsharp_packed_float32_array_new_mem_copy(const float *p_src, int32_t p_length) { + godot_packed_float32_array ret; + memnew_placement(&ret, PackedFloat32Array); + PackedFloat32Array *array = reinterpret_cast(&ret); + array->resize(p_length); + float *dst = array->ptrw(); + memcpy(dst, p_src, p_length * sizeof(float)); + return ret; +} + +GD_PINVOKE_EXPORT godot_packed_float64_array godotsharp_packed_float64_array_new_mem_copy(const double *p_src, int32_t p_length) { + godot_packed_float64_array ret; + memnew_placement(&ret, PackedFloat64Array); + PackedFloat64Array *array = reinterpret_cast(&ret); + array->resize(p_length); + double *dst = array->ptrw(); + memcpy(dst, p_src, p_length * sizeof(double)); + return ret; +} + +GD_PINVOKE_EXPORT godot_packed_vector2_array godotsharp_packed_vector2_array_new_mem_copy(const Vector2 *p_src, int32_t p_length) { + godot_packed_vector2_array ret; + memnew_placement(&ret, PackedVector2Array); + PackedVector2Array *array = reinterpret_cast(&ret); + array->resize(p_length); + Vector2 *dst = array->ptrw(); + memcpy(dst, p_src, p_length * sizeof(Vector2)); + return ret; +} + +GD_PINVOKE_EXPORT godot_packed_vector3_array godotsharp_packed_vector3_array_new_mem_copy(const Vector3 *p_src, int32_t p_length) { + godot_packed_vector3_array ret; + memnew_placement(&ret, PackedVector3Array); + PackedVector3Array *array = reinterpret_cast(&ret); + array->resize(p_length); + Vector3 *dst = array->ptrw(); + memcpy(dst, p_src, p_length * sizeof(Vector3)); + return ret; +} + +GD_PINVOKE_EXPORT godot_packed_color_array godotsharp_packed_color_array_new_mem_copy(const Color *p_src, int32_t p_length) { + godot_packed_color_array ret; + memnew_placement(&ret, PackedColorArray); + PackedColorArray *array = reinterpret_cast(&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(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(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(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(godot_method_bind *p_method_bind, godot_object *p_instance, const void **p_args, void *p_ret) { + godot_method_bind_ptrcall(p_method_bind, p_instance, p_args, p_ret); +} + +GD_PINVOKE_EXPORT godot_variant godotsharp_method_bind_call(godot_method_bind *p_method_bind, godot_object *p_instance, const godot_variant **p_args, const int p_arg_count, godot_variant_call_error *p_call_error) { + return godot_method_bind_call(p_method_bind, p_instance, p_args, p_arg_count, p_call_error); +} + +// variant.h + +GD_PINVOKE_EXPORT void godotsharp_variant_new_string_name(godot_variant *r_dest, const godot_string_name *p_s) { + godot_variant_new_string_name(r_dest, p_s); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_node_path(godot_variant *r_dest, const godot_node_path *p_np) { + godot_variant_new_node_path(r_dest, p_np); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_object(godot_variant *r_dest, const godot_object *p_obj) { + godot_variant_new_object(r_dest, p_obj); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_transform2d(godot_variant *r_dest, const godot_transform2d *p_t2d) { + godot_variant_new_transform2d(r_dest, p_t2d); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_basis(godot_variant *r_dest, const godot_basis *p_basis) { + godot_variant_new_basis(r_dest, p_basis); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_transform3d(godot_variant *r_dest, const godot_transform3d *p_trans) { + godot_variant_new_transform3d(r_dest, p_trans); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_aabb(godot_variant *r_dest, const godot_aabb *p_aabb) { + godot_variant_new_aabb(r_dest, p_aabb); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_dictionary(godot_variant *r_dest, const godot_dictionary *p_dict) { + godot_variant_new_dictionary(r_dest, p_dict); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_array(godot_variant *r_dest, const godot_array *p_arr) { + godot_variant_new_array(r_dest, p_arr); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_packed_byte_array(godot_variant *r_dest, const godot_packed_byte_array *p_pba) { + godot_variant_new_packed_byte_array(r_dest, p_pba); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_packed_int32_array(godot_variant *r_dest, const godot_packed_int32_array *p_pia) { + godot_variant_new_packed_int32_array(r_dest, p_pia); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_packed_int64_array(godot_variant *r_dest, const godot_packed_int64_array *p_pia) { + godot_variant_new_packed_int64_array(r_dest, p_pia); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_packed_float32_array(godot_variant *r_dest, const godot_packed_float32_array *p_pra) { + godot_variant_new_packed_float32_array(r_dest, p_pra); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_packed_float64_array(godot_variant *r_dest, const godot_packed_float64_array *p_pra) { + godot_variant_new_packed_float64_array(r_dest, p_pra); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_packed_string_array(godot_variant *r_dest, const godot_packed_string_array *p_psa) { + godot_variant_new_packed_string_array(r_dest, p_psa); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_packed_vector2_array(godot_variant *r_dest, const godot_packed_vector2_array *p_pv2a) { + godot_variant_new_packed_vector2_array(r_dest, p_pv2a); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_packed_vector3_array(godot_variant *r_dest, const godot_packed_vector3_array *p_pv3a) { + godot_variant_new_packed_vector3_array(r_dest, p_pv3a); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_packed_color_array(godot_variant *r_dest, const godot_packed_color_array *p_pca) { + godot_variant_new_packed_color_array(r_dest, p_pca); +} + +GD_PINVOKE_EXPORT godot_bool godotsharp_variant_as_bool(const godot_variant *p_self) { + return godot_variant_as_bool(p_self); +} + +GD_PINVOKE_EXPORT godot_int godotsharp_variant_as_int(const godot_variant *p_self) { + return godot_variant_as_int(p_self); +} + +GD_PINVOKE_EXPORT godot_float godotsharp_variant_as_float(const godot_variant *p_self) { + return godot_variant_as_float(p_self); +} + +GD_PINVOKE_EXPORT godot_string godotsharp_variant_as_string(const godot_variant *p_self) { + return godot_variant_as_string(p_self); +} + +GD_PINVOKE_EXPORT godot_vector2 godotsharp_variant_as_vector2(const godot_variant *p_self) { + return godot_variant_as_vector2(p_self); +} + +GD_PINVOKE_EXPORT godot_vector2i godotsharp_variant_as_vector2i(const godot_variant *p_self) { + return godot_variant_as_vector2i(p_self); +} + +GD_PINVOKE_EXPORT godot_rect2 godotsharp_variant_as_rect2(const godot_variant *p_self) { + return godot_variant_as_rect2(p_self); +} + +GD_PINVOKE_EXPORT godot_rect2i godotsharp_variant_as_rect2i(const godot_variant *p_self) { + return godot_variant_as_rect2i(p_self); +} + +GD_PINVOKE_EXPORT godot_vector3 godotsharp_variant_as_vector3(const godot_variant *p_self) { + return godot_variant_as_vector3(p_self); +} + +GD_PINVOKE_EXPORT godot_vector3i godotsharp_variant_as_vector3i(const godot_variant *p_self) { + return godot_variant_as_vector3i(p_self); +} + +GD_PINVOKE_EXPORT godot_transform2d godotsharp_variant_as_transform2d(const godot_variant *p_self) { + return godot_variant_as_transform2d(p_self); +} + +GD_PINVOKE_EXPORT godot_plane godotsharp_variant_as_plane(const godot_variant *p_self) { + return godot_variant_as_plane(p_self); +} + +GD_PINVOKE_EXPORT godot_quaternion godotsharp_variant_as_quaternion(const godot_variant *p_self) { + return godot_variant_as_quaternion(p_self); +} + +GD_PINVOKE_EXPORT godot_aabb godotsharp_variant_as_aabb(const godot_variant *p_self) { + return godot_variant_as_aabb(p_self); +} + +GD_PINVOKE_EXPORT godot_basis godotsharp_variant_as_basis(const godot_variant *p_self) { + return godot_variant_as_basis(p_self); +} + +GD_PINVOKE_EXPORT godot_transform3d godotsharp_variant_as_transform3d(const godot_variant *p_self) { + return godot_variant_as_transform3d(p_self); +} + +GD_PINVOKE_EXPORT godot_color godotsharp_variant_as_color(const godot_variant *p_self) { + return godot_variant_as_color(p_self); +} + +GD_PINVOKE_EXPORT godot_string_name godotsharp_variant_as_string_name(const godot_variant *p_self) { + return godot_variant_as_string_name(p_self); +} + +GD_PINVOKE_EXPORT godot_node_path godotsharp_variant_as_node_path(const godot_variant *p_self) { + return godot_variant_as_node_path(p_self); +} + +GD_PINVOKE_EXPORT godot_rid godotsharp_variant_as_rid(const godot_variant *p_self) { + return godot_variant_as_rid(p_self); +} + +GD_PINVOKE_EXPORT godot_callable godotsharp_variant_as_callable(const godot_variant *p_self) { + return godot_variant_as_callable(p_self); +} + +GD_PINVOKE_EXPORT godot_signal godotsharp_variant_as_signal(const godot_variant *p_self) { + return godot_variant_as_signal(p_self); +} + +GD_PINVOKE_EXPORT godot_dictionary godotsharp_variant_as_dictionary(const godot_variant *p_self) { + return godot_variant_as_dictionary(p_self); +} + +GD_PINVOKE_EXPORT godot_array godotsharp_variant_as_array(const godot_variant *p_self) { + return godot_variant_as_array(p_self); +} + +GD_PINVOKE_EXPORT godot_packed_byte_array godotsharp_variant_as_packed_byte_array(const godot_variant *p_self) { + return godot_variant_as_packed_byte_array(p_self); +} + +GD_PINVOKE_EXPORT godot_packed_int32_array godotsharp_variant_as_packed_int32_array(const godot_variant *p_self) { + return godot_variant_as_packed_int32_array(p_self); +} + +GD_PINVOKE_EXPORT godot_packed_int64_array godotsharp_variant_as_packed_int64_array(const godot_variant *p_self) { + return godot_variant_as_packed_int64_array(p_self); +} + +GD_PINVOKE_EXPORT godot_packed_float32_array godotsharp_variant_as_packed_float32_array(const godot_variant *p_self) { + return godot_variant_as_packed_float32_array(p_self); +} + +GD_PINVOKE_EXPORT godot_packed_float64_array godotsharp_variant_as_packed_float64_array(const godot_variant *p_self) { + return godot_variant_as_packed_float64_array(p_self); +} + +GD_PINVOKE_EXPORT godot_packed_string_array godotsharp_variant_as_packed_string_array(const godot_variant *p_self) { + return godot_variant_as_packed_string_array(p_self); +} + +GD_PINVOKE_EXPORT godot_packed_vector2_array godotsharp_variant_as_packed_vector2_array(const godot_variant *p_self) { + return godot_variant_as_packed_vector2_array(p_self); +} + +GD_PINVOKE_EXPORT godot_packed_vector3_array godotsharp_variant_as_packed_vector3_array(const godot_variant *p_self) { + return godot_variant_as_packed_vector3_array(p_self); +} + +GD_PINVOKE_EXPORT godot_packed_color_array godotsharp_variant_as_packed_color_array(const godot_variant *p_self) { + return godot_variant_as_packed_color_array(p_self); +} + +// string.h + +GD_PINVOKE_EXPORT void godotsharp_string_new_with_utf16_chars(godot_string *r_dest, const char16_t *p_contents) { + godot_string_new_with_utf16_chars(r_dest, p_contents); +} + +// string_name.h + +GD_PINVOKE_EXPORT void godotsharp_string_name_new_copy(godot_string_name *r_dest, const godot_string_name *p_src) { + godot_string_name_new_copy(r_dest, p_src); +} + +// node_path.h + +GD_PINVOKE_EXPORT void godotsharp_node_path_new_copy(godot_node_path *r_dest, const godot_node_path *p_src) { + godot_node_path_new_copy(r_dest, p_src); +} + +// array.h + +GD_PINVOKE_EXPORT void godotsharp_array_new_copy(godot_array *r_dest, const godot_array *p_src) { + godot_array_new_copy(r_dest, p_src); +} + +// dictionary.h + +GD_PINVOKE_EXPORT void godotsharp_dictionary_new_copy(godot_dictionary *r_dest, const godot_dictionary *p_src) { + godot_dictionary_new_copy(r_dest, p_src); +} + +// destroy functions + +GD_PINVOKE_EXPORT void godotsharp_packed_byte_array_destroy(godot_packed_byte_array *p_self) { + godot_packed_byte_array_destroy(p_self); +} + +GD_PINVOKE_EXPORT void godotsharp_packed_int32_array_destroy(godot_packed_int32_array *p_self) { + godot_packed_int32_array_destroy(p_self); +} + +GD_PINVOKE_EXPORT void godotsharp_packed_int64_array_destroy(godot_packed_int64_array *p_self) { + godot_packed_int64_array_destroy(p_self); +} + +GD_PINVOKE_EXPORT void godotsharp_packed_float32_array_destroy(godot_packed_float32_array *p_self) { + godot_packed_float32_array_destroy(p_self); +} + +GD_PINVOKE_EXPORT void godotsharp_packed_float64_array_destroy(godot_packed_float64_array *p_self) { + godot_packed_float64_array_destroy(p_self); +} + +GD_PINVOKE_EXPORT void godotsharp_packed_string_array_destroy(godot_packed_string_array *p_self) { + godot_packed_string_array_destroy(p_self); +} + +GD_PINVOKE_EXPORT void godotsharp_packed_vector2_array_destroy(godot_packed_vector2_array *p_self) { + godot_packed_vector2_array_destroy(p_self); +} + +GD_PINVOKE_EXPORT void godotsharp_packed_vector3_array_destroy(godot_packed_vector3_array *p_self) { + godot_packed_vector3_array_destroy(p_self); +} + +GD_PINVOKE_EXPORT void godotsharp_packed_color_array_destroy(godot_packed_color_array *p_self) { + godot_packed_color_array_destroy(p_self); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_destroy(godot_variant *p_self) { + godot_variant_destroy(p_self); +} + +GD_PINVOKE_EXPORT void godotsharp_string_destroy(godot_string *p_self) { + godot_string_destroy(p_self); +} + +GD_PINVOKE_EXPORT void godotsharp_string_name_destroy(godot_string_name *p_self) { + godot_string_name_destroy(p_self); +} + +GD_PINVOKE_EXPORT void godotsharp_node_path_destroy(godot_node_path *p_self) { + godot_node_path_destroy(p_self); +} + +GD_PINVOKE_EXPORT void godotsharp_signal_destroy(godot_signal *p_self) { + godot_signal_destroy(p_self); +} + +GD_PINVOKE_EXPORT void godotsharp_callable_destroy(godot_callable *p_self) { + godot_callable_destroy(p_self); +} + +GD_PINVOKE_EXPORT void godotsharp_array_destroy(godot_array *p_self) { + godot_array_destroy(p_self); +} + +GD_PINVOKE_EXPORT void godotsharp_dictionary_destroy(godot_dictionary *p_self) { + godot_dictionary_destroy(p_self); +} + +#ifdef __cplusplus +} +#endif + +// We need this to prevent the functions from being stripped. +void *godotsharp_pinvoke_funcs[95] = { + (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_basis, + (void *)godotsharp_variant_new_transform3d, + (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_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_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 5a6fd69db8..11ea98b31b 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 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 18a9221f89..c87084281a 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" @@ -75,5 +73,3 @@ void godot_register_string_icalls() { GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_sha256_buffer", godot_icall_String_sha256_buffer); GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_sha256_text", godot_icall_String_sha256_text); } - -#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 f537896559..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-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. */ -/*************************************************************************/ - -#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 273dba52f9..356b8bba07 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/managed_callable.cpp b/modules/mono/managed_callable.cpp index 6d868b527c..346c3d6648 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(p_a); const ManagedCallable *b = static_cast(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,9 +69,7 @@ bool ManagedCallable::compare_less(const CallableCustom *p_a, const CallableCust } uint32_t ManagedCallable::hash() const { - // hmm - uint32_t hash = delegate_invoke->get_name().hash(); - return hash_djb2_one_64(delegate_handle.handle, hash); + return hash_djb2_one_64((uint64_t)delegate_handle); } String ManagedCallable::get_as_text() const { @@ -92,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); + + if (exc) { + GDMonoUtils::debug_print_unhandled_exception(exc); + } + + delegate_handle = nullptr; + } } -ManagedCallable::ManagedCallable(MonoDelegate *p_delegate) { -#ifdef DEBUG_ENABLED - CRASH_COND(p_delegate == nullptr); -#endif - - set_delegate(p_delegate); - +// 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); @@ -134,6 +128,7 @@ ManagedCallable::ManagedCallable(MonoDelegate *p_delegate) { } #endif } +/* clang-format on */ ManagedCallable::~ManagedCallable() { #ifdef GD_MONO_HOT_RELOAD @@ -144,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 c620eee60d..84b824d615 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; + void *delegate_handle; #ifdef GD_MONO_HOT_RELOAD SelfList 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 1b1349a3a3..b2c4cfb7bc 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -410,10 +410,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. @@ -448,59 +444,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() { @@ -603,16 +574,6 @@ ApiAssemblyInfo::Version ApiAssemblyInfo::Version::get_from_loaded_assembly(GDMo if (api_hash_field) { api_assembly_version.godot_api_hash = GDMonoMarshal::unbox(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(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(cs_glue_ver_field->get_value(nullptr)); - } } return api_assembly_version; @@ -701,12 +662,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; } @@ -722,14 +679,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); @@ -771,7 +723,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)) { @@ -831,14 +783,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; } @@ -861,14 +815,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; } @@ -1098,13 +1054,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 4170e5053f..03afa00f24 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 0b36796d98..db2740e431 100644 --- a/modules/mono/mono_gd/gd_mono_cache.cpp +++ b/modules/mono/mono_gd/gd_mono_cache.cpp @@ -48,11 +48,8 @@ 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) #define CACHE_METHOD_THUNK_AND_CHECK_IMPL(m_var, m_val) \ { \ @@ -68,23 +65,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,35 +80,11 @@ 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_Basis = nullptr; - class_Quaternion = nullptr; - class_Transform3D = 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 @@ -152,65 +109,37 @@ 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_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_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_ArrayGetElementType.nullify(); - methodthunk_MarshalUtils_DictionaryGetKeyValueTypes.nullify(); - - methodthunk_MarshalUtils_MakeGenericArrayType.nullify(); - methodthunk_MarshalUtils_MakeGenericDictionaryType.nullify(); - - // End of MarshalUtils methods + methodthunk_Marshaling_SetFieldValue.nullify(); task_scheduler_handle = Ref(); } #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")); @@ -227,33 +156,11 @@ void update_corlib_cache() { } void update_godot_api_cache() { - CACHE_CLASS_AND_CHECK(Vector2, GODOT_API_CLASS(Vector2)); - CACHE_CLASS_AND_CHECK(Vector2i, GODOT_API_CLASS(Vector2i)); - CACHE_CLASS_AND_CHECK(Rect2, GODOT_API_CLASS(Rect2)); - CACHE_CLASS_AND_CHECK(Rect2i, GODOT_API_CLASS(Rect2i)); - CACHE_CLASS_AND_CHECK(Transform2D, GODOT_API_CLASS(Transform2D)); - CACHE_CLASS_AND_CHECK(Vector3, GODOT_API_CLASS(Vector3)); - CACHE_CLASS_AND_CHECK(Vector3i, GODOT_API_CLASS(Vector3i)); - CACHE_CLASS_AND_CHECK(Basis, GODOT_API_CLASS(Basis)); - CACHE_CLASS_AND_CHECK(Quaternion, GODOT_API_CLASS(Quaternion)); - CACHE_CLASS_AND_CHECK(Transform3D, GODOT_API_CLASS(Transform3D)); - 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 @@ -278,36 +185,38 @@ 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)); - // Start of MarshalUtils methods + GDMonoClass *gd_mono_marshal_class = GDMono::get_singleton()->get_core_api_assembly()->get_class( + "Godot.NativeInterop", "Marshaling"); - 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)); + ERR_FAIL_COND_MSG(gd_mono_marshal_class == nullptr, + "Mono Cache: Class `Godot.NativeInterop.Marshaling` not found."); - 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(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(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 + CACHE_METHOD_THUNK_AND_CHECK(Marshaling, SetFieldValue, + gd_mono_marshal_class->get_method("SetFieldValue", 3)); #ifdef DEBUG_ENABLED CACHE_METHOD_THUNK_AND_CHECK(DebuggingUtils, GetStackFrameInfo, GODOT_API_CLASS(DebuggingUtils)->get_method("GetStackFrameInfo", 4)); diff --git a/modules/mono/mono_gd/gd_mono_cache.h b/modules/mono/mono_gd/gd_mono_cache.h index 6d49da0af3..e08b9d24b8 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; // object - GDMonoClass *class_bool; // bool - GDMonoClass *class_int8_t; // sbyte - GDMonoClass *class_int16_t; // short - GDMonoClass *class_int32_t; // int - GDMonoClass *class_int64_t; // long - GDMonoClass *class_uint8_t; // byte - GDMonoClass *class_uint16_t; // ushort - GDMonoClass *class_uint32_t; // uint - GDMonoClass *class_uint64_t; // ulong - GDMonoClass *class_float; // float - GDMonoClass *class_double; // double GDMonoClass *class_String; // string - GDMonoClass *class_IntPtr; // System.IntPtr - - GDMonoClass *class_System_Collections_IEnumerable; - GDMonoClass *class_System_Collections_ICollection; - GDMonoClass *class_System_Collections_IDictionary; #ifdef DEBUG_ENABLED GDMonoClass *class_System_Diagnostics_StackTrace; @@ -68,37 +52,13 @@ struct CachedData { #endif GDMonoClass *class_KeyNotFoundException; - - MonoClass *rawclass_Dictionary; // ----------------------------------------------- - GDMonoClass *class_Vector2; - GDMonoClass *class_Vector2i; - GDMonoClass *class_Rect2; - GDMonoClass *class_Rect2i; - GDMonoClass *class_Transform2D; - GDMonoClass *class_Vector3; - GDMonoClass *class_Vector3i; - GDMonoClass *class_Basis; - GDMonoClass *class_Quaternion; - GDMonoClass *class_Transform3D; - GDMonoClass *class_AABB; - GDMonoClass *class_Color; - GDMonoClass *class_Plane; - GDMonoClass *class_StringName; - GDMonoClass *class_NodePath; - GDMonoClass *class_RID; GDMonoClass *class_GodotObject; GDMonoClass *class_GodotResource; - GDMonoClass *class_Node; GDMonoClass *class_Control; - GDMonoClass *class_Node3D; - GDMonoClass *class_WeakRef; GDMonoClass *class_Callable; GDMonoClass *class_SignalInfo; - GDMonoClass *class_Array; - GDMonoClass *class_Dictionary; - GDMonoClass *class_MarshalUtils; GDMonoClass *class_ISerializationListener; #ifdef DEBUG_ENABLED @@ -123,39 +83,30 @@ struct CachedData { GDMonoField *field_AssemblyHasScriptsAttribute_scriptTypes; GDMonoField *field_GodotObject_ptr; - GDMonoField *field_StringName_ptr; - GDMonoField *field_NodePath_ptr; - GDMonoField *field_Image_ptr; - GDMonoField *field_RID_ptr; GDMonoMethodThunk methodthunk_GodotObject_Dispose; - GDMonoMethodThunkR methodthunk_Array_GetPtr; - GDMonoMethodThunkR methodthunk_Dictionary_GetPtr; GDMonoMethodThunk methodthunk_SignalAwaiter_SignalCallback; GDMonoMethodThunk methodthunk_GodotTaskScheduler_Activate; GDMonoMethodThunkR methodthunk_Delegate_Equals; + GDMonoMethodThunkR methodthunk_DelegateUtils_TrySerializeDelegateWithGCHandle; + GDMonoMethodThunkR methodthunk_DelegateUtils_TryDeserializeDelegateWithGCHandle; + GDMonoMethodThunkR methodthunk_DelegateUtils_TrySerializeDelegate; GDMonoMethodThunkR methodthunk_DelegateUtils_TryDeserializeDelegate; - // Start of MarshalUtils methods + GDMonoMethodThunk methodthunk_DelegateUtils_InvokeWithVariantArgs; + GDMonoMethodThunkR methodthunk_DelegateUtils_DelegateEquals; + GDMonoMethodThunk methodthunk_DelegateUtils_FreeGCHandle; - GDMonoMethodThunkR methodthunk_MarshalUtils_TypeIsGenericArray; - GDMonoMethodThunkR methodthunk_MarshalUtils_TypeIsGenericDictionary; - GDMonoMethodThunkR methodthunk_MarshalUtils_TypeIsSystemGenericList; - GDMonoMethodThunkR methodthunk_MarshalUtils_TypeIsSystemGenericDictionary; - GDMonoMethodThunkR methodthunk_MarshalUtils_TypeIsGenericIEnumerable; - GDMonoMethodThunkR methodthunk_MarshalUtils_TypeIsGenericICollection; - GDMonoMethodThunkR methodthunk_MarshalUtils_TypeIsGenericIDictionary; + GDMonoMethodThunkR methodthunk_Marshaling_managed_to_variant_type; + GDMonoMethodThunkR methodthunk_Marshaling_try_get_array_element_type; + GDMonoMethodThunkR methodthunk_Marshaling_variant_to_mono_object_of_type; + GDMonoMethodThunkR methodthunk_Marshaling_variant_to_mono_object; + GDMonoMethodThunk methodthunk_Marshaling_mono_object_to_variant_out; - GDMonoMethodThunk methodthunk_MarshalUtils_ArrayGetElementType; - GDMonoMethodThunk methodthunk_MarshalUtils_DictionaryGetKeyValueTypes; - - GDMonoMethodThunkR methodthunk_MarshalUtils_MakeGenericArrayType; - GDMonoMethodThunkR methodthunk_MarshalUtils_MakeGenericDictionaryType; - - // End of MarshalUtils methods + GDMonoMethodThunk methodthunk_Marshaling_SetFieldValue; Ref task_scheduler_handle; @@ -176,10 +127,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(); } @@ -187,10 +134,8 @@ 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) -#define CACHED_PROPERTY(m_class, m_property) (GDMonoCache::cached_data.property_##m_class##_##m_property) #endif // GD_MONO_CACHE_H diff --git a/modules/mono/mono_gd/gd_mono_field.cpp b/modules/mono/mono_gd/gd_mono_field.cpp index 111eaa0bbf..2bb7a7a17e 100644 --- a/modules/mono/mono_gd/gd_mono_field.cpp +++ b/modules/mono/mono_gd/gd_mono_field.cpp @@ -46,391 +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(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(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::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::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 9f2977bfa2..143975f22f 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.cpp +++ b/modules/mono/mono_gd/gd_mono_marshal.cpp @@ -37,1210 +37,100 @@ namespace GDMonoMarshal { +// TODO: Those are just temporary until the code that needs them is moved to C# + 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; + MonoReflectionType *refltype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type()); + MonoBoolean nil_is_variant = false; - 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; + MonoException *exc = nullptr; + int32_t ret = CACHED_METHOD_THUNK(Marshaling, managed_to_variant_type) + .invoke(refltype, &nil_is_variant, &exc); - 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(Basis)) { - return Variant::BASIS; - } - - if (vtclass == CACHED_CLASS(Quaternion)) { - return Variant::QUATERNION; - } - - if (vtclass == CACHED_CLASS(Transform3D)) { - return Variant::TRANSFORM3D; - } - - 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: { - MonoArrayType *array_type = mono_type_get_array_type(p_type.type_class->get_mono_type()); - - if (array_type->eklass == CACHED_CLASS_RAW(MonoObject)) { - return Variant::ARRAY; - } - - if (array_type->eklass == CACHED_CLASS_RAW(uint8_t)) { - return Variant::PACKED_BYTE_ARRAY; - } - - if (array_type->eklass == CACHED_CLASS_RAW(int32_t)) { - return Variant::PACKED_INT32_ARRAY; - } - - if (array_type->eklass == CACHED_CLASS_RAW(int64_t)) { - return Variant::PACKED_INT64_ARRAY; - } - - if (array_type->eklass == CACHED_CLASS_RAW(float)) { - return Variant::PACKED_FLOAT32_ARRAY; - } - - if (array_type->eklass == CACHED_CLASS_RAW(double)) { - return Variant::PACKED_FLOAT64_ARRAY; - } - - if (array_type->eklass == CACHED_CLASS_RAW(String)) { - return Variant::PACKED_STRING_ARRAY; - } - - if (array_type->eklass == CACHED_CLASS_RAW(Vector2)) { - return Variant::PACKED_VECTOR2_ARRAY; - } - - if (array_type->eklass == CACHED_CLASS_RAW(Vector3)) { - return Variant::PACKED_VECTOR3_ARRAY; - } - - if (array_type->eklass == CACHED_CLASS_RAW(Color)) { - return Variant::PACKED_COLOR_ARRAY; - } - - GDMonoClass *array_type_class = GDMono::get_singleton()->get_class(array_type->eklass); - 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; - - // 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 - if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) { - return Variant::DICTIONARY; - } - - // Godot.Collections.Array - if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) { - return Variant::ARRAY; - } - - // System.Collections.Generic.Dictionary - if (GDMonoUtils::Marshal::type_is_system_generic_dictionary(reftype)) { - return Variant::DICTIONARY; - } - - // System.Collections.Generic.List - if (GDMonoUtils::Marshal::type_is_system_generic_list(reftype)) { - return Variant::ARRAY; - } - - // IDictionary - if (GDMonoUtils::Marshal::type_is_generic_idictionary(reftype)) { - return Variant::DICTIONARY; - } - - // ICollection or IEnumerable - if (GDMonoUtils::Marshal::type_is_generic_icollection(reftype) || GDMonoUtils::Marshal::type_is_generic_ienumerable(reftype)) { - return Variant::ARRAY; - } - } break; - - 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: { - MonoArrayType *array_type = mono_type_get_array_type(p_array_type.type_class->get_mono_type()); - GDMonoClass *array_type_class = GDMono::get_singleton()->get_class(array_type->eklass); - r_elem_type = ManagedType::from_class(array_type_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()); + MonoReflectionType *array_refltype = mono_type_get_object(mono_domain_get(), p_array_type.type_class->get_mono_type()); + MonoReflectionType *elem_refltype = nullptr; - 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; + MonoException *exc = nullptr; + MonoBoolean ret = CACHED_METHOD_THUNK(Marshaling, try_get_array_element_type) + .invoke(array_refltype, &elem_refltype, &exc); - GDMonoUtils::Marshal::array_get_element_type(array_reftype, &elem_reftype); - - r_elem_type = ManagedType::from_reftype(elem_reftype); - return true; - } - } break; - default: { - } break; + if (exc) { + GDMonoUtils::debug_print_unhandled_exception(exc); + return Variant::NIL; } - return false; + r_elem_type = ManagedType::from_reftype(elem_refltype); + return ret; } -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()); -} +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()); -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()); + MonoException *exc = nullptr; + MonoObject *ret = CACHED_METHOD_THUNK(Marshaling, variant_to_mono_object_of_type) + .invoke(&p_var, refltype, &exc); - if (array_type->eklass == CACHED_CLASS_RAW(MonoObject)) { - return Array_to_mono_array(p_var.operator Array()); + if (exc) { + GDMonoUtils::debug_print_unhandled_exception(exc); + return nullptr; } - 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 (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); - } - - 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)); - } - - // 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)); - } - - ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported type: '" + - p_type_class->get_full_name() + "'."); -} - -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 - if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) { - return GDMonoUtils::create_managed_from(p_var.operator Dictionary(), p_type_class); - } - - // Godot.Collections.Array - if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) { - return GDMonoUtils::create_managed_from(p_var.operator Array(), p_type_class); - } - - // System.Collections.Generic.Dictionary - 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); - } - - // System.Collections.Generic.List - 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 - 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 or IEnumerable - 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); - } - - 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::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::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; + MonoException *exc = nullptr; + MonoObject *ret = CACHED_METHOD_THUNK(Marshaling, variant_to_mono_object) + .invoke(&p_var, &exc); + + if (exc) { + GDMonoUtils::debug_print_unhandled_exception(exc); + return nullptr; } + + return ret; } -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(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(p_obj); - case MONO_TYPE_CHAR: - return unbox(p_obj); - case MONO_TYPE_I1: - return unbox(p_obj); - case MONO_TYPE_I2: - return unbox(p_obj); - case MONO_TYPE_I4: - return unbox(p_obj); - case MONO_TYPE_I8: - return unbox(p_obj); - case MONO_TYPE_U1: - return unbox(p_obj); - case MONO_TYPE_U2: - return unbox(p_obj); - case MONO_TYPE_U4: - return unbox(p_obj); - case MONO_TYPE_U8: - return unbox(p_obj); - case MONO_TYPE_R4: - return unbox(p_obj); - case MONO_TYPE_R8: - return unbox(p_obj); - case MONO_TYPE_VALUETYPE: { - GDMonoClass *vtclass = p_type.type_class; - - if (vtclass == CACHED_CLASS(Vector2)) { - return MARSHALLED_IN(Vector2, unbox_addr(p_obj)); - } - - if (vtclass == CACHED_CLASS(Vector2i)) { - return MARSHALLED_IN(Vector2i, unbox_addr(p_obj)); - } - - if (vtclass == CACHED_CLASS(Rect2)) { - return MARSHALLED_IN(Rect2, unbox_addr(p_obj)); - } - - if (vtclass == CACHED_CLASS(Rect2i)) { - return MARSHALLED_IN(Rect2i, unbox_addr(p_obj)); - } - - if (vtclass == CACHED_CLASS(Transform2D)) { - return MARSHALLED_IN(Transform2D, unbox_addr(p_obj)); - } - - if (vtclass == CACHED_CLASS(Vector3)) { - return MARSHALLED_IN(Vector3, unbox_addr(p_obj)); - } - - if (vtclass == CACHED_CLASS(Vector3i)) { - return MARSHALLED_IN(Vector3i, unbox_addr(p_obj)); - } - - if (vtclass == CACHED_CLASS(Basis)) { - return MARSHALLED_IN(Basis, unbox_addr(p_obj)); - } - - if (vtclass == CACHED_CLASS(Quaternion)) { - return MARSHALLED_IN(Quaternion, unbox_addr(p_obj)); - } - - if (vtclass == CACHED_CLASS(Transform3D)) { - return MARSHALLED_IN(Transform3D, unbox_addr(p_obj)); - } - - if (vtclass == CACHED_CLASS(AABB)) { - return MARSHALLED_IN(AABB, unbox_addr(p_obj)); - } - - if (vtclass == CACHED_CLASS(Color)) { - return MARSHALLED_IN(Color, unbox_addr(p_obj)); - } - - if (vtclass == CACHED_CLASS(Plane)) { - return MARSHALLED_IN(Plane, unbox_addr(p_obj)); - } - - if (vtclass == CACHED_CLASS(Callable)) { - return managed_to_callable(unbox(p_obj)); - } - - if (vtclass == CACHED_CLASS(SignalInfo)) { - return managed_to_signal_info(unbox(p_obj)); - } - - if (mono_class_is_enum(vtclass->get_mono_ptr())) { - return unbox(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); - } - - 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(CACHED_FIELD(GodotObject, ptr)->get_value(p_obj)); - if (ptr != nullptr) { - RefCounted *rc = Object::cast_to(ptr); - return rc ? Variant(Ref(rc)) : Variant(ptr); - } - return Variant(); - } - - if (CACHED_CLASS(StringName) == type_class) { - StringName *ptr = unbox(CACHED_FIELD(StringName, ptr)->get_value(p_obj)); - return ptr ? Variant(*ptr) : Variant(); - } - - if (CACHED_CLASS(NodePath) == type_class) { - NodePath *ptr = unbox(CACHED_FIELD(NodePath, ptr)->get_value(p_obj)); - return ptr ? Variant(*ptr) : Variant(); - } - - if (CACHED_CLASS(RID) == type_class) { - RID *ptr = unbox(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 - 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(ret); - } - - // Godot.Collections.Array - 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(ret); - } - - // System.Collections.Generic.Dictionary - 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 - 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); - } - } break; - } - - 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 { +static Variant mono_object_to_variant_impl(MonoObject *p_obj, bool p_fail_with_err) { + if (!p_obj) { return Variant(); } + + MonoBoolean fail_with_error = p_fail_with_err; + + Variant ret; + + 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 ret; } Variant mono_object_to_variant(MonoObject *p_obj) { - if (!p_obj) { - return Variant(); - } - - ManagedType type = ManagedType::from_class(mono_object_get_class(p_obj)); - - return mono_object_to_variant_impl(p_obj, type); + return mono_object_to_variant_impl(p_obj, /* fail_with_err: */ true); } -Variant mono_object_to_variant(MonoObject *p_obj, const ManagedType &p_type) { - if (!p_obj) { - return Variant(); - } - - return mono_object_to_variant_impl(p_obj, p_type); -} - -Variant mono_object_to_variant_no_err(MonoObject *p_obj, const ManagedType &p_type) { - if (!p_obj) { - return Variant(); - } - - 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) { @@ -1248,8 +138,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()'. @@ -1269,88 +158,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(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); - MonoClass *elem_class = mono_class_from_mono_type(elem_type); - - 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); - - void *ctor_args[1] = { Array_to_mono_array(p_array, elem_class) }; - - 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); @@ -1363,18 +170,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) { @@ -1391,141 +186,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(); @@ -1539,208 +199,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(CACHED_FIELD(GodotObject, ptr)->get_value(p_managed_callable.target)) : - nullptr; - StringName *method_ptr = unbox(CACHED_FIELD(StringName, ptr)->get_value(p_managed_callable.method_string_name)); - StringName method = method_ptr ? *method_ptr : StringName(); - return Callable(target, method); - } -} - -M_Callable callable_to_managed(const Callable &p_callable) { - if (p_callable.is_custom()) { - CallableCustom *custom = p_callable.get_custom(); - CallableCustom::CompareEqualFunc compare_equal_func = custom->get_compare_equal_func(); - - if (compare_equal_func == ManagedCallable::compare_equal_func_ptr) { - ManagedCallable *managed_callable = static_cast(custom); - return { - nullptr, nullptr, - managed_callable->get_delegate() - }; - } else if (compare_equal_func == SignalAwaiterCallable::compare_equal_func_ptr) { - SignalAwaiterCallable *signal_awaiter_callable = static_cast(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(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(CACHED_FIELD(GodotObject, ptr)->get_value(p_managed_signal.owner)) : - nullptr; - StringName *name_ptr = unbox(CACHED_FIELD(StringName, ptr)->get_value(p_managed_signal.name_string_name)); - StringName name = name_ptr ? *name_ptr : StringName(); - return Signal(owner, name); -} - -M_SignalInfo signal_info_to_managed(const Signal &p_signal) { - Object *owner = p_signal.get_object(); - MonoObject *owner_managed = GDMonoUtils::unmanaged_get_managed(owner); - MonoObject *name_string_name_managed = GDMonoUtils::create_managed_from(p_signal.get_name()); - return { owner_managed, name_string_name_managed }; -} } // namespace GDMonoMarshal diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h index 88afc7ebc5..353d70d0c0 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.h +++ b/modules/mono/mono_gd/gd_mono_marshal.h @@ -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 -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,457 +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; - MonoObject *method_string_name; - MonoDelegate *delegate; -}; - -struct M_SignalInfo { - MonoObject *owner; - MonoObject *name_string_name; -}; - -#pragma pack(pop) - -// Callable -Callable managed_to_callable(const M_Callable &p_managed_callable); -M_Callable callable_to_managed(const Callable &p_callable); - -// SignalInfo -Signal managed_to_signal_info(const M_SignalInfo &p_managed_signal); -M_SignalInfo signal_info_to_managed(const Signal &p_signal); - -// Structures - -namespace InteropLayout { - -enum { - MATCHES_int = (sizeof(int32_t) == sizeof(uint32_t)), - - MATCHES_float = (sizeof(float) == sizeof(uint32_t)), - - MATCHES_double = (sizeof(double) == sizeof(uint64_t)), - -#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_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_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_Basis && MATCHES_Quaternion && MATCHES_Transform3D && MATCHES_AABB && MATCHES_Color && - MATCHES_Plane && MATCHES_Vector2i && MATCHES_Rect2i && MATCHES_Vector3i); -/* 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.elements[0]), - M_Vector2::convert_from(p_from.elements[1]), - M_Vector2::convert_from(p_from.elements[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_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.elements[0]), - M_Vector3::convert_from(p_from.elements[1]), - M_Vector3::convert_from(p_from.elements[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_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 \ - _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(p_from); \ - } \ - \ - _FORCE_INLINE_ m_type marshalled_in_##m_type(const M_##m_type *p_from) { \ - return marshalled_in_##m_type##_impl(p_from); \ - } \ - \ - template \ - _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(&p_from); \ - } \ - \ - _FORCE_INLINE_ M_##m_type marshalled_out_##m_type(const m_type &p_from) { \ - return marshalled_out_##m_type##_impl(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(Quaternion) -DECL_TYPE_MARSHAL_TEMPLATES(Transform3D) -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 // GDMONOMARSHAL_H diff --git a/modules/mono/mono_gd/gd_mono_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp index 67aabcde10..fa5b646387 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 c08ffe904b..dff6a040eb 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 param_types; diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp index 13939bd014..f686c2d4d0 100644 --- a/modules/mono/mono_gd/gd_mono_utils.cpp +++ b/modules/mono/mono_gd/gd_mono_utils.cpp @@ -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); @@ -540,109 +444,6 @@ void dispose(MonoObject *p_mono_object, MonoException **r_exc) { CACHED_METHOD_THUNK(GodotObject, Dispose).invoke(p_mono_object, r_exc); } -namespace Marshal { - -#ifdef MONO_GLUE_ENABLED -#ifdef TOOLS_ENABLED -#define NO_GLUE_RET(m_ret) \ - { \ - if (!GDMonoCache::cached_data.godot_api_cache_updated) \ - return m_ret; \ - } -#else -#define NO_GLUE_RET(m_ret) \ - {} -#endif -#else -#define NO_GLUE_RET(m_ret) \ - { 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; -} - -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() { if (likely(GDMono::get_singleton()->is_runtime_initialized()) && unlikely(!mono_domain_get())) { mono_thread = GDMonoUtils::attach_current_thread(); @@ -657,7 +458,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(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 773501e93d..e4157fbbea 100644 --- a/modules/mono/mono_gd/gd_mono_utils.h +++ b/modules/mono/mono_gd/gd_mono_utils.h @@ -52,23 +52,6 @@ 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); - -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) { p_hash ^= p_with_hash + 0x9e3779b9 + (p_hash << 6) + (p_hash >> 2); } @@ -94,20 +77,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); @@ -138,6 +113,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 3aaf726fc8..f76f133b3a 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 e12ea45b3f..bc242ab57c 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; 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 053618ebe4..4425efb304 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 3290cb38b9..d412799aa7 100644 --- a/modules/mono/utils/string_utils.h +++ b/modules/mono/utils/string_utils.h @@ -36,7 +36,8 @@ #include -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);