From 124fbf95f8ef065215e9fcc937a370dbef3196e1 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 01/31] 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 | 31 - .../mono/build_scripts/api_solution_build.py | 80 - .../mono/build_scripts/build_assemblies.py | 306 +++ .../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 | 2 +- modules/mono/csharp_script.cpp | 49 +- modules/mono/csharp_script.h | 2 +- .../GodotTools/HotReloadAssemblyWatcher.cs | 2 +- modules/mono/editor/bindings_generator.cpp | 1487 ++++++-------- modules/mono/editor/bindings_generator.h | 182 +- modules/mono/editor/editor_internal_calls.cpp | 1 - .../GodotSharp/GodotSharp.sln.DotSettings | 7 + .../glue/GodotSharp/GodotSharp/Core/Array.cs | 281 ++- .../GodotSharp/Core/DelegateUtils.cs | 59 +- .../GodotSharp/GodotSharp/Core/Dictionary.cs | 316 +-- .../Core/Extensions/SceneTreeExtensions.cs | 7 +- .../glue/GodotSharp/GodotSharp/Core/GD.cs | 23 +- .../GodotSharp/Core/MarshalUtils.cs | 141 -- .../Core/NativeInterop/InteropStructs.cs | 438 +++++ .../Core/NativeInterop/InteropUtils.cs | 32 + .../Core/NativeInterop/Marshaling.cs | 1370 +++++++++++++ .../Core/NativeInterop/NativeFuncs.cs | 346 ++++ .../NativeInterop/NativeFuncs.extended.cs | 70 + .../Core/NativeInterop/VariantSpanHelpers.cs | 33 + .../Core/NativeInterop/VariantUtils.cs | 335 ++++ .../GodotSharp/GodotSharp/Core/NodePath.cs | 119 +- .../GodotSharp/GodotSharp/Core/Object.base.cs | 92 +- .../GodotSharp/Core/Object.exceptions.cs | 135 ++ .../glue/GodotSharp/GodotSharp/Core/RID.cs | 85 +- .../GodotSharp/Core/SignalAwaiter.cs | 23 +- .../GodotSharp/Core/StringExtensions.cs | 6 +- .../GodotSharp/GodotSharp/Core/StringName.cs | 67 +- .../GodotSharp/GodotSharp/GodotSharp.csproj | 17 + .../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 | 154 +- modules/mono/glue/gd_glue.cpp | 22 +- modules/mono/glue/glue_header.h | 91 - .../{nodepath_glue.cpp => node_path_glue.cpp} | 33 +- .../{rid_glue.cpp => placeholder_glue.cpp} | 44 +- modules/mono/glue/runtime_interop.cpp | 786 ++++++++ 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/interop_types.h | 208 ++ modules/mono/managed_callable.cpp | 62 +- 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 | 168 +- modules/mono/mono_gd/gd_mono_cache.h | 86 +- modules/mono/mono_gd/gd_mono_class.cpp | 11 +- modules/mono/mono_gd/gd_mono_field.cpp | 419 +--- modules/mono/mono_gd/gd_mono_marshal.cpp | 1750 +---------------- modules/mono/mono_gd/gd_mono_marshal.h | 511 +---- modules/mono/mono_gd/gd_mono_method.cpp | 13 +- modules/mono/mono_gd/gd_mono_method.h | 1 - modules/mono/mono_gd/gd_mono_property.cpp | 19 +- modules/mono/mono_gd/gd_mono_property.h | 2 - modules/mono/mono_gd/gd_mono_utils.cpp | 206 +- modules/mono/mono_gd/gd_mono_utils.h | 29 +- 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 +- 71 files changed, 5715 insertions(+), 5608 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 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} (77%) 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 create mode 100644 modules/mono/interop_types.h diff --git a/modules/mono/SCsub b/modules/mono/SCsub index d10ebc7b475..5757917f567 100644 --- a/modules/mono/SCsub +++ b/modules/mono/SCsub @@ -7,21 +7,6 @@ Import("env_modules") env_mono = env_modules.Clone() -if env_mono["tools"]: - # NOTE: It is safe to generate this file here, since this is still executed serially - import build_scripts.gen_cs_glue_version as gen_cs_glue_version - - gen_cs_glue_version.generate_header("glue/GodotSharp", "glue/cs_glue_version.gen.h") - -# Glue sources -if env_mono["mono_glue"]: - env_mono.Append(CPPDEFINES=["MONO_GLUE_ENABLED"]) - - import os.path - - if not os.path.isfile("glue/mono_glue.gen.cpp"): - raise RuntimeError("Mono glue sources not found. Did you forget to run '--generate-mono-glue'?") - if env_mono["tools"] or env_mono["target"] != "release": env_mono.Append(CPPDEFINES=["GD_MONO_HOT_RELOAD"]) @@ -29,22 +14,6 @@ if env_mono["tools"] or env_mono["target"] != "release": mono_configure.configure(env, env_mono) -if env_mono["tools"] and env_mono["mono_glue"] and env_mono["build_cil"]: - # Build Godot API solution - import build_scripts.api_solution_build as api_solution_build - - api_sln_cmd = api_solution_build.build(env_mono) - - # Build GodotTools - import build_scripts.godot_tools_build as godot_tools_build - - godot_tools_build.build(env_mono, api_sln_cmd) - - # Build Godot.NET.Sdk - import build_scripts.godot_net_sdk_build as godot_net_sdk_build - - godot_net_sdk_build.build(env_mono) - # Add sources env_mono.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/mono/build_scripts/api_solution_build.py b/modules/mono/build_scripts/api_solution_build.py deleted file mode 100644 index 9abac22df60..00000000000 --- 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 00000000000..dd96f40f6bb --- /dev/null +++ b/modules/mono/build_scripts/build_assemblies.py @@ -0,0 +1,306 @@ +#!/usr/bin/python3 + +import os +import os.path +import shlex +import subprocess +from dataclasses import dataclass + + +def find_dotnet_cli(): + if os.name == "nt": + for hint_dir in os.environ["PATH"].split(os.pathsep): + hint_dir = hint_dir.strip('"') + hint_path = os.path.join(hint_dir, "dotnet") + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + if os.path.isfile(hint_path + ".exe") and os.access(hint_path + ".exe", os.X_OK): + return hint_path + ".exe" + else: + for hint_dir in os.environ["PATH"].split(os.pathsep): + hint_dir = hint_dir.strip('"') + hint_path = os.path.join(hint_dir, "dotnet") + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + + +def find_msbuild_standalone_windows(): + msbuild_tools_path = find_msbuild_tools_path_reg() + + if msbuild_tools_path: + return os.path.join(msbuild_tools_path, "MSBuild.exe") + + return None + + +def find_msbuild_mono_windows(mono_prefix): + assert mono_prefix is not None + + mono_bin_dir = os.path.join(mono_prefix, "bin") + msbuild_mono = os.path.join(mono_bin_dir, "msbuild.bat") + + if os.path.isfile(msbuild_mono): + return msbuild_mono + + return None + + +def find_msbuild_mono_unix(): + import sys + + hint_dirs = [] + if sys.platform == "darwin": + hint_dirs[:0] = [ + "/Library/Frameworks/Mono.framework/Versions/Current/bin", + "/usr/local/var/homebrew/linked/mono/bin", + ] + + for hint_dir in hint_dirs: + hint_path = os.path.join(hint_dir, "msbuild") + if os.path.isfile(hint_path): + return hint_path + elif os.path.isfile(hint_path + ".exe"): + return hint_path + ".exe" + + for hint_dir in os.environ["PATH"].split(os.pathsep): + hint_dir = hint_dir.strip('"') + hint_path = os.path.join(hint_dir, "msbuild") + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + if os.path.isfile(hint_path + ".exe") and os.access(hint_path + ".exe", os.X_OK): + return hint_path + ".exe" + + return None + + +def find_msbuild_tools_path_reg(): + import subprocess + + program_files = os.getenv("PROGRAMFILES(X86)") + if not program_files: + program_files = os.getenv("PROGRAMFILES") + vswhere = os.path.join(program_files, "Microsoft Visual Studio", "Installer", "vswhere.exe") + + vswhere_args = ["-latest", "-products", "*", "-requires", "Microsoft.Component.MSBuild"] + + try: + lines = subprocess.check_output([vswhere] + vswhere_args).splitlines() + + for line in lines: + parts = line.decode("utf-8").split(":", 1) + + if len(parts) < 2 or parts[0] != "installationPath": + continue + + val = parts[1].strip() + + if not val: + raise ValueError("Value of `installationPath` entry is empty") + + # Since VS2019, the directory is simply named "Current" + msbuild_dir = os.path.join(val, "MSBuild", "Current", "Bin") + if os.path.isdir(msbuild_dir): + return msbuild_dir + + # Directory name "15.0" is used in VS 2017 + return os.path.join(val, "MSBuild", "15.0", "Bin") + + raise ValueError("Cannot find `installationPath` entry") + except ValueError as e: + print("Error reading output from vswhere: " + str(e)) + except OSError: + pass # Fine, vswhere not found + except (subprocess.CalledProcessError, OSError): + pass + + +@dataclass +class ToolsLocation: + dotnet_cli: str = "" + msbuild_standalone: str = "" + msbuild_mono: str = "" + mono_bin_dir: str = "" + + +def find_any_msbuild_tool(mono_prefix): + # Preference order: dotnet CLI > Standalone MSBuild > Mono's MSBuild + + # Find dotnet CLI + dotnet_cli = find_dotnet_cli() + if dotnet_cli: + return ToolsLocation(dotnet_cli=dotnet_cli) + + # Find standalone MSBuild + if os.name == "nt": + msbuild_standalone = find_msbuild_standalone_windows() + if msbuild_standalone: + return ToolsLocation(msbuild_standalone=msbuild_standalone) + + if mono_prefix: + # Find Mono's MSBuild + if os.name == "nt": + msbuild_mono = find_msbuild_mono_windows(mono_prefix) + if msbuild_mono: + return ToolsLocation(msbuild_mono=msbuild_mono) + else: + msbuild_mono = find_msbuild_mono_unix() + if msbuild_mono: + return ToolsLocation(msbuild_mono=msbuild_mono) + + return None + + +def run_msbuild(tools: ToolsLocation, sln: str, msbuild_args: [str] = None): + if msbuild_args is None: + msbuild_args = [] + + using_msbuild_mono = False + + # Preference order: dotnet CLI > Standalone MSBuild > Mono's MSBuild + if tools.dotnet_cli: + args = [tools.dotnet_cli, "msbuild"] + elif tools.msbuild_standalone: + args = [tools.msbuild_standalone] + elif tools.msbuild_mono: + args = [tools.msbuild_mono] + using_msbuild_mono = True + else: + raise RuntimeError("Path to MSBuild or dotnet CLI not provided.") + + args += [sln] + + if len(msbuild_args) > 0: + args += msbuild_args + + print("Running MSBuild: ", " ".join(shlex.quote(arg) for arg in args), flush=True) + + msbuild_env = os.environ.copy() + + # Needed when running from Developer Command Prompt for VS + if "PLATFORM" in msbuild_env: + del msbuild_env["PLATFORM"] + + if using_msbuild_mono: + # The (Csc/Vbc/Fsc)ToolExe environment variables are required when + # building with Mono's MSBuild. They must point to the batch files + # in Mono's bin directory to make sure they are executed with Mono. + msbuild_env.update( + { + "CscToolExe": os.path.join(tools.mono_bin_dir, "csc.bat"), + "VbcToolExe": os.path.join(tools.mono_bin_dir, "vbc.bat"), + "FscToolExe": os.path.join(tools.mono_bin_dir, "fsharpc.bat"), + } + ) + + return subprocess.call(args, env=msbuild_env) + + +def build_godot_api(msbuild_tool, module_dir, output_dir): + target_filenames = [ + "GodotSharp.dll", + "GodotSharp.pdb", + "GodotSharp.xml", + "GodotSharpEditor.dll", + "GodotSharpEditor.pdb", + "GodotSharpEditor.xml", + ] + + for build_config in ["Debug", "Release"]: + editor_api_dir = os.path.join(output_dir, "GodotSharp", "Api", build_config) + + targets = [os.path.join(editor_api_dir, filename) for filename in target_filenames] + + sln = os.path.join(module_dir, "glue/GodotSharp/GodotSharp.sln") + exit_code = run_msbuild( + msbuild_tool, + sln=sln, + msbuild_args=["/restore", "/t:Build", "/p:Configuration=" + build_config, "/p:NoWarn=1591"], + ) + if exit_code != 0: + return exit_code + + # Copy targets + + core_src_dir = os.path.abspath(os.path.join(sln, os.pardir, "GodotSharp", "bin", build_config)) + editor_src_dir = os.path.abspath(os.path.join(sln, os.pardir, "GodotSharpEditor", "bin", build_config)) + + if not os.path.isdir(editor_api_dir): + assert not os.path.isfile(editor_api_dir) + os.makedirs(editor_api_dir) + + def copy_target(target_path): + from shutil import copy + + filename = os.path.basename(target_path) + + src_path = os.path.join(core_src_dir, filename) + if not os.path.isfile(src_path): + src_path = os.path.join(editor_src_dir, filename) + + print(f"Copying assembly to {target_path}...") + copy(src_path, target_path) + + for scons_target in targets: + copy_target(scons_target) + + return 0 + + +def build_all(msbuild_tool, module_dir, output_dir, dev_debug, godot_platform): + # Godot API + exit_code = build_godot_api(msbuild_tool, module_dir, output_dir) + if exit_code != 0: + return exit_code + + # GodotTools + sln = os.path.join(module_dir, "editor/GodotTools/GodotTools.sln") + args = ["/restore", "/t:Build", "/p:Configuration=" + ("Debug" if dev_debug else "Release")] + ( + ["/p:GodotPlatform=" + godot_platform] if godot_platform else [] + ) + exit_code = run_msbuild(msbuild_tool, sln=sln, msbuild_args=args) + if exit_code != 0: + return exit_code + + # Godot.NET.Sdk + sln = os.path.join(module_dir, "editor/Godot.NET.Sdk/Godot.NET.Sdk.sln") + exit_code = run_msbuild(msbuild_tool, sln=sln, msbuild_args=["/restore", "/t:Build", "/p:Configuration=Release"]) + if exit_code != 0: + return exit_code + + return 0 + + +def main(): + import argparse + import sys + + parser = argparse.ArgumentParser(description="Builds all Godot .NET solutions") + parser.add_argument("--godot-output-dir", type=str, required=True) + parser.add_argument( + "--dev-debug", + action="store_true", + default=False, + help="Build GodotTools and Godot.NET.Sdk with 'Configuration=Debug'", + ) + parser.add_argument("--godot-platform", type=str, default="") + parser.add_argument("--mono-prefix", type=str, default="") + + args = parser.parse_args() + + this_script_dir = os.path.dirname(os.path.realpath(__file__)) + module_dir = os.path.abspath(os.path.join(this_script_dir, os.pardir)) + + output_dir = os.path.abspath(args.godot_output_dir) + + msbuild_tool = find_any_msbuild_tool(args.mono_prefix) + + if msbuild_tool is None: + print("Unable to find MSBuild") + sys.exit(1) + + exit_code = build_all(msbuild_tool, module_dir, output_dir, args.godot_platform, args.dev_debug) + sys.exit(exit_code) + + +if __name__ == "__main__": + main() diff --git a/modules/mono/build_scripts/gen_cs_glue_version.py b/modules/mono/build_scripts/gen_cs_glue_version.py deleted file mode 100644 index 98bbb4d9be3..00000000000 --- 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 8c5a60d2dbe..00000000000 --- 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 3bbbf29d3bd..00000000000 --- 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 6a621c3c8b4..00000000000 --- 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 d895d2d92da..010f1ef1e53 100644 --- a/modules/mono/config.py +++ b/modules/mono/config.py @@ -25,11 +25,11 @@ def get_opts(platform): PathVariable.PathAccept, ), BoolVariable("mono_static", "Statically link Mono", default_mono_static), - BoolVariable("mono_glue", "Build with the Mono glue sources", True), BoolVariable("build_cil", "Build C# solutions", True), BoolVariable( "copy_mono_root", "Make a copy of the Mono installation directory to bundle with the editor", True ), + # TODO: It would be great if this could be detected automatically instead BoolVariable( "mono_bundles_zlib", "Specify if the Mono runtime was built with bundled zlib", default_mono_bundles_zlib ), diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index b95b63cf1fe..2e6a6ef7e05 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -57,6 +57,7 @@ #endif #include "godotsharp_dirs.h" +#include "managed_callable.h" #include "mono_gd/gd_mono_cache.h" #include "mono_gd/gd_mono_class.h" #include "mono_gd/gd_mono_marshal.h" @@ -108,6 +109,9 @@ Error CSharpLanguage::execute_file(const String &p_path) { return OK; } +extern void *godotsharp_pinvoke_funcs[101]; +[[maybe_unused]] volatile void **do_not_strip_godotsharp_pinvoke_funcs; + void CSharpLanguage::init() { #ifdef DEBUG_METHODS_ENABLED if (OS::get_singleton()->get_cmdline_args().find("--class-db-json")) { @@ -118,20 +122,18 @@ void CSharpLanguage::init() { } #endif - gdmono = memnew(GDMono); - gdmono->initialize(); + // Hopefully this will be enough for all compilers. Otherwise we could use the printf on fake getenv trick. + do_not_strip_godotsharp_pinvoke_funcs = (volatile void **)godotsharp_pinvoke_funcs; #if defined(TOOLS_ENABLED) && defined(DEBUG_METHODS_ENABLED) - // Generate bindings here, before loading assemblies. 'initialize_load_assemblies' aborts - // the applications if the api assemblies or the main tools assembly is missing, but this - // is not a problem for BindingsGenerator as it only needs the tools project editor assembly. + // Generate the bindings here, before loading assemblies. The Godot assemblies + // may be missing if the glue wasn't generated yet in order to build them. List cmdline_args = OS::get_singleton()->get_cmdline_args(); BindingsGenerator::handle_cmdline_args(cmdline_args); #endif -#ifndef MONO_GLUE_ENABLED - print_line("Run this binary with '--generate-mono-glue path/to/modules/mono/glue'"); -#endif + gdmono = memnew(GDMono); + gdmono->initialize(); if (gdmono->is_runtime_initialized()) { gdmono->initialize_load_assemblies(); @@ -596,7 +598,9 @@ Vector CSharpLanguage::debug_get_current_stack_info() return Vector(); } _recursion_flag_ = true; - SCOPE_EXIT { _recursion_flag_ = false; }; + SCOPE_EXIT { + _recursion_flag_ = false; + }; GD_MONO_SCOPE_THREAD_ATTACH; @@ -628,7 +632,9 @@ Vector CSharpLanguage::stack_trace_get_info(MonoObjec return Vector(); } _recursion_flag_ = true; - SCOPE_EXIT { _recursion_flag_ = false; }; + SCOPE_EXIT { + _recursion_flag_ = false; + }; GD_MONO_SCOPE_THREAD_ATTACH; @@ -824,13 +830,13 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { for (SelfList *elem = ManagedCallable::instances.first(); elem; elem = elem->next()) { ManagedCallable *managed_callable = elem->self(); - MonoDelegate *delegate = (MonoDelegate *)managed_callable->delegate_handle.get_target(); - Array serialized_data; MonoObject *managed_serialized_data = GDMonoMarshal::variant_to_mono_object(serialized_data); MonoException *exc = nullptr; - bool success = (bool)CACHED_METHOD_THUNK(DelegateUtils, TrySerializeDelegate).invoke(delegate, managed_serialized_data, &exc); + bool success = (bool)CACHED_METHOD_THUNK(DelegateUtils, TrySerializeDelegateWithGCHandle) + .invoke(managed_callable->delegate_handle, + managed_serialized_data, &exc); if (exc) { GDMonoUtils::debug_print_unhandled_exception(exc); @@ -1149,10 +1155,11 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { const Array &serialized_data = elem.value; MonoObject *managed_serialized_data = GDMonoMarshal::variant_to_mono_object(serialized_data); - MonoDelegate *delegate = nullptr; + void *delegate = nullptr; MonoException *exc = nullptr; - bool success = (bool)CACHED_METHOD_THUNK(DelegateUtils, TryDeserializeDelegate).invoke(managed_serialized_data, &delegate, &exc); + bool success = (bool)CACHED_METHOD_THUNK(DelegateUtils, TryDeserializeDelegateWithGCHandle) + .invoke(managed_serialized_data, &delegate, &exc); if (exc) { GDMonoUtils::debug_print_unhandled_exception(exc); @@ -1161,7 +1168,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { if (success) { ERR_CONTINUE(delegate == nullptr); - managed_callable->set_delegate(delegate); + managed_callable->delegate_handle = delegate; } else if (OS::get_singleton()->is_stdout_verbose()) { OS::get_singleton()->print("Failed to deserialize delegate\n"); } @@ -1289,7 +1296,7 @@ bool CSharpLanguage::debug_break(const String &p_error, bool p_allow_continue) { } } -void CSharpLanguage::_on_scripts_domain_unloaded() { +void CSharpLanguage::_on_scripts_domain_about_to_unload() { for (KeyValue &E : script_bindings) { CSharpScriptBinding &script_binding = E.value; script_binding.gchandle.release(); @@ -1302,8 +1309,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 @@ -1455,7 +1461,7 @@ void CSharpLanguage::_instance_binding_free_callback(void *, void *, void *p_bin if (GDMono::get_singleton() == nullptr) { #ifdef DEBUG_ENABLED - CRASH_COND(!csharp_lang->script_bindings.is_empty()); + CRASH_COND(csharp_lang && !csharp_lang->script_bindings.is_empty()); #endif // Mono runtime finalized, all the gchandle bindings were already released return; @@ -1784,7 +1790,8 @@ void CSharpInstance::get_event_signals_state_for_reloading(List ignored_types = { "PhysicsServer3DExtension" }; -const char *BindingsGenerator::TypeInterface::DEFAULT_VARARG_C_IN("\t%0 %1_in = %1;\n"); +typedef String string; + +void BindingsGenerator::TypeInterface::postsetup_enum_type(BindingsGenerator::TypeInterface &r_enum_itype) { + // C interface for enums is the same as that of 'uint32_t'. Remember to apply + // any of the changes done here to the 'uint32_t' type interface as well. + + r_enum_itype.cs_type = r_enum_itype.proxy_name; + r_enum_itype.cs_in = "(int)%s"; + r_enum_itype.cs_out = "%5return (%2)%0(%1);"; + + { + // The expected types for parameters and return value in ptrcall are 'int64_t' or 'uint64_t'. + r_enum_itype.c_in = "%5%0 %1_in = %1;\n"; + r_enum_itype.c_out = "%5return (%0)%1;\n"; + r_enum_itype.c_type = "long"; + r_enum_itype.c_arg_in = "&%s_in"; + } + r_enum_itype.c_type_in = "int"; + r_enum_itype.c_type_out = r_enum_itype.c_type_in; + r_enum_itype.class_doc = &EditorHelp::get_doc_data()->class_list[r_enum_itype.proxy_name]; +} static String fix_doc_description(const String &p_bbcode) { // This seems to be the correct way to do this. It's the same EditorHelp does. @@ -794,47 +826,29 @@ void BindingsGenerator::_apply_prefix_to_enum_constants(BindingsGenerator::EnumI } } -void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) { +Error BindingsGenerator::_populate_method_icalls_table(const TypeInterface &p_itype) { for (const MethodInterface &imethod : p_itype.methods) { if (imethod.is_virtual) { continue; } - const TypeInterface *return_type = _get_type_or_placeholder(imethod.return_type); + const TypeInterface *return_type = _get_type_or_null(imethod.return_type); + ERR_FAIL_NULL_V(return_type, ERR_BUG); // Return type not found - String im_sig = "IntPtr " CS_PARAM_METHODBIND; - String im_unique_sig = imethod.return_type.cname.operator String() + ",IntPtr"; + String im_unique_sig = get_ret_unique_sig(return_type) + ",CallMethodBind"; if (!imethod.is_static) { - im_sig += ", IntPtr " CS_PARAM_INSTANCE; - im_unique_sig += ",IntPtr"; + im_unique_sig += ",CallInstance"; } // Get arguments information int i = 0; for (const ArgumentInterface &iarg : imethod.arguments) { - const TypeInterface *arg_type = _get_type_or_placeholder(iarg.type); - - im_sig += ", "; - im_sig += arg_type->im_type_in; - im_sig += " arg"; - im_sig += itos(i + 1); + const TypeInterface *arg_type = _get_type_or_null(iarg.type); + ERR_FAIL_NULL_V(arg_type, ERR_BUG); // Argument type not found im_unique_sig += ","; - im_unique_sig += get_unique_sig(*arg_type); - - i++; - } - - String im_type_out = return_type->im_type_out; - - if (return_type->ret_as_byref_arg) { - // Doesn't affect the unique signature - im_type_out = "void"; - - im_sig += ", "; - im_sig += return_type->im_type_out; - im_sig += " argRet"; + im_unique_sig += get_arg_unique_sig(*arg_type); i++; } @@ -845,7 +859,15 @@ void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) { icall_method += "_"; icall_method += itos(method_icalls.size()); - InternalCall im_icall = InternalCall(p_itype.api_type, icall_method, im_type_out, im_sig, im_unique_sig); + InternalCall im_icall = InternalCall(p_itype.api_type, icall_method, im_unique_sig); + + im_icall.is_vararg = imethod.is_vararg; + im_icall.is_static = imethod.is_static; + im_icall.return_type = imethod.return_type; + + for (const List::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); @@ -859,6 +881,8 @@ void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) { method_icalls_map.insert(&imethod, &added->get()); } } + + return OK; } void BindingsGenerator::_generate_array_extensions(StringBuilder &p_output) { @@ -874,7 +898,7 @@ void BindingsGenerator::_generate_array_extensions(StringBuilder &p_output) { p_output.append(INDENT2 "/// 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); @@ -886,7 +910,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); @@ -897,7 +921,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); @@ -989,7 +1013,7 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) { p_output.append("\n" INDENT1 "public static partial class "); p_output.append(enum_class_name); - p_output.append("\n" INDENT1 OPEN_BLOCK); + p_output.append("\n" OPEN_BLOCK_L1); } if (ienum.is_flags) { @@ -999,7 +1023,7 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) { p_output.append("\n" INDENT1 "public enum "); p_output.append(enum_proxy_name); p_output.append(" : long"); - p_output.append("\n" INDENT1 OPEN_BLOCK); + p_output.append("\n" OPEN_BLOCK_L1); const ConstantInterface &last = ienum.constants.back()->get(); for (const ConstantInterface &iconstant : ienum.constants) { @@ -1106,40 +1130,38 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir) { compile_items.push_back(output_file); } - // Generate sources from compressed files + // Generate native calls StringBuilder cs_icalls_content; cs_icalls_content.append("using System;\n" - "using System.Runtime.CompilerServices;\n" + "using System.Diagnostics.CodeAnalysis;\n" + "using System.Runtime.InteropServices;\n" + "using Godot.NativeInterop;\n" "\n"); cs_icalls_content.append("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK); + cs_icalls_content.append(INDENT1 "[SuppressMessage(\"ReSharper\", \"InconsistentNaming\")]\n"); + cs_icalls_content.append(INDENT1 "[SuppressMessage(\"ReSharper\", \"RedundantUnsafeContext\")]\n"); + cs_icalls_content.append(INDENT1 "[SuppressMessage(\"ReSharper\", \"RedundantNameQualifier\")]\n"); + cs_icalls_content.append("#if NET\n"); + cs_icalls_content.append(INDENT1 "[System.Runtime.CompilerServices.SkipLocalsInit]\n"); + cs_icalls_content.append("#endif\n"); cs_icalls_content.append(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS "\n" INDENT1 "{"); cs_icalls_content.append(MEMBER_BEGIN "internal static ulong godot_api_hash = "); - cs_icalls_content.append(String::num_uint64(GDMono::get_singleton()->get_api_core_hash()) + ";\n"); - cs_icalls_content.append(MEMBER_BEGIN "internal static uint bindings_version = "); - cs_icalls_content.append(String::num_uint64(BINDINGS_GENERATOR_VERSION) + ";\n"); - cs_icalls_content.append(MEMBER_BEGIN "internal static uint cs_glue_version = "); - cs_icalls_content.append(String::num_uint64(CS_GLUE_VERSION) + ";\n"); + cs_icalls_content.append(String::num_uint64(ClassDB::get_api_hash(ClassDB::API_CORE)) + ";\n"); -#define ADD_INTERNAL_CALL(m_icall) \ - if (!m_icall.editor_only) { \ - cs_icalls_content.append(MEMBER_BEGIN "[MethodImpl(MethodImplOptions.InternalCall)]\n"); \ - cs_icalls_content.append(INDENT2 "internal static extern "); \ - cs_icalls_content.append(m_icall.im_type_out + " "); \ - cs_icalls_content.append(m_icall.name + "("); \ - cs_icalls_content.append(m_icall.im_sig + ");\n"); \ - } + cs_icalls_content.append(MEMBER_BEGIN "private const int VarArgsSpanThreshold = 10;\n"); - for (const InternalCall &internal_call : core_custom_icalls) { - ADD_INTERNAL_CALL(internal_call); + for (const InternalCall &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 : method_icalls) { - ADD_INTERNAL_CALL(internal_call); - } - -#undef ADD_INTERNAL_CALL cs_icalls_content.append(INDENT1 CLOSE_BLOCK CLOSE_BLOCK); @@ -1152,6 +1174,8 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir) { compile_items.push_back(internal_methods_file); + // Generate GeneratedIncludes.props + StringBuilder includes_props_content; includes_props_content.append("\n" " \n"); @@ -1215,40 +1239,41 @@ 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(MEMBER_BEGIN "private const int VarArgsSpanThreshold = 10;\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"); @@ -1260,6 +1285,8 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_proj_dir) { compile_items.push_back(internal_methods_file); + // Generate GeneratedIncludes.props + StringBuilder includes_props_content; includes_props_content.append("\n" " \n"); @@ -1343,12 +1370,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 @@ -1491,53 +1514,96 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str // Add the type name and the singleton pointer as static fields output.append(MEMBER_BEGIN "private static Godot.Object singleton;\n"); - output.append(MEMBER_BEGIN "public static Godot.Object Singleton\n" INDENT2 "{\n" INDENT3 - "get\n" INDENT3 "{\n" INDENT4 "if (singleton == null)\n" INDENT5 - "singleton = Engine.GetSingleton(typeof("); - output.append(itype.proxy_name); - output.append(").Name);\n" INDENT4 "return singleton;\n" INDENT3 "}\n" INDENT2 "}\n"); - output.append(MEMBER_BEGIN "private static StringName " BINDINGS_NATIVE_NAME_FIELD " = \""); + output << MEMBER_BEGIN "public static Godot.Object " CS_PROPERTY_SINGLETON "\n" INDENT2 "{\n" + << INDENT3 "get\n" INDENT3 "{\n" INDENT4 "if (singleton == null)\n" + << INDENT5 "singleton = " C_METHOD_ENGINE_GET_SINGLETON "(typeof(" + << itype.proxy_name + << ").Name);\n" INDENT4 "return singleton;\n" INDENT3 "}\n" INDENT2 "}\n"; + + output.append(MEMBER_BEGIN "private static readonly StringName " BINDINGS_NATIVE_NAME_FIELD " = \""); + output.append(itype.name); + output.append("\";\n"); + } else { + // IMPORTANT: We also generate the static fields for Godot.Object instead of declaring + // them manually in the `Object.base.cs` partial class declaration, because they're + // required by other static fields in this generated partial class declaration. + // Static fields are initialized in order of declaration, but when they're in different + // partial class declarations then it becomes harder to tell (Rider warns about this). + + // 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; @@ -1553,22 +1619,6 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str "Failed to generate signal '" + isignal.name + "' for class '" + itype.name + "'."); } - if (itype.is_singleton) { - InternalCall singleton_icall = InternalCall(itype.api_type, ICALL_PREFIX + itype.name + SINGLETON_ICALL_SUFFIX, "IntPtr"); - - if (!find_icall_by_name(singleton_icall.name, custom_icalls)) { - custom_icalls.push_back(singleton_icall); - } - } - - if (is_derived_type && itype.is_instantiable) { - InternalCall ctor_icall = InternalCall(itype.api_type, ctor_method, "IntPtr", itype.proxy_name + " obj"); - - if (!find_icall_by_name(ctor_icall.name, custom_icalls)) { - custom_icalls.push_back(ctor_icall); - } - } - output.append(INDENT1 CLOSE_BLOCK /* class */ CLOSE_BLOCK /* namespace */); @@ -1669,7 +1719,7 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte p_output.append(prop_cs_type); p_output.append(" "); p_output.append(p_iprop.proxy_name); - p_output.append("\n" INDENT2 OPEN_BLOCK); + p_output.append("\n" OPEN_BLOCK_L2); if (getter) { p_output.append(INDENT3 "get\n" @@ -1677,7 +1727,7 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte // TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that) "#pragma warning disable CS0618 // Disable warning about obsolete method\n" - OPEN_BLOCK_L3); + OPEN_BLOCK_L3 INDENT4); p_output.append("return "); p_output.append(getter->proxy_name + "("); @@ -1706,7 +1756,7 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte // TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that) "#pragma warning disable CS0618 // Disable warning about obsolete method\n" - OPEN_BLOCK_L3); + OPEN_BLOCK_L3 INDENT4); p_output.append(setter->proxy_name + "("); if (p_iprop.index != -1) { @@ -1734,7 +1784,8 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte } Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::MethodInterface &p_imethod, int &p_method_bind_count, StringBuilder &p_output) { - const TypeInterface *return_type = _get_type_or_placeholder(p_imethod.return_type); + const TypeInterface *return_type = _get_type_or_null(p_imethod.return_type); + ERR_FAIL_NULL_V(return_type, ERR_BUG); // Return type not found ERR_FAIL_COND_V_MSG(return_type->is_singleton, ERR_BUG, "Method return type is a singleton: '" + p_itype.name + "." + p_imethod.name + "'."); @@ -1745,10 +1796,11 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf "' from the editor API. Core API cannot have dependencies on the editor API."); } - String method_bind_field = "__method_bind_" + itos(p_method_bind_count); + String method_bind_field = CS_STATIC_FIELD_METHOD_BIND_PREFIX + itos(p_method_bind_count); String arguments_sig; - String cs_in_statements; + StringBuilder cs_in_statements; + bool cs_in_is_unsafe = false; String icall_params = method_bind_field; if (!p_imethod.is_static) { @@ -1760,7 +1812,8 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf // Retrieve information from the arguments const ArgumentInterface &first = p_imethod.arguments.front()->get(); for (const ArgumentInterface &iarg : p_imethod.arguments) { - const TypeInterface *arg_type = _get_type_or_placeholder(iarg.type); + const TypeInterface *arg_type = _get_type_or_null(iarg.type); + ERR_FAIL_NULL_V(arg_type, ERR_BUG); // Argument type not found ERR_FAIL_COND_V_MSG(arg_type->is_singleton, ERR_BUG, "Argument type is a singleton: '" + iarg.name + "' of method '" + p_itype.name + "." + p_imethod.name + "'."); @@ -1816,24 +1869,20 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf String arg_in = iarg.name; arg_in += "_in"; - cs_in_statements += arg_cs_type; - cs_in_statements += " "; - cs_in_statements += arg_in; - cs_in_statements += " = "; - cs_in_statements += iarg.name; + cs_in_statements << INDENT3 << arg_cs_type << " " << arg_in << " = " << iarg.name; if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL) { - cs_in_statements += ".HasValue ? "; + cs_in_statements << ".HasValue ? "; } else { - cs_in_statements += " != null ? "; + cs_in_statements << " != null ? "; } - cs_in_statements += iarg.name; + cs_in_statements << iarg.name; if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL) { - cs_in_statements += ".Value : "; + cs_in_statements << ".Value : "; } else { - cs_in_statements += " : "; + cs_in_statements << " : "; } String cs_type = arg_cs_type; @@ -1843,8 +1892,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf String def_arg = sformat(iarg.default_argument, cs_type); - cs_in_statements += def_arg; - cs_in_statements += ";\n" INDENT3; + cs_in_statements << def_arg << ";\n"; icall_params += arg_type->cs_in.is_empty() ? arg_in : sformat(arg_type->cs_in, arg_in); @@ -1857,16 +1905,25 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf } else { icall_params += arg_type->cs_in.is_empty() ? iarg.name : sformat(arg_type->cs_in, iarg.name); } + + cs_in_is_unsafe |= arg_type->cs_in_is_unsafe; } // Generate method { if (!p_imethod.is_virtual && !p_imethod.requires_object_call) { - p_output.append(MEMBER_BEGIN "[DebuggerBrowsable(DebuggerBrowsableState.Never)]" MEMBER_BEGIN "private static readonly IntPtr "); - p_output.append(method_bind_field); - p_output.append(" = Object." ICALL_GET_METHODBIND "(" BINDINGS_NATIVE_NAME_FIELD ", \""); - p_output.append(p_imethod.name); - p_output.append("\");\n"); + p_output << MEMBER_BEGIN "[DebuggerBrowsable(DebuggerBrowsableState.Never)]" MEMBER_BEGIN "private static readonly IntPtr " + << method_bind_field << " = "; + + if (p_itype.is_singleton) { + // Singletons are static classes. They don't derive Godot.Object, + // so we need to specify the type to call the static method. + p_output << "Object."; + } + + p_output << ICALL_CLASSDB_GET_METHOD "(" BINDINGS_NATIVE_NAME_FIELD ", \"" + << p_imethod.name + << "\");\n"; } if (p_imethod.method_doc && p_imethod.method_doc->description.size()) { @@ -1919,6 +1976,10 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf p_output.append("virtual "); } + if (cs_in_is_unsafe) { + p_output.append("unsafe "); + } + String return_cs_type = return_type->cs_type + _get_generic_type_parameters(*return_type, p_imethod.return_type.generic_type_parameters); p_output.append(return_cs_type + " "); @@ -1929,11 +1990,9 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf // Godot virtual method must be overridden, therefore we return a default value by default. if (return_type->cname == name_cache.type_void) { - p_output.append("return;\n" CLOSE_BLOCK_L2); + p_output.append(CLOSE_BLOCK_L2); } else { - p_output.append("return default("); - p_output.append(return_cs_type); - p_output.append(");\n" CLOSE_BLOCK_L2); + p_output.append(INDENT3 "return default;\n" CLOSE_BLOCK_L2); } return OK; // Won't increment method bind count @@ -1942,7 +2001,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf if (p_imethod.requires_object_call) { // Fallback to Godot's object.Call(string, params) - p_output.append(CS_METHOD_CALL "(\""); + p_output.append(INDENT3 CS_METHOD_CALL "(\""); p_output.append(p_imethod.name); p_output.append("\""); @@ -1966,15 +2025,16 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf im_call += im_icall->name; if (p_imethod.arguments.size()) { - p_output.append(cs_in_statements); + p_output.append(cs_in_statements.as_string()); } if (return_type->cname == name_cache.type_void) { - p_output.append(im_call + "(" + icall_params + ");\n"); + p_output << INDENT3 << im_call << "(" << icall_params << ");\n"; } else if (return_type->cs_out.is_empty()) { - p_output.append("return " + im_call + "(" + icall_params + ");\n"); + p_output << INDENT3 "return " << im_call << "(" << icall_params << ");\n"; } else { - p_output.append(sformat(return_type->cs_out, im_call, icall_params, return_cs_type, return_type->im_type_out)); + p_output.append(sformat(return_type->cs_out, im_call, icall_params, + return_cs_type, return_type->c_type_out, String(), INDENT3)); p_output.append("\n"); } @@ -1992,7 +2052,8 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf // Retrieve information from the arguments const ArgumentInterface &first = p_isignal.arguments.front()->get(); for (const ArgumentInterface &iarg : p_isignal.arguments) { - const TypeInterface *arg_type = _get_type_or_placeholder(iarg.type); + const TypeInterface *arg_type = _get_type_or_null(iarg.type); + ERR_FAIL_NULL_V(arg_type, ERR_BUG); // Argument type not found ERR_FAIL_COND_V_MSG(arg_type->is_singleton, ERR_BUG, "Argument type is a singleton: '" + iarg.name + "' of signal '" + p_itype.name + "." + p_isignal.name + "'."); @@ -2058,7 +2119,8 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf // If so, we could store the pointer we get from `data_unique_pointer()` instead of allocating StringName here. // Cached signal name (StringName) - p_output.append(MEMBER_BEGIN "[DebuggerBrowsable(DebuggerBrowsableState.Never)]" MEMBER_BEGIN "private static StringName __signal_name_"); + p_output.append(MEMBER_BEGIN "[DebuggerBrowsable(DebuggerBrowsableState.Never)]" MEMBER_BEGIN + "private static readonly StringName " CS_STATIC_FIELD_SIGNAL_NAME_PREFIX); p_output.append(p_isignal.name); p_output.append(" = \""); p_output.append(p_isignal.name); @@ -2075,21 +2137,21 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf p_output.append(delegate_name); p_output.append(" "); p_output.append(p_isignal.proxy_name); - p_output.append("\n" OPEN_BLOCK_L2); + p_output.append("\n" OPEN_BLOCK_L2 INDENT3); if (p_itype.is_singleton) { - p_output.append("add => Singleton.Connect(__signal_name_"); + p_output.append("add => " CS_PROPERTY_SINGLETON ".Connect(" CS_STATIC_FIELD_SIGNAL_NAME_PREFIX); } else { - p_output.append("add => Connect(__signal_name_"); + p_output.append("add => Connect(" CS_STATIC_FIELD_SIGNAL_NAME_PREFIX); } p_output.append(p_isignal.name); p_output.append(", new Callable(value));\n"); if (p_itype.is_singleton) { - p_output.append(INDENT3 "remove => Singleton.Disconnect(__signal_name_"); + p_output.append(INDENT3 "remove => " CS_PROPERTY_SINGLETON ".Disconnect(" CS_STATIC_FIELD_SIGNAL_NAME_PREFIX); } else { - p_output.append(INDENT3 "remove => Disconnect(__signal_name_"); + p_output.append(INDENT3 "remove => Disconnect(" CS_STATIC_FIELD_SIGNAL_NAME_PREFIX); } p_output.append(p_isignal.name); @@ -2100,178 +2162,212 @@ 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; - generated_icall_funcs.clear(); - - for (const KeyValue &type_elem : obj_types) { - const TypeInterface &itype = type_elem.value; - - bool is_derived_type = itype.base_name != StringName(); - - if (!is_derived_type) { - // Some Object assertions - CRASH_COND(itype.cname != name_cache.type_Object); - CRASH_COND(!itype.is_instantiable); - CRASH_COND(itype.api_type != ClassDB::API_CORE); - CRASH_COND(itype.is_singleton); - } - - List &custom_icalls = itype.api_type == ClassDB::API_EDITOR ? editor_custom_icalls : core_custom_icalls; - - OS::get_singleton()->print("Generating %s...\n", itype.name.utf8().get_data()); - - String ctor_method(ICALL_PREFIX + itype.proxy_name + "_Ctor"); // Used only for derived types - - for (const MethodInterface &imethod : itype.methods) { - Error method_err = _generate_glue_method(itype, imethod, output); - ERR_FAIL_COND_V_MSG(method_err != OK, method_err, - "Failed to generate method '" + imethod.name + "' for class '" + itype.name + "'."); - } - - if (itype.is_singleton) { - String singleton_icall_name = ICALL_PREFIX + itype.name + SINGLETON_ICALL_SUFFIX; - InternalCall singleton_icall = InternalCall(itype.api_type, singleton_icall_name, "IntPtr"); - - if (!find_icall_by_name(singleton_icall.name, custom_icalls)) { - custom_icalls.push_back(singleton_icall); - } - - 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"); - } + if (!p_icall.is_static) { + c_func_sig += ", IntPtr " CS_PARAM_INSTANCE; } - output.append("namespace GodotSharpBindings\n" OPEN_BLOCK "\n"); + // 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 - output.append("uint64_t get_core_api_hash() { return "); - output.append(String::num_uint64(GDMono::get_singleton()->get_api_core_hash()) + "U; }\n"); + String c_param_name = "arg" + itos(i + 1); - 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"); + if (p_icall.is_vararg) { + if (i < p_icall.get_arguments_count() - 1) { + string c_in_vararg = arg_type->c_in_vararg; - output.append("uint32_t get_bindings_version() { return "); - output.append(String::num_uint64(BINDINGS_GENERATOR_VERSION) + "; }\n"); + if (arg_type->is_object_type) { + c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromGodotObject(%1);\n"; + } - output.append("uint32_t get_cs_glue_version() { return "); - output.append(String::num_uint64(CS_GLUE_VERSION) + "; }\n"); + ERR_FAIL_COND_V_MSG(c_in_vararg.is_empty(), ERR_BUG, + "VarArg support not implemented for parameter type: " + arg_type->name); - 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); } - ADD_INTERNAL_CALL_REGISTRATION(internal_call); + c_func_sig << ", " << arg_type->c_type_in << " " << c_param_name; + + i++; } - if (tools_sequence) { - tools_sequence = false; - output.append("#endif\n"); + 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 = " = default"; + } 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"; + } + } else if (return_type->c_ret_needs_default_initialization) { + initialization = " = default"; + } + + r_output << ptrcall_return_type << " " C_LOCAL_RET << initialization << ";\n"; } - output.append("#ifdef TOOLS_ENABLED\n"); - for (const InternalCall &internal_call : editor_custom_icalls) { - ADD_INTERNAL_CALL_REGISTRATION(internal_call); + if (!p_icall.is_static) { + r_output << INDENT3 "if (" CS_PARAM_INSTANCE " == IntPtr.Zero)\n" + << INDENT4 "throw new ArgumentNullException(nameof(" CS_PARAM_INSTANCE "));\n"; } - output.append("#endif // TOOLS_ENABLED\n"); - for (const InternalCall &internal_call : method_icalls) { - if (tools_sequence) { - if (!internal_call.editor_only) { - tools_sequence = false; - output.append("#endif\n"); + String argc_str = itos(p_icall.get_arguments_count()); + + 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 ", " << (p_icall.is_static ? "IntPtr.Zero" : CS_PARAM_INSTANCE) + << ", " << (p_icall.get_arguments_count() ? "(godot_variant**)" C_LOCAL_PTRCALL_ARGS : "null") + << ", total_length, &vcall_error);\n"; + + if (!ret_void) { + if (return_type->cname != name_cache.type_Variant) { + if (return_type->cname == name_cache.enum_Error) { + r_output << base_indent << C_LOCAL_RET " = VariantUtils.ConvertToInt64(&" C_LOCAL_VARARG_RET ");\n"; + } else { + // TODO: Use something similar to c_in_vararg (see usage above, with error if not implemented) + CRASH_NOW_MSG("Custom VarArg return type not implemented: " + return_type->name); + r_output << base_indent << C_LOCAL_RET " = " C_LOCAL_VARARG_RET ";\n"; + } + } } } else { - 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 ", " << (p_icall.is_static ? "IntPtr.Zero" : CS_PARAM_INSTANCE) + << ", " << (p_icall.get_arguments_count() ? C_LOCAL_PTRCALL_ARGS : "null") + << ", " << (!ret_void ? "&" C_LOCAL_RET ");\n" : "null);\n"); } - 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 <= VarArgsSpanThreshold ?\n" + << INDENT4 "stackalloc godot_variant[VarArgsSpanThreshold].Cleared() :\n" + << INDENT4 "new godot_variant[vararg_length];\n"; + + r_output << INDENT3 "Span " C_LOCAL_PTRCALL_ARGS "_span = total_length <= VarArgsSpanThreshold ?\n" + << INDENT4 "stackalloc IntPtr[VarArgsSpanThreshold] :\n" + << INDENT4 "new IntPtr[total_length];\n"; + + r_output << INDENT3 "using var variantSpanDisposer = new VariantSpanDisposer(varargs_span);\n"; + + r_output << INDENT3 "fixed (godot_variant* varargs = &MemoryMarshal.GetReference(varargs_span))\n" + << INDENT3 "fixed (IntPtr* " C_LOCAL_PTRCALL_ARGS " = " + "&MemoryMarshal.GetReference(" C_LOCAL_PTRCALL_ARGS "_span))\n" + << OPEN_BLOCK_L3; + + r_output << c_in_statements.as_string(); + + 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) { Ref file = FileAccess::open(p_path, FileAccess::WRITE); ERR_FAIL_COND_V_MSG(file.is_null(), ERR_FILE_CANT_WRITE, "Cannot open file: '" + p_path + "'."); @@ -2281,210 +2377,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; - if (!p_imethod.is_static) { - c_func_sig += ", " + p_itype.c_type_in + " " CS_PARAM_INSTANCE; - } - String c_in_statements; - String c_args_var_content; - - // Get arguments information - int i = 0; - for (const ArgumentInterface &iarg : p_imethod.arguments) { - const TypeInterface *arg_type = _get_type_or_placeholder(iarg.type); - - 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++; - } - - HashMap::ConstIterator 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 (!p_imethod.is_static) { - if (return_type->ret_as_byref_arg) { - p_output.append("\tif (" CS_PARAM_INSTANCE " == nullptr) { *arg_ret = "); - p_output.append(fail_ret); - p_output.append("; ERR_FAIL_MSG(\"Parameter ' " CS_PARAM_INSTANCE " ' is null.\"); }\n"); - } else { - p_output.append("\tERR_FAIL_NULL_V(" CS_PARAM_INSTANCE ", "); - p_output.append(fail_ret); - p_output.append(");\n"); - } - } - } else { - if (!p_imethod.is_static) { - p_output.append("\tERR_FAIL_NULL(" CS_PARAM_INSTANCE ");\n"); - } - } - - 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("); - p_output.append(p_imethod.is_static ? "nullptr" : CS_PARAM_INSTANCE); - p_output.append(", "); - p_output.append(p_imethod.arguments.size() ? C_LOCAL_PTRCALL_ARGS ".ptr()" : "nullptr"); - p_output.append(", total_length, vcall_error);\n"); - - 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("); - p_output.append(p_imethod.is_static ? "nullptr" : CS_PARAM_INSTANCE); - p_output.append(", "); - p_output.append(p_imethod.arguments.size() ? C_LOCAL_PTRCALL_ARGS ", " : "nullptr, "); - p_output.append(!ret_void ? "&" C_LOCAL_RET ");\n" : "nullptr);\n"); - } - - 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) { HashMap::ConstIterator builtin_type_match = builtin_types.find(p_typeref.cname); @@ -2514,27 +2406,6 @@ const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_null(con return nullptr; } -const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_placeholder(const TypeReference &p_typeref) { - const TypeInterface *found = _get_type_or_null(p_typeref); - - if (found) { - return found; - } - - ERR_PRINT(String() + "Type not found. Creating placeholder: '" + p_typeref.cname.operator String() + "'."); - - HashMap::ConstIterator match = placeholder_types.find(p_typeref.cname); - - if (match) { - return &match->value; - } - - TypeInterface placeholder; - TypeInterface::create_placeholder_type(placeholder, p_typeref.cname); - - return &placeholder_types.insert(placeholder.cname, placeholder)->value; -} - const String BindingsGenerator::_get_generic_type_parameters(const TypeInterface &p_itype, const List &p_generic_type_parameters) { if (p_generic_type_parameters.is_empty()) { return ""; @@ -2548,7 +2419,8 @@ const String BindingsGenerator::_get_generic_type_parameters(const TypeInterface int i = 0; String params = "<"; for (const TypeReference ¶m_type : p_generic_type_parameters) { - const TypeInterface *param_itype = _get_type_or_placeholder(param_type); + const TypeInterface *param_itype = _get_type_or_null(param_type); + ERR_FAIL_NULL_V(param_itype, ""); ERR_FAIL_COND_V_MSG(param_itype->is_singleton, "", "Generic type parameter is a singleton: '" + param_itype->name + "'."); @@ -2666,8 +2538,6 @@ bool BindingsGenerator::_arg_default_value_is_assignable_to_type(const Variant & case Variant::RECT2: case Variant::VECTOR3: case Variant::RID: - case Variant::ARRAY: - case Variant::DICTIONARY: case Variant::PACKED_BYTE_ARRAY: case Variant::PACKED_INT32_ARRAY: case Variant::PACKED_INT64_ARRAY: @@ -2680,6 +2550,10 @@ bool BindingsGenerator::_arg_default_value_is_assignable_to_type(const Variant & case Variant::CALLABLE: case Variant::SIGNAL: return p_arg_type.name == Variant::get_type_name(p_val.get_type()); + case Variant::ARRAY: + return p_arg_type.name == Variant::get_type_name(p_val.get_type()) || p_arg_type.cname == name_cache.type_Array_generic; + case Variant::DICTIONARY: + return p_arg_type.name == Variant::get_type_name(p_val.get_type()) || p_arg_type.cname == name_cache.type_Dictionary_generic; case Variant::OBJECT: return p_arg_type.is_object_type; case Variant::VECTOR2I: @@ -2744,18 +2618,24 @@ bool BindingsGenerator::_populate_object_type_interfaces() { itype.is_ref_counted = ClassDB::is_parent_class(type_cname, name_cache.type_RefCounted); itype.memory_own = itype.is_ref_counted; - itype.c_out = "\treturn "; + itype.c_out = "%5return "; itype.c_out += C_METHOD_UNMANAGED_GET_MANAGED; - itype.c_out += itype.is_ref_counted ? "(%1.ptr());\n" : "(%1);\n"; + itype.c_out += itype.is_ref_counted ? "(%1._reference);\n" : "(%1);\n"; - itype.cs_in = itype.is_singleton ? BINDINGS_PTR_FIELD : "Object." CS_SMETHOD_GETINSTANCE "(%0)"; - - itype.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 @@ -2887,7 +2767,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() { String() + "Return type is reference but hint is not '" _STR(PROPERTY_HINT_RESOURCE_TYPE) "'." + " Are you returning a reference type by pointer? Method: '" + itype.name + "." + imethod.name + "'."); } else if (return_info.type == Variant::ARRAY && return_info.hint == PROPERTY_HINT_ARRAY_TYPE) { - imethod.return_type.cname = Variant::get_type_name(return_info.type); + imethod.return_type.cname = Variant::get_type_name(return_info.type) + "_@generic"; imethod.return_type.generic_type_parameters.push_back(TypeReference(return_info.hint_string)); } else if (return_info.hint == PROPERTY_HINT_RESOURCE_TYPE) { imethod.return_type.cname = return_info.hint_string; @@ -2919,7 +2799,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() { } else if (arginfo.class_name != StringName()) { iarg.type.cname = arginfo.class_name; } else if (arginfo.type == Variant::ARRAY && arginfo.hint == PROPERTY_HINT_ARRAY_TYPE) { - iarg.type.cname = Variant::get_type_name(arginfo.type); + iarg.type.cname = Variant::get_type_name(arginfo.type) + "_@generic"; iarg.type.generic_type_parameters.push_back(TypeReference(arginfo.hint_string)); } else if (arginfo.hint == PROPERTY_HINT_RESOURCE_TYPE) { iarg.type.cname = arginfo.hint_string; @@ -3027,7 +2907,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() { } else if (arginfo.class_name != StringName()) { iarg.type.cname = arginfo.class_name; } else if (arginfo.type == Variant::ARRAY && arginfo.hint == PROPERTY_HINT_ARRAY_TYPE) { - iarg.type.cname = Variant::get_type_name(arginfo.type); + iarg.type.cname = Variant::get_type_name(arginfo.type) + "_@generic"; iarg.type.generic_type_parameters.push_back(TypeReference(arginfo.hint_string)); } else if (arginfo.hint == PROPERTY_HINT_RESOURCE_TYPE) { iarg.type.cname = arginfo.hint_string; @@ -3246,7 +3126,7 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar ERR_FAIL_COND_V_MSG(!p_val.is_zero(), false, "Parameter of type '" + String(r_iarg.type.cname) + "' can only have null/zero as the default value."); - r_iarg.default_argument = "null"; + r_iarg.default_argument = "default"; break; case Variant::ARRAY: r_iarg.default_argument = "new %s { }"; @@ -3325,7 +3205,7 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar r_iarg.default_argument = "default"; break; default: - CRASH_NOW_MSG("Unexpected Variant type: " + itos(p_val.get_type())); + ERR_FAIL_V_MSG(false, "Unexpected Variant type: " + itos(p_val.get_type())); break; } @@ -3341,20 +3221,14 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { TypeInterface itype; -#define INSERT_STRUCT_TYPE(m_type) \ - { \ - itype = TypeInterface::create_value_type(String(#m_type)); \ - itype.c_in = "\t%0 %1_in = MARSHALLED_IN(" #m_type ", %1);\n"; \ - itype.c_out = "\t*%3 = MARSHALLED_OUT(" #m_type ", %1);\n"; \ - itype.c_arg_in = "&%s_in"; \ - itype.c_type_in = "GDMonoMarshal::M_" #m_type "*"; \ - itype.c_type_out = "GDMonoMarshal::M_" #m_type; \ - itype.cs_in = "ref %s"; \ - /* in cs_out, im_type_out (%3) includes the 'out ' part */ \ - itype.cs_out = "%0(%1, %3 argRet); return argRet;"; \ - itype.im_type_out = "out " + itype.cs_type; \ - itype.ret_as_byref_arg = true; \ - builtin_types.insert(itype.cname, itype); \ +#define INSERT_STRUCT_TYPE(m_type) \ + { \ + itype = TypeInterface::create_value_type(String(#m_type)); \ + itype.c_type_in = #m_type "*"; \ + itype.c_type_out = itype.cs_type; \ + itype.cs_in = "&%s"; \ + itype.cs_in_is_unsafe = true; \ + builtin_types.insert(itype.cname, itype); \ } INSERT_STRUCT_TYPE(Vector2) @@ -3370,56 +3244,56 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { INSERT_STRUCT_TYPE(AABB) INSERT_STRUCT_TYPE(Color) INSERT_STRUCT_TYPE(Plane) + INSERT_STRUCT_TYPE(Vector4) + INSERT_STRUCT_TYPE(Vector4i) + INSERT_STRUCT_TYPE(Projection) #undef INSERT_STRUCT_TYPE // bool itype = TypeInterface::create_value_type(String("bool")); - { - // MonoBoolean <---> bool - itype.c_in = "\t%0 %1_in = (%0)%1;\n"; - itype.c_out = "\treturn (%0)%1;\n"; - itype.c_type = "bool"; - itype.c_type_in = "MonoBoolean"; - itype.c_type_out = itype.c_type_in; - itype.c_arg_in = "&%s_in"; - } - itype.im_type_in = itype.name; - itype.im_type_out = itype.name; + itype.c_type = "godot_bool"; + itype.c_type_in = itype.c_type; + itype.c_type_out = itype.c_type; + itype.c_arg_in = "&%s"; + itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromBool(%1);\n"; builtin_types.insert(itype.cname, itype); // Integer types { // C interface for 'uint32_t' is the same as that of enums. Remember to apply // any of the changes done here to 'TypeInterface::postsetup_enum_type' as well. -#define INSERT_INT_TYPE(m_name, m_c_type_in_out, m_c_type) \ - { \ - itype = TypeInterface::create_value_type(String(m_name)); \ - { \ - itype.c_in = "\t%0 %1_in = (%0)%1;\n"; \ - itype.c_out = "\treturn (%0)%1;\n"; \ - itype.c_type = #m_c_type; \ - itype.c_arg_in = "&%s_in"; \ - } \ - itype.c_type_in = #m_c_type_in_out; \ - itype.c_type_out = itype.c_type_in; \ - itype.im_type_in = itype.name; \ - itype.im_type_out = itype.name; \ - builtin_types.insert(itype.cname, itype); \ +#define INSERT_INT_TYPE(m_name) \ + { \ + itype = TypeInterface::create_value_type(String(m_name)); \ + if (itype.name != "long" && itype.name != "ulong") { \ + itype.c_in = "%5%0 %1_in = %1;\n"; \ + itype.c_out = "%5return (%0)%1;\n"; \ + itype.c_type = "long"; \ + itype.c_arg_in = "&%s_in"; \ + } else { \ + itype.c_arg_in = "&%s"; \ + } \ + itype.c_type_in = itype.name; \ + itype.c_type_out = itype.name; \ + itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromInt(%1);\n"; \ + builtin_types.insert(itype.cname, itype); \ } // The expected type for all integers in ptrcall is 'int64_t', so that's what we use for 'c_type' - INSERT_INT_TYPE("sbyte", int8_t, int64_t); - INSERT_INT_TYPE("short", int16_t, int64_t); - INSERT_INT_TYPE("int", int32_t, int64_t); - INSERT_INT_TYPE("long", int64_t, int64_t); - INSERT_INT_TYPE("byte", uint8_t, int64_t); - INSERT_INT_TYPE("ushort", uint16_t, int64_t); - INSERT_INT_TYPE("uint", uint32_t, int64_t); - INSERT_INT_TYPE("ulong", uint64_t, int64_t); + INSERT_INT_TYPE("sbyte"); + INSERT_INT_TYPE("short"); + INSERT_INT_TYPE("int"); + INSERT_INT_TYPE("long"); + INSERT_INT_TYPE("byte"); + INSERT_INT_TYPE("ushort"); + INSERT_INT_TYPE("uint"); + INSERT_INT_TYPE("ulong"); } +#undef INSERT_INT_TYPE + // Floating point types { // float @@ -3427,18 +3301,17 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.name = "float"; itype.cname = itype.name; itype.proxy_name = "float"; + itype.cs_type = itype.proxy_name; { // The expected type for 'float' in ptrcall is 'double' - itype.c_in = "\t%0 %1_in = (%0)%1;\n"; - itype.c_out = "\treturn (%0)%1;\n"; + itype.c_in = "%5%0 %1_in = %1;\n"; + itype.c_out = "%5return (%0)%1;\n"; itype.c_type = "double"; - itype.c_type_in = "float"; - itype.c_type_out = "float"; itype.c_arg_in = "&%s_in"; } - itype.cs_type = itype.proxy_name; - itype.im_type_in = itype.proxy_name; - itype.im_type_out = itype.proxy_name; + itype.c_type_in = itype.proxy_name; + itype.c_type_out = itype.proxy_name; + itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromFloat(%1);\n"; builtin_types.insert(itype.cname, itype); // double @@ -3446,15 +3319,12 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.name = "double"; itype.cname = itype.name; itype.proxy_name = "double"; - { - itype.c_type = "double"; - itype.c_type_in = "double"; - itype.c_type_out = "double"; - itype.c_arg_in = "&%s"; - } itype.cs_type = itype.proxy_name; - itype.im_type_in = itype.proxy_name; - itype.im_type_out = itype.proxy_name; + itype.c_type = "double"; + itype.c_arg_in = "&%s"; + itype.c_type_in = itype.proxy_name; + itype.c_type_out = itype.proxy_name; + itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromFloat(%1);\n"; builtin_types.insert(itype.cname, itype); } @@ -3463,15 +3333,15 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.name = "String"; itype.cname = itype.name; itype.proxy_name = "string"; - itype.c_in = "\t%0 %1_in = " C_METHOD_MONOSTR_TO_GODOT "(%1);\n"; - itype.c_out = "\treturn " C_METHOD_MONOSTR_FROM_GODOT "(%1);\n"; - itype.c_arg_in = "&%s_in"; - itype.c_type = itype.name; - itype.c_type_in = "MonoString*"; - itype.c_type_out = "MonoString*"; itype.cs_type = itype.proxy_name; - itype.im_type_in = itype.proxy_name; - itype.im_type_out = itype.proxy_name; + itype.c_in = "%5using %0 %1_in = " C_METHOD_MONOSTR_TO_GODOT "(%1);\n"; + itype.c_out = "%5return " C_METHOD_MONOSTR_FROM_GODOT "(&%1);\n"; + itype.c_arg_in = "&%s_in"; + itype.c_type = "godot_string"; + itype.c_type_in = itype.cs_type; + itype.c_type_out = itype.cs_type; + itype.c_type_is_disposable_struct = true; + itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromStringTakingOwnershipOfDisposableValue(" C_METHOD_MONOSTR_TO_GODOT "(%1));\n"; builtin_types.insert(itype.cname, itype); // StringName @@ -3479,17 +3349,18 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.name = "StringName"; itype.cname = itype.name; itype.proxy_name = "StringName"; - itype.c_in = "\t%0 %1_in = %1 ? *%1 : StringName();\n"; - itype.c_out = "\treturn memnew(StringName(%1));\n"; - itype.c_arg_in = "&%s_in"; - itype.c_type = itype.name; - itype.c_type_in = itype.c_type + "*"; - itype.c_type_out = itype.c_type + "*"; itype.cs_type = itype.proxy_name; - itype.cs_in = "StringName." CS_SMETHOD_GETINSTANCE "(%0)"; - itype.cs_out = "return new %2(%0(%1));"; - itype.im_type_in = "IntPtr"; - itype.im_type_out = "IntPtr"; + itype.cs_in = "ref %0.NativeValue"; + // Cannot pass null StringName to ptrcall + itype.c_in = "%5using %0 %1_in = " C_CLASS_NATIVE_FUNCS ".godotsharp_string_name_new_copy(%1);\n"; + itype.c_out = "%5return %0.CreateTakingOwnershipOfDisposableValue(%1);\n"; + itype.c_arg_in = "&%s_in"; + itype.c_type = "godot_string_name"; + itype.c_type_in = "ref " + itype.c_type; + itype.c_type_out = itype.cs_type; + itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromStringName(ref %1);\n"; + itype.c_type_is_disposable_struct = false; // [c_out] takes ownership + itype.c_ret_needs_default_initialization = true; builtin_types.insert(itype.cname, itype); // NodePath @@ -3497,15 +3368,17 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.name = "NodePath"; itype.cname = itype.name; itype.proxy_name = "NodePath"; - itype.c_out = "\treturn memnew(NodePath(%1));\n"; - itype.c_type = itype.name; - itype.c_type_in = itype.c_type + "*"; - itype.c_type_out = itype.c_type + "*"; itype.cs_type = itype.proxy_name; - itype.cs_in = "NodePath." CS_SMETHOD_GETINSTANCE "(%0)"; - itype.cs_out = "return new %2(%0(%1));"; - itype.im_type_in = "IntPtr"; - itype.im_type_out = "IntPtr"; + itype.cs_in = "ref %0.NativeValue"; + // Cannot pass null NodePath to ptrcall + itype.c_in = "%5using %0 %1_in = " C_CLASS_NATIVE_FUNCS ".godotsharp_node_path_new_copy(%1);\n"; + itype.c_out = "%5return %0.CreateTakingOwnershipOfDisposableValue(%1);\n"; + itype.c_arg_in = "&%s_in"; + itype.c_type = "godot_node_path"; + itype.c_type_in = "ref " + itype.c_type; + itype.c_type_out = itype.cs_type; + itype.c_type_is_disposable_struct = false; // [c_out] takes ownership + itype.c_ret_needs_default_initialization = true; builtin_types.insert(itype.cname, itype); // RID @@ -3513,15 +3386,11 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.name = "RID"; itype.cname = itype.name; itype.proxy_name = "RID"; - itype.c_out = "\treturn memnew(RID(%1));\n"; - itype.c_type = itype.name; - itype.c_type_in = itype.c_type + "*"; - itype.c_type_out = itype.c_type + "*"; itype.cs_type = itype.proxy_name; - itype.cs_in = "RID." CS_SMETHOD_GETINSTANCE "(%0)"; - itype.cs_out = "return new %2(%0(%1));"; - itype.im_type_in = "IntPtr"; - itype.im_type_out = "IntPtr"; + itype.c_arg_in = "&%s"; + itype.c_type = itype.cs_type; + itype.c_type_in = itype.c_type; + itype.c_type_out = itype.c_type; builtin_types.insert(itype.cname, itype); // Variant @@ -3529,29 +3398,26 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.name = "Variant"; itype.cname = itype.name; itype.proxy_name = "object"; - itype.c_in = "\t%0 %1_in = " C_METHOD_MANAGED_TO_VARIANT "(%1);\n"; - itype.c_out = "\treturn " C_METHOD_MANAGED_FROM_VARIANT "(%1);\n"; - itype.c_arg_in = "&%s_in"; - itype.c_type = itype.name; - itype.c_type_in = "MonoObject*"; - itype.c_type_out = "MonoObject*"; itype.cs_type = itype.proxy_name; - itype.im_type_in = "object"; - itype.im_type_out = itype.proxy_name; + itype.c_in = "%5using %0 %1_in = " C_METHOD_MANAGED_TO_VARIANT "(%1);\n"; + itype.c_out = "%5return " C_METHOD_MANAGED_FROM_VARIANT "(&%1);\n"; + itype.c_arg_in = "&%s_in"; + itype.c_type = "godot_variant"; + itype.c_type_in = itype.cs_type; + itype.c_type_out = itype.cs_type; + itype.c_type_is_disposable_struct = true; builtin_types.insert(itype.cname, itype); // Callable itype = TypeInterface::create_value_type(String("Callable")); - itype.c_in = "\t%0 %1_in = " C_METHOD_MANAGED_TO_CALLABLE "(*%1);\n"; - itype.c_out = "\t*%3 = " C_METHOD_MANAGED_FROM_CALLABLE "(%1);\n"; - itype.c_arg_in = "&%s_in"; - itype.c_type_in = "GDMonoMarshal::M_Callable*"; - itype.c_type_out = "GDMonoMarshal::M_Callable"; itype.cs_in = "ref %s"; - /* in cs_out, im_type_out (%3) includes the 'out ' part */ - itype.cs_out = "%0(%1, %3 argRet); return argRet;"; - itype.im_type_out = "out " + itype.cs_type; - itype.ret_as_byref_arg = true; + itype.c_in = "%5using %0 %1_in = " C_METHOD_MANAGED_TO_CALLABLE "(ref %1);\n"; + itype.c_out = "%5return " C_METHOD_MANAGED_FROM_CALLABLE "(&%1);\n"; + itype.c_arg_in = "&%s_in"; + itype.c_type = "godot_callable"; + itype.c_type_in = "ref " + itype.cs_type; + itype.c_type_out = itype.cs_type; + itype.c_type_is_disposable_struct = true; builtin_types.insert(itype.cname, itype); // Signal @@ -3559,19 +3425,15 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.name = "Signal"; itype.cname = itype.name; itype.proxy_name = "SignalInfo"; - itype.c_in = "\t%0 %1_in = " C_METHOD_MANAGED_TO_SIGNAL "(*%1);\n"; - itype.c_out = "\t*%3 = " C_METHOD_MANAGED_FROM_SIGNAL "(%1);\n"; - itype.c_arg_in = "&%s_in"; - itype.c_type = itype.name; - itype.c_type_in = "GDMonoMarshal::M_SignalInfo*"; - itype.c_type_out = "GDMonoMarshal::M_SignalInfo"; - itype.cs_in = "ref %s"; - /* in cs_out, im_type_out (%3) includes the 'out ' part */ - itype.cs_out = "%0(%1, %3 argRet); return argRet;"; itype.cs_type = itype.proxy_name; - itype.im_type_in = "ref " + itype.cs_type; - itype.im_type_out = "out " + itype.cs_type; - itype.ret_as_byref_arg = true; + itype.cs_in = "ref %s"; + itype.c_in = "%5using %0 %1_in = " C_METHOD_MANAGED_TO_SIGNAL "(ref %1);\n"; + itype.c_out = "%5return " C_METHOD_MANAGED_FROM_SIGNAL "(&%1);\n"; + itype.c_arg_in = "&%s_in"; + itype.c_type = "godot_signal"; + itype.c_type_in = "ref " + itype.cs_type; + itype.c_type_out = itype.cs_type; + itype.c_type_is_disposable_struct = true; builtin_types.insert(itype.cname, itype); // VarArg (fictitious type to represent variable arguments) @@ -3579,46 +3441,44 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.name = "VarArg"; itype.cname = itype.name; itype.proxy_name = "object[]"; - itype.c_in = "\t%0 %1_in = " C_METHOD_MONOARRAY_TO(Array) "(%1);\n"; - itype.c_arg_in = "&%s_in"; - itype.c_type = "Array"; - itype.c_type_in = "MonoArray*"; itype.cs_type = "params object[]"; - itype.im_type_in = "object[]"; + // c_type, c_in and c_arg_in are hard-coded in the generator. + // c_out and c_type_out are not applicable to VarArg. + itype.c_arg_in = "&%s_in"; + itype.c_type_in = "object[]"; builtin_types.insert(itype.cname, itype); -#define INSERT_ARRAY_FULL(m_name, m_type, m_proxy_t) \ - { \ - itype = TypeInterface(); \ - itype.name = #m_name; \ - itype.cname = itype.name; \ - itype.proxy_name = #m_proxy_t "[]"; \ - itype.c_in = "\t%0 %1_in = " C_METHOD_MONOARRAY_TO(m_type) "(%1);\n"; \ - itype.c_out = "\treturn " C_METHOD_MONOARRAY_FROM(m_type) "(%1);\n"; \ - itype.c_arg_in = "&%s_in"; \ - itype.c_type = #m_type; \ - itype.c_type_in = "MonoArray*"; \ - itype.c_type_out = "MonoArray*"; \ - itype.cs_type = itype.proxy_name; \ - itype.im_type_in = itype.proxy_name; \ - itype.im_type_out = itype.proxy_name; \ - builtin_types.insert(itype.name, itype); \ +#define INSERT_ARRAY_FULL(m_name, m_type, m_managed_type, m_proxy_t) \ + { \ + itype = TypeInterface(); \ + itype.name = #m_name; \ + itype.cname = itype.name; \ + itype.proxy_name = #m_proxy_t "[]"; \ + itype.cs_type = itype.proxy_name; \ + itype.c_in = "%5using %0 %1_in = " C_METHOD_MONOARRAY_TO(m_type) "(%1);\n"; \ + itype.c_out = "%5return " C_METHOD_MONOARRAY_FROM(m_type) "(&%1);\n"; \ + itype.c_arg_in = "&%s_in"; \ + itype.c_type = #m_managed_type; \ + itype.c_type_in = itype.proxy_name; \ + itype.c_type_out = itype.proxy_name; \ + itype.c_type_is_disposable_struct = true; \ + builtin_types.insert(itype.name, itype); \ } -#define INSERT_ARRAY(m_type, m_proxy_t) INSERT_ARRAY_FULL(m_type, m_type, m_proxy_t) +#define INSERT_ARRAY(m_type, m_managed_type, m_proxy_t) INSERT_ARRAY_FULL(m_type, m_type, m_managed_type, m_proxy_t) - INSERT_ARRAY(PackedInt32Array, int); - INSERT_ARRAY(PackedInt64Array, long); - INSERT_ARRAY_FULL(PackedByteArray, PackedByteArray, byte); + INSERT_ARRAY(PackedInt32Array, godot_packed_int32_array, int); + INSERT_ARRAY(PackedInt64Array, godot_packed_int64_array, long); + INSERT_ARRAY_FULL(PackedByteArray, PackedByteArray, godot_packed_byte_array, byte); - INSERT_ARRAY(PackedFloat32Array, float); - INSERT_ARRAY(PackedFloat64Array, double); + INSERT_ARRAY(PackedFloat32Array, godot_packed_float32_array, float); + INSERT_ARRAY(PackedFloat64Array, godot_packed_float64_array, double); - INSERT_ARRAY(PackedStringArray, string); + INSERT_ARRAY(PackedStringArray, godot_packed_string_array, string); - INSERT_ARRAY(PackedColorArray, Color); - INSERT_ARRAY(PackedVector2Array, Vector2); - INSERT_ARRAY(PackedVector3Array, Vector3); + INSERT_ARRAY(PackedColorArray, godot_packed_color_array, Color); + INSERT_ARRAY(PackedVector2Array, godot_packed_vector2_array, Vector2); + INSERT_ARRAY(PackedVector3Array, godot_packed_vector3_array, Vector3); #undef INSERT_ARRAY @@ -3628,15 +3488,23 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.cname = itype.name; itype.proxy_name = itype.name; itype.type_parameter_count = 1; - itype.c_out = "\treturn memnew(Array(%1));\n"; - itype.c_type = itype.name; - itype.c_type_in = itype.c_type + "*"; - itype.c_type_out = itype.c_type + "*"; itype.cs_type = BINDINGS_NAMESPACE_COLLECTIONS "." + itype.proxy_name; - itype.cs_in = "%0." CS_SMETHOD_GETINSTANCE "()"; - itype.cs_out = "return new %2(%0(%1));"; - itype.im_type_in = "IntPtr"; - itype.im_type_out = "IntPtr"; + itype.cs_in = "ref %0.NativeValue"; + itype.c_in = "%5using %0 %1_in = " C_CLASS_NATIVE_FUNCS ".godotsharp_array_new_copy(%1);\n"; + itype.c_out = "%5return %0.CreateTakingOwnershipOfDisposableValue(%1);\n"; + itype.c_arg_in = "&%s_in"; + itype.c_type = "godot_array"; + itype.c_type_in = "ref " + itype.c_type; + itype.c_type_out = itype.cs_type; + itype.c_type_is_disposable_struct = false; // [c_out] takes ownership + itype.c_ret_needs_default_initialization = true; + builtin_types.insert(itype.cname, itype); + + // Array_@generic + // Re-use Array's itype + itype.name = "Array_@generic"; + itype.cname = itype.name; + itype.cs_out = "%5return new %2(%0(%1));"; builtin_types.insert(itype.cname, itype); // Dictionary @@ -3645,15 +3513,23 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.cname = itype.name; itype.proxy_name = itype.name; itype.type_parameter_count = 2; - itype.c_out = "\treturn memnew(Dictionary(%1));\n"; - itype.c_type = itype.name; - itype.c_type_in = itype.c_type + "*"; - itype.c_type_out = itype.c_type + "*"; itype.cs_type = BINDINGS_NAMESPACE_COLLECTIONS "." + itype.proxy_name; - itype.cs_in = "%0." CS_SMETHOD_GETINSTANCE "()"; - itype.cs_out = "return new %2(%0(%1));"; - itype.im_type_in = "IntPtr"; - itype.im_type_out = "IntPtr"; + itype.cs_in = "ref %0.NativeValue"; + itype.c_in = "%5using %0 %1_in = " C_CLASS_NATIVE_FUNCS ".godotsharp_dictionary_new_copy(%1);\n"; + itype.c_out = "%5return %0.CreateTakingOwnershipOfDisposableValue(%1);\n"; + itype.c_arg_in = "&%s_in"; + itype.c_type = "godot_dictionary"; + itype.c_type_in = "ref " + itype.c_type; + itype.c_type_out = itype.cs_type; + itype.c_type_is_disposable_struct = false; // [c_out] takes ownership + itype.c_ret_needs_default_initialization = true; + builtin_types.insert(itype.cname, itype); + + // Dictionary_@generic + // Re-use Dictionary's itype + itype.name = "Dictionary_@generic"; + itype.cname = itype.name; + itype.cs_out = "%5return new %2(%0(%1));"; builtin_types.insert(itype.cname, itype); // void (fictitious type to represent the return type of methods that do not return anything) @@ -3661,12 +3537,10 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.name = "void"; itype.cname = itype.name; itype.proxy_name = itype.name; - itype.c_type = itype.name; + itype.cs_type = itype.proxy_name; + itype.c_type = itype.proxy_name; itype.c_type_in = itype.c_type; itype.c_type_out = itype.c_type; - itype.cs_type = itype.proxy_name; - itype.im_type_in = itype.proxy_name; - itype.im_type_out = itype.proxy_name; builtin_types.insert(itype.cname, itype); } @@ -3791,21 +3665,18 @@ void BindingsGenerator::_initialize() { // Generate internal calls (after populating type interfaces and global constants) - core_custom_icalls.clear(); - editor_custom_icalls.clear(); - for (const KeyValue &E : obj_types) { - _generate_method_icalls(E.value); + const TypeInterface &itype = E.value; + Error err = _populate_method_icalls_table(itype); + ERR_FAIL_COND_MSG(err != OK, "Failed to generate icalls table for type: " + itype.name); } initialized = true; } static String generate_all_glue_option = "--generate-mono-glue"; -static String generate_cs_glue_option = "--generate-mono-cs-glue"; -static String generate_cpp_glue_option = "--generate-mono-cpp-glue"; -static void handle_cmdline_options(String glue_dir_path, String cs_dir_path, String cpp_dir_path) { +static void handle_cmdline_options(String glue_dir_path) { BindingsGenerator bindings_generator; bindings_generator.set_log_print_enabled(true); @@ -3814,43 +3685,25 @@ static void handle_cmdline_options(String glue_dir_path, String cs_dir_path, Str return; } - if (glue_dir_path.length()) { - if (bindings_generator.generate_glue(glue_dir_path) != OK) { - ERR_PRINT(generate_all_glue_option + ": Failed to generate the C++ glue."); - } + 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(); @@ -3859,48 +3712,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 ee170e45581..1e1e8be1784 100644 --- a/modules/mono/editor/bindings_generator.h +++ b/modules/mono/editor/bindings_generator.h @@ -228,6 +228,23 @@ class BindingsGenerator { bool is_singleton = false; bool is_ref_counted = false; + /** + * Determines whether the native return value of this type must be disposed + * by the generated internal call (think of `godot_string`, whose destructor + * must be called). Some structs that are disposable may still disable this + * flag if the ownership is transferred. + */ + bool c_type_is_disposable_struct = false; + + /** + * Determines whether the native return value of this type must be zero initialized + * before its address is passed to ptrcall. This is required for types whose destructor + * is called before being assigned the return value by `PtrToArg::encode`, e.g.: + * Array, Dictionary, String, StringName. + * It's not necessary to set this to `true` if [c_type_is_disposable_struct] is already `true`. + */ + bool c_ret_needs_default_initialization = false; + /** * Used only by Object-derived types. * Determines if this type is not abstract (incomplete). @@ -242,32 +259,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, @@ -291,7 +311,8 @@ class BindingsGenerator { * %0: [c_type_out] of the return type * %1: name of the variable to be returned * %2: [name] of the return type - * %3: name of the parameter that must be assigned the return value + * %3-4: reserved + * %5: indentation text */ String c_out; @@ -330,6 +351,7 @@ class BindingsGenerator { * %0 or %s: name of the parameter */ String cs_in; + bool cs_in_is_unsafe = false; /** * One or more statements that determine how a variable of this type is returned from a method. @@ -338,7 +360,9 @@ class BindingsGenerator { * %0: internal method name * %1: internal method call arguments without surrounding parenthesis * %2: [cs_type] of the return type - * %3: [im_type_out] of the return type + * %3: [c_type_out] of the return type + * %4: reserved + * %5: indentation text */ String cs_out; @@ -348,16 +372,6 @@ class BindingsGenerator { */ String cs_type; - /** - * Type used for parameters of internal call methods. - */ - String im_type_in; - - /** - * Type used for the return type of internal call methods. - */ - String im_type_out; - const DocData::ClassDoc *class_doc = nullptr; List constants; @@ -432,8 +446,8 @@ class BindingsGenerator { itype.c_type = itype.name; itype.cs_type = itype.proxy_name; - itype.im_type_in = "ref " + itype.proxy_name; - itype.im_type_out = itype.proxy_name; + itype.c_type_in = itype.proxy_name + "*"; + itype.c_type_out = itype.proxy_name; itype.class_doc = &EditorHelp::get_doc_data()->class_list[itype.proxy_name]; } @@ -467,65 +481,27 @@ class BindingsGenerator { return itype; } - static void create_placeholder_type(TypeInterface &r_itype, const StringName &p_cname) { - r_itype.name = p_cname; - r_itype.cname = p_cname; - r_itype.proxy_name = r_itype.name; - - r_itype.c_type = r_itype.name; - r_itype.c_type_in = "MonoObject*"; - r_itype.c_type_out = "MonoObject*"; - r_itype.cs_type = r_itype.proxy_name; - r_itype.im_type_in = r_itype.proxy_name; - r_itype.im_type_out = r_itype.proxy_name; - } - - static void postsetup_enum_type(TypeInterface &r_enum_itype) { - // C interface for enums is the same as that of 'uint32_t'. Remember to apply - // any of the changes done here to the 'uint32_t' type interface as well. - - r_enum_itype.c_arg_in = "&%s_in"; - { - // The expected types for parameters and return value in ptrcall are 'int64_t' or 'uint64_t'. - r_enum_itype.c_in = "\t%0 %1_in = (%0)%1;\n"; - r_enum_itype.c_out = "\treturn (%0)%1;\n"; - r_enum_itype.c_type = "int64_t"; - } - r_enum_itype.c_type_in = "int32_t"; - r_enum_itype.c_type_out = r_enum_itype.c_type_in; - - r_enum_itype.cs_type = r_enum_itype.proxy_name; - r_enum_itype.cs_in = "(int)%s"; - r_enum_itype.cs_out = "return (%2)%0(%1);"; - r_enum_itype.im_type_in = "int"; - r_enum_itype.im_type_out = "int"; - r_enum_itype.class_doc = &EditorHelp::get_doc_data()->class_list[r_enum_itype.proxy_name]; - } + static void postsetup_enum_type(TypeInterface &r_enum_itype); TypeInterface() {} }; struct InternalCall { String name; - String im_type_out; // Return type for the C# method declaration. Also used as companion of [unique_siq] - String im_sig; // Signature for the C# method declaration String unique_sig; // Unique signature to avoid duplicates in containers bool editor_only = false; + bool is_vararg = false; + bool is_static = false; + 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; } @@ -540,7 +516,6 @@ class BindingsGenerator { HashMap obj_types; - HashMap placeholder_types; HashMap builtin_types; HashMap enum_types; @@ -548,13 +523,9 @@ class BindingsGenerator { List global_constants; List method_icalls; + /// Stores the unique internal calls from [method_icalls] that are assigned to each method. HashMap method_icalls_map; - List generated_icall_funcs; - - List core_custom_icalls; - List editor_custom_icalls; - HashMap> blacklisted_methods; void _initialize_blacklisted_methods(); @@ -571,6 +542,8 @@ class BindingsGenerator { StringName type_String = StaticCString::create("String"); StringName type_StringName = StaticCString::create("StringName"); StringName type_NodePath = StaticCString::create("NodePath"); + StringName type_Array_generic = StaticCString::create("Array_@generic"); + StringName type_Dictionary_generic = StaticCString::create("Dictionary_@generic"); StringName type_at_GlobalScope = StaticCString::create("@GlobalScope"); StringName enum_Error = StaticCString::create("Error"); @@ -595,12 +568,14 @@ class BindingsGenerator { StringName type_Vector4i = StaticCString::create("Vector4i"); // Object not included as it must be checked for all derived classes - static constexpr int nullable_types_count = 17; + static constexpr int nullable_types_count = 18; StringName nullable_types[nullable_types_count] = { type_String, type_StringName, type_NodePath, + type_Array_generic, + type_Dictionary_generic, StaticCString::create(_STR(Array)), StaticCString::create(_STR(Dictionary)), StaticCString::create(_STR(Callable)), @@ -636,17 +611,6 @@ class BindingsGenerator { NameCache name_cache; - const List::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) { @@ -657,18 +621,38 @@ class BindingsGenerator { return nullptr; } - inline String get_unique_sig(const TypeInterface &p_type) { - if (p_type.is_ref_counted) { - return "Ref"; - } else if (p_type.is_object_type) { + inline String get_arg_unique_sig(const TypeInterface &p_type) { + // For parameters, we treat reference and non-reference derived types the same. + if (p_type.is_object_type) { return "Obj"; } else if (p_type.is_enum) { return "int"; + } else if (p_type.cname == name_cache.type_Array_generic) { + return "Array"; + } else if (p_type.cname == name_cache.type_Dictionary_generic) { + return "Dictionary"; } return p_type.name; } + inline String get_ret_unique_sig(const TypeInterface *p_type) { + // Reference derived return types are treated differently. + if (p_type->is_ref_counted) { + return "Ref"; + } else if (p_type->is_object_type) { + return "Obj"; + } else if (p_type->is_enum) { + return "int"; + } else if (p_type->cname == name_cache.type_Array_generic) { + return "Array"; + } else if (p_type->cname == name_cache.type_Dictionary_generic) { + return "Dictionary"; + } + + return p_type->name; + } + String bbcode_to_xml(const String &p_bbcode, const TypeInterface *p_itype); void _append_xml_method(StringBuilder &p_xml_output, const TypeInterface *p_target_itype, const StringName &p_target_cname, const String &p_link_target, const Vector &p_link_target_parts); @@ -682,10 +666,9 @@ class BindingsGenerator { int _determine_enum_prefix(const EnumInterface &p_ienum); void _apply_prefix_to_enum_constants(EnumInterface &p_ienum, int p_prefix_length); - void _generate_method_icalls(const TypeInterface &p_itype); + Error _populate_method_icalls_table(const TypeInterface &p_itype); const TypeInterface *_get_type_or_null(const TypeReference &p_typeref); - const TypeInterface *_get_type_or_placeholder(const TypeReference &p_typeref); const String _get_generic_type_parameters(const TypeInterface &p_itype, const List &p_generic_type_parameters); @@ -706,11 +689,11 @@ class BindingsGenerator { Error _generate_cs_method(const TypeInterface &p_itype, const MethodInterface &p_imethod, int &p_method_bind_count, StringBuilder &p_output); Error _generate_cs_signal(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::SignalInterface &p_isignal, StringBuilder &p_output); + Error _generate_cs_native_calls(const InternalCall &p_icall, StringBuilder &r_output); + void _generate_array_extensions(StringBuilder &p_output); void _generate_global_constants(StringBuilder &p_output); - Error _generate_glue_method(const TypeInterface &p_itype, const MethodInterface &p_imethod, StringBuilder &p_output); - Error _save_file(const String &p_path, const StringBuilder &p_content); void _log(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3; @@ -721,15 +704,12 @@ public: Error generate_cs_core_project(const String &p_proj_dir); Error generate_cs_editor_project(const String &p_proj_dir); Error generate_cs_api(const String &p_output_dir); - Error generate_glue(const String &p_output_dir); _FORCE_INLINE_ bool is_log_print_enabled() { return log_print_enabled; } _FORCE_INLINE_ void set_log_print_enabled(bool p_enabled) { log_print_enabled = p_enabled; } _FORCE_INLINE_ bool is_initialized() { return initialized; } - static uint32_t get_version(); - static void handle_cmdline_args(const List &p_cmdline_args); BindingsGenerator() { diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp index f830c7ffe1c..102d27378fa 100644 --- a/modules/mono/editor/editor_internal_calls.cpp +++ b/modules/mono/editor/editor_internal_calls.cpp @@ -46,7 +46,6 @@ #include "main/main.h" #include "../csharp_script.h" -#include "../glue/cs_glue_version.gen.h" #include "../godotsharp_dirs.h" #include "../mono_gd/gd_mono_marshal.h" #include "../utils/macos_utils.h" diff --git a/modules/mono/glue/GodotSharp/GodotSharp.sln.DotSettings b/modules/mono/glue/GodotSharp/GodotSharp.sln.DotSettings new file mode 100644 index 00000000000..3103fa78c7d --- /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 a4120471967..c32895baab1 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs @@ -1,47 +1,28 @@ using System; using System.Collections.Generic; using System.Collections; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; +using Godot.NativeInterop; namespace Godot.Collections { - internal class ArraySafeHandle : SafeHandle - { - public ArraySafeHandle(IntPtr handle) : base(IntPtr.Zero, true) - { - this.handle = handle; - } - - public override bool IsInvalid - { - get { return handle == IntPtr.Zero; } - } - - protected override bool ReleaseHandle() - { - Array.godot_icall_Array_Dtor(handle); - return true; - } - } - /// /// 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 { - private ArraySafeHandle _safeHandle; - private 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 . 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 /// 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 static extern IntPtr godot_icall_Array_Ctor(); + internal static extern void godot_icall_Array_Ctor(out godot_array dest); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern 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 static extern 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 static extern 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 static extern 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 static extern 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 static extern int godot_icall_Array_Count(IntPtr ptr); + internal static extern void godot_icall_Array_Clear(ref godot_array ptr); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern 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 static extern 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 static extern 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 static extern 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 static extern 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 static extern 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 static extern 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 static extern 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 static extern 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 static extern 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 static extern Error godot_icall_Array_Resize(IntPtr ptr, int newSize); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern Error godot_icall_Array_Shuffle(IntPtr ptr); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_Generic_GetElementTypeInfo(Type elemType, out int elemTypeEncoding, out IntPtr elemTypeClass); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern 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,29 @@ 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 { - private Array _objectArray; + private readonly Array _underlyingArray; - internal static int elemTypeEncoding; - internal static IntPtr elemTypeClass; + internal ref godot_array NativeValue => ref _underlyingArray.NativeValue; - static Array() - { - Array.godot_icall_Array_Generic_GetElementTypeInfo(typeof(T), out elemTypeEncoding, out elemTypeClass); - } + // ReSharper disable StaticMemberInGenericType + // Warning is about unique static fields being created for each generic type combination: + // https://www.jetbrains.com/help/resharper/StaticMemberInGenericType.html + // In our case this is exactly what we want. + private static readonly Type TypeOfElements = typeof(T); + // ReSharper restore StaticMemberInGenericType + + Array IGenericGodotArray.UnderlyingArray => _underlyingArray; + Type IGenericGodotArray.TypeOfElements => TypeOfElements; /// /// Constructs a new empty . /// public Array() { - _objectArray = new Array(); + _underlyingArray = new Array(); } /// @@ -372,7 +362,7 @@ namespace Godot.Collections if (collection == null) throw new NullReferenceException($"Parameter '{nameof(collection)} cannot be null.'"); - _objectArray = new Array(collection); + _underlyingArray = new Array(collection); } /// @@ -386,7 +376,8 @@ namespace Godot.Collections { throw new NullReferenceException($"Parameter '{nameof(array)} cannot be null.'"); } - _objectArray = new Array(array); + + _underlyingArray = new Array(array); } /// @@ -395,23 +386,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 +399,7 @@ namespace Godot.Collections /// The typed array to convert. public static explicit operator Array(Array from) { - return from._objectArray; + return from._underlyingArray; } /// @@ -429,7 +409,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 +419,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 +427,7 @@ namespace Godot.Collections /// public void Shuffle() { - _objectArray.Shuffle(); + _underlyingArray.Shuffle(); } /// @@ -458,7 +438,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 +449,16 @@ namespace Godot.Collections /// The value at the given . 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 +469,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 +482,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 +491,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 +501,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 +513,7 @@ namespace Godot.Collections /// The new size after adding the item. public void Add(T item) { - _objectArray.Add(item); + _underlyingArray.Add(item); } /// @@ -536,7 +521,7 @@ namespace Godot.Collections /// public void Clear() { - _objectArray.Clear(); + _underlyingArray.Clear(); } /// @@ -546,7 +531,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 +548,14 @@ namespace Godot.Collections if (arrayIndex < 0) throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Number was less than the array's lower bound in the first dimension."); - // TODO This may be quite slow because every element access is an internal call. - // It could be moved entirely to an internal call if we find out how to do the cast there. - - int count = _objectArray.Count; + int count = _underlyingArray.Count; if (array.Length < (arrayIndex + count)) throw new ArgumentException("Destination array was not long enough. Check destIndex and length, and the array's lower bounds."); for (int i = 0; i < count; i++) { - array[arrayIndex] = (T)this[i]; + array[arrayIndex] = this[i]; arrayIndex++; } } @@ -586,7 +568,7 @@ namespace Godot.Collections /// A 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 +579,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/DelegateUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs index 1dca9e6ea74..187d910f9f9 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 e80b6af68f8..d0c7e4523b7 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs @@ -2,46 +2,28 @@ using System; using System.Collections.Generic; using System.Collections; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; +using Godot.NativeInterop; using System.Diagnostics.CodeAnalysis; namespace Godot.Collections { - internal class DictionarySafeHandle : SafeHandle - { - public DictionarySafeHandle(IntPtr handle) : base(IntPtr.Zero, true) - { - this.handle = handle; - } - - public override bool IsInvalid - { - get { return handle == IntPtr.Zero; } - } - - protected override bool ReleaseHandle() - { - Dictionary.godot_icall_Dictionary_Dtor(handle); - return true; - } - } - /// /// 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 : IDictionary, IDisposable + public sealed class Dictionary : + IDictionary, + IDisposable { - private DictionarySafeHandle _safeHandle; - private 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); } /// @@ -58,22 +40,18 @@ namespace Godot.Collections Add(entry.Key, entry.Value); } - internal Dictionary(DictionarySafeHandle handle) + private Dictionary(godot_dictionary nativeValueToOwn) { - _safeHandle = handle; + NativeValue = nativeValueToOwn; } - internal Dictionary(IntPtr handle) - { - _safeHandle = new DictionarySafeHandle(handle); - } + // Explicit name to make it very clear + internal static Dictionary CreateTakingOwnershipOfDisposableValue(godot_dictionary nativeValueToOwn) + => new Dictionary(nativeValueToOwn); - internal IntPtr GetPtr() + ~Dictionary() { - if (_disposed) - throw new ObjectDisposedException(GetType().FullName); - - return _safeHandle.DangerousGetHandle(); + Dispose(false); } /// @@ -81,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(); } /// @@ -100,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 @@ -112,8 +90,9 @@ namespace Godot.Collections { get { - IntPtr handle = godot_icall_Dictionary_Keys(GetPtr()); - return new Array(new ArraySafeHandle(handle)); + godot_array keysArray; + godot_icall_Dictionary_Keys(ref NativeValue, out keysArray); + return Array.CreateTakingOwnershipOfDisposableValue(keysArray); } } @@ -124,16 +103,19 @@ namespace Godot.Collections { get { - IntPtr handle = godot_icall_Dictionary_Values(GetPtr()); - return new Array(new ArraySafeHandle(handle)); + godot_array valuesArray; + godot_icall_Dictionary_Values(ref NativeValue, out valuesArray); + return Array.CreateTakingOwnershipOfDisposableValue(valuesArray); } } private (Array keys, Array values, int count) GetKeyValuePairs() { - int count = godot_icall_Dictionary_KeyValuePairs(GetPtr(), out IntPtr keysHandle, out IntPtr valuesHandle); - Array keys = new Array(new ArraySafeHandle(keysHandle)); - Array values = new Array(new ArraySafeHandle(valuesHandle)); + godot_array keysArray; + godot_array valuesArray; + int count = godot_icall_Dictionary_KeyValuePairs(ref NativeValue, out keysArray, out valuesArray); + var keys = Array.CreateTakingOwnershipOfDisposableValue(keysArray); + var values = Array.CreateTakingOwnershipOfDisposableValue(valuesArray); return (keys, values, count); } @@ -147,8 +129,16 @@ namespace Godot.Collections /// 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); } /// @@ -157,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 . @@ -181,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 @@ -194,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 @@ -258,8 +248,17 @@ namespace Godot.Collections private void UpdateEntry() { _dirty = false; - godot_icall_Dictionary_KeyValuePairAt(_dictionary.GetPtr(), _index, out object key, out object value); - _entry = new DictionaryEntry(key, value); + godot_icall_Dictionary_KeyValuePairAt(ref _dictionary.NativeValue, _index, out var vKey, out var vValue); + unsafe + { + using (vKey) + using (vValue) + { + var key = Marshaling.variant_to_mono_object(&vKey); + var value = Marshaling.variant_to_mono_object(&vValue); + _entry = new DictionaryEntry(key, value); + } + } } public object Key => Entry.Key; @@ -286,76 +285,70 @@ namespace Godot.Collections /// 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 static extern IntPtr godot_icall_Dictionary_Ctor(); + internal static extern void godot_icall_Dictionary_Ctor(out godot_dictionary dest); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern 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 static extern 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 static extern 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 static extern 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 static extern 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 static extern IntPtr godot_icall_Dictionary_Values(IntPtr ptr); + internal static extern void godot_icall_Dictionary_KeyValuePairAt(ref godot_dictionary ptr, int index, out godot_variant key, out godot_variant value); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern int godot_icall_Dictionary_Count(IntPtr ptr); + internal static extern void godot_icall_Dictionary_Add(ref godot_dictionary ptr, object key, object value); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern 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 static extern 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 static extern void godot_icall_Dictionary_KeyValuePairAt_Generic(IntPtr ptr, int index, out object key, out object value, int valueTypeEncoding, IntPtr valueTypeClass); + internal static extern bool godot_icall_Dictionary_Contains(ref godot_dictionary ptr, object key, object value); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_Add(IntPtr ptr, object key, object value); + internal static extern bool godot_icall_Dictionary_ContainsKey(ref godot_dictionary ptr, object key); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_Clear(IntPtr ptr); + internal static extern void godot_icall_Dictionary_Duplicate(ref godot_dictionary ptr, bool deep, out godot_dictionary dest); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_Dictionary_Contains(IntPtr ptr, object key, object value); + internal static extern bool godot_icall_Dictionary_RemoveKey(ref godot_dictionary ptr, object key); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_Dictionary_ContainsKey(IntPtr ptr, object key); + internal static extern bool godot_icall_Dictionary_Remove(ref godot_dictionary ptr, object key, object value); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern IntPtr godot_icall_Dictionary_Duplicate(IntPtr ptr, bool deep); + internal static extern bool godot_icall_Dictionary_TryGetValue(ref godot_dictionary ptr, object key, out godot_variant value); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_Dictionary_RemoveKey(IntPtr ptr, object key); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_Dictionary_Remove(IntPtr ptr, object key, object value); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_Dictionary_TryGetValue(IntPtr ptr, object key, out object value); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_Dictionary_TryGetValue_Generic(IntPtr ptr, object key, out object value, int valTypeEncoding, IntPtr valTypeClass); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_Generic_GetValueTypeInfo(Type valueType, out int valTypeEncoding, out IntPtr valTypeClass); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern 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 @@ -364,24 +357,32 @@ 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; + internal ref godot_dictionary NativeValue => ref _underlyingDict.NativeValue; - static Dictionary() - { - Dictionary.godot_icall_Dictionary_Generic_GetValueTypeInfo(typeof(TValue), out valTypeEncoding, out valTypeClass); - } + // ReSharper disable StaticMemberInGenericType + // Warning is about unique static fields being created for each generic type combination: + // https://www.jetbrains.com/help/resharper/StaticMemberInGenericType.html + // In our case this is exactly what we want. + private static readonly Type TypeOfKeys = typeof(TKey); + + private static readonly Type TypeOfValues = typeof(TValue); + // ReSharper restore StaticMemberInGenericType + + Dictionary IGenericGodotDictionary.UnderlyingDictionary => _underlyingDict; + Type IGenericGodotDictionary.TypeOfKeys => TypeOfKeys; + Type IGenericGodotDictionary.TypeOfValues => TypeOfValues; /// /// Constructs a new empty . /// public Dictionary() { - _objectDict = new Dictionary(); + _underlyingDict = new Dictionary(); } /// @@ -391,19 +392,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 +408,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 +421,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 +431,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 +442,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 +461,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,15 +474,25 @@ 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_Generic(GetPtr(), index, out object key, out object value, valTypeEncoding, valTypeClass); - return new KeyValuePair((TKey)key, (TValue)value); + Dictionary.godot_icall_Dictionary_KeyValuePairAt(ref _underlyingDict.NativeValue, index, out var vKey, out var vValue); + unsafe + { + using (vKey) + using (vValue) + { + var key = Marshaling.variant_to_mono_object_of_type(&vKey, TypeOfKeys); + var value = Marshaling.variant_to_mono_object_of_type(&vValue, TypeOfValues); + return new KeyValuePair((TKey)key, (TValue)value); + } + } } /// @@ -500,7 +503,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 +513,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 +522,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 +533,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 +555,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 +569,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 +605,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 +622,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 df130a5c774..7922f38ac57 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using Godot.Collections; +using Godot.NativeInterop; namespace Godot { @@ -12,10 +13,12 @@ namespace Godot /// The type to cast to. Should be a descendant of . public Array GetNodesInGroup(StringName group) where T : class { - return new Array(godot_icall_SceneTree_get_nodes_in_group_Generic(GetPtr(this), StringName.GetPtr(group), typeof(T))); + godot_array array; + godot_icall_SceneTree_get_nodes_in_group_Generic(GetPtr(this), ref group.NativeValue, typeof(T), out array); + return Array.CreateTakingOwnershipOfDisposableValue(array); } [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern IntPtr godot_icall_SceneTree_get_nodes_in_group_Generic(IntPtr obj, IntPtr group, Type elemType); + internal static extern void godot_icall_SceneTree_get_nodes_in_group_Generic(IntPtr obj, ref godot_string_name group, Type elemType, out godot_array dest); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs index 236d0666bc1..e8ea8f379bd 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs @@ -6,6 +6,7 @@ using real_t = System.Single; using System; using System.Collections.Generic; using System.Runtime.CompilerServices; +using Godot.NativeInterop; namespace Godot { @@ -26,9 +27,10 @@ namespace Godot /// Byte array that will be decoded to a Variant. /// If objects should be decoded. /// The decoded Variant. - 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); } /// @@ -527,7 +529,7 @@ namespace Godot /// If the class exists in . public static bool TypeExists(StringName type) { - return godot_icall_GD_type_exists(StringName.GetPtr(type)); + return godot_icall_GD_type_exists(ref type.NativeValue); } /// @@ -539,9 +541,14 @@ namespace Godot /// Variant that will be encoded. /// If objects should be serialized. /// The Variant encoded as an array of bytes. - 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); + } } /// @@ -576,7 +583,7 @@ namespace Godot } [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern object godot_icall_GD_bytes2var(byte[] bytes, bool allowObjects); + internal static extern unsafe object godot_icall_GD_bytes2var(godot_packed_byte_array* bytes, bool allowObjects); [MethodImpl(MethodImplOptions.InternalCall)] internal static extern object godot_icall_GD_convert(object what, Variant.Type type); @@ -636,10 +643,10 @@ namespace Godot internal static extern object godot_icall_GD_str2var(string str); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_GD_type_exists(IntPtr type); + internal static extern bool godot_icall_GD_type_exists(ref godot_string_name type); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern byte[] godot_icall_GD_var2bytes(object what, bool fullObjects); + internal static extern unsafe void godot_icall_GD_var2bytes(object what, bool fullObjects, godot_packed_byte_array* bytes); [MethodImpl(MethodImplOptions.InternalCall)] internal static extern string godot_icall_GD_var2str(object var); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs index 50ae2eb1125..733a8ac1143 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs @@ -5,150 +5,9 @@ namespace Godot { internal static class MarshalUtils { - /// - /// Returns if the generic type definition of - /// is ; otherwise returns . - /// - /// - /// Thrown when the given is not a generic type. - /// That is, returns . - /// - private static bool TypeIsGenericArray(Type type) => - type.GetGenericTypeDefinition() == typeof(Collections.Array<>); - - /// - /// Returns if the generic type definition of - /// is ; otherwise returns . - /// - /// - /// Thrown when the given is not a generic type. - /// That is, returns . - /// - private static bool TypeIsGenericDictionary(Type type) => - type.GetGenericTypeDefinition() == typeof(Collections.Dictionary<,>); - - /// - /// Returns if the generic type definition of - /// is ; otherwise returns . - /// - /// - /// Thrown when the given is not a generic type. - /// That is, returns . - /// - private static bool TypeIsSystemGenericList(Type type) => - type.GetGenericTypeDefinition() == typeof(List<>); - - /// - /// Returns if the generic type definition of - /// is ; otherwise returns . - /// - /// - /// Thrown when the given is not a generic type. - /// That is, returns . - /// - private static bool TypeIsSystemGenericDictionary(Type type) => - type.GetGenericTypeDefinition() == typeof(Dictionary<,>); - - /// - /// Returns if the generic type definition of - /// is ; otherwise returns . - /// - /// - /// Thrown when the given is not a generic type. - /// That is, returns . - /// - private static bool TypeIsGenericIEnumerable(Type type) => type.GetGenericTypeDefinition() == typeof(IEnumerable<>); - - /// - /// Returns if the generic type definition of - /// is ; otherwise returns . - /// - /// - /// Thrown when the given is not a generic type. - /// That is, returns . - /// - private static bool TypeIsGenericICollection(Type type) => type.GetGenericTypeDefinition() == typeof(ICollection<>); - - /// - /// Returns if the generic type definition of - /// is ; otherwise returns . - /// - /// - /// Thrown when the given is not a generic type. - /// That is, returns . - /// - private static bool TypeIsGenericIDictionary(Type type) => type.GetGenericTypeDefinition() == typeof(IDictionary<,>); - /// /// Returns if the is applied to the given type. /// private static bool TypeHasFlagsAttribute(Type type) => type.IsDefined(typeof(FlagsAttribute), false); - - /// - /// Returns the generic type definition of . - /// - /// - /// Thrown when the given is not a generic type. - /// That is, returns . - /// - private static void GetGenericTypeDefinition(Type type, out Type genericTypeDefinition) - { - genericTypeDefinition = type.GetGenericTypeDefinition(); - } - - /// - /// Gets the element type for the given . - /// - /// Type for the generic array. - /// Element type for the generic array. - /// - /// Thrown when the given is not a generic type. - /// That is, returns . - /// - private static void ArrayGetElementType(Type arrayType, out Type elementType) - { - elementType = arrayType.GetGenericArguments()[0]; - } - - /// - /// Gets the key type and the value type for the given . - /// - /// The type for the generic dictionary. - /// Key type for the generic dictionary. - /// Value type for the generic dictionary. - /// - /// Thrown when the given is not a generic type. - /// That is, returns . - /// - private static void DictionaryGetKeyValueTypes(Type dictionaryType, out Type keyType, out Type valueType) - { - var genericArgs = dictionaryType.GetGenericArguments(); - keyType = genericArgs[0]; - valueType = genericArgs[1]; - } - - /// - /// Constructs a new from - /// where the generic type for the elements is . - /// - /// Element type for the array. - /// The generic array type with the specified element type. - private static Type MakeGenericArrayType(Type elemType) - { - return typeof(Collections.Array<>).MakeGenericType(elemType); - } - - /// - /// Constructs a new from - /// where the generic type for the keys is and - /// for the values is . - /// - /// Key type for the dictionary. - /// Key type for the dictionary. - /// The generic dictionary type with the specified key and value types. - private static Type MakeGenericDictionaryType(Type keyType, Type valueType) - { - return typeof(Collections.Dictionary<,>).MakeGenericType(keyType, valueType); - } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs new file mode 100644 index 00000000000..6ee8cbc0bc6 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs @@ -0,0 +1,438 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; + +#endif + +namespace Godot.NativeInterop +{ + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_bool + { + public byte _value; + + public unsafe godot_bool(bool value) => _value = *(byte*)&value; + + public static unsafe implicit operator bool(godot_bool godotBool) => *(bool*)&godotBool._value; + public static implicit operator godot_bool(bool @bool) => new godot_bool(@bool); + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_ref : IDisposable + { + internal IntPtr _reference; + + public void Dispose() + { + if (_reference == IntPtr.Zero) + return; + NativeFuncs.godotsharp_ref_destroy(ref this); + _reference = IntPtr.Zero; + } + } + + [SuppressMessage("ReSharper", "InconsistentNaming")] + internal enum godot_variant_call_error_error + { + GODOT_CALL_ERROR_CALL_OK = 0, + GODOT_CALL_ERROR_CALL_ERROR_INVALID_METHOD, + GODOT_CALL_ERROR_CALL_ERROR_INVALID_ARGUMENT, + GODOT_CALL_ERROR_CALL_ERROR_TOO_MANY_ARGUMENTS, + GODOT_CALL_ERROR_CALL_ERROR_TOO_FEW_ARGUMENTS, + GODOT_CALL_ERROR_CALL_ERROR_INSTANCE_IS_NULL, + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_variant_call_error + { + godot_variant_call_error_error error; + int argument; + int expected; + } + + [StructLayout(LayoutKind.Explicit)] + // ReSharper disable once InconsistentNaming + internal struct godot_variant : IDisposable + { + // Variant.Type is generated as an enum of type long, so we can't use for the field as it must only take 32-bits. + [FieldOffset(0)] private int _typeField; + + // There's padding here + + [FieldOffset(8)] internal godot_variant_data _data; + + public Variant.Type _type + { + get => (Variant.Type)_typeField; + set => _typeField = (int)value; + } + + [StructLayout(LayoutKind.Explicit)] + // ReSharper disable once InconsistentNaming + internal unsafe struct godot_variant_data + { + [FieldOffset(0)] public godot_bool _bool; + [FieldOffset(0)] public long _int; + [FieldOffset(0)] public double _float; + [FieldOffset(0)] public Transform2D* _transform2d; + [FieldOffset(0)] public AABB* _aabb; + [FieldOffset(0)] public Basis* _basis; + [FieldOffset(0)] public Transform3D* _transform3d; + [FieldOffset(0)] public Vector4* _vector4; + [FieldOffset(0)] public Vector4i* _vector4i; + [FieldOffset(0)] public Projection* _projection; + [FieldOffset(0)] private godot_variant_data_mem _mem; + + // The following fields are not in the C++ union, but this is how they're stored in _mem. + [FieldOffset(0)] public godot_string_name _m_string_name; + [FieldOffset(0)] public godot_string _m_string; + [FieldOffset(0)] public Vector3 _m_vector3; + [FieldOffset(0)] public Vector3i _m_vector3i; + [FieldOffset(0)] public Vector2 _m_vector2; + [FieldOffset(0)] public Vector2i _m_vector2i; + [FieldOffset(0)] public Rect2 _m_rect2; + [FieldOffset(0)] public Rect2i _m_rect2i; + [FieldOffset(0)] public Plane _m_plane; + [FieldOffset(0)] public Quaternion _m_quaternion; + [FieldOffset(0)] public Color _m_color; + [FieldOffset(0)] public godot_node_path _m_node_path; + [FieldOffset(0)] public RID _m_rid; + [FieldOffset(0)] public godot_variant_obj_data _m_obj_data; + [FieldOffset(0)] public godot_callable _m_callable; + [FieldOffset(0)] public godot_signal _m_signal; + [FieldOffset(0)] public godot_dictionary _m_dictionary; + [FieldOffset(0)] public godot_array _m_array; + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_variant_obj_data + { + public UInt64 id; + public IntPtr obj; + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + private struct godot_variant_data_mem + { +#pragma warning disable 169 + private real_t _mem0; + private real_t _mem1; + private real_t _mem2; + private real_t _mem3; +#pragma warning restore 169 + } + } + + public void Dispose() + { + switch (_type) + { + case Variant.Type.Nil: + case Variant.Type.Bool: + case Variant.Type.Int: + case Variant.Type.Float: + case Variant.Type.Vector2: + case Variant.Type.Vector2i: + case Variant.Type.Rect2: + case Variant.Type.Rect2i: + case Variant.Type.Vector3: + case Variant.Type.Vector3i: + case Variant.Type.Plane: + case Variant.Type.Quaternion: + case Variant.Type.Color: + case Variant.Type.Rid: + return; + } + + NativeFuncs.godotsharp_variant_destroy(ref this); + _type = Variant.Type.Nil; + } + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_string : IDisposable + { + internal IntPtr _ptr; + + public void Dispose() + { + if (_ptr == IntPtr.Zero) + return; + NativeFuncs.godotsharp_string_destroy(ref this); + _ptr = IntPtr.Zero; + } + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_string_name : IDisposable + { + internal IntPtr _data; + + public void Dispose() + { + if (_data == IntPtr.Zero) + return; + NativeFuncs.godotsharp_string_name_destroy(ref this); + _data = IntPtr.Zero; + } + + // An static method because an instance method could result in a hidden copy if called on an `in` parameter. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsEmpty(in godot_string_name name) => + // This is all that's needed to check if it's empty. Equivalent to `== StringName()` in C++. + name._data == IntPtr.Zero; + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_node_path : IDisposable + { + internal IntPtr _data; + + public void Dispose() + { + if (_data == IntPtr.Zero) + return; + NativeFuncs.godotsharp_node_path_destroy(ref this); + _data = IntPtr.Zero; + } + + // An static method because an instance method could result in a hidden copy if called on an `in` parameter. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsEmpty(in godot_node_path nodePath) => + // This is all that's needed to check if it's empty. It's what the `is_empty()` C++ method does. + nodePath._data == IntPtr.Zero; + } + + [StructLayout(LayoutKind.Explicit)] + // ReSharper disable once InconsistentNaming + internal struct godot_signal : IDisposable + { + [FieldOffset(0)] public godot_string_name _name; + + // There's padding here on 32-bit + + [FieldOffset(8)] public UInt64 _objectId; + + public void Dispose() + { + if (_name._data == IntPtr.Zero) + return; + NativeFuncs.godotsharp_signal_destroy(ref this); + _name._data = IntPtr.Zero; + } + } + + [StructLayout(LayoutKind.Explicit)] + // ReSharper disable once InconsistentNaming + internal struct godot_callable : IDisposable + { + [FieldOffset(0)] public godot_string_name _method; + + // There's padding here on 32-bit + + [FieldOffset(8)] public UInt64 _objectId; + [FieldOffset(8)] public IntPtr _custom; + + public void Dispose() + { + if (_method._data == IntPtr.Zero && _custom == IntPtr.Zero) + return; + NativeFuncs.godotsharp_callable_destroy(ref this); + _method._data = IntPtr.Zero; + _custom = IntPtr.Zero; + } + } + + // A correctly constructed value needs to call the native default constructor to allocate `_p`. + // Don't pass a C# default constructed `godot_array` to native code, unless it's going to + // be re-assigned a new value (the copy constructor checks if `_p` is null so that's fine). + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_array : IDisposable + { + internal IntPtr _p; + + public void Dispose() + { + if (_p == IntPtr.Zero) + return; + NativeFuncs.godotsharp_array_destroy(ref this); + _p = IntPtr.Zero; + } + } + + // IMPORTANT: + // A correctly constructed value needs to call the native default constructor to allocate `_p`. + // Don't pass a C# default constructed `godot_dictionary` to native code, unless it's going to + // be re-assigned a new value (the copy constructor checks if `_p` is null so that's fine). + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_dictionary : IDisposable + { + internal IntPtr _p; + + public void Dispose() + { + if (_p == IntPtr.Zero) + return; + NativeFuncs.godotsharp_dictionary_destroy(ref this); + _p = IntPtr.Zero; + } + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_packed_byte_array : IDisposable + { + internal IntPtr _writeProxy; + internal IntPtr _ptr; + + public void Dispose() + { + if (_ptr == IntPtr.Zero) + return; + NativeFuncs.godotsharp_packed_byte_array_destroy(ref this); + _ptr = IntPtr.Zero; + } + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_packed_int32_array : IDisposable + { + internal IntPtr _writeProxy; + internal IntPtr _ptr; + + public void Dispose() + { + if (_ptr == IntPtr.Zero) + return; + NativeFuncs.godotsharp_packed_int32_array_destroy(ref this); + _ptr = IntPtr.Zero; + } + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_packed_int64_array : IDisposable + { + internal IntPtr _writeProxy; + internal IntPtr _ptr; + + public void Dispose() + { + if (_ptr == IntPtr.Zero) + return; + NativeFuncs.godotsharp_packed_int64_array_destroy(ref this); + _ptr = IntPtr.Zero; + } + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_packed_float32_array : IDisposable + { + internal IntPtr _writeProxy; + internal IntPtr _ptr; + + public void Dispose() + { + if (_ptr == IntPtr.Zero) + return; + NativeFuncs.godotsharp_packed_float32_array_destroy(ref this); + _ptr = IntPtr.Zero; + } + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_packed_float64_array : IDisposable + { + internal IntPtr _writeProxy; + internal IntPtr _ptr; + + public void Dispose() + { + if (_ptr == IntPtr.Zero) + return; + NativeFuncs.godotsharp_packed_float64_array_destroy(ref this); + _ptr = IntPtr.Zero; + } + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_packed_string_array : IDisposable + { + internal IntPtr _writeProxy; + internal IntPtr _ptr; + + public void Dispose() + { + if (_ptr == IntPtr.Zero) + return; + NativeFuncs.godotsharp_packed_string_array_destroy(ref this); + _ptr = IntPtr.Zero; + } + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_packed_vector2_array : IDisposable + { + internal IntPtr _writeProxy; + internal IntPtr _ptr; + + public void Dispose() + { + if (_ptr == IntPtr.Zero) + return; + NativeFuncs.godotsharp_packed_vector2_array_destroy(ref this); + _ptr = IntPtr.Zero; + } + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_packed_vector3_array : IDisposable + { + internal IntPtr _writeProxy; + internal IntPtr _ptr; + + public void Dispose() + { + if (_ptr == IntPtr.Zero) + return; + NativeFuncs.godotsharp_packed_vector3_array_destroy(ref this); + _ptr = IntPtr.Zero; + } + } + + [StructLayout(LayoutKind.Sequential)] + // ReSharper disable once InconsistentNaming + internal struct godot_packed_color_array : IDisposable + { + internal IntPtr _writeProxy; + internal IntPtr _ptr; + + public void Dispose() + { + if (_ptr == IntPtr.Zero) + return; + NativeFuncs.godotsharp_packed_color_array_destroy(ref this); + _ptr = IntPtr.Zero; + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs new file mode 100644 index 00000000000..08d49bb9379 --- /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 00000000000..9b6f9633d1c --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs @@ -0,0 +1,1370 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Runtime.InteropServices; + +// ReSharper disable InconsistentNaming + +namespace Godot.NativeInterop +{ + // We want to use full name qualifiers here even if redundant for clarity + [SuppressMessage("ReSharper", "RedundantNameQualifier")] + internal static class Marshaling + { + public static unsafe void SetFieldValue(FieldInfo fieldInfo, object obj, godot_variant* value) + { + var valueObj = variant_to_mono_object_of_type(value, fieldInfo.FieldType); + fieldInfo.SetValue(obj, valueObj); + } + + public static Variant.Type managed_to_variant_type(Type type, ref bool r_nil_is_variant) + { + switch (Type.GetTypeCode(type)) + { + case TypeCode.Boolean: + return Variant.Type.Bool; + case TypeCode.Char: + return Variant.Type.Int; + case TypeCode.SByte: + return Variant.Type.Int; + case TypeCode.Int16: + return Variant.Type.Int; + case TypeCode.Int32: + return Variant.Type.Int; + case TypeCode.Int64: + return Variant.Type.Int; + case TypeCode.Byte: + return Variant.Type.Int; + case TypeCode.UInt16: + return Variant.Type.Int; + case TypeCode.UInt32: + return Variant.Type.Int; + case TypeCode.UInt64: + return Variant.Type.Int; + case TypeCode.Single: + return Variant.Type.Float; + case TypeCode.Double: + return Variant.Type.Float; + case TypeCode.String: + return Variant.Type.String; + default: + { + if (type == typeof(Vector2)) + return Variant.Type.Vector2; + + if (type == typeof(Vector2i)) + return Variant.Type.Vector2i; + + if (type == typeof(Rect2)) + return Variant.Type.Rect2; + + if (type == typeof(Rect2i)) + return Variant.Type.Rect2i; + + if (type == typeof(Transform2D)) + return Variant.Type.Transform2d; + + if (type == typeof(Vector3)) + return Variant.Type.Vector3; + + if (type == typeof(Vector3i)) + return Variant.Type.Vector3i; + + if (type == typeof(Vector4)) + return Variant.Type.Vector4; + + if (type == typeof(Vector4i)) + return Variant.Type.Vector4i; + + if (type == typeof(Basis)) + return Variant.Type.Basis; + + if (type == typeof(Quaternion)) + return Variant.Type.Quaternion; + + if (type == typeof(Transform3D)) + return Variant.Type.Transform3d; + + if (type == typeof(Projection)) + return Variant.Type.Projection; + + if (type == typeof(AABB)) + return Variant.Type.Aabb; + + if (type == typeof(Color)) + return Variant.Type.Color; + + if (type == typeof(Plane)) + return Variant.Type.Plane; + + if (type == typeof(Callable)) + return Variant.Type.Callable; + + if (type == typeof(SignalInfo)) + return Variant.Type.Signal; + + if (type.IsEnum) + return Variant.Type.Int; + + if (type.IsArray || type.IsSZArray) + { + if (type == typeof(Byte[])) + return Variant.Type.PackedByteArray; + + if (type == typeof(Int32[])) + return Variant.Type.PackedInt32Array; + + if (type == typeof(Int64[])) + return Variant.Type.PackedInt64Array; + + if (type == typeof(float[])) + return Variant.Type.PackedFloat32Array; + + if (type == typeof(double[])) + return Variant.Type.PackedFloat64Array; + + if (type == typeof(string[])) + return Variant.Type.PackedStringArray; + + if (type == typeof(Vector2[])) + return Variant.Type.PackedVector2Array; + + if (type == typeof(Vector3[])) + return Variant.Type.PackedVector3Array; + + if (type == typeof(Color[])) + return Variant.Type.PackedColorArray; + + if (typeof(Godot.Object[]).IsAssignableFrom(type)) + return Variant.Type.Array; + + if (type == typeof(object[])) + return Variant.Type.Array; + } + else if (type.IsGenericType) + { + var genericTypeDefinition = type.GetGenericTypeDefinition(); + + if (genericTypeDefinition == typeof(Collections.Dictionary<,>)) + return Variant.Type.Dictionary; + + if (genericTypeDefinition == typeof(Collections.Array<>)) + return Variant.Type.Array; + + if (genericTypeDefinition == typeof(System.Collections.Generic.Dictionary<,>)) + return Variant.Type.Dictionary; + + if (genericTypeDefinition == typeof(System.Collections.Generic.List<>)) + return Variant.Type.Array; + + if (genericTypeDefinition == typeof(IDictionary<,>)) + return Variant.Type.Dictionary; + + if (genericTypeDefinition == typeof(ICollection<>) || genericTypeDefinition == typeof(IEnumerable<>)) + return Variant.Type.Array; + } + else if (type == typeof(object)) + { + r_nil_is_variant = true; + return Variant.Type.Nil; + } + else + { + if (typeof(Godot.Object).IsAssignableFrom(type)) + return Variant.Type.Object; + + if (typeof(StringName) == type) + return Variant.Type.StringName; + + if (typeof(NodePath) == type) + return Variant.Type.NodePath; + + if (typeof(RID) == type) + return Variant.Type.Rid; + + if (typeof(Collections.Dictionary) == type || typeof(System.Collections.IDictionary) == type) + return Variant.Type.Dictionary; + + if (typeof(Collections.Array) == type || + typeof(System.Collections.ICollection) == type || + typeof(System.Collections.IEnumerable) == type) + { + return Variant.Type.Array; + } + } + + break; + } + } + + r_nil_is_variant = false; + + // Unknown + return Variant.Type.Nil; + } + + public static bool try_get_array_element_type(Type p_array_type, out Type r_elem_type) + { + if (p_array_type.IsArray || p_array_type.IsSZArray) + { + r_elem_type = p_array_type.GetElementType(); + return true; + } + else if (p_array_type.IsGenericType) + { + var genericTypeDefinition = p_array_type.GetGenericTypeDefinition(); + + if (typeof(Collections.Array) == genericTypeDefinition || + typeof(System.Collections.Generic.List<>) == genericTypeDefinition || + typeof(System.Collections.ICollection) == genericTypeDefinition || + typeof(System.Collections.IEnumerable) == genericTypeDefinition) + { + r_elem_type = p_array_type.GetGenericArguments()[0]; + return true; + } + } + + r_elem_type = null; + return false; + } + + /* TODO: Reflection and type checking each time is slow. This will be replaced with source generators. */ + + public static godot_variant mono_object_to_variant(object p_obj) + { + return mono_object_to_variant_impl(p_obj); + } + + public static godot_variant mono_object_to_variant_no_err(object p_obj) + { + return mono_object_to_variant_impl(p_obj); + } + + // TODO: Only called from C++. Remove once no longer needed. + private static unsafe void mono_object_to_variant_out(object p_obj, bool p_fail_with_err, godot_variant* r_ret) + => *r_ret = mono_object_to_variant_impl(p_obj, p_fail_with_err); + + private static unsafe godot_variant mono_object_to_variant_impl(object p_obj, bool p_fail_with_err = true) + { + if (p_obj == null) + return new godot_variant(); + + switch (p_obj) + { + case bool @bool: + return VariantUtils.CreateFromBool(@bool); + case char @char: + return VariantUtils.CreateFromInt(@char); + case SByte @int8: + return VariantUtils.CreateFromInt(@int8); + case Int16 @int16: + return VariantUtils.CreateFromInt(@int16); + case Int32 @int32: + return VariantUtils.CreateFromInt(@int32); + case Int64 @int64: + return VariantUtils.CreateFromInt(@int64); + case Byte @uint8: + return VariantUtils.CreateFromInt(@uint8); + case UInt16 @uint16: + return VariantUtils.CreateFromInt(@uint16); + case UInt32 @uint32: + return VariantUtils.CreateFromInt(@uint32); + case UInt64 @uint64: + return VariantUtils.CreateFromInt(@uint64); + case float @float: + return VariantUtils.CreateFromFloat(@float); + case double @double: + return VariantUtils.CreateFromFloat(@double); + case Vector2 @vector2: + return VariantUtils.CreateFromVector2(@vector2); + case Vector2i @vector2i: + return VariantUtils.CreateFromVector2i(@vector2i); + case Rect2 @rect2: + return VariantUtils.CreateFromRect2(@rect2); + case Rect2i @rect2i: + return VariantUtils.CreateFromRect2i(@rect2i); + case Transform2D @transform2D: + return VariantUtils.CreateFromTransform2D(@transform2D); + case Vector3 @vector3: + return VariantUtils.CreateFromVector3(@vector3); + case Vector3i @vector3i: + return VariantUtils.CreateFromVector3i(@vector3i); + case Vector4 @vector4: + return VariantUtils.CreateFromVector4(@vector4); + case Vector4i @vector4i: + return VariantUtils.CreateFromVector4i(@vector4i); + case Basis @basis: + return VariantUtils.CreateFromBasis(@basis); + case Quaternion @quaternion: + return VariantUtils.CreateFromQuaternion(@quaternion); + case Transform3D @transform3d: + return VariantUtils.CreateFromTransform3D(@transform3d); + case Projection @projection: + return VariantUtils.CreateFromProjection(@projection); + case AABB @aabb: + return VariantUtils.CreateFromAABB(@aabb); + case Color @color: + return VariantUtils.CreateFromColor(@color); + case Plane @plane: + return VariantUtils.CreateFromPlane(@plane); + case Callable @callable: + return VariantUtils.CreateFromCallableTakingOwnershipOfDisposableValue( + ConvertCallableToNative(ref @callable)); + case SignalInfo @signalInfo: + return VariantUtils.CreateFromSignalTakingOwnershipOfDisposableValue( + ConvertSignalToNative(ref @signalInfo)); + case Enum @enum: + return VariantUtils.CreateFromInt(Convert.ToInt64(@enum)); + case string @string: + { + return VariantUtils.CreateFromStringTakingOwnershipOfDisposableValue( + mono_string_to_godot(@string)); + } + case Byte[] byteArray: + { + using godot_packed_byte_array array = mono_array_to_PackedByteArray(byteArray); + return VariantUtils.CreateFromPackedByteArray(&array); + } + case Int32[] int32Array: + { + using godot_packed_int32_array array = mono_array_to_PackedInt32Array(int32Array); + return VariantUtils.CreateFromPackedInt32Array(&array); + } + case Int64[] int64Array: + { + using godot_packed_int64_array array = mono_array_to_PackedInt64Array(int64Array); + return VariantUtils.CreateFromPackedInt64Array(&array); + } + case float[] floatArray: + { + using godot_packed_float32_array array = mono_array_to_PackedFloat32Array(floatArray); + return VariantUtils.CreateFromPackedFloat32Array(&array); + } + case double[] doubleArray: + { + using godot_packed_float64_array array = mono_array_to_PackedFloat64Array(doubleArray); + return VariantUtils.CreateFromPackedFloat64Array(&array); + } + case string[] stringArray: + { + using godot_packed_string_array array = mono_array_to_PackedStringArray(stringArray); + return VariantUtils.CreateFromPackedStringArray(&array); + } + case Vector2[] vector2Array: + { + using godot_packed_vector2_array array = mono_array_to_PackedVector2Array(vector2Array); + return VariantUtils.CreateFromPackedVector2Array(&array); + } + case Vector3[] vector3Array: + { + using godot_packed_vector3_array array = mono_array_to_PackedVector3Array(vector3Array); + return VariantUtils.CreateFromPackedVector3Array(&array); + } + case Color[] colorArray: + { + using godot_packed_color_array array = mono_array_to_PackedColorArray(colorArray); + return VariantUtils.CreateFromPackedColorArray(&array); + } + case Godot.Object[] godotObjectArray: + { + // ReSharper disable once CoVariantArrayConversion + using godot_array array = mono_array_to_Array(godotObjectArray); + return VariantUtils.CreateFromArray(&array); + } + case object[] objectArray: // Last one to avoid catching others like string[] and Godot.Object[] + { + // The pattern match for `object[]` catches arrays on any reference type, + // so we need to check the actual type to make sure it's truly `object[]`. + if (objectArray.GetType() == typeof(object[])) + { + using godot_array array = mono_array_to_Array(objectArray); + return VariantUtils.CreateFromArray(&array); + } + + if (p_fail_with_err) + { + GD.PushError("Attempted to convert a managed array of unmarshallable element type to Variant."); + return new godot_variant(); + } + else + { + return new godot_variant(); + } + } + case Godot.Object godotObject: + return VariantUtils.CreateFromGodotObject(godotObject.NativeInstance); + case StringName stringName: + return VariantUtils.CreateFromStringName(ref stringName.NativeValue); + case NodePath nodePath: + return VariantUtils.CreateFromNodePath(ref nodePath.NativeValue); + case RID rid: + return VariantUtils.CreateFromRID(rid); + case Collections.Dictionary godotDictionary: + return VariantUtils.CreateFromDictionary(godotDictionary.NativeValue); + case Collections.Array godotArray: + return VariantUtils.CreateFromArray(godotArray.NativeValue); + case Collections.IGenericGodotDictionary genericGodotDictionary: + { + var godotDict = genericGodotDictionary.UnderlyingDictionary; + if (godotDict == null) + return new godot_variant(); + return VariantUtils.CreateFromDictionary(godotDict.NativeValue); + } + case Collections.IGenericGodotArray genericGodotArray: + { + var godotArray = genericGodotArray.UnderlyingArray; + if (godotArray == null) + return new godot_variant(); + return VariantUtils.CreateFromArray(godotArray.NativeValue); + } + default: + { + var type = p_obj.GetType(); + + if (type.IsGenericType) + { + var genericTypeDefinition = type.GetGenericTypeDefinition(); + + if (genericTypeDefinition == typeof(System.Collections.Generic.Dictionary<,>)) + { + // TODO: Validate key and value types are compatible with Variant +#if NET + Collections.IGenericGodotDictionary genericGodotDictionary = IDictionaryToGenericGodotDictionary((dynamic)p_obj); +#else + var genericArguments = type.GetGenericArguments(); + + // With .NET Standard we need a package reference for Microsoft.CSharp in order to + // use dynamic, so we have this workaround for now until we switch to .NET 5/6. + var method = typeof(Marshaling).GetMethod(nameof(IDictionaryToGenericGodotDictionary), + BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.DeclaredOnly)! + .MakeGenericMethod(genericArguments[0], genericArguments[1]); + + var genericGodotDictionary = (Collections.IGenericGodotDictionary)method + .Invoke(null, new[] {p_obj}); +#endif + + var godotDict = genericGodotDictionary.UnderlyingDictionary; + if (godotDict == null) + return new godot_variant(); + return VariantUtils.CreateFromDictionary(godotDict.NativeValue); + } + + if (genericTypeDefinition == typeof(System.Collections.Generic.List<>)) + { + // TODO: Validate element type is compatible with Variant +#if NET + var nativeGodotArray = mono_array_to_Array(System.Runtime.InteropServices.CollectionsMarshal.AsSpan((dynamic)p_obj)); +#else + // With .NET Standard we need a package reference for Microsoft.CSharp in order to + // use dynamic, so we have this workaround for now until we switch to .NET 5/6. + // Also CollectionsMarshal.AsSpan is not available with .NET Standard. + + var collection = (System.Collections.ICollection)p_obj; + var array = new object[collection.Count]; + collection.CopyTo(array, 0); + var nativeGodotArray = mono_array_to_Array(array); +#endif + return VariantUtils.CreateFromArray(&nativeGodotArray); + } + } + + break; + } + } + + if (p_fail_with_err) + { + GD.PushError("Attempted to convert an unmarshallable managed type to Variant. Name: '" + + p_obj.GetType().FullName + "."); + return new godot_variant(); + } + else + { + return new godot_variant(); + } + } + + private static Collections.Dictionary 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(Vector4)) + return VariantUtils.ConvertToVector4(p_var); + + if (type == typeof(Vector4i)) + return VariantUtils.ConvertToVector4i(p_var); + + if (type == typeof(Basis)) + return VariantUtils.ConvertToBasis(p_var); + + if (type == typeof(Quaternion)) + return VariantUtils.ConvertToQuaternion(p_var); + + if (type == typeof(Transform3D)) + return VariantUtils.ConvertToTransform3D(p_var); + + if (type == typeof(Projection)) + return VariantUtils.ConvertToProjection(p_var); + + if (type == typeof(AABB)) + return VariantUtils.ConvertToAABB(p_var); + + if (type == typeof(Color)) + return VariantUtils.ConvertToColor(p_var); + + if (type == typeof(Plane)) + return VariantUtils.ConvertToPlane(p_var); + + if (type == typeof(Callable)) + { + using godot_callable callable = NativeFuncs.godotsharp_variant_as_callable(p_var); + return ConvertCallableToManaged(&callable); + } + + if (type == typeof(SignalInfo)) + { + using godot_signal signal = NativeFuncs.godotsharp_variant_as_signal(p_var); + return ConvertSignalToManaged(&signal); + } + + if (type.IsEnum) + { + var enumUnderlyingType = type.GetEnumUnderlyingType(); + switch (Type.GetTypeCode(enumUnderlyingType)) + { + case TypeCode.SByte: + return VariantUtils.ConvertToInt8(p_var); + case TypeCode.Int16: + return VariantUtils.ConvertToInt16(p_var); + case TypeCode.Int32: + return VariantUtils.ConvertToInt32(p_var); + case TypeCode.Int64: + return VariantUtils.ConvertToInt64(p_var); + case TypeCode.Byte: + return VariantUtils.ConvertToUInt8(p_var); + case TypeCode.UInt16: + return VariantUtils.ConvertToUInt16(p_var); + case TypeCode.UInt32: + return VariantUtils.ConvertToUInt32(p_var); + case TypeCode.UInt64: + return VariantUtils.ConvertToUInt64(p_var); + default: + { + GD.PushError("Attempted to convert Variant to enum value of unsupported underlying type. Name: " + + type.FullName + " : " + enumUnderlyingType.FullName + "."); + return null; + } + } + } + + if (type.IsArray || type.IsSZArray) + return variant_to_mono_array_of_type(p_var, type); + else if (type.IsGenericType) + return variant_to_mono_object_of_genericinst(p_var, type); + else if (type == typeof(object)) + return variant_to_mono_object(p_var); + if (variant_to_mono_object_of_class(p_var, type, out object res)) + return res; + + break; + } + } + + GD.PushError("Attempted to convert Variant to unsupported type. Name: " + + type.FullName + "."); + return null; + } + + private static unsafe object variant_to_mono_array_of_type(godot_variant* p_var, Type type) + { + if (type == typeof(Byte[])) + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_byte_array(p_var); + return PackedByteArray_to_mono_array(&packedArray); + } + + if (type == typeof(Int32[])) + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_int32_array(p_var); + return PackedInt32Array_to_mono_array(&packedArray); + } + + if (type == typeof(Int64[])) + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_int64_array(p_var); + return PackedInt64Array_to_mono_array(&packedArray); + } + + if (type == typeof(float[])) + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_float32_array(p_var); + return PackedFloat32Array_to_mono_array(&packedArray); + } + + if (type == typeof(double[])) + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_float64_array(p_var); + return PackedFloat64Array_to_mono_array(&packedArray); + } + + if (type == typeof(string[])) + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_string_array(p_var); + return PackedStringArray_to_mono_array(&packedArray); + } + + if (type == typeof(Vector2[])) + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector2_array(p_var); + return PackedVector2Array_to_mono_array(&packedArray); + } + + if (type == typeof(Vector3[])) + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector3_array(p_var); + return PackedVector3Array_to_mono_array(&packedArray); + } + + if (type == typeof(Color[])) + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_color_array(p_var); + return PackedColorArray_to_mono_array(&packedArray); + } + + if (typeof(Godot.Object[]).IsAssignableFrom(type)) + { + using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var); + return Array_to_mono_array_of_type(&godotArray, type); + } + + if (type == typeof(object[])) + { + using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var); + return Array_to_mono_array(&godotArray); + } + + GD.PushError("Attempted to convert Variant to array of unsupported element type. Name: " + + type.GetElementType()!.FullName + "."); + return null; + } + + private static unsafe bool variant_to_mono_object_of_class(godot_variant* p_var, Type type, out object res) + { + if (typeof(Godot.Object).IsAssignableFrom(type)) + { + res = InteropUtils.UnmanagedGetManaged(VariantUtils.ConvertToGodotObject(p_var)); + return true; + } + + if (typeof(StringName) == type) + { + res = StringName.CreateTakingOwnershipOfDisposableValue( + VariantUtils.ConvertToStringName(p_var)); + return true; + } + + if (typeof(NodePath) == type) + { + res = NodePath.CreateTakingOwnershipOfDisposableValue( + VariantUtils.ConvertToNodePath(p_var)); + return true; + } + + if (typeof(RID) == type) + { + res = VariantUtils.ConvertToRID(p_var); + return true; + } + + if (typeof(Collections.Dictionary) == type || typeof(System.Collections.IDictionary) == type) + { + res = Collections.Dictionary.CreateTakingOwnershipOfDisposableValue( + VariantUtils.ConvertToDictionary(p_var)); + return true; + } + + if (typeof(Collections.Array) == type || + typeof(System.Collections.ICollection) == type || + typeof(System.Collections.IEnumerable) == type) + { + res = Collections.Array.CreateTakingOwnershipOfDisposableValue( + VariantUtils.ConvertToArray(p_var)); + return true; + } + + res = null; + return false; + } + + private static unsafe object variant_to_mono_object_of_genericinst(godot_variant* p_var, Type type) + { + static object variant_to_generic_godot_collections_dictionary(godot_variant* p_var, Type fullType) + { + var underlyingDict = Collections.Dictionary.CreateTakingOwnershipOfDisposableValue( + VariantUtils.ConvertToDictionary(p_var)); + return Activator.CreateInstance(fullType, + BindingFlags.Public | BindingFlags.Instance, null, + args: new object[] {underlyingDict}, null); + } + + static object variant_to_generic_godot_collections_array(godot_variant* p_var, Type fullType) + { + var underlyingArray = Collections.Array.CreateTakingOwnershipOfDisposableValue( + VariantUtils.ConvertToArray(p_var)); + return Activator.CreateInstance(fullType, + BindingFlags.Public | BindingFlags.Instance, null, + args: new object[] {underlyingArray}, null); + } + + var genericTypeDefinition = type.GetGenericTypeDefinition(); + + if (genericTypeDefinition == typeof(Collections.Dictionary<,>)) + return variant_to_generic_godot_collections_dictionary(p_var, type); + + if (genericTypeDefinition == typeof(Collections.Array<>)) + return variant_to_generic_godot_collections_array(p_var, type); + + if (genericTypeDefinition == typeof(System.Collections.Generic.Dictionary<,>)) + { + using var godotDictionary = Collections.Dictionary.CreateTakingOwnershipOfDisposableValue( + VariantUtils.ConvertToDictionary(p_var)); + + var dictionary = (System.Collections.IDictionary)Activator.CreateInstance(type, + BindingFlags.Public | BindingFlags.Instance, null, + args: new object[] + { + /* capacity: */ godotDictionary.Count + }, null); + + foreach (System.Collections.DictionaryEntry pair in godotDictionary) + dictionary.Add(pair.Key, pair.Value); + + return dictionary; + } + + if (genericTypeDefinition == typeof(System.Collections.Generic.List<>)) + { + using var godotArray = Collections.Array.CreateTakingOwnershipOfDisposableValue( + VariantUtils.ConvertToArray(p_var)); + + var list = (System.Collections.IList)Activator.CreateInstance(type, + BindingFlags.Public | BindingFlags.Instance, null, + args: new object[] + { + /* capacity: */ godotArray.Count + }, null); + + foreach (object elem in godotArray) + list.Add(elem); + + return list; + } + + if (genericTypeDefinition == typeof(IDictionary<,>)) + { + var genericArgs = type.GetGenericArguments(); + var keyType = genericArgs[0]; + var valueType = genericArgs[1]; + var genericGodotDictionaryType = typeof(Collections.Dictionary<,>) + .MakeGenericType(keyType, valueType); + + return variant_to_generic_godot_collections_dictionary(p_var, genericGodotDictionaryType); + } + + if (genericTypeDefinition == typeof(ICollection<>) || genericTypeDefinition == typeof(IEnumerable<>)) + { + var elementType = type.GetGenericArguments()[0]; + var genericGodotArrayType = typeof(Collections.Array<>) + .MakeGenericType(elementType); + + return variant_to_generic_godot_collections_array(p_var, genericGodotArrayType); + } + + return null; + } + + public static unsafe object variant_to_mono_object(godot_variant* p_var) + { + switch ((*p_var)._type) + { + case Variant.Type.Bool: + return (bool)(*p_var)._data._bool; + case Variant.Type.Int: + return (*p_var)._data._int; + case Variant.Type.Float: + { +#if REAL_T_IS_DOUBLE + return (*p_var)._data._float; +#else + return (float)(*p_var)._data._float; +#endif + } + case Variant.Type.String: + return mono_string_from_godot(&(*p_var)._data._m_string); + case Variant.Type.Vector2: + return (*p_var)._data._m_vector2; + case Variant.Type.Vector2i: + return (*p_var)._data._m_vector2i; + case Variant.Type.Rect2: + return (*p_var)._data._m_rect2; + case Variant.Type.Rect2i: + return (*p_var)._data._m_rect2i; + case Variant.Type.Vector3: + return (*p_var)._data._m_vector3; + case Variant.Type.Vector3i: + return (*p_var)._data._m_vector3i; + case Variant.Type.Transform2d: + return *(*p_var)._data._transform2d; + case Variant.Type.Vector4: + return *(*p_var)._data._vector4; + case Variant.Type.Vector4i: + return *(*p_var)._data._vector4i; + case Variant.Type.Plane: + return (*p_var)._data._m_plane; + case Variant.Type.Quaternion: + return (*p_var)._data._m_quaternion; + case Variant.Type.Aabb: + return *(*p_var)._data._aabb; + case Variant.Type.Basis: + return *(*p_var)._data._basis; + case Variant.Type.Transform3d: + return *(*p_var)._data._transform3d; + case Variant.Type.Projection: + return *(*p_var)._data._projection; + case Variant.Type.Color: + return (*p_var)._data._m_color; + case Variant.Type.StringName: + { + // The Variant owns the value, so we need to make a copy + return StringName.CreateTakingOwnershipOfDisposableValue( + NativeFuncs.godotsharp_string_name_new_copy(&(*p_var)._data._m_string_name)); + } + case Variant.Type.NodePath: + { + // The Variant owns the value, so we need to make a copy + return NodePath.CreateTakingOwnershipOfDisposableValue( + NativeFuncs.godotsharp_node_path_new_copy(&(*p_var)._data._m_node_path)); + } + case Variant.Type.Rid: + return (*p_var)._data._m_rid; + case Variant.Type.Object: + return InteropUtils.UnmanagedGetManaged((*p_var)._data._m_obj_data.obj); + case Variant.Type.Callable: + return ConvertCallableToManaged(&(*p_var)._data._m_callable); + case Variant.Type.Signal: + return ConvertSignalToManaged(&(*p_var)._data._m_signal); + case Variant.Type.Dictionary: + { + // The Variant owns the value, so we need to make a copy + return Collections.Dictionary.CreateTakingOwnershipOfDisposableValue( + NativeFuncs.godotsharp_dictionary_new_copy(&(*p_var)._data._m_dictionary)); + } + case Variant.Type.Array: + { + // The Variant owns the value, so we need to make a copy + return Collections.Array.CreateTakingOwnershipOfDisposableValue( + NativeFuncs.godotsharp_array_new_copy(&(*p_var)._data._m_array)); + } + case Variant.Type.PackedByteArray: + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_byte_array(p_var); + return PackedByteArray_to_mono_array(&packedArray); + } + case Variant.Type.PackedInt32Array: + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_int32_array(p_var); + return PackedInt32Array_to_mono_array(&packedArray); + } + case Variant.Type.PackedInt64Array: + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_int64_array(p_var); + return PackedInt64Array_to_mono_array(&packedArray); + } + case Variant.Type.PackedFloat32Array: + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_float32_array(p_var); + return PackedFloat32Array_to_mono_array(&packedArray); + } + case Variant.Type.PackedFloat64Array: + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_float64_array(p_var); + return PackedFloat64Array_to_mono_array(&packedArray); + } + case Variant.Type.PackedStringArray: + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_string_array(p_var); + return PackedStringArray_to_mono_array(&packedArray); + } + case Variant.Type.PackedVector2Array: + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector2_array(p_var); + return PackedVector2Array_to_mono_array(&packedArray); + } + case Variant.Type.PackedVector3Array: + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector3_array(p_var); + return PackedVector3Array_to_mono_array(&packedArray); + } + case Variant.Type.PackedColorArray: + { + using var packedArray = NativeFuncs.godotsharp_variant_as_packed_color_array(p_var); + return PackedColorArray_to_mono_array(&packedArray); + } + default: + return null; + } + } + + // String + + public static unsafe godot_string mono_string_to_godot(string p_mono_string) + { + if (p_mono_string == null) + return new godot_string(); + + fixed (char* methodChars = p_mono_string) + { + godot_string dest; + NativeFuncs.godotsharp_string_new_with_utf16_chars(&dest, methodChars); + return dest; + } + } + + public static unsafe string mono_string_from_godot(godot_string* p_string) + { + if ((*p_string)._ptr == IntPtr.Zero) + return string.Empty; + + const int sizeOfChar32 = 4; + byte* bytes = (byte*)(*p_string)._ptr; + int size = *((int*)(*p_string)._ptr - 1); + if (size == 0) + return string.Empty; + size -= 1; // zero at the end + int sizeInBytes = size * sizeOfChar32; + return System.Text.Encoding.UTF32.GetString(bytes, sizeInBytes); + } + + // Callable + + public static godot_callable ConvertCallableToNative(ref Callable p_managed_callable) + { + if (p_managed_callable.Delegate != null) + { + unsafe + { + godot_callable callable; + NativeFuncs.godotsharp_callable_new_with_delegate( + GCHandle.ToIntPtr(GCHandle.Alloc(p_managed_callable.Delegate)), &callable); + return callable; + } + } + else + { + unsafe + { + godot_string_name method; + + if (p_managed_callable.Method != null && !p_managed_callable.Method.IsEmpty) + { + godot_string_name src = p_managed_callable.Method.NativeValue; + method = NativeFuncs.godotsharp_string_name_new_copy(&src); + } + else + { + method = default; + } + + return new godot_callable + { + _method = method, // Takes ownership of disposable + _objectId = p_managed_callable.Target.GetInstanceId() + }; + } + } + } + + public static unsafe Callable ConvertCallableToManaged(godot_callable* p_callable) + { + IntPtr delegateGCHandle; + IntPtr godotObject; + godot_string_name name; + + if (NativeFuncs.godotsharp_callable_get_data_for_marshalling( + p_callable, &delegateGCHandle, &godotObject, &name)) + { + if (delegateGCHandle != IntPtr.Zero) + { + return new Callable((Delegate)GCHandle.FromIntPtr(delegateGCHandle).Target); + } + else + { + return new Callable( + InteropUtils.UnmanagedGetManaged(godotObject), + StringName.CreateTakingOwnershipOfDisposableValue(name)); + } + } + + // Some other unsupported callable + return new Callable(); + } + + // SignalInfo + + public static godot_signal ConvertSignalToNative(ref SignalInfo p_managed_signal) + { + ulong ownerId = p_managed_signal.Owner.GetInstanceId(); + unsafe + { + godot_string_name name; + + if (p_managed_signal.Name != null && !p_managed_signal.Name.IsEmpty) + { + godot_string_name src = p_managed_signal.Name.NativeValue; + name = NativeFuncs.godotsharp_string_name_new_copy(&src); + } + else + { + name = default; + } + + return new godot_signal() + { + _name = name, + _objectId = ownerId + }; + } + } + + public static unsafe SignalInfo ConvertSignalToManaged(godot_signal* p_signal) + { + var owner = GD.InstanceFromId((*p_signal)._objectId); + var name = StringName.CreateTakingOwnershipOfDisposableValue( + NativeFuncs.godotsharp_string_name_new_copy(&(*p_signal)._name)); + return new SignalInfo(owner, name); + } + + // Array + + public static unsafe object[] Array_to_mono_array(godot_array* p_array) + { + var array = Collections.Array.CreateTakingOwnershipOfDisposableValue( + NativeFuncs.godotsharp_array_new_copy(p_array)); + + int length = array.Count; + var ret = new object[length]; + + array.CopyTo(ret, 0); // variant_to_mono_object handled by Collections.Array + + return ret; + } + + public static unsafe object Array_to_mono_array_of_type(godot_array* p_array, Type type) + { + var array = Collections.Array.CreateTakingOwnershipOfDisposableValue( + NativeFuncs.godotsharp_array_new_copy(p_array)); + + int length = array.Count; + object ret = Activator.CreateInstance(type, length); + + array.CopyTo((object[])ret, 0); // variant_to_mono_object handled by Collections.Array + + return ret; + } + + public static godot_array mono_array_to_Array(Span 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 00000000000..2f1056219d3 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs @@ -0,0 +1,346 @@ +using System; +using System.Runtime.InteropServices; + +// ReSharper disable InconsistentNaming + +namespace Godot.NativeInterop +{ +#if !NET + // This improves P/Invoke performance. + // The attribute is not available with .NET Core and it's not needed there. + [System.Security.SuppressUnmanagedCodeSecurity] +#endif + internal static unsafe partial class NativeFuncs + { + private const string GodotDllName = "__Internal"; + + // Custom functions + + [DllImport(GodotDllName)] + public static extern IntPtr godotsharp_method_bind_get_method(ref godot_string_name p_classname, char* p_methodname); + +#if NET + [DllImport(GodotDllName)] + public static extern delegate* unmanaged godotsharp_get_class_constructor(ref godot_string_name p_classname); +#else + // Workaround until we switch to .NET 5/6 + [DllImport(GodotDllName)] + public static extern IntPtr godotsharp_get_class_constructor(ref godot_string_name p_classname); + + [DllImport(GodotDllName)] + public static extern IntPtr godotsharp_invoke_class_constructor(IntPtr p_creation_func); +#endif + + [DllImport(GodotDllName)] + public static extern IntPtr godotsharp_engine_get_singleton(godot_string* p_name); + + [DllImport(GodotDllName)] + public static extern void godotsharp_ref_destroy(ref godot_ref p_instance); + + [DllImport(GodotDllName)] + public static extern void godotsharp_string_name_new_from_string(godot_string_name* dest, godot_string* name); + + [DllImport(GodotDllName)] + public static extern void godotsharp_node_path_new_from_string(godot_node_path* dest, godot_string* name); + + [DllImport(GodotDllName)] + public static extern void godotsharp_string_name_as_string(godot_string* r_dest, godot_string_name* p_name); + + [DllImport(GodotDllName)] + public static extern void godotsharp_node_path_as_string(godot_string* r_dest, godot_node_path* p_np); + + [DllImport(GodotDllName)] + public static extern godot_packed_byte_array godotsharp_packed_byte_array_new_mem_copy(byte* p_src, int p_length); + + [DllImport(GodotDllName)] + public static extern godot_packed_int32_array godotsharp_packed_int32_array_new_mem_copy(int* p_src, int p_length); + + [DllImport(GodotDllName)] + public static extern godot_packed_int64_array godotsharp_packed_int64_array_new_mem_copy(long* p_src, int p_length); + + [DllImport(GodotDllName)] + public static extern godot_packed_float32_array godotsharp_packed_float32_array_new_mem_copy(float* p_src, int p_length); + + [DllImport(GodotDllName)] + public static extern godot_packed_float64_array godotsharp_packed_float64_array_new_mem_copy(double* p_src, int p_length); + + [DllImport(GodotDllName)] + public static extern godot_packed_vector2_array godotsharp_packed_vector2_array_new_mem_copy(Vector2* p_src, int p_length); + + [DllImport(GodotDllName)] + public static extern godot_packed_vector3_array godotsharp_packed_vector3_array_new_mem_copy(Vector3* p_src, int p_length); + + [DllImport(GodotDllName)] + public static extern godot_packed_color_array godotsharp_packed_color_array_new_mem_copy(Color* p_src, int p_length); + + [DllImport(GodotDllName)] + public static extern void godotsharp_packed_string_array_add(godot_packed_string_array* r_dest, godot_string* p_element); + + [DllImport(GodotDllName)] + public static extern void godotsharp_callable_new_with_delegate(IntPtr p_delegate_handle, godot_callable* r_callable); + + [DllImport(GodotDllName)] + public static extern bool godotsharp_callable_get_data_for_marshalling(godot_callable* p_callable, IntPtr* r_delegate_handle, IntPtr* r_object, godot_string_name* r_name); + + // GDNative functions + + // gdnative.h + + [DllImport(GodotDllName)] + public static extern void godotsharp_method_bind_ptrcall(IntPtr p_method_bind, IntPtr p_instance, void** p_args, void* p_ret); + + [DllImport(GodotDllName)] + public static extern godot_variant godotsharp_method_bind_call(IntPtr p_method_bind, IntPtr p_instance, godot_variant** p_args, int p_arg_count, godot_variant_call_error* p_call_error); + + // variant.h + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_string_name(godot_variant* r_dest, godot_string_name* p_s); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_node_path(godot_variant* r_dest, godot_node_path* p_np); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_object(godot_variant* r_dest, IntPtr p_obj); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_transform2d(godot_variant* r_dest, Transform2D* p_t2d); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_vector4(godot_variant* r_dest, Vector4* p_vec4); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_vector4i(godot_variant* r_dest, Vector4i* p_vec4i); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_basis(godot_variant* r_dest, Basis* p_basis); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_transform3d(godot_variant* r_dest, Transform3D* p_trans); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_projection(godot_variant* r_dest, Projection* p_proj); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_aabb(godot_variant* r_dest, AABB* p_aabb); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_dictionary(godot_variant* r_dest, godot_dictionary* p_dict); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_array(godot_variant* r_dest, godot_array* p_arr); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_packed_byte_array(godot_variant* r_dest, godot_packed_byte_array* p_pba); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_packed_int32_array(godot_variant* r_dest, godot_packed_int32_array* p_pia); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_packed_int64_array(godot_variant* r_dest, godot_packed_int64_array* p_pia); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_packed_float32_array(godot_variant* r_dest, godot_packed_float32_array* p_pra); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_packed_float64_array(godot_variant* r_dest, godot_packed_float64_array* p_pra); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_packed_string_array(godot_variant* r_dest, godot_packed_string_array* p_psa); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_packed_vector2_array(godot_variant* r_dest, godot_packed_vector2_array* p_pv2a); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_packed_vector3_array(godot_variant* r_dest, godot_packed_vector3_array* p_pv3a); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_new_packed_color_array(godot_variant* r_dest, godot_packed_color_array* p_pca); + + [DllImport(GodotDllName)] + public static extern bool godotsharp_variant_as_bool(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Int64 godotsharp_variant_as_int(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern double godotsharp_variant_as_float(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_string godotsharp_variant_as_string(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Vector2 godotsharp_variant_as_vector2(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Vector2i godotsharp_variant_as_vector2i(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Rect2 godotsharp_variant_as_rect2(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Rect2i godotsharp_variant_as_rect2i(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Vector3 godotsharp_variant_as_vector3(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Vector3i godotsharp_variant_as_vector3i(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Transform2D godotsharp_variant_as_transform2d(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Vector4 godotsharp_variant_as_vector4(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Vector4i godotsharp_variant_as_vector4i(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Plane godotsharp_variant_as_plane(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Quaternion godotsharp_variant_as_quaternion(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern AABB godotsharp_variant_as_aabb(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Basis godotsharp_variant_as_basis(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Transform3D godotsharp_variant_as_transform3d(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Projection godotsharp_variant_as_projection(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern Color godotsharp_variant_as_color(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_string_name godotsharp_variant_as_string_name(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_node_path godotsharp_variant_as_node_path(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern RID godotsharp_variant_as_rid(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_callable godotsharp_variant_as_callable(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_signal godotsharp_variant_as_signal(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_dictionary godotsharp_variant_as_dictionary(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_array godotsharp_variant_as_array(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_packed_byte_array godotsharp_variant_as_packed_byte_array(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_packed_int32_array godotsharp_variant_as_packed_int32_array(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_packed_int64_array godotsharp_variant_as_packed_int64_array(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_packed_float32_array godotsharp_variant_as_packed_float32_array(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_packed_float64_array godotsharp_variant_as_packed_float64_array(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_packed_string_array godotsharp_variant_as_packed_string_array(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_packed_vector2_array godotsharp_variant_as_packed_vector2_array(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_packed_vector3_array godotsharp_variant_as_packed_vector3_array(godot_variant* p_self); + + [DllImport(GodotDllName)] + public static extern godot_packed_color_array godotsharp_variant_as_packed_color_array(godot_variant* p_self); + + // string.h + + [DllImport(GodotDllName)] + public static extern void godotsharp_string_new_with_utf16_chars(godot_string* r_dest, char* p_contents); + + // string_name.h + + [DllImport(GodotDllName)] + public static extern void godotsharp_string_name_new_copy(godot_string_name* r_dest, godot_string_name* p_src); + + // node_path.h + + [DllImport(GodotDllName)] + public static extern void godotsharp_node_path_new_copy(godot_node_path* r_dest, godot_node_path* p_src); + + // array.h + + [DllImport(GodotDllName)] + public static extern void godotsharp_array_new_copy(godot_array* r_dest, godot_array* p_src); + + // dictionary.h + + [DllImport(GodotDllName)] + public static extern void godotsharp_dictionary_new_copy(godot_dictionary* r_dest, godot_dictionary* p_src); + + // destroy functions + + [DllImport(GodotDllName)] + public static extern void godotsharp_packed_byte_array_destroy(ref godot_packed_byte_array p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_packed_int32_array_destroy(ref godot_packed_int32_array p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_packed_int64_array_destroy(ref godot_packed_int64_array p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_packed_float32_array_destroy(ref godot_packed_float32_array p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_packed_float64_array_destroy(ref godot_packed_float64_array p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_packed_string_array_destroy(ref godot_packed_string_array p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_packed_vector2_array_destroy(ref godot_packed_vector2_array p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_packed_vector3_array_destroy(ref godot_packed_vector3_array p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_packed_color_array_destroy(ref godot_packed_color_array p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_variant_destroy(ref godot_variant p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_string_destroy(ref godot_string p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_string_name_destroy(ref godot_string_name p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_node_path_destroy(ref godot_node_path p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_signal_destroy(ref godot_signal p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_callable_destroy(ref godot_callable p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_array_destroy(ref godot_array p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_dictionary_destroy(ref godot_dictionary p_self); + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs new file mode 100644 index 00000000000..70df79c1de3 --- /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 00000000000..2814f9d506b --- /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 00000000000..67f9e23893b --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs @@ -0,0 +1,335 @@ +using System; +using System.Runtime.CompilerServices; + +// ReSharper disable InconsistentNaming + +namespace Godot.NativeInterop +{ + internal static class VariantUtils + { + public static godot_variant CreateFromRID(RID from) + => new() {_type = Variant.Type.Rid, _data = {_m_rid = from}}; + + public static godot_variant CreateFromBool(bool from) + => new() {_type = Variant.Type.Bool, _data = {_bool = from}}; + + public static godot_variant CreateFromInt(long from) + => new() {_type = Variant.Type.Int, _data = {_int = from}}; + + public static godot_variant CreateFromInt(ulong from) + => new() {_type = Variant.Type.Int, _data = {_int = (long)from}}; + + public static godot_variant CreateFromFloat(double from) + => new() {_type = Variant.Type.Float, _data = {_float = from}}; + + public static godot_variant CreateFromVector2(Vector2 from) + => new() {_type = Variant.Type.Vector2, _data = {_m_vector2 = from}}; + + public static godot_variant CreateFromVector2i(Vector2i from) + => new() {_type = Variant.Type.Vector2i, _data = {_m_vector2i = from}}; + + public static godot_variant CreateFromVector3(Vector3 from) + => new() {_type = Variant.Type.Vector3, _data = {_m_vector3 = from}}; + + public static godot_variant CreateFromVector3i(Vector3i from) + => new() {_type = Variant.Type.Vector3i, _data = {_m_vector3i = from}}; + + public static godot_variant CreateFromRect2(Rect2 from) + => new() {_type = Variant.Type.Rect2, _data = {_m_rect2 = from}}; + + public static godot_variant CreateFromRect2i(Rect2i from) + => new() {_type = Variant.Type.Rect2i, _data = {_m_rect2i = from}}; + + public static godot_variant CreateFromQuaternion(Quaternion from) + => new() {_type = Variant.Type.Quaternion, _data = {_m_quaternion = from}}; + + public static godot_variant CreateFromColor(Color from) + => new() {_type = Variant.Type.Color, _data = {_m_color = from}}; + + public static godot_variant CreateFromPlane(Plane from) + => new() {_type = Variant.Type.Plane, _data = {_m_plane = from}}; + + public static unsafe godot_variant CreateFromTransform2D(Transform2D from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_transform2d(&ret, &from); + return ret; + } + + public static unsafe godot_variant CreateFromVector4(Vector4 from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_vector4(&ret, &from); + return ret; + } + + public static unsafe godot_variant CreateFromVector4i(Vector4i from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_vector4i(&ret, &from); + return ret; + } + + public static unsafe godot_variant CreateFromBasis(Basis from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_basis(&ret, &from); + return ret; + } + + public static unsafe godot_variant CreateFromTransform3D(Transform3D from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_transform3d(&ret, &from); + return ret; + } + + public static unsafe godot_variant CreateFromProjection(Projection from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_projection(&ret, &from); + return ret; + } + + public static unsafe godot_variant CreateFromAABB(AABB from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_aabb(&ret, &from); + return ret; + } + + // Explicit name to make it very clear + public static godot_variant CreateFromCallableTakingOwnershipOfDisposableValue(godot_callable from) + => new() {_type = Variant.Type.Callable, _data = {_m_callable = from}}; + + // Explicit name to make it very clear + public static godot_variant CreateFromSignalTakingOwnershipOfDisposableValue(godot_signal from) + => new() {_type = Variant.Type.Signal, _data = {_m_signal = from}}; + + // Explicit name to make it very clear + public static godot_variant CreateFromStringTakingOwnershipOfDisposableValue(godot_string from) + => new() {_type = Variant.Type.String, _data = {_m_string = from}}; + + public static unsafe godot_variant CreateFromPackedByteArray(godot_packed_byte_array* from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_packed_byte_array(&ret, from); + return ret; + } + + public static unsafe godot_variant CreateFromPackedInt32Array(godot_packed_int32_array* from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_packed_int32_array(&ret, from); + return ret; + } + + public static unsafe godot_variant CreateFromPackedInt64Array(godot_packed_int64_array* from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_packed_int64_array(&ret, from); + return ret; + } + + public static unsafe godot_variant CreateFromPackedFloat32Array(godot_packed_float32_array* from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_packed_float32_array(&ret, from); + return ret; + } + + public static unsafe godot_variant CreateFromPackedFloat64Array(godot_packed_float64_array* from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_packed_float64_array(&ret, from); + return ret; + } + + public static unsafe godot_variant CreateFromPackedStringArray(godot_packed_string_array* from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_packed_string_array(&ret, from); + return ret; + } + + public static unsafe godot_variant CreateFromPackedVector2Array(godot_packed_vector2_array* from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_packed_vector2_array(&ret, from); + return ret; + } + + public static unsafe godot_variant CreateFromPackedVector3Array(godot_packed_vector3_array* from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_packed_vector3_array(&ret, from); + return ret; + } + + public static unsafe godot_variant CreateFromPackedColorArray(godot_packed_color_array* from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_packed_color_array(&ret, from); + return ret; + } + + public static unsafe godot_variant CreateFromArray(godot_array* from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_array(&ret, from); + return ret; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe godot_variant CreateFromArray(godot_array from) + => CreateFromArray(&from); + + public static unsafe godot_variant CreateFromDictionary(godot_dictionary* from) + { + godot_variant ret; + NativeFuncs.godotsharp_variant_new_dictionary(&ret, from); + return ret; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe godot_variant CreateFromDictionary(godot_dictionary from) + => CreateFromDictionary(&from); + + public static unsafe godot_variant CreateFromStringName(ref godot_string_name arg1) + { + godot_variant ret; + godot_string_name src = arg1; + NativeFuncs.godotsharp_variant_new_string_name(&ret, &src); + return ret; + } + + public static unsafe godot_variant CreateFromNodePath(ref godot_node_path arg1) + { + godot_variant ret; + godot_node_path src = arg1; + NativeFuncs.godotsharp_variant_new_node_path(&ret, &src); + return ret; + } + + public static unsafe godot_variant CreateFromGodotObject(IntPtr from) + { + if (from == IntPtr.Zero) + return new godot_variant(); + godot_variant ret; + NativeFuncs.godotsharp_variant_new_object(&ret, from); + return ret; + } + + // We avoid the internal call if the stored type is the same we want. + + public static unsafe bool ConvertToBool(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Bool ? (*p_var)._data._bool : NativeFuncs.godotsharp_variant_as_bool(p_var); + + public static unsafe char ConvertToChar(godot_variant* p_var) + => (char)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var)); + + public static unsafe sbyte ConvertToInt8(godot_variant* p_var) + => (sbyte)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var)); + + public static unsafe Int16 ConvertToInt16(godot_variant* p_var) + => (Int16)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var)); + + public static unsafe Int32 ConvertToInt32(godot_variant* p_var) + => (Int32)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var)); + + public static unsafe Int64 ConvertToInt64(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var); + + public static unsafe byte ConvertToUInt8(godot_variant* p_var) + => (byte)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var)); + + public static unsafe UInt16 ConvertToUInt16(godot_variant* p_var) + => (UInt16)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var)); + + public static unsafe UInt32 ConvertToUInt32(godot_variant* p_var) + => (UInt32)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var)); + + public static unsafe UInt64 ConvertToUInt64(godot_variant* p_var) + => (UInt64)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var)); + + public static unsafe float ConvertToFloat32(godot_variant* p_var) + => (float)((*p_var)._type == Variant.Type.Float ? (*p_var)._data._float : NativeFuncs.godotsharp_variant_as_float(p_var)); + + public static unsafe double ConvertToFloat64(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Float ? (*p_var)._data._float : NativeFuncs.godotsharp_variant_as_float(p_var); + + public static unsafe Vector2 ConvertToVector2(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Vector2 ? (*p_var)._data._m_vector2 : NativeFuncs.godotsharp_variant_as_vector2(p_var); + + public static unsafe Vector2i ConvertToVector2i(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Vector2i ? (*p_var)._data._m_vector2i : NativeFuncs.godotsharp_variant_as_vector2i(p_var); + + public static unsafe Rect2 ConvertToRect2(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Rect2 ? (*p_var)._data._m_rect2 : NativeFuncs.godotsharp_variant_as_rect2(p_var); + + public static unsafe Rect2i ConvertToRect2i(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Rect2i ? (*p_var)._data._m_rect2i : NativeFuncs.godotsharp_variant_as_rect2i(p_var); + + public static unsafe Transform2D ConvertToTransform2D(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Transform2d ? *(*p_var)._data._transform2d : NativeFuncs.godotsharp_variant_as_transform2d(p_var); + + public static unsafe Vector3 ConvertToVector3(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Vector3 ? (*p_var)._data._m_vector3 : NativeFuncs.godotsharp_variant_as_vector3(p_var); + + public static unsafe Vector3i ConvertToVector3i(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Vector3i ? (*p_var)._data._m_vector3i : NativeFuncs.godotsharp_variant_as_vector3i(p_var); + + public static unsafe Vector4 ConvertToVector4(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Vector4 ? *(*p_var)._data._vector4 : NativeFuncs.godotsharp_variant_as_vector4(p_var); + + public static unsafe Vector4i ConvertToVector4i(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Vector4i ? *(*p_var)._data._vector4i : NativeFuncs.godotsharp_variant_as_vector4i(p_var); + + public static unsafe Basis ConvertToBasis(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Basis ? *(*p_var)._data._basis : NativeFuncs.godotsharp_variant_as_basis(p_var); + + public static unsafe Quaternion ConvertToQuaternion(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Quaternion ? (*p_var)._data._m_quaternion : NativeFuncs.godotsharp_variant_as_quaternion(p_var); + + public static unsafe Transform3D ConvertToTransform3D(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Transform3d ? *(*p_var)._data._transform3d : NativeFuncs.godotsharp_variant_as_transform3d(p_var); + + public static unsafe Projection ConvertToProjection(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Projection ? *(*p_var)._data._projection : NativeFuncs.godotsharp_variant_as_projection(p_var); + + public static unsafe AABB ConvertToAABB(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Aabb ? *(*p_var)._data._aabb : NativeFuncs.godotsharp_variant_as_aabb(p_var); + + public static unsafe Color ConvertToColor(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Color ? (*p_var)._data._m_color : NativeFuncs.godotsharp_variant_as_color(p_var); + + public static unsafe Plane ConvertToPlane(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Plane ? (*p_var)._data._m_plane : NativeFuncs.godotsharp_variant_as_plane(p_var); + + public static unsafe IntPtr ConvertToGodotObject(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Object ? (*p_var)._data._m_obj_data.obj : IntPtr.Zero; + + public static unsafe RID ConvertToRID(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Rid ? (*p_var)._data._m_rid : NativeFuncs.godotsharp_variant_as_rid(p_var); + + public static unsafe godot_string_name ConvertToStringName(godot_variant* p_var) + => (*p_var)._type == Variant.Type.StringName ? + NativeFuncs.godotsharp_string_name_new_copy(&(*p_var)._data._m_string_name) : + NativeFuncs.godotsharp_variant_as_string_name(p_var); + + public static unsafe godot_node_path ConvertToNodePath(godot_variant* p_var) + => (*p_var)._type == Variant.Type.NodePath ? + NativeFuncs.godotsharp_node_path_new_copy(&(*p_var)._data._m_node_path) : + NativeFuncs.godotsharp_variant_as_node_path(p_var); + + public static unsafe godot_array ConvertToArray(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Array ? + NativeFuncs.godotsharp_array_new_copy(&(*p_var)._data._m_array) : + NativeFuncs.godotsharp_variant_as_array(p_var); + + public static unsafe godot_dictionary ConvertToDictionary(godot_variant* p_var) + => (*p_var)._type == Variant.Type.Dictionary ? + NativeFuncs.godotsharp_dictionary_new_copy(&(*p_var)._data._m_dictionary) : + NativeFuncs.godotsharp_variant_as_dictionary(p_var); + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs index 9ae01016cb4..90e51e8a1a7 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.CompilerServices; +using Godot.NativeInterop; namespace Godot { @@ -39,22 +40,9 @@ namespace Godot /// new NodePath("/root/MyAutoload"); // If you have an autoloaded node or scene. /// /// - public sealed partial class NodePath : IDisposable + public sealed class NodePath : IDisposable { - private bool _disposed = false; - - private IntPtr ptr; - - internal static IntPtr GetPtr(NodePath instance) - { - if (instance == null) - throw new NullReferenceException($"The instance of type {nameof(NodePath)} is null."); - - if (instance._disposed) - throw new ObjectDisposedException(instance.GetType().FullName); - - return instance.ptr; - } + internal godot_node_path NativeValue; ~NodePath() { @@ -70,29 +58,27 @@ namespace Godot GC.SuppressFinalize(this); } - private void Dispose(bool disposing) + public void Dispose(bool disposing) { - if (_disposed) - return; - - if (ptr != IntPtr.Zero) - { - godot_icall_NodePath_Dtor(ptr); - ptr = IntPtr.Zero; - } - - _disposed = true; + // Always dispose `NativeValue` even if disposing is true + NativeValue.Dispose(); } - internal NodePath(IntPtr ptr) + private NodePath(godot_node_path nativeValueToOwn) { - this.ptr = ptr; + NativeValue = nativeValueToOwn; } + // Explicit name to make it very clear + internal static NodePath CreateTakingOwnershipOfDisposableValue(godot_node_path nativeValueToOwn) + => new NodePath(nativeValueToOwn); + /// /// Constructs an empty . /// - public NodePath() : this(string.Empty) { } + public NodePath() + { + } /// /// Constructs a from a string , @@ -125,7 +111,8 @@ namespace Godot /// A string that represents a path in a scene tree. public NodePath(string path) { - ptr = godot_icall_NodePath_Ctor(path); + if (!string.IsNullOrEmpty(path)) + NativeValue = NativeFuncs.godotsharp_node_path_new_from_string(path); } /// @@ -144,9 +131,16 @@ namespace Godot /// Converts this to a string. /// /// A string representation of this . - 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); } /// @@ -166,7 +160,9 @@ namespace Godot /// The as a pure property path. 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); } /// @@ -181,7 +177,7 @@ namespace Godot /// The names concatenated with /. public string GetConcatenatedNames() { - return godot_icall_NodePath_get_concatenated_names(GetPtr(this)); + return godot_icall_NodePath_get_concatenated_names(ref NativeValue); } /// @@ -195,9 +191,9 @@ namespace Godot /// /// /// The subnames concatenated with :. - public string GetConcatenatedSubnames() + public string GetConcatenatedSubNames() { - return godot_icall_NodePath_get_concatenated_subnames(GetPtr(this)); + return godot_icall_NodePath_get_concatenated_subnames(ref NativeValue); } /// @@ -215,28 +211,28 @@ namespace Godot /// The name at the given index . public string GetName(int idx) { - return godot_icall_NodePath_get_name(GetPtr(this), idx); + return godot_icall_NodePath_get_name(ref NativeValue, idx); } /// /// Gets the number of node names which make up the path. - /// Subnames (see ) are not included. + /// Subnames (see ) are not included. /// For example, "Path2D/PathFollow2D/Sprite2D" has 3 names. /// /// The number of node names which make up the path. public int GetNameCount() { - return godot_icall_NodePath_get_name_count(GetPtr(this)); + return godot_icall_NodePath_get_name_count(ref NativeValue); } /// - /// Gets the resource or property name indicated by (0 to ). + /// Gets the resource or property name indicated by (0 to ). /// /// The subname index. /// The subname at the given index . - 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); } /// @@ -245,9 +241,9 @@ namespace Godot /// For example, "Path2D/PathFollow2D/Sprite2D:texture:load_path" has 2 subnames. /// /// The number of subnames in the path. - public int GetSubnameCount() + public int GetSubNameCount() { - return godot_icall_NodePath_get_subname_count(GetPtr(this)); + return godot_icall_NodePath_get_subname_count(ref NativeValue); } /// @@ -259,52 +255,37 @@ namespace Godot /// If the is an absolute path. public bool IsAbsolute() { - return godot_icall_NodePath_is_absolute(GetPtr(this)); + return godot_icall_NodePath_is_absolute(ref NativeValue); } /// /// Returns if the node path is empty. /// /// If the is empty. - 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_names(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_concatenated_subnames(ref godot_node_path ptr); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern IntPtr godot_icall_NodePath_get_as_property_path(IntPtr ptr); + private static extern string godot_icall_NodePath_get_name(ref godot_node_path ptr, int arg1); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string godot_icall_NodePath_get_concatenated_names(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 746612477dc..80d63581c19 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs @@ -7,38 +7,44 @@ namespace Godot { private bool _disposed = false; - private static StringName nativeName = "Object"; - - internal IntPtr ptr; - internal bool memoryOwn; + internal IntPtr NativePtr; + internal bool MemoryOwn; /// /// Constructs a new . /// 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; } /// /// The pointer to the native instance of this . /// - public IntPtr NativeInstance - { - get { return ptr; } - } + public IntPtr NativeInstance => NativePtr; internal static IntPtr GetPtr(Object instance) { @@ -48,7 +54,7 @@ namespace Godot if (instance._disposed) throw new ObjectDisposedException(instance.GetType().FullName); - return instance.ptr; + return instance.NativePtr; } ~Object() @@ -73,19 +79,19 @@ namespace Godot if (_disposed) return; - if (ptr != IntPtr.Zero) + if (NativePtr != IntPtr.Zero) { - if (memoryOwn) + if (MemoryOwn) { - memoryOwn = false; - godot_icall_RefCounted_Disposed(this, ptr, !disposing); + MemoryOwn = false; + godot_icall_RefCounted_Disposed(this, NativePtr, !disposing); } else { - godot_icall_Object_Disposed(this, ptr); + godot_icall_Object_Disposed(this, NativePtr); } - ptr = IntPtr.Zero; + this.NativePtr = IntPtr.Zero; } _disposed = true; @@ -137,13 +143,49 @@ namespace Godot /// 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); @@ -156,9 +198,5 @@ namespace Godot [MethodImpl(MethodImplOptions.InternalCall)] internal static extern string godot_icall_Object_ToString(IntPtr ptr); - - // Used by the generated API - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern IntPtr godot_icall_Object_ClassDB_get_method(IntPtr type, string method); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.exceptions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.exceptions.cs new file mode 100644 index 00000000000..eb2811c73d9 --- /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 1588869ec08..a31fef83607 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/RID.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/RID.cs @@ -1,5 +1,7 @@ using System; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Godot.NativeInterop; namespace Godot { @@ -9,99 +11,32 @@ namespace Godot /// resource by themselves. They are used by and with the low-level Server /// classes such as . /// - 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); - } - - /// - /// Disposes of this . - /// - 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; - } - - /// - /// The pointer to the native instance of this . - /// - public IntPtr NativeInstance - { - get { return ptr; } - } - - internal RID() - { - this.ptr = IntPtr.Zero; + _id = id; } /// /// Constructs a new for the given . /// public RID(Object from) - { - this.ptr = godot_icall_RID_Ctor(Object.GetPtr(from)); - } + => _id = from is Resource res ? res.GetRid()._id : default; /// /// Returns the ID of the referenced resource. /// /// The ID of the referenced resource. - public int GetId() - { - return godot_icall_RID_get_id(GetPtr(this)); - } + public ulong Id => _id; /// /// Converts this to a string. /// /// A string representation of this RID. - public override string ToString() => "[RID]"; - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern IntPtr godot_icall_RID_Ctor(IntPtr from); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_RID_Dtor(IntPtr ptr); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern int godot_icall_RID_get_id(IntPtr ptr); + public override string ToString() => $"RID({Id})"; } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs index 2ba04930020..e485207fb40 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.CompilerServices; +using Godot.NativeInterop; namespace Godot { @@ -11,34 +12,22 @@ namespace Godot public SignalAwaiter(Object source, StringName signal, Object target) { - godot_icall_SignalAwaiter_connect(Object.GetPtr(source), StringName.GetPtr(signal), Object.GetPtr(target), this); + godot_icall_SignalAwaiter_connect(Object.GetPtr(source), ref signal.NativeValue, Object.GetPtr(target), this); } [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern Error godot_icall_SignalAwaiter_connect(IntPtr source, IntPtr signal, IntPtr target, SignalAwaiter awaiter); + internal static extern Error godot_icall_SignalAwaiter_connect(IntPtr source, ref godot_string_name signal, IntPtr target, SignalAwaiter awaiter); - public bool IsCompleted - { - get - { - return _completed; - } - } + public bool IsCompleted => _completed; public void OnCompleted(Action action) { this._action = action; } - public object[] GetResult() - { - return _result; - } + public object[] GetResult() => _result; - public IAwaiter GetAwaiter() - { - return this; - } + public IAwaiter GetAwaiter() => this; internal void SignalCallback(object[] args) { diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs index a1f058ffe53..058c5447e2e 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs @@ -214,7 +214,7 @@ namespace Godot /// The escaped string. public static string CEscape(this string instance) { - var sb = new StringBuilder(string.Copy(instance)); + var sb = new StringBuilder(instance); sb.Replace("\\", "\\\\"); sb.Replace("\a", "\\a"); @@ -239,7 +239,7 @@ namespace Godot /// The unescaped string. public static string CUnescape(this string instance) { - var sb = new StringBuilder(string.Copy(instance)); + var sb = new StringBuilder(instance); sb.Replace("\\a", "\a"); sb.Replace("\\b", "\b"); @@ -926,7 +926,7 @@ namespace Godot /// The escaped string. 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 b1d504410b7..40d282eab4b 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.CompilerServices; +using Godot.NativeInterop; namespace Godot { @@ -10,20 +11,9 @@ namespace Godot /// Comparing them is much faster than with regular strings, because only the pointers are compared, /// not the whole strings. /// - public sealed partial class StringName : IDisposable + public sealed class StringName : IDisposable { - private IntPtr ptr; - - internal static IntPtr GetPtr(StringName instance) - { - if (instance == null) - throw new NullReferenceException($"The instance of type {nameof(StringName)} is null."); - - if (instance.ptr == IntPtr.Zero) - throw new ObjectDisposedException(instance.GetType().FullName); - - return instance.ptr; - } + internal godot_string_name NativeValue; ~StringName() { @@ -39,35 +29,36 @@ namespace Godot GC.SuppressFinalize(this); } - private void Dispose(bool disposing) + public void Dispose(bool disposing) { - if (ptr != IntPtr.Zero) - { - godot_icall_StringName_Dtor(ptr); - ptr = IntPtr.Zero; - } + // Always dispose `NativeValue` even if disposing is true + NativeValue.Dispose(); } - internal StringName(IntPtr ptr) + private StringName(godot_string_name nativeValueToOwn) { - this.ptr = ptr; + NativeValue = nativeValueToOwn; } + // Explicit name to make it very clear + internal static StringName CreateTakingOwnershipOfDisposableValue(godot_string_name nativeValueToOwn) + => new StringName(nativeValueToOwn); + /// /// Constructs an empty . /// public StringName() { - ptr = IntPtr.Zero; } /// /// Constructs a from the given string. /// /// String to construct the from. - 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); } /// @@ -86,30 +77,22 @@ namespace Godot /// Converts this to a string. /// /// A string representation of this . - 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); } /// /// Check whether this is empty. /// /// If the is empty. - 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 4f55ce47e87..9683d8e812d 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 + + + + @@ -47,14 +56,22 @@ + + + + + + + + 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 00000000000..1add6cc77e1 --- /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 a8c4ba96b53..1082c74448e 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 00000000000..c7ff6fd3ee2 --- /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 7b9dbc87cf5..05a8ef20f93 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 8a9f30459cf..30adea60cc8 100644 --- a/modules/mono/glue/collections_glue.cpp +++ b/modules/mono/glue/collections_glue.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -28,8 +28,6 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifdef MONO_GLUE_ENABLED - #include #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,56 +168,37 @@ 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) { return ptr->size(); } -int32_t godot_icall_Dictionary_KeyValuePairs(Dictionary *ptr, Array **keys, Array **values) { - *keys = godot_icall_Dictionary_Keys(ptr); - *values = godot_icall_Dictionary_Values(ptr); - return godot_icall_Dictionary_Count(ptr); +int32_t godot_icall_Dictionary_KeyValuePairs(Dictionary *ptr, Array *keys, Array *values) { + memnew_placement(keys, Array(ptr->keys())); + memnew_placement(values, Array(ptr->values())); + return ptr->size(); } -void godot_icall_Dictionary_KeyValuePairAt(Dictionary *ptr, int index, MonoObject **key, MonoObject **value) { - *key = GDMonoMarshal::variant_to_mono_object(ptr->get_key_at_index(index)); - *value = GDMonoMarshal::variant_to_mono_object(ptr->get_value_at_index(index)); -} - -void godot_icall_Dictionary_KeyValuePairAt_Generic(Dictionary *ptr, int index, MonoObject **key, MonoObject **value, uint32_t value_type_encoding, GDMonoClass *value_type_class) { - ManagedType type(value_type_encoding, value_type_class); - *key = GDMonoMarshal::variant_to_mono_object(ptr->get_key_at_index(index)); - *value = GDMonoMarshal::variant_to_mono_object(ptr->get_value_at_index(index), type); +void godot_icall_Dictionary_KeyValuePairAt(Dictionary *ptr, int index, Variant *r_key, Variant *r_value) { + memnew_placement(r_key, Variant(ptr->get_key_at_index(index))); + memnew_placement(r_value, Variant(ptr->get_value_at_index(index))); } void godot_icall_Dictionary_Add(Dictionary *ptr, MonoObject *key, MonoObject *value) { @@ -271,8 +225,8 @@ MonoBoolean godot_icall_Dictionary_ContainsKey(Dictionary *ptr, MonoObject *key) return ptr->has(GDMonoMarshal::mono_object_to_variant(key)); } -Dictionary *godot_icall_Dictionary_Duplicate(Dictionary *ptr, MonoBoolean deep) { - return memnew(Dictionary(ptr->duplicate(deep))); +void godot_icall_Dictionary_Duplicate(Dictionary *ptr, MonoBoolean deep, Dictionary *r_dest) { + memnew_placement(r_dest, Dictionary(ptr->duplicate(deep))); } MonoBoolean godot_icall_Dictionary_RemoveKey(Dictionary *ptr, MonoObject *key) { @@ -292,34 +246,16 @@ MonoBoolean godot_icall_Dictionary_Remove(Dictionary *ptr, MonoObject *key, Mono return false; } -MonoBoolean godot_icall_Dictionary_TryGetValue(Dictionary *ptr, MonoObject *key, MonoObject **value) { +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()); } @@ -327,9 +263,7 @@ MonoString *godot_icall_Dictionary_ToString(Dictionary *ptr) { void godot_register_collections_icalls() { GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Ctor", godot_icall_Array_Ctor); GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Ctor_MonoArray", godot_icall_Array_Ctor_MonoArray); - GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Dtor", godot_icall_Array_Dtor); GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_At", godot_icall_Array_At); - GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_At_Generic", godot_icall_Array_At_Generic); GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_SetAt", godot_icall_Array_SetAt); GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Count", godot_icall_Array_Count); GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Add", godot_icall_Array_Add); @@ -344,20 +278,16 @@ void godot_register_collections_icalls() { GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_RemoveAt", godot_icall_Array_RemoveAt); GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Resize", godot_icall_Array_Resize); GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Shuffle", godot_icall_Array_Shuffle); - GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Generic_GetElementTypeInfo", godot_icall_Array_Generic_GetElementTypeInfo); GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_ToString", godot_icall_Array_ToString); GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Ctor", godot_icall_Dictionary_Ctor); - GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Dtor", godot_icall_Dictionary_Dtor); GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_GetValue", godot_icall_Dictionary_GetValue); - GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_GetValue_Generic", godot_icall_Dictionary_GetValue_Generic); GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_SetValue", godot_icall_Dictionary_SetValue); GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Keys", godot_icall_Dictionary_Keys); GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Values", godot_icall_Dictionary_Values); GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Count", godot_icall_Dictionary_Count); GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_KeyValuePairs", godot_icall_Dictionary_KeyValuePairs); GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_KeyValuePairAt", godot_icall_Dictionary_KeyValuePairAt); - GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_KeyValuePairAt_Generic", godot_icall_Dictionary_KeyValuePairAt_Generic); GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Add", godot_icall_Dictionary_Add); GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Clear", godot_icall_Dictionary_Clear); GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Contains", godot_icall_Dictionary_Contains); @@ -366,9 +296,5 @@ void godot_register_collections_icalls() { GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_RemoveKey", godot_icall_Dictionary_RemoveKey); GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Remove", godot_icall_Dictionary_Remove); GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_TryGetValue", godot_icall_Dictionary_TryGetValue); - GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_TryGetValue_Generic", godot_icall_Dictionary_TryGetValue_Generic); - GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Generic_GetValueTypeInfo", godot_icall_Dictionary_Generic_GetValueTypeInfo); GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_ToString", godot_icall_Dictionary_ToString); } - -#endif // MONO_GLUE_ENABLED diff --git a/modules/mono/glue/gd_glue.cpp b/modules/mono/glue/gd_glue.cpp index 8b1c2b729ea..b3158318190 100644 --- a/modules/mono/glue/gd_glue.cpp +++ b/modules/mono/glue/gd_glue.cpp @@ -28,8 +28,6 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifdef MONO_GLUE_ENABLED - #include "core/io/marshalls.h" #include "core/os/os.h" #include "core/string/ustring.h" @@ -41,10 +39,9 @@ #include "../mono_gd/gd_mono_marshal.h" #include "../mono_gd/gd_mono_utils.h" -MonoObject *godot_icall_GD_bytes2var(MonoArray *p_bytes, MonoBoolean p_allow_objects) { +MonoObject *godot_icall_GD_bytes2var(PackedByteArray *p_bytes, MonoBoolean p_allow_objects) { Variant ret; - PackedByteArray varr = GDMonoMarshal::mono_array_to_PackedByteArray(p_bytes); - Error err = decode_variant(ret, varr.ptr(), varr.size(), nullptr, p_allow_objects); + Error err = decode_variant(ret, p_bytes->ptr(), p_bytes->size(), nullptr, p_allow_objects); if (err != OK) { ret = RTR("Not enough bytes for decoding bytes, or invalid format."); } @@ -285,18 +282,17 @@ void godot_icall_GD_pushwarning(MonoString *p_str) { WARN_PRINT(GDMonoMarshal::mono_string_to_godot(p_str)); } -MonoArray *godot_icall_GD_var2bytes(MonoObject *p_var, MonoBoolean p_full_objects) { +void godot_icall_GD_var2bytes(MonoObject *p_var, MonoBoolean p_full_objects, PackedByteArray *r_bytes) { + memnew_placement(r_bytes, PackedByteArray); + Variant var = GDMonoMarshal::mono_object_to_variant(p_var); - PackedByteArray barr; int len; Error err = encode_variant(var, nullptr, len, p_full_objects); - ERR_FAIL_COND_V_MSG(err != OK, nullptr, "Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID)."); + ERR_FAIL_COND_MSG(err != OK, "Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID)."); - barr.resize(len); - encode_variant(var, barr.ptrw(), len, p_full_objects); - - return GDMonoMarshal::PackedByteArray_to_mono_array(barr); + r_bytes->resize(len); + encode_variant(var, r_bytes->ptrw(), len, p_full_objects); } MonoString *godot_icall_GD_var2str(MonoObject *p_var) { @@ -344,5 +340,3 @@ void godot_register_gd_icalls() { // Dispatcher GDMonoUtils::add_internal_call("Godot.Dispatcher::godot_icall_DefaultGodotTaskScheduler", godot_icall_DefaultGodotTaskScheduler); } - -#endif // MONO_GLUE_ENABLED diff --git a/modules/mono/glue/glue_header.h b/modules/mono/glue/glue_header.h deleted file mode 100644 index f9ad1a98931..00000000000 --- a/modules/mono/glue/glue_header.h +++ /dev/null @@ -1,91 +0,0 @@ -/*************************************************************************/ -/* glue_header.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef GLUE_HEADER_H -#define GLUE_HEADER_H - -#ifdef MONO_GLUE_ENABLED - -#include "../mono_gd/gd_mono_marshal.h" - -void godot_register_collections_icalls(); -void godot_register_gd_icalls(); -void godot_register_string_name_icalls(); -void godot_register_nodepath_icalls(); -void godot_register_callable_icalls(); -void godot_register_object_icalls(); -void godot_register_rid_icalls(); -void godot_register_string_icalls(); -void godot_register_scene_tree_icalls(); - -/** - * Registers internal calls that were not generated. This function is called - * from the generated GodotSharpBindings::register_generated_icalls() function. - */ -void godot_register_glue_header_icalls() { - godot_register_collections_icalls(); - godot_register_gd_icalls(); - godot_register_string_name_icalls(); - godot_register_nodepath_icalls(); - godot_register_callable_icalls(); - godot_register_object_icalls(); - godot_register_rid_icalls(); - godot_register_string_icalls(); - godot_register_scene_tree_icalls(); -} - -// Used by the generated glue - -#include "core/config/engine.h" -#include "core/object/class_db.h" -#include "core/object/method_bind.h" -#include "core/object/ref_counted.h" -#include "core/string/node_path.h" -#include "core/string/ustring.h" -#include "core/typedefs.h" -#include "core/variant/array.h" -#include "core/variant/dictionary.h" - -#include "../mono_gd/gd_mono_class.h" -#include "../mono_gd/gd_mono_internals.h" -#include "../mono_gd/gd_mono_utils.h" - -#define GODOTSHARP_INSTANCE_OBJECT(m_instance, m_type) \ - static ClassDB::ClassInfo *ci = nullptr; \ - if (!ci) { \ - ci = ClassDB::classes.getptr(m_type); \ - } \ - Object *m_instance = ci->creation_func(); - -#include "arguments_vector.h" - -#endif // MONO_GLUE_ENABLED - -#endif // GLUE_HEADER_H diff --git a/modules/mono/glue/nodepath_glue.cpp b/modules/mono/glue/node_path_glue.cpp similarity index 77% rename from modules/mono/glue/nodepath_glue.cpp rename to modules/mono/glue/node_path_glue.cpp index 16e1509eb04..770ed312604 100644 --- a/modules/mono/glue/nodepath_glue.cpp +++ b/modules/mono/glue/node_path_glue.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* nodepath_glue.cpp */ +/* node_path_glue.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,26 +28,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifdef MONO_GLUE_ENABLED - #include "core/string/node_path.h" #include "core/string/ustring.h" #include "../mono_gd/gd_mono_marshal.h" -NodePath *godot_icall_NodePath_Ctor(MonoString *p_path) { - return memnew(NodePath(GDMonoMarshal::mono_string_to_godot(p_path))); -} - -void godot_icall_NodePath_Dtor(NodePath *p_ptr) { - ERR_FAIL_NULL(p_ptr); - memdelete(p_ptr); -} - -MonoString *godot_icall_NodePath_operator_String(NodePath *p_np) { - return GDMonoMarshal::mono_string_from_godot(p_np->operator String()); -} - MonoBoolean godot_icall_NodePath_is_absolute(NodePath *p_ptr) { return (MonoBoolean)p_ptr->is_absolute(); } @@ -76,18 +61,11 @@ MonoString *godot_icall_NodePath_get_concatenated_subnames(NodePath *p_ptr) { return GDMonoMarshal::mono_string_from_godot(p_ptr->get_concatenated_subnames()); } -NodePath *godot_icall_NodePath_get_as_property_path(NodePath *p_ptr) { - return memnew(NodePath(p_ptr->get_as_property_path())); +void godot_icall_NodePath_get_as_property_path(NodePath *p_ptr, NodePath *r_dest) { + *r_dest = p_ptr->get_as_property_path(); } -MonoBoolean godot_icall_NodePath_is_empty(NodePath *p_ptr) { - return (MonoBoolean)p_ptr->is_empty(); -} - -void godot_register_nodepath_icalls() { - GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_Ctor", godot_icall_NodePath_Ctor); - GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_Dtor", godot_icall_NodePath_Dtor); - GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_operator_String", godot_icall_NodePath_operator_String); +void godot_register_node_path_icalls() { GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_as_property_path", godot_icall_NodePath_get_as_property_path); GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_concatenated_names", godot_icall_NodePath_get_concatenated_names); GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_concatenated_subnames", godot_icall_NodePath_get_concatenated_subnames); @@ -96,7 +74,4 @@ void godot_register_nodepath_icalls() { GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_subname", godot_icall_NodePath_get_subname); GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_subname_count", godot_icall_NodePath_get_subname_count); GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_is_absolute", godot_icall_NodePath_is_absolute); - GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_is_empty", godot_icall_NodePath_is_empty); } - -#endif // MONO_GLUE_ENABLED diff --git a/modules/mono/glue/rid_glue.cpp b/modules/mono/glue/placeholder_glue.cpp similarity index 70% rename from modules/mono/glue/rid_glue.cpp rename to modules/mono/glue/placeholder_glue.cpp index 3e095645398..edac231bb4c 100644 --- a/modules/mono/glue/rid_glue.cpp +++ b/modules/mono/glue/placeholder_glue.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* rid_glue.cpp */ +/* placeholder_glue.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,37 +28,29 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifdef MONO_GLUE_ENABLED +#ifndef GLUE_HEADER_H +#define GLUE_HEADER_H -#include "core/io/resource.h" -#include "core/object/class_db.h" -#include "core/templates/rid.h" +#include "core/object/object.h" -#include "../mono_gd/gd_mono_marshal.h" +#include "../mono_gd/gd_mono_internals.h" +#include "../mono_gd/gd_mono_utils.h" -RID *godot_icall_RID_Ctor(Object *p_from) { - Resource *res_from = Object::cast_to(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 +#endif // GLUE_HEADER_H diff --git a/modules/mono/glue/runtime_interop.cpp b/modules/mono/glue/runtime_interop.cpp new file mode 100644 index 00000000000..a9c47f8880b --- /dev/null +++ b/modules/mono/glue/runtime_interop.cpp @@ -0,0 +1,786 @@ +/*************************************************************************/ +/* runtime_interop.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "core/config/engine.h" +#include "core/object/class_db.h" +#include "core/object/method_bind.h" +#include "core/string/string_name.h" + +#include "../interop_types.h" + +#include "modules/mono/managed_callable.h" +#include "modules/mono/signal_awaiter_utils.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +#define MAYBE_UNUSED [[maybe_unused]] +#else +#define MAYBE_UNUSED +#endif + +#ifdef __GNUC__ +#define GD_PINVOKE_EXPORT MAYBE_UNUSED __attribute__((visibility("default"))) +#elif defined(_WIN32) +#define GD_PINVOKE_EXPORT MAYBE_UNUSED __declspec(dllexport) +#else +#define GD_PINVOKE_EXPORT MAYBE_UNUSED +#endif + +typedef Object *(*godotsharp_class_creation_func)(); + +GD_PINVOKE_EXPORT MethodBind *godotsharp_method_bind_get_method(const StringName *p_classname, const char16_t *p_methodname) { + return ClassDB::get_method(*p_classname, StringName(String::utf16(p_methodname))); +} + +GD_PINVOKE_EXPORT godotsharp_class_creation_func godotsharp_get_class_constructor(const StringName *p_classname) { + ClassDB::ClassInfo *class_info = ClassDB::classes.getptr(*p_classname); + if (class_info) { + return class_info->creation_func; + } + return nullptr; +} + +GD_PINVOKE_EXPORT Object *godotsharp_invoke_class_constructor(godotsharp_class_creation_func p_creation_func) { + return p_creation_func(); +} + +GD_PINVOKE_EXPORT Object *godotsharp_engine_get_singleton(const String *p_name) { + return Engine::get_singleton()->get_singleton_object(*p_name); +} + +GD_PINVOKE_EXPORT void godotsharp_ref_destroy(Ref *p_instance) { + p_instance->~Ref(); +} + +GD_PINVOKE_EXPORT void godotsharp_string_name_new_from_string(StringName *r_dest, const String *p_name) { + memnew_placement(r_dest, StringName(*p_name)); +} + +GD_PINVOKE_EXPORT void godotsharp_node_path_new_from_string(NodePath *r_dest, const String *p_name) { + memnew_placement(r_dest, NodePath(*p_name)); +} + +GD_PINVOKE_EXPORT void godotsharp_string_name_as_string(String *r_dest, const StringName *p_name) { + memnew_placement(r_dest, String(p_name->operator String())); +} + +GD_PINVOKE_EXPORT void godotsharp_node_path_as_string(String *r_dest, const NodePath *p_np) { + memnew_placement(r_dest, String(p_np->operator String())); +} + +GD_PINVOKE_EXPORT godot_packed_array godotsharp_packed_byte_array_new_mem_copy(const uint8_t *p_src, int32_t p_length) { + godot_packed_array ret; + memnew_placement(&ret, PackedByteArray); + PackedByteArray *array = reinterpret_cast(&ret); + array->resize(p_length); + uint8_t *dst = array->ptrw(); + memcpy(dst, p_src, p_length * sizeof(uint8_t)); + return ret; +} + +GD_PINVOKE_EXPORT godot_packed_array godotsharp_packed_int32_array_new_mem_copy(const int32_t *p_src, int32_t p_length) { + godot_packed_array ret; + memnew_placement(&ret, PackedInt32Array); + PackedInt32Array *array = reinterpret_cast(&ret); + array->resize(p_length); + int32_t *dst = array->ptrw(); + memcpy(dst, p_src, p_length * sizeof(int32_t)); + return ret; +} + +GD_PINVOKE_EXPORT godot_packed_array godotsharp_packed_int64_array_new_mem_copy(const int64_t *p_src, int32_t p_length) { + godot_packed_array ret; + memnew_placement(&ret, PackedInt64Array); + PackedInt64Array *array = reinterpret_cast(&ret); + array->resize(p_length); + int64_t *dst = array->ptrw(); + memcpy(dst, p_src, p_length * sizeof(int64_t)); + return ret; +} + +GD_PINVOKE_EXPORT godot_packed_array godotsharp_packed_float32_array_new_mem_copy(const float *p_src, int32_t p_length) { + godot_packed_array ret; + memnew_placement(&ret, PackedFloat32Array); + PackedFloat32Array *array = reinterpret_cast(&ret); + array->resize(p_length); + float *dst = array->ptrw(); + memcpy(dst, p_src, p_length * sizeof(float)); + return ret; +} + +GD_PINVOKE_EXPORT godot_packed_array godotsharp_packed_float64_array_new_mem_copy(const double *p_src, int32_t p_length) { + godot_packed_array ret; + memnew_placement(&ret, PackedFloat64Array); + PackedFloat64Array *array = reinterpret_cast(&ret); + array->resize(p_length); + double *dst = array->ptrw(); + memcpy(dst, p_src, p_length * sizeof(double)); + return ret; +} + +GD_PINVOKE_EXPORT godot_packed_array godotsharp_packed_vector2_array_new_mem_copy(const Vector2 *p_src, int32_t p_length) { + godot_packed_array ret; + memnew_placement(&ret, PackedVector2Array); + PackedVector2Array *array = reinterpret_cast(&ret); + array->resize(p_length); + Vector2 *dst = array->ptrw(); + memcpy(dst, p_src, p_length * sizeof(Vector2)); + return ret; +} + +GD_PINVOKE_EXPORT godot_packed_array godotsharp_packed_vector3_array_new_mem_copy(const Vector3 *p_src, int32_t p_length) { + godot_packed_array ret; + memnew_placement(&ret, PackedVector3Array); + PackedVector3Array *array = reinterpret_cast(&ret); + array->resize(p_length); + Vector3 *dst = array->ptrw(); + memcpy(dst, p_src, p_length * sizeof(Vector3)); + return ret; +} + +GD_PINVOKE_EXPORT godot_packed_array godotsharp_packed_color_array_new_mem_copy(const Color *p_src, int32_t p_length) { + godot_packed_array ret; + memnew_placement(&ret, PackedColorArray); + PackedColorArray *array = reinterpret_cast(&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(MethodBind *p_method_bind, Object *p_instance, const void **p_args, void *p_ret) { + p_method_bind->ptrcall(p_instance, p_args, p_ret); +} + +GD_PINVOKE_EXPORT godot_variant godotsharp_method_bind_call(MethodBind *p_method_bind, Object *p_instance, const godot_variant **p_args, const int p_arg_count, Callable::CallError *p_call_error) { + godot_variant ret; + memnew_placement(&ret, Variant()); + + Variant *ret_val = (Variant *)&ret; + + *ret_val = p_method_bind->call(p_instance, (const Variant **)p_args, p_arg_count, *p_call_error); + + return ret; +} + +// variant.h + +GD_PINVOKE_EXPORT void godotsharp_variant_new_string_name(godot_variant *r_dest, const StringName *p_s) { + memnew_placement(r_dest, Variant(*p_s)); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_node_path(godot_variant *r_dest, const NodePath *p_np) { + memnew_placement(r_dest, Variant(*p_np)); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_object(godot_variant *r_dest, const Object *p_obj) { + memnew_placement(r_dest, Variant(p_obj)); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_transform2d(godot_variant *r_dest, const Transform2D *p_t2d) { + memnew_placement(r_dest, Variant(*p_t2d)); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_vector4(godot_variant *r_dest, const Vector4 *p_vec4) { + memnew_placement(r_dest, Variant(*p_vec4)); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_vector4i(godot_variant *r_dest, const Vector4i *p_vec4i) { + memnew_placement(r_dest, Variant(*p_vec4i)); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_basis(godot_variant *r_dest, const Basis *p_basis) { + memnew_placement(r_dest, Variant(*p_basis)); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_transform3d(godot_variant *r_dest, const Transform3D *p_trans) { + memnew_placement(r_dest, Variant(*p_trans)); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_projection(godot_variant *r_dest, const Projection *p_proj) { + memnew_placement(r_dest, Variant(*p_proj)); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_aabb(godot_variant *r_dest, const AABB *p_aabb) { + memnew_placement(r_dest, Variant(*p_aabb)); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_dictionary(godot_variant *r_dest, const Dictionary *p_dict) { + memnew_placement(r_dest, Variant(*p_dict)); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_array(godot_variant *r_dest, const Array *p_arr) { + memnew_placement(r_dest, Variant(*p_arr)); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_packed_byte_array(godot_variant *r_dest, const PackedByteArray *p_pba) { + memnew_placement(r_dest, Variant(*p_pba)); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_packed_int32_array(godot_variant *r_dest, const PackedInt32Array *p_pia) { + memnew_placement(r_dest, Variant(*p_pia)); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_packed_int64_array(godot_variant *r_dest, const PackedInt64Array *p_pia) { + memnew_placement(r_dest, Variant(*p_pia)); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_packed_float32_array(godot_variant *r_dest, const PackedFloat32Array *p_pra) { + memnew_placement(r_dest, Variant(*p_pra)); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_packed_float64_array(godot_variant *r_dest, const PackedFloat64Array *p_pra) { + memnew_placement(r_dest, Variant(*p_pra)); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_packed_string_array(godot_variant *r_dest, const PackedStringArray *p_psa) { + memnew_placement(r_dest, Variant(*p_psa)); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_packed_vector2_array(godot_variant *r_dest, const PackedVector2Array *p_pv2a) { + memnew_placement(r_dest, Variant(*p_pv2a)); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_packed_vector3_array(godot_variant *r_dest, const PackedVector3Array *p_pv3a) { + memnew_placement(r_dest, Variant(*p_pv3a)); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_new_packed_color_array(godot_variant *r_dest, const PackedColorArray *p_pca) { + memnew_placement(r_dest, Variant(*p_pca)); +} + +GD_PINVOKE_EXPORT bool godotsharp_variant_as_bool(const Variant *p_self) { + return p_self->operator bool(); +} + +GD_PINVOKE_EXPORT int64_t godotsharp_variant_as_int(const Variant *p_self) { + return p_self->operator int64_t(); +} + +GD_PINVOKE_EXPORT double godotsharp_variant_as_float(const Variant *p_self) { + return p_self->operator double(); +} + +GD_PINVOKE_EXPORT godot_string godotsharp_variant_as_string(const Variant *p_self) { + godot_string raw_dest; + String *dest = (String *)&raw_dest; + memnew_placement(dest, String(p_self->operator String())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_vector2 godotsharp_variant_as_vector2(const Variant *p_self) { + godot_vector2 raw_dest; + Vector2 *dest = (Vector2 *)&raw_dest; + memnew_placement(dest, Vector2(p_self->operator Vector2())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_vector2i godotsharp_variant_as_vector2i(const Variant *p_self) { + godot_vector2i raw_dest; + Vector2i *dest = (Vector2i *)&raw_dest; + memnew_placement(dest, Vector2i(p_self->operator Vector2i())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_rect2 godotsharp_variant_as_rect2(const Variant *p_self) { + godot_rect2 raw_dest; + Rect2 *dest = (Rect2 *)&raw_dest; + memnew_placement(dest, Rect2(p_self->operator Rect2())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_rect2i godotsharp_variant_as_rect2i(const Variant *p_self) { + godot_rect2i raw_dest; + Rect2i *dest = (Rect2i *)&raw_dest; + memnew_placement(dest, Rect2i(p_self->operator Rect2i())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_vector3 godotsharp_variant_as_vector3(const Variant *p_self) { + godot_vector3 raw_dest; + Vector3 *dest = (Vector3 *)&raw_dest; + memnew_placement(dest, Vector3(p_self->operator Vector3())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_vector3i godotsharp_variant_as_vector3i(const Variant *p_self) { + godot_vector3i raw_dest; + Vector3i *dest = (Vector3i *)&raw_dest; + memnew_placement(dest, Vector3i(p_self->operator Vector3i())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_transform2d godotsharp_variant_as_transform2d(const Variant *p_self) { + godot_transform2d raw_dest; + Transform2D *dest = (Transform2D *)&raw_dest; + memnew_placement(dest, Transform2D(p_self->operator Transform2D())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_vector4 godotsharp_variant_as_vector4(const Variant *p_self) { + godot_vector4 raw_dest; + Vector4 *dest = (Vector4 *)&raw_dest; + memnew_placement(dest, Vector4(p_self->operator Vector4())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_vector4i godotsharp_variant_as_vector4i(const Variant *p_self) { + godot_vector4i raw_dest; + Vector4i *dest = (Vector4i *)&raw_dest; + memnew_placement(dest, Vector4i(p_self->operator Vector4i())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_plane godotsharp_variant_as_plane(const Variant *p_self) { + godot_plane raw_dest; + Plane *dest = (Plane *)&raw_dest; + memnew_placement(dest, Plane(p_self->operator Plane())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_quaternion godotsharp_variant_as_quaternion(const Variant *p_self) { + godot_quaternion raw_dest; + Quaternion *dest = (Quaternion *)&raw_dest; + memnew_placement(dest, Quaternion(p_self->operator Quaternion())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_aabb godotsharp_variant_as_aabb(const Variant *p_self) { + godot_aabb raw_dest; + AABB *dest = (AABB *)&raw_dest; + memnew_placement(dest, AABB(p_self->operator ::AABB())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_basis godotsharp_variant_as_basis(const Variant *p_self) { + godot_basis raw_dest; + Basis *dest = (Basis *)&raw_dest; + memnew_placement(dest, Basis(p_self->operator Basis())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_transform3d godotsharp_variant_as_transform3d(const Variant *p_self) { + godot_transform3d raw_dest; + Transform3D *dest = (Transform3D *)&raw_dest; + memnew_placement(dest, Transform3D(p_self->operator Transform3D())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_projection godotsharp_variant_as_projection(const Variant *p_self) { + godot_projection raw_dest; + Projection *dest = (Projection *)&raw_dest; + memnew_placement(dest, Projection(p_self->operator Projection())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_color godotsharp_variant_as_color(const Variant *p_self) { + godot_color raw_dest; + Color *dest = (Color *)&raw_dest; + memnew_placement(dest, Color(p_self->operator Color())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_string_name godotsharp_variant_as_string_name(const Variant *p_self) { + godot_string_name raw_dest; + StringName *dest = (StringName *)&raw_dest; + memnew_placement(dest, StringName(p_self->operator StringName())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_node_path godotsharp_variant_as_node_path(const Variant *p_self) { + godot_node_path raw_dest; + NodePath *dest = (NodePath *)&raw_dest; + memnew_placement(dest, NodePath(p_self->operator NodePath())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_rid godotsharp_variant_as_rid(const Variant *p_self) { + godot_rid raw_dest; + RID *dest = (RID *)&raw_dest; + memnew_placement(dest, RID(p_self->operator ::RID())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_callable godotsharp_variant_as_callable(const Variant *p_self) { + godot_callable raw_dest; + Callable *dest = (Callable *)&raw_dest; + memnew_placement(dest, Callable(p_self->operator Callable())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_signal godotsharp_variant_as_signal(const Variant *p_self) { + godot_signal raw_dest; + Signal *dest = (Signal *)&raw_dest; + memnew_placement(dest, Signal(p_self->operator Signal())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_dictionary godotsharp_variant_as_dictionary(const Variant *p_self) { + godot_dictionary raw_dest; + Dictionary *dest = (Dictionary *)&raw_dest; + memnew_placement(dest, Dictionary(p_self->operator Dictionary())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_array godotsharp_variant_as_array(const Variant *p_self) { + godot_array raw_dest; + Array *dest = (Array *)&raw_dest; + memnew_placement(dest, Array(p_self->operator Array())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_packed_array godotsharp_variant_as_packed_byte_array(const Variant *p_self) { + godot_packed_array raw_dest; + PackedByteArray *dest = (PackedByteArray *)&raw_dest; + memnew_placement(dest, PackedByteArray(p_self->operator PackedByteArray())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_packed_array godotsharp_variant_as_packed_int32_array(const Variant *p_self) { + godot_packed_array raw_dest; + PackedInt32Array *dest = (PackedInt32Array *)&raw_dest; + memnew_placement(dest, PackedInt32Array(p_self->operator PackedInt32Array())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_packed_array godotsharp_variant_as_packed_int64_array(const Variant *p_self) { + godot_packed_array raw_dest; + PackedInt64Array *dest = (PackedInt64Array *)&raw_dest; + memnew_placement(dest, PackedInt64Array(p_self->operator PackedInt64Array())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_packed_array godotsharp_variant_as_packed_float32_array(const Variant *p_self) { + godot_packed_array raw_dest; + PackedFloat32Array *dest = (PackedFloat32Array *)&raw_dest; + memnew_placement(dest, PackedFloat32Array(p_self->operator PackedFloat32Array())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_packed_array godotsharp_variant_as_packed_float64_array(const Variant *p_self) { + godot_packed_array raw_dest; + PackedFloat64Array *dest = (PackedFloat64Array *)&raw_dest; + memnew_placement(dest, PackedFloat64Array(p_self->operator PackedFloat64Array())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_packed_array godotsharp_variant_as_packed_string_array(const Variant *p_self) { + godot_packed_array raw_dest; + PackedStringArray *dest = (PackedStringArray *)&raw_dest; + memnew_placement(dest, PackedStringArray(p_self->operator PackedStringArray())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_packed_array godotsharp_variant_as_packed_vector2_array(const Variant *p_self) { + godot_packed_array raw_dest; + PackedVector2Array *dest = (PackedVector2Array *)&raw_dest; + memnew_placement(dest, PackedVector2Array(p_self->operator PackedVector2Array())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_packed_array godotsharp_variant_as_packed_vector3_array(const Variant *p_self) { + godot_packed_array raw_dest; + PackedVector3Array *dest = (PackedVector3Array *)&raw_dest; + memnew_placement(dest, PackedVector3Array(p_self->operator PackedVector3Array())); + return raw_dest; +} + +GD_PINVOKE_EXPORT godot_packed_array godotsharp_variant_as_packed_color_array(const Variant *p_self) { + godot_packed_array raw_dest; + PackedColorArray *dest = (PackedColorArray *)&raw_dest; + memnew_placement(dest, PackedColorArray(p_self->operator PackedColorArray())); + return raw_dest; +} + +// string.h + +GD_PINVOKE_EXPORT void godotsharp_string_new_with_utf16_chars(String *r_dest, const char16_t *p_contents) { + memnew_placement(r_dest, String()); + r_dest->parse_utf16(p_contents); +} + +// string_name.h + +GD_PINVOKE_EXPORT void godotsharp_string_name_new_copy(StringName *r_dest, const StringName *p_src) { + memnew_placement(r_dest, StringName(*p_src)); +} + +// node_path.h + +GD_PINVOKE_EXPORT void godotsharp_node_path_new_copy(NodePath *r_dest, const NodePath *p_src) { + memnew_placement(r_dest, NodePath(*p_src)); +} + +// array.h + +GD_PINVOKE_EXPORT void godotsharp_array_new_copy(Array *r_dest, const Array *p_src) { + memnew_placement(r_dest, Array(*p_src)); +} + +// dictionary.h + +GD_PINVOKE_EXPORT void godotsharp_dictionary_new_copy(Dictionary *r_dest, const Dictionary *p_src) { + memnew_placement(r_dest, Dictionary(*p_src)); +} + +// destroy functions + +GD_PINVOKE_EXPORT void godotsharp_packed_byte_array_destroy(PackedByteArray *p_self) { + p_self->~PackedByteArray(); +} + +GD_PINVOKE_EXPORT void godotsharp_packed_int32_array_destroy(PackedInt32Array *p_self) { + p_self->~PackedInt32Array(); +} + +GD_PINVOKE_EXPORT void godotsharp_packed_int64_array_destroy(PackedInt64Array *p_self) { + p_self->~PackedInt64Array(); +} + +GD_PINVOKE_EXPORT void godotsharp_packed_float32_array_destroy(PackedFloat32Array *p_self) { + p_self->~PackedFloat32Array(); +} + +GD_PINVOKE_EXPORT void godotsharp_packed_float64_array_destroy(PackedFloat64Array *p_self) { + p_self->~PackedFloat64Array(); +} + +GD_PINVOKE_EXPORT void godotsharp_packed_string_array_destroy(PackedStringArray *p_self) { + p_self->~PackedStringArray(); +} + +GD_PINVOKE_EXPORT void godotsharp_packed_vector2_array_destroy(PackedVector2Array *p_self) { + p_self->~PackedVector2Array(); +} + +GD_PINVOKE_EXPORT void godotsharp_packed_vector3_array_destroy(PackedVector3Array *p_self) { + p_self->~PackedVector3Array(); +} + +GD_PINVOKE_EXPORT void godotsharp_packed_color_array_destroy(PackedColorArray *p_self) { + p_self->~PackedColorArray(); +} + +GD_PINVOKE_EXPORT void godotsharp_variant_destroy(Variant *p_self) { + p_self->~Variant(); +} + +GD_PINVOKE_EXPORT void godotsharp_string_destroy(String *p_self) { + p_self->~String(); +} + +GD_PINVOKE_EXPORT void godotsharp_string_name_destroy(StringName *p_self) { + p_self->~StringName(); +} + +GD_PINVOKE_EXPORT void godotsharp_node_path_destroy(NodePath *p_self) { + p_self->~NodePath(); +} + +GD_PINVOKE_EXPORT void godotsharp_signal_destroy(Signal *p_self) { + p_self->~Signal(); +} + +GD_PINVOKE_EXPORT void godotsharp_callable_destroy(Callable *p_self) { + p_self->~Callable(); +} + +GD_PINVOKE_EXPORT void godotsharp_array_destroy(Array *p_self) { + p_self->~Array(); +} + +GD_PINVOKE_EXPORT void godotsharp_dictionary_destroy(Dictionary *p_self) { + p_self->~Dictionary(); +} + +#ifdef __cplusplus +} +#endif + +// We need this to prevent the functions from being stripped. +void *godotsharp_pinvoke_funcs[101] = { + (void *)godotsharp_method_bind_get_method, + (void *)godotsharp_get_class_constructor, + (void *)godotsharp_invoke_class_constructor, + (void *)godotsharp_engine_get_singleton, + (void *)godotsharp_ref_destroy, + (void *)godotsharp_string_name_new_from_string, + (void *)godotsharp_node_path_new_from_string, + (void *)godotsharp_string_name_as_string, + (void *)godotsharp_node_path_as_string, + (void *)godotsharp_packed_byte_array_new_mem_copy, + (void *)godotsharp_packed_int32_array_new_mem_copy, + (void *)godotsharp_packed_int64_array_new_mem_copy, + (void *)godotsharp_packed_float32_array_new_mem_copy, + (void *)godotsharp_packed_float64_array_new_mem_copy, + (void *)godotsharp_packed_vector2_array_new_mem_copy, + (void *)godotsharp_packed_vector3_array_new_mem_copy, + (void *)godotsharp_packed_color_array_new_mem_copy, + (void *)godotsharp_packed_string_array_add, + (void *)godotsharp_callable_new_with_delegate, + (void *)godotsharp_callable_get_data_for_marshalling, + (void *)godotsharp_method_bind_ptrcall, + (void *)godotsharp_method_bind_call, + (void *)godotsharp_variant_new_string_name, + (void *)godotsharp_variant_new_node_path, + (void *)godotsharp_variant_new_object, + (void *)godotsharp_variant_new_transform2d, + (void *)godotsharp_variant_new_vector4, + (void *)godotsharp_variant_new_vector4i, + (void *)godotsharp_variant_new_basis, + (void *)godotsharp_variant_new_transform3d, + (void *)godotsharp_variant_new_projection, + (void *)godotsharp_variant_new_aabb, + (void *)godotsharp_variant_new_dictionary, + (void *)godotsharp_variant_new_array, + (void *)godotsharp_variant_new_packed_byte_array, + (void *)godotsharp_variant_new_packed_int32_array, + (void *)godotsharp_variant_new_packed_int64_array, + (void *)godotsharp_variant_new_packed_float32_array, + (void *)godotsharp_variant_new_packed_float64_array, + (void *)godotsharp_variant_new_packed_string_array, + (void *)godotsharp_variant_new_packed_vector2_array, + (void *)godotsharp_variant_new_packed_vector3_array, + (void *)godotsharp_variant_new_packed_color_array, + (void *)godotsharp_variant_as_bool, + (void *)godotsharp_variant_as_int, + (void *)godotsharp_variant_as_float, + (void *)godotsharp_variant_as_string, + (void *)godotsharp_variant_as_vector2, + (void *)godotsharp_variant_as_vector2i, + (void *)godotsharp_variant_as_rect2, + (void *)godotsharp_variant_as_rect2i, + (void *)godotsharp_variant_as_vector3, + (void *)godotsharp_variant_as_vector3i, + (void *)godotsharp_variant_as_transform2d, + (void *)godotsharp_variant_as_vector4, + (void *)godotsharp_variant_as_vector4i, + (void *)godotsharp_variant_as_plane, + (void *)godotsharp_variant_as_quaternion, + (void *)godotsharp_variant_as_aabb, + (void *)godotsharp_variant_as_basis, + (void *)godotsharp_variant_as_transform3d, + (void *)godotsharp_variant_as_projection, + (void *)godotsharp_variant_as_color, + (void *)godotsharp_variant_as_string_name, + (void *)godotsharp_variant_as_node_path, + (void *)godotsharp_variant_as_rid, + (void *)godotsharp_variant_as_callable, + (void *)godotsharp_variant_as_signal, + (void *)godotsharp_variant_as_dictionary, + (void *)godotsharp_variant_as_array, + (void *)godotsharp_variant_as_packed_byte_array, + (void *)godotsharp_variant_as_packed_int32_array, + (void *)godotsharp_variant_as_packed_int64_array, + (void *)godotsharp_variant_as_packed_float32_array, + (void *)godotsharp_variant_as_packed_float64_array, + (void *)godotsharp_variant_as_packed_string_array, + (void *)godotsharp_variant_as_packed_vector2_array, + (void *)godotsharp_variant_as_packed_vector3_array, + (void *)godotsharp_variant_as_packed_color_array, + (void *)godotsharp_string_new_with_utf16_chars, + (void *)godotsharp_string_name_new_copy, + (void *)godotsharp_node_path_new_copy, + (void *)godotsharp_array_new_copy, + (void *)godotsharp_dictionary_new_copy, + (void *)godotsharp_packed_byte_array_destroy, + (void *)godotsharp_packed_int32_array_destroy, + (void *)godotsharp_packed_int64_array_destroy, + (void *)godotsharp_packed_float32_array_destroy, + (void *)godotsharp_packed_float64_array_destroy, + (void *)godotsharp_packed_string_array_destroy, + (void *)godotsharp_packed_vector2_array_destroy, + (void *)godotsharp_packed_vector3_array_destroy, + (void *)godotsharp_packed_color_array_destroy, + (void *)godotsharp_variant_destroy, + (void *)godotsharp_string_destroy, + (void *)godotsharp_string_name_destroy, + (void *)godotsharp_node_path_destroy, + (void *)godotsharp_signal_destroy, + (void *)godotsharp_callable_destroy, + (void *)godotsharp_array_destroy, + (void *)godotsharp_dictionary_destroy +}; diff --git a/modules/mono/glue/scene_tree_glue.cpp b/modules/mono/glue/scene_tree_glue.cpp index c60e7c4869c..55a46ad3685 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 fc6b13ceb3b..fe1c0b5f8cf 100644 --- a/modules/mono/glue/string_glue.cpp +++ b/modules/mono/glue/string_glue.cpp @@ -28,8 +28,6 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifdef MONO_GLUE_ENABLED - #include "core/string/ustring.h" #include "core/templates/vector.h" #include "core/variant/variant.h" @@ -81,5 +79,3 @@ void godot_register_string_icalls() { GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_sha256_text", godot_icall_String_sha256_text); GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_simplify_path", godot_icall_String_simplify_path); } - -#endif // MONO_GLUE_ENABLED diff --git a/modules/mono/glue/string_name_glue.cpp b/modules/mono/glue/string_name_glue.cpp deleted file mode 100644 index 46d15316ba3..00000000000 --- a/modules/mono/glue/string_name_glue.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/*************************************************************************/ -/* string_name_glue.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifdef MONO_GLUE_ENABLED - -#include "core/string/string_name.h" -#include "core/string/ustring.h" - -#include "../mono_gd/gd_mono_marshal.h" - -StringName *godot_icall_StringName_Ctor(MonoString *p_path) { - return memnew(StringName(GDMonoMarshal::mono_string_to_godot(p_path))); -} - -void godot_icall_StringName_Dtor(StringName *p_ptr) { - ERR_FAIL_NULL(p_ptr); - memdelete(p_ptr); -} - -MonoString *godot_icall_StringName_operator_String(StringName *p_np) { - return GDMonoMarshal::mono_string_from_godot(p_np->operator String()); -} - -MonoBoolean godot_icall_StringName_is_empty(StringName *p_ptr) { - return (MonoBoolean)(*p_ptr == StringName()); -} - -void godot_register_string_name_icalls() { - GDMonoUtils::add_internal_call("Godot.StringName::godot_icall_StringName_Ctor", godot_icall_StringName_Ctor); - GDMonoUtils::add_internal_call("Godot.StringName::godot_icall_StringName_Dtor", godot_icall_StringName_Dtor); - GDMonoUtils::add_internal_call("Godot.StringName::godot_icall_StringName_operator_String", godot_icall_StringName_operator_String); - GDMonoUtils::add_internal_call("Godot.StringName::godot_icall_StringName_is_empty", godot_icall_StringName_is_empty); -} - -#endif // MONO_GLUE_ENABLED diff --git a/modules/mono/godotsharp_defs.h b/modules/mono/godotsharp_defs.h index e5f1abe8d7d..a637e0be9b7 100644 --- a/modules/mono/godotsharp_defs.h +++ b/modules/mono/godotsharp_defs.h @@ -34,8 +34,8 @@ #define BINDINGS_NAMESPACE "Godot" #define BINDINGS_NAMESPACE_COLLECTIONS BINDINGS_NAMESPACE ".Collections" #define BINDINGS_GLOBAL_SCOPE_CLASS "GD" -#define BINDINGS_PTR_FIELD "ptr" -#define BINDINGS_NATIVE_NAME_FIELD "nativeName" +#define BINDINGS_PTR_FIELD "NativePtr" +#define BINDINGS_NATIVE_NAME_FIELD "NativeName" #define API_SOLUTION_NAME "GodotSharp" #define CORE_API_ASSEMBLY_NAME "GodotSharp" #define EDITOR_API_ASSEMBLY_NAME "GodotSharpEditor" diff --git a/modules/mono/interop_types.h b/modules/mono/interop_types.h new file mode 100644 index 00000000000..6942a91559a --- /dev/null +++ b/modules/mono/interop_types.h @@ -0,0 +1,208 @@ +/*************************************************************************/ +/* interop_types.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef INTEROP_TYPES_H +#define INTEROP_TYPES_H + +#include "core/math/math_defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +// This is taken from the old GDNative, which was removed. + +#define GODOT_VARIANT_SIZE (sizeof(real_t) * 4 + sizeof(int64_t)) + +typedef struct { + uint8_t _dont_touch_that[GODOT_VARIANT_SIZE]; +} godot_variant; + +#define GODOT_ARRAY_SIZE sizeof(void *) + +typedef struct { + uint8_t _dont_touch_that[GODOT_ARRAY_SIZE]; +} godot_array; + +#define GODOT_DICTIONARY_SIZE sizeof(void *) + +typedef struct { + uint8_t _dont_touch_that[GODOT_DICTIONARY_SIZE]; +} godot_dictionary; + +#define GODOT_STRING_SIZE sizeof(void *) + +typedef struct { + uint8_t _dont_touch_that[GODOT_STRING_SIZE]; +} godot_string; + +#define GODOT_STRING_NAME_SIZE sizeof(void *) + +typedef struct { + uint8_t _dont_touch_that[GODOT_STRING_NAME_SIZE]; +} godot_string_name; + +#define GODOT_PACKED_ARRAY_SIZE (2 * sizeof(void *)) + +typedef struct { + uint8_t _dont_touch_that[GODOT_PACKED_ARRAY_SIZE]; +} godot_packed_array; + +#define GODOT_VECTOR2_SIZE (sizeof(real_t) * 2) + +typedef struct { + uint8_t _dont_touch_that[GODOT_VECTOR2_SIZE]; +} godot_vector2; + +#define GODOT_VECTOR2I_SIZE (sizeof(int32_t) * 2) + +typedef struct { + uint8_t _dont_touch_that[GODOT_VECTOR2I_SIZE]; +} godot_vector2i; + +#define GODOT_RECT2_SIZE (sizeof(real_t) * 4) + +typedef struct godot_rect2 { + uint8_t _dont_touch_that[GODOT_RECT2_SIZE]; +} godot_rect2; + +#define GODOT_RECT2I_SIZE (sizeof(int32_t) * 4) + +typedef struct godot_rect2i { + uint8_t _dont_touch_that[GODOT_RECT2I_SIZE]; +} godot_rect2i; + +#define GODOT_VECTOR3_SIZE (sizeof(real_t) * 3) + +typedef struct { + uint8_t _dont_touch_that[GODOT_VECTOR3_SIZE]; +} godot_vector3; + +#define GODOT_VECTOR3I_SIZE (sizeof(int32_t) * 3) + +typedef struct { + uint8_t _dont_touch_that[GODOT_VECTOR3I_SIZE]; +} godot_vector3i; + +#define GODOT_TRANSFORM2D_SIZE (sizeof(real_t) * 6) + +typedef struct { + uint8_t _dont_touch_that[GODOT_TRANSFORM2D_SIZE]; +} godot_transform2d; + +#define GODOT_VECTOR4_SIZE (sizeof(real_t) * 4) + +typedef struct { + uint8_t _dont_touch_that[GODOT_VECTOR4_SIZE]; +} godot_vector4; + +#define GODOT_VECTOR4I_SIZE (sizeof(int32_t) * 4) + +typedef struct { + uint8_t _dont_touch_that[GODOT_VECTOR4I_SIZE]; +} godot_vector4i; + +#define GODOT_PLANE_SIZE (sizeof(real_t) * 4) + +typedef struct { + uint8_t _dont_touch_that[GODOT_PLANE_SIZE]; +} godot_plane; + +#define GODOT_QUATERNION_SIZE (sizeof(real_t) * 4) + +typedef struct { + uint8_t _dont_touch_that[GODOT_QUATERNION_SIZE]; +} godot_quaternion; + +#define GODOT_AABB_SIZE (sizeof(real_t) * 6) + +typedef struct { + uint8_t _dont_touch_that[GODOT_AABB_SIZE]; +} godot_aabb; + +#define GODOT_BASIS_SIZE (sizeof(real_t) * 9) + +typedef struct { + uint8_t _dont_touch_that[GODOT_BASIS_SIZE]; +} godot_basis; + +#define GODOT_TRANSFORM3D_SIZE (sizeof(real_t) * 12) + +typedef struct { + uint8_t _dont_touch_that[GODOT_TRANSFORM3D_SIZE]; +} godot_transform3d; + +#define GODOT_PROJECTION_SIZE (sizeof(real_t) * 4 * 4) + +typedef struct { + uint8_t _dont_touch_that[GODOT_PROJECTION_SIZE]; +} godot_projection; + +// Colors should always use 32-bit floats, so don't use real_t here. +#define GODOT_COLOR_SIZE (sizeof(float) * 4) + +typedef struct { + uint8_t _dont_touch_that[GODOT_COLOR_SIZE]; +} godot_color; + +#define GODOT_NODE_PATH_SIZE sizeof(void *) + +typedef struct { + uint8_t _dont_touch_that[GODOT_NODE_PATH_SIZE]; +} godot_node_path; + +#define GODOT_RID_SIZE sizeof(uint64_t) + +typedef struct { + uint8_t _dont_touch_that[GODOT_RID_SIZE]; +} godot_rid; + +// Alignment hardcoded in `core/variant/callable.h`. +#define GODOT_CALLABLE_SIZE (16) + +typedef struct { + uint8_t _dont_touch_that[GODOT_CALLABLE_SIZE]; +} godot_callable; + +// Alignment hardcoded in `core/variant/callable.h`. +#define GODOT_SIGNAL_SIZE (16) + +typedef struct { + uint8_t _dont_touch_that[GODOT_SIGNAL_SIZE]; +} godot_signal; + +#ifdef __cplusplus +} +#endif + +#endif // INTEROP_TYPES_H diff --git a/modules/mono/managed_callable.cpp b/modules/mono/managed_callable.cpp index c159bb9eeaf..72b353d8c1a 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,8 +69,7 @@ bool ManagedCallable::compare_less(const CallableCustom *p_a, const CallableCust } uint32_t ManagedCallable::hash() const { - uint32_t hash = delegate_invoke->get_name().hash(); - return hash_murmur3_one_64(delegate_handle.handle, hash); + return hash_murmur3_one_64((uint64_t)delegate_handle); } String ManagedCallable::get_as_text() const { @@ -91,41 +93,34 @@ void ManagedCallable::call(const Variant **p_arguments, int p_argcount, Variant r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; // Can't find anything better r_return_value = Variant(); -#ifdef GD_MONO_HOT_RELOAD - // Lost during hot-reload - ERR_FAIL_NULL(delegate_invoke); - ERR_FAIL_COND(delegate_handle.is_released()); -#endif - - ERR_FAIL_COND(delegate_invoke->get_parameters_count() < p_argcount); - - MonoObject *delegate = delegate_handle.get_target(); - MonoException *exc = nullptr; - MonoObject *ret = delegate_invoke->invoke(delegate, p_arguments, &exc); + CACHED_METHOD_THUNK(DelegateUtils, InvokeWithVariantArgs) + .invoke(delegate_handle, p_arguments, + p_argcount, &r_return_value, &exc); if (exc) { GDMonoUtils::set_pending_exception(exc); } else { - r_return_value = GDMonoMarshal::mono_object_to_variant(ret); r_call_error.error = Callable::CallError::CALL_OK; } } -void ManagedCallable::set_delegate(MonoDelegate *p_delegate) { - delegate_handle = MonoGCHandleData::new_strong_handle((MonoObject *)p_delegate); - MonoMethod *delegate_invoke_raw = mono_get_delegate_invoke(mono_object_get_class((MonoObject *)p_delegate)); - const StringName &delegate_invoke_name = CSharpLanguage::get_singleton()->get_string_names().delegate_invoke_method_name; - delegate_invoke = memnew(GDMonoMethod(delegate_invoke_name, delegate_invoke_raw)); // TODO: Use pooling for this GDMonoMethod instances +void ManagedCallable::release_delegate_handle() { + if (delegate_handle) { + MonoException *exc = nullptr; + CACHED_METHOD_THUNK(DelegateUtils, FreeGCHandle).invoke(delegate_handle, &exc); + + 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); @@ -133,6 +128,7 @@ ManagedCallable::ManagedCallable(MonoDelegate *p_delegate) { } #endif } +/* clang-format on */ ManagedCallable::~ManagedCallable() { #ifdef GD_MONO_HOT_RELOAD @@ -143,5 +139,5 @@ ManagedCallable::~ManagedCallable() { } #endif - delegate_handle.release(); + release_delegate_handle(); } diff --git a/modules/mono/managed_callable.h b/modules/mono/managed_callable.h index 11bee6cf600..56c11e7e450 100644 --- a/modules/mono/managed_callable.h +++ b/modules/mono/managed_callable.h @@ -42,8 +42,7 @@ class ManagedCallable : public CallableCustom { friend class CSharpLanguage; - MonoGCHandleData delegate_handle; - GDMonoMethod *delegate_invoke = nullptr; + void *delegate_handle = nullptr; #ifdef GD_MONO_HOT_RELOAD SelfList 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 d3d3bb2beff..eeaec30d739 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -408,10 +408,6 @@ void GDMono::initialize() { } void GDMono::initialize_load_assemblies() { -#ifndef MONO_GLUE_ENABLED - CRASH_NOW_MSG("Mono: This binary was built with 'mono_glue=no'; cannot load assemblies."); -#endif - // Load assemblies. The API and tools assemblies are required, // the application is aborted if these assemblies cannot be loaded. @@ -446,59 +442,34 @@ bool GDMono::_are_api_assemblies_out_of_sync() { return out_of_sync; } -namespace GodotSharpBindings { -#ifdef MONO_GLUE_ENABLED - -uint64_t get_core_api_hash(); -#ifdef TOOLS_ENABLED -uint64_t get_editor_api_hash(); -#endif -uint32_t get_bindings_version(); -uint32_t get_cs_glue_version(); - -void register_generated_icalls(); - -#else - -uint64_t get_core_api_hash() { - GD_UNREACHABLE(); -} -#ifdef TOOLS_ENABLED -uint64_t get_editor_api_hash() { - GD_UNREACHABLE(); -} -#endif -uint32_t get_bindings_version() { - GD_UNREACHABLE(); -} - -uint32_t get_cs_glue_version() { - GD_UNREACHABLE(); -} - -void register_generated_icalls() { - /* Fine, just do nothing */ -} - -#endif // MONO_GLUE_ENABLED -} // namespace GodotSharpBindings +void godot_register_collections_icalls(); +void godot_register_gd_icalls(); +void godot_register_node_path_icalls(); +void godot_register_object_icalls(); +void godot_register_rid_icalls(); +void godot_register_string_icalls(); +void godot_register_scene_tree_icalls(); +void godot_register_placeholder_icalls(); void GDMono::_register_internal_calls() { - GodotSharpBindings::register_generated_icalls(); + // Registers internal calls that were not generated. + godot_register_collections_icalls(); + godot_register_gd_icalls(); + godot_register_node_path_icalls(); + godot_register_object_icalls(); + godot_register_string_icalls(); + godot_register_scene_tree_icalls(); + godot_register_placeholder_icalls(); } void GDMono::_init_godot_api_hashes() { -#if defined(MONO_GLUE_ENABLED) && defined(DEBUG_METHODS_ENABLED) - if (get_api_core_hash() != GodotSharpBindings::get_core_api_hash()) { - ERR_PRINT("Mono: Core API hash mismatch."); - } +#ifdef DEBUG_METHODS_ENABLED + get_api_core_hash(); #ifdef TOOLS_ENABLED - if (get_api_editor_hash() != GodotSharpBindings::get_editor_api_hash()) { - ERR_PRINT("Mono: Editor API hash mismatch."); - } + get_api_editor_hash(); #endif // TOOLS_ENABLED -#endif // MONO_GLUE_ENABLED && DEBUG_METHODS_ENABLED +#endif // DEBUG_METHODS_ENABLED } void GDMono::_init_exception_policy() { @@ -601,16 +572,6 @@ ApiAssemblyInfo::Version ApiAssemblyInfo::Version::get_from_loaded_assembly(GDMo if (api_hash_field) { api_assembly_version.godot_api_hash = GDMonoMarshal::unbox(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; @@ -698,12 +659,8 @@ static bool try_get_cached_api_hash_for(const String &p_api_assemblies_dir, bool return false; } - r_out_of_sync = GodotSharpBindings::get_bindings_version() != (uint32_t)cfg->get_value("core", "bindings_version") || - GodotSharpBindings::get_cs_glue_version() != (uint32_t)cfg->get_value("core", "cs_glue_version") || - GodotSharpBindings::get_bindings_version() != (uint32_t)cfg->get_value("editor", "bindings_version") || - GodotSharpBindings::get_cs_glue_version() != (uint32_t)cfg->get_value("editor", "cs_glue_version") || - GodotSharpBindings::get_core_api_hash() != (uint64_t)cfg->get_value("core", "api_hash") || - GodotSharpBindings::get_editor_api_hash() != (uint64_t)cfg->get_value("editor", "api_hash"); + r_out_of_sync = GDMono::get_singleton()->get_api_core_hash() != (uint64_t)cfg->get_value("core", "api_hash") || + GDMono::get_singleton()->get_api_editor_hash() != (uint64_t)cfg->get_value("editor", "api_hash"); return true; } @@ -719,14 +676,9 @@ static void create_cached_api_hash_for(const String &p_api_assemblies_dir) { cfg->set_value("core", "modified_time", FileAccess::get_modified_time(core_api_assembly_path)); cfg->set_value("editor", "modified_time", FileAccess::get_modified_time(editor_api_assembly_path)); - cfg->set_value("core", "bindings_version", GodotSharpBindings::get_bindings_version()); - cfg->set_value("core", "cs_glue_version", GodotSharpBindings::get_cs_glue_version()); - cfg->set_value("editor", "bindings_version", GodotSharpBindings::get_bindings_version()); - cfg->set_value("editor", "cs_glue_version", GodotSharpBindings::get_cs_glue_version()); - // This assumes the prebuilt api assemblies we copied to the project are not out of sync - cfg->set_value("core", "api_hash", GodotSharpBindings::get_core_api_hash()); - cfg->set_value("editor", "api_hash", GodotSharpBindings::get_editor_api_hash()); + cfg->set_value("core", "api_hash", GDMono::get_singleton()->get_api_core_hash()); + cfg->set_value("editor", "api_hash", GDMono::get_singleton()->get_api_editor_hash()); Error err = cfg->save(cached_api_hash_path); ERR_FAIL_COND(err != OK); @@ -764,7 +716,7 @@ String GDMono::update_api_assemblies_from_prebuilt(const String &p_config, const bool api_assemblies_out_of_sync = false; if (p_core_api_out_of_sync && p_editor_api_out_of_sync) { - api_assemblies_out_of_sync = p_core_api_out_of_sync || p_editor_api_out_of_sync; + api_assemblies_out_of_sync = *p_core_api_out_of_sync || *p_editor_api_out_of_sync; } else if (FileAccess::exists(core_assembly_path) && FileAccess::exists(editor_assembly_path)) { // Determine if they're out of sync if (!try_get_cached_api_hash_for(dst_assemblies_dir, api_assemblies_out_of_sync)) { @@ -824,14 +776,16 @@ bool GDMono::_load_core_api_assembly(LoadedApiAssembly &r_loaded_api_assembly, c bool success = load_assembly(CORE_API_ASSEMBLY_NAME, &r_loaded_api_assembly.assembly, p_refonly); #endif +#ifdef DEBUG_METHODS_ENABLED if (success) { ApiAssemblyInfo::Version api_assembly_ver = ApiAssemblyInfo::Version::get_from_loaded_assembly(r_loaded_api_assembly.assembly, ApiAssemblyInfo::API_CORE); - r_loaded_api_assembly.out_of_sync = GodotSharpBindings::get_core_api_hash() != api_assembly_ver.godot_api_hash || - GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version || - GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version; + r_loaded_api_assembly.out_of_sync = get_api_core_hash() != api_assembly_ver.godot_api_hash; } else { r_loaded_api_assembly.out_of_sync = false; } +#else + r_loaded_api_assembly.out_of_sync = false; +#endif return success; } @@ -854,14 +808,16 @@ bool GDMono::_load_editor_api_assembly(LoadedApiAssembly &r_loaded_api_assembly, bool success = FileAccess::exists(assembly_path) && load_assembly_from(EDITOR_API_ASSEMBLY_NAME, assembly_path, &r_loaded_api_assembly.assembly, p_refonly); +#ifdef DEBUG_METHODS_ENABLED if (success) { ApiAssemblyInfo::Version api_assembly_ver = ApiAssemblyInfo::Version::get_from_loaded_assembly(r_loaded_api_assembly.assembly, ApiAssemblyInfo::API_EDITOR); - r_loaded_api_assembly.out_of_sync = GodotSharpBindings::get_editor_api_hash() != api_assembly_ver.godot_api_hash || - GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version || - GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version; + r_loaded_api_assembly.out_of_sync = get_api_editor_hash() != api_assembly_ver.godot_api_hash; } else { r_loaded_api_assembly.out_of_sync = false; } +#else + r_loaded_api_assembly.out_of_sync = false; +#endif return success; } @@ -1091,13 +1047,13 @@ Error GDMono::_unload_scripts_domain() { Error GDMono::reload_scripts_domain() { ERR_FAIL_COND_V(!runtime_initialized, ERR_BUG); + CSharpLanguage::get_singleton()->_on_scripts_domain_about_to_unload(); + if (scripts_domain) { Error domain_unload_err = _unload_scripts_domain(); ERR_FAIL_COND_V_MSG(domain_unload_err != OK, domain_unload_err, "Mono: Failed to unload scripts domain."); } - CSharpLanguage::get_singleton()->_on_scripts_domain_unloaded(); - Error domain_load_err = _load_scripts_domain(); ERR_FAIL_COND_V_MSG(domain_load_err != OK, domain_load_err, "Mono: Failed to load scripts domain."); diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h index 51fd0f8483a..bc871fe750a 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 69d8c7edc94..08660e37016 100644 --- a/modules/mono/mono_gd/gd_mono_cache.cpp +++ b/modules/mono/mono_gd/gd_mono_cache.cpp @@ -48,8 +48,6 @@ CachedData cached_data; } #define CACHE_CLASS_AND_CHECK(m_class, m_val) CACHE_AND_CHECK(cached_data.class_##m_class, m_val) -#define CACHE_NS_CLASS_AND_CHECK(m_ns, m_class, m_val) CACHE_AND_CHECK(cached_data.class_##m_ns##_##m_class, m_val) -#define CACHE_RAW_MONO_CLASS_AND_CHECK(m_class, m_val) CACHE_AND_CHECK(cached_data.rawclass_##m_class, m_val) #define CACHE_FIELD_AND_CHECK(m_class, m_field, m_val) CACHE_AND_CHECK(cached_data.field_##m_class##_##m_field, m_val) #define CACHE_METHOD_AND_CHECK(m_class, m_method, m_val) CACHE_AND_CHECK(cached_data.method_##m_class##_##m_method, m_val) #define CACHE_PROPERTY_AND_CHECK(m_class, m_property, m_val) CACHE_AND_CHECK(cached_data.property_##m_class##_##m_property, m_val) @@ -68,23 +66,7 @@ void CachedData::clear_corlib_cache() { corlib_cache_updated = false; class_MonoObject = nullptr; - class_bool = nullptr; - class_int8_t = nullptr; - class_int16_t = nullptr; - class_int32_t = nullptr; - class_int64_t = nullptr; - class_uint8_t = nullptr; - class_uint16_t = nullptr; - class_uint32_t = nullptr; - class_uint64_t = nullptr; - class_float = nullptr; - class_double = nullptr; class_String = nullptr; - class_IntPtr = nullptr; - - class_System_Collections_IEnumerable = nullptr; - class_System_Collections_ICollection = nullptr; - class_System_Collections_IDictionary = nullptr; #ifdef DEBUG_ENABLED class_System_Diagnostics_StackTrace = nullptr; @@ -99,38 +81,12 @@ void CachedData::clear_corlib_cache() { void CachedData::clear_godot_api_cache() { godot_api_cache_updated = false; - rawclass_Dictionary = nullptr; - - class_Vector2 = nullptr; - class_Vector2i = nullptr; - class_Rect2 = nullptr; - class_Rect2i = nullptr; - class_Transform2D = nullptr; - class_Vector3 = nullptr; - class_Vector3i = nullptr; - class_Vector4 = nullptr; - class_Vector4i = nullptr; - class_Basis = nullptr; - class_Quaternion = nullptr; - class_Transform3D = nullptr; - class_Projection = nullptr; - class_AABB = nullptr; - class_Color = nullptr; - class_Plane = nullptr; - class_StringName = nullptr; - class_NodePath = nullptr; - class_RID = nullptr; class_GodotObject = nullptr; class_GodotResource = nullptr; class_Node = nullptr; class_Control = nullptr; - class_Node3D = nullptr; - class_WeakRef = nullptr; class_Callable = nullptr; class_SignalInfo = nullptr; - class_Array = nullptr; - class_Dictionary = nullptr; - class_MarshalUtils = nullptr; class_ISerializationListener = nullptr; #ifdef DEBUG_ENABLED @@ -157,68 +113,39 @@ void CachedData::clear_godot_api_cache() { field_AssemblyHasScriptsAttribute_scriptTypes = nullptr; field_GodotObject_ptr = nullptr; - field_StringName_ptr = nullptr; - field_NodePath_ptr = nullptr; - field_Image_ptr = nullptr; - field_RID_ptr = nullptr; methodthunk_GodotObject_Dispose.nullify(); - methodthunk_Array_GetPtr.nullify(); - methodthunk_Dictionary_GetPtr.nullify(); methodthunk_SignalAwaiter_SignalCallback.nullify(); methodthunk_GodotTaskScheduler_Activate.nullify(); methodthunk_Delegate_Equals.nullify(); + methodthunk_DelegateUtils_TrySerializeDelegateWithGCHandle.nullify(); + methodthunk_DelegateUtils_TryDeserializeDelegateWithGCHandle.nullify(); methodthunk_DelegateUtils_TrySerializeDelegate.nullify(); methodthunk_DelegateUtils_TryDeserializeDelegate.nullify(); + methodthunk_DelegateUtils_InvokeWithVariantArgs.nullify(); + methodthunk_DelegateUtils_DelegateEquals.nullify(); + methodthunk_DelegateUtils_FreeGCHandle.nullify(); - // Start of MarshalUtils methods + methodthunk_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_Marshaling_SetFieldValue.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_TypeHasFlagsAttribute.nullify(); - methodthunk_MarshalUtils_GetGenericTypeDefinition.nullify(); - - methodthunk_MarshalUtils_ArrayGetElementType.nullify(); - methodthunk_MarshalUtils_DictionaryGetKeyValueTypes.nullify(); - - methodthunk_MarshalUtils_MakeGenericArrayType.nullify(); - methodthunk_MarshalUtils_MakeGenericDictionaryType.nullify(); - - // End of MarshalUtils methods - 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")); @@ -235,36 +162,12 @@ void update_corlib_cache() { } void update_godot_api_cache() { - CACHE_CLASS_AND_CHECK(Vector2, GODOT_API_CLASS(Vector2)); - CACHE_CLASS_AND_CHECK(Vector2i, GODOT_API_CLASS(Vector2i)); - CACHE_CLASS_AND_CHECK(Rect2, GODOT_API_CLASS(Rect2)); - CACHE_CLASS_AND_CHECK(Rect2i, GODOT_API_CLASS(Rect2i)); - CACHE_CLASS_AND_CHECK(Transform2D, GODOT_API_CLASS(Transform2D)); - CACHE_CLASS_AND_CHECK(Vector3, GODOT_API_CLASS(Vector3)); - CACHE_CLASS_AND_CHECK(Vector3i, GODOT_API_CLASS(Vector3i)); - CACHE_CLASS_AND_CHECK(Vector4, GODOT_API_CLASS(Vector4)); - CACHE_CLASS_AND_CHECK(Vector4i, GODOT_API_CLASS(Vector4i)); - CACHE_CLASS_AND_CHECK(Basis, GODOT_API_CLASS(Basis)); - CACHE_CLASS_AND_CHECK(Quaternion, GODOT_API_CLASS(Quaternion)); - CACHE_CLASS_AND_CHECK(Transform3D, GODOT_API_CLASS(Transform3D)); - CACHE_CLASS_AND_CHECK(Projection, GODOT_API_CLASS(Projection)); - CACHE_CLASS_AND_CHECK(AABB, GODOT_API_CLASS(AABB)); - CACHE_CLASS_AND_CHECK(Color, GODOT_API_CLASS(Color)); - CACHE_CLASS_AND_CHECK(Plane, GODOT_API_CLASS(Plane)); - CACHE_CLASS_AND_CHECK(StringName, GODOT_API_CLASS(StringName)); - CACHE_CLASS_AND_CHECK(NodePath, GODOT_API_CLASS(NodePath)); - CACHE_CLASS_AND_CHECK(RID, GODOT_API_CLASS(RID)); CACHE_CLASS_AND_CHECK(GodotObject, GODOT_API_CLASS(Object)); CACHE_CLASS_AND_CHECK(GodotResource, GODOT_API_CLASS(Resource)); CACHE_CLASS_AND_CHECK(Node, GODOT_API_CLASS(Node)); CACHE_CLASS_AND_CHECK(Control, GODOT_API_CLASS(Control)); - CACHE_CLASS_AND_CHECK(Node3D, GODOT_API_CLASS(Node3D)); - CACHE_CLASS_AND_CHECK(WeakRef, GODOT_API_CLASS(WeakRef)); CACHE_CLASS_AND_CHECK(Callable, GODOT_API_CLASS(Callable)); CACHE_CLASS_AND_CHECK(SignalInfo, GODOT_API_CLASS(SignalInfo)); - CACHE_CLASS_AND_CHECK(Array, GODOT_API_NS_CLASS(BINDINGS_NAMESPACE_COLLECTIONS, Array)); - CACHE_CLASS_AND_CHECK(Dictionary, GODOT_API_NS_CLASS(BINDINGS_NAMESPACE_COLLECTIONS, Dictionary)); - CACHE_CLASS_AND_CHECK(MarshalUtils, GODOT_API_CLASS(MarshalUtils)); CACHE_CLASS_AND_CHECK(ISerializationListener, GODOT_API_CLASS(ISerializationListener)); #ifdef DEBUG_ENABLED @@ -291,40 +194,41 @@ void update_godot_api_cache() { CACHE_FIELD_AND_CHECK(AssemblyHasScriptsAttribute, scriptTypes, CACHED_CLASS(AssemblyHasScriptsAttribute)->get_field("scriptTypes")); CACHE_FIELD_AND_CHECK(GodotObject, ptr, CACHED_CLASS(GodotObject)->get_field(BINDINGS_PTR_FIELD)); - CACHE_FIELD_AND_CHECK(StringName, ptr, CACHED_CLASS(StringName)->get_field(BINDINGS_PTR_FIELD)); - CACHE_FIELD_AND_CHECK(NodePath, ptr, CACHED_CLASS(NodePath)->get_field(BINDINGS_PTR_FIELD)); - CACHE_FIELD_AND_CHECK(RID, ptr, CACHED_CLASS(RID)->get_field(BINDINGS_PTR_FIELD)); CACHE_METHOD_THUNK_AND_CHECK(GodotObject, Dispose, CACHED_CLASS(GodotObject)->get_method("Dispose", 0)); - CACHE_METHOD_THUNK_AND_CHECK(Array, GetPtr, GODOT_API_NS_CLASS(BINDINGS_NAMESPACE_COLLECTIONS, Array)->get_method("GetPtr", 0)); - CACHE_METHOD_THUNK_AND_CHECK(Dictionary, GetPtr, GODOT_API_NS_CLASS(BINDINGS_NAMESPACE_COLLECTIONS, Dictionary)->get_method("GetPtr", 0)); CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, SignalCallback, GODOT_API_CLASS(SignalAwaiter)->get_method("SignalCallback", 1)); CACHE_METHOD_THUNK_AND_CHECK(GodotTaskScheduler, Activate, GODOT_API_CLASS(GodotTaskScheduler)->get_method("Activate", 0)); + CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, TrySerializeDelegateWithGCHandle, GODOT_API_CLASS(DelegateUtils)->get_method("TrySerializeDelegateWithGCHandle", 2)); + CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, TryDeserializeDelegateWithGCHandle, GODOT_API_CLASS(DelegateUtils)->get_method("TryDeserializeDelegateWithGCHandle", 2)); CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, TrySerializeDelegate, GODOT_API_CLASS(DelegateUtils)->get_method("TrySerializeDelegate", 2)); CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, TryDeserializeDelegate, GODOT_API_CLASS(DelegateUtils)->get_method("TryDeserializeDelegate", 2)); + CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, InvokeWithVariantArgs, GODOT_API_CLASS(DelegateUtils)->get_method("InvokeWithVariantArgs", 4)); + CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, DelegateEquals, GODOT_API_CLASS(DelegateUtils)->get_method("DelegateEquals", 2)); + CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, FreeGCHandle, GODOT_API_CLASS(DelegateUtils)->get_method("FreeGCHandle", 1)); - // Start of MarshalUtils methods + GDMonoClass *gd_mono_marshal_class = GDMono::get_singleton()->get_core_api_assembly()->get_class( + "Godot.NativeInterop", "Marshaling"); + + ERR_FAIL_COND_MSG(gd_mono_marshal_class == nullptr, + "Mono Cache: Class `Godot.NativeInterop.Marshaling` not found."); + + CACHE_METHOD_THUNK_AND_CHECK(Marshaling, managed_to_variant_type, + gd_mono_marshal_class->get_method("managed_to_variant_type", 2)); + CACHE_METHOD_THUNK_AND_CHECK(Marshaling, try_get_array_element_type, + gd_mono_marshal_class->get_method("try_get_array_element_type", 2)); + CACHE_METHOD_THUNK_AND_CHECK(Marshaling, variant_to_mono_object_of_type, + gd_mono_marshal_class->get_method("variant_to_mono_object_of_type", 2)); + CACHE_METHOD_THUNK_AND_CHECK(Marshaling, variant_to_mono_object, + gd_mono_marshal_class->get_method("variant_to_mono_object", 1)); + CACHE_METHOD_THUNK_AND_CHECK(Marshaling, mono_object_to_variant_out, + gd_mono_marshal_class->get_method("mono_object_to_variant_out", 3)); + + CACHE_METHOD_THUNK_AND_CHECK(Marshaling, SetFieldValue, + gd_mono_marshal_class->get_method("SetFieldValue", 3)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericArray, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericArray", 1)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericDictionary, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericDictionary", 1)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsSystemGenericList, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsSystemGenericList", 1)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsSystemGenericDictionary, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsSystemGenericDictionary", 1)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericIEnumerable, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericIEnumerable", 1)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericICollection, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericICollection", 1)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericIDictionary, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericIDictionary", 1)); CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeHasFlagsAttribute, GODOT_API_CLASS(MarshalUtils)->get_method("TypeHasFlagsAttribute", 1)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GetGenericTypeDefinition, GODOT_API_CLASS(MarshalUtils)->get_method("GetGenericTypeDefinition", 2)); - - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, ArrayGetElementType, GODOT_API_CLASS(MarshalUtils)->get_method("ArrayGetElementType", 2)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, DictionaryGetKeyValueTypes, GODOT_API_CLASS(MarshalUtils)->get_method("DictionaryGetKeyValueTypes", 3)); - - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, MakeGenericArrayType, GODOT_API_CLASS(MarshalUtils)->get_method("MakeGenericArrayType", 1)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, MakeGenericDictionaryType, GODOT_API_CLASS(MarshalUtils)->get_method("MakeGenericDictionaryType", 2)); - - // End of MarshalUtils methods - #ifdef DEBUG_ENABLED CACHE_METHOD_THUNK_AND_CHECK(DebuggingUtils, GetStackFrameInfo, GODOT_API_CLASS(DebuggingUtils)->get_method("GetStackFrameInfo", 4)); #endif diff --git a/modules/mono/mono_gd/gd_mono_cache.h b/modules/mono/mono_gd/gd_mono_cache.h index e9cc26899e5..0db32b58857 100644 --- a/modules/mono/mono_gd/gd_mono_cache.h +++ b/modules/mono/mono_gd/gd_mono_cache.h @@ -42,23 +42,7 @@ struct CachedData { // Let's use the no-namespace format for these too GDMonoClass *class_MonoObject = nullptr; // object - GDMonoClass *class_bool = nullptr; // bool - GDMonoClass *class_int8_t = nullptr; // sbyte - GDMonoClass *class_int16_t = nullptr; // short - GDMonoClass *class_int32_t = nullptr; // int - GDMonoClass *class_int64_t = nullptr; // long - GDMonoClass *class_uint8_t = nullptr; // byte - GDMonoClass *class_uint16_t = nullptr; // ushort - GDMonoClass *class_uint32_t = nullptr; // uint - GDMonoClass *class_uint64_t = nullptr; // ulong - GDMonoClass *class_float = nullptr; // float - GDMonoClass *class_double = nullptr; // double GDMonoClass *class_String = nullptr; // string - GDMonoClass *class_IntPtr = nullptr; // System.IntPtr - - GDMonoClass *class_System_Collections_IEnumerable = nullptr; - GDMonoClass *class_System_Collections_ICollection = nullptr; - GDMonoClass *class_System_Collections_IDictionary = nullptr; #ifdef DEBUG_ENABLED GDMonoClass *class_System_Diagnostics_StackTrace = nullptr; @@ -68,40 +52,14 @@ struct CachedData { #endif GDMonoClass *class_KeyNotFoundException = nullptr; - - MonoClass *rawclass_Dictionary = nullptr; // ----------------------------------------------- - GDMonoClass *class_Vector2 = nullptr; - GDMonoClass *class_Vector2i = nullptr; - GDMonoClass *class_Rect2 = nullptr; - GDMonoClass *class_Rect2i = nullptr; - GDMonoClass *class_Transform2D = nullptr; - GDMonoClass *class_Vector3 = nullptr; - GDMonoClass *class_Vector3i = nullptr; - GDMonoClass *class_Vector4 = nullptr; - GDMonoClass *class_Vector4i = nullptr; - GDMonoClass *class_Basis = nullptr; - GDMonoClass *class_Quaternion = nullptr; - GDMonoClass *class_Transform3D = nullptr; - GDMonoClass *class_Projection = nullptr; - GDMonoClass *class_AABB = nullptr; - GDMonoClass *class_Color = nullptr; - GDMonoClass *class_Plane = nullptr; - GDMonoClass *class_StringName = nullptr; - GDMonoClass *class_NodePath = nullptr; - GDMonoClass *class_RID = nullptr; GDMonoClass *class_GodotObject = nullptr; GDMonoClass *class_GodotResource = nullptr; GDMonoClass *class_Node = nullptr; GDMonoClass *class_Control = nullptr; - GDMonoClass *class_Node3D = nullptr; - GDMonoClass *class_WeakRef = nullptr; GDMonoClass *class_Callable = nullptr; GDMonoClass *class_SignalInfo = nullptr; - GDMonoClass *class_Array = nullptr; - GDMonoClass *class_Dictionary = nullptr; - GDMonoClass *class_MarshalUtils = nullptr; GDMonoClass *class_ISerializationListener = nullptr; #ifdef DEBUG_ENABLED @@ -127,44 +85,35 @@ struct CachedData { GDMonoField *field_AssemblyHasScriptsAttribute_requiresLookup = nullptr; GDMonoField *field_AssemblyHasScriptsAttribute_scriptTypes = nullptr; + GDMonoField *field_GodotObject_ptr = nullptr; - GDMonoField *field_StringName_ptr = nullptr; - GDMonoField *field_NodePath_ptr = nullptr; - GDMonoField *field_Image_ptr = nullptr; - GDMonoField *field_RID_ptr = nullptr; GDMonoMethodThunk 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_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_Marshaling_SetFieldValue; - 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_MarshalUtils_TypeHasFlagsAttribute; - GDMonoMethodThunk methodthunk_MarshalUtils_GetGenericTypeDefinition; - - GDMonoMethodThunk methodthunk_MarshalUtils_ArrayGetElementType; - GDMonoMethodThunk methodthunk_MarshalUtils_DictionaryGetKeyValueTypes; - - GDMonoMethodThunkR methodthunk_MarshalUtils_MakeGenericArrayType; - GDMonoMethodThunkR methodthunk_MarshalUtils_MakeGenericDictionaryType; - - // End of MarshalUtils methods - Ref task_scheduler_handle; bool corlib_cache_updated; @@ -184,10 +133,6 @@ extern CachedData cached_data; void update_corlib_cache(); void update_godot_api_cache(); -inline void clear_corlib_cache() { - cached_data.clear_corlib_cache(); -} - inline void clear_godot_api_cache() { cached_data.clear_godot_api_cache(); } @@ -195,7 +140,6 @@ inline void clear_godot_api_cache() { #define CACHED_CLASS(m_class) (GDMonoCache::cached_data.class_##m_class) #define CACHED_CLASS_RAW(m_class) (GDMonoCache::cached_data.class_##m_class->get_mono_ptr()) -#define CACHED_RAW_MONO_CLASS(m_class) (GDMonoCache::cached_data.rawclass_##m_class) #define CACHED_FIELD(m_class, m_field) (GDMonoCache::cached_data.field_##m_class##_##m_field) #define CACHED_METHOD(m_class, m_method) (GDMonoCache::cached_data.method_##m_class##_##m_method) #define CACHED_METHOD_THUNK(m_class, m_method) (GDMonoCache::cached_data.methodthunk_##m_class##_##m_method) diff --git a/modules/mono/mono_gd/gd_mono_class.cpp b/modules/mono/mono_gd/gd_mono_class.cpp index 51c5aa3542a..24b46d2ce8b 100644 --- a/modules/mono/mono_gd/gd_mono_class.cpp +++ b/modules/mono/mono_gd/gd_mono_class.cpp @@ -462,18 +462,11 @@ const Vector &GDMonoClass::get_all_delegates() { return delegates_list; } - // If the class is generic we must use the generic type definition. - MonoClass *klass = mono_class; - if (mono_type_get_type(get_mono_type()) == MONO_TYPE_GENERICINST) { - MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), get_mono_type()); - GDMonoUtils::Marshal::get_generic_type_definition(reftype, &reftype); - MonoType *type = mono_reflection_type_get_type(reftype); - klass = mono_class_from_mono_type(type); - } + // NOTE: Temporarily reverted d28be4d5808947606b8189ae1b2900b8fd2925cf, while we move code to C# void *iter = nullptr; MonoClass *raw_class = nullptr; - while ((raw_class = mono_class_get_nested_types(klass, &iter)) != nullptr) { + while ((raw_class = mono_class_get_nested_types(mono_class, &iter)) != nullptr) { if (mono_class_is_delegate(raw_class)) { StringName name = String::utf8(mono_class_get_name(raw_class)); diff --git a/modules/mono/mono_gd/gd_mono_field.cpp b/modules/mono/mono_gd/gd_mono_field.cpp index cb025fc67ae..6beeca79c32 100644 --- a/modules/mono/mono_gd/gd_mono_field.cpp +++ b/modules/mono/mono_gd/gd_mono_field.cpp @@ -46,421 +46,14 @@ void GDMonoField::set_value_raw(MonoObject *p_object, void *p_ptr) { } void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_value) { - switch (type.type_encoding) { - case MONO_TYPE_BOOLEAN: { - MonoBoolean val = p_value.operator bool(); - mono_field_set_value(p_object, mono_field, &val); - } break; - case MONO_TYPE_CHAR: { - int16_t val = p_value.operator unsigned short(); - mono_field_set_value(p_object, mono_field, &val); - } break; - case MONO_TYPE_I1: { - int8_t val = p_value.operator signed char(); - mono_field_set_value(p_object, mono_field, &val); - } break; - case MONO_TYPE_I2: { - int16_t val = p_value.operator signed short(); - mono_field_set_value(p_object, mono_field, &val); - } break; - case MONO_TYPE_I4: { - int32_t val = p_value.operator signed int(); - mono_field_set_value(p_object, mono_field, &val); - } break; - case MONO_TYPE_I8: { - int64_t val = p_value.operator int64_t(); - mono_field_set_value(p_object, mono_field, &val); - } break; - case MONO_TYPE_U1: { - uint8_t val = p_value.operator unsigned char(); - mono_field_set_value(p_object, mono_field, &val); - } break; - case MONO_TYPE_U2: { - uint16_t val = p_value.operator unsigned short(); - mono_field_set_value(p_object, mono_field, &val); - } break; - case MONO_TYPE_U4: { - uint32_t val = p_value.operator unsigned int(); - mono_field_set_value(p_object, mono_field, &val); - } break; - case MONO_TYPE_U8: { - uint64_t val = p_value.operator uint64_t(); - mono_field_set_value(p_object, mono_field, &val); - } break; - case MONO_TYPE_R4: { - float val = p_value.operator float(); - mono_field_set_value(p_object, mono_field, &val); - } break; - case MONO_TYPE_R8: { - double val = p_value.operator double(); - mono_field_set_value(p_object, mono_field, &val); - } break; - case MONO_TYPE_VALUETYPE: { - GDMonoClass *tclass = type.type_class; + MonoReflectionField *reflfield = mono_field_get_object(mono_domain_get(), owner->get_mono_ptr(), mono_field); - if (tclass == CACHED_CLASS(Vector2)) { - GDMonoMarshal::M_Vector2 from = MARSHALLED_OUT(Vector2, p_value.operator ::Vector2()); - mono_field_set_value(p_object, mono_field, &from); - break; - } + MonoException *exc = nullptr; + CACHED_METHOD_THUNK(Marshaling, SetFieldValue) + .invoke(reflfield, p_object, &p_value, &exc); - if (tclass == CACHED_CLASS(Vector2i)) { - GDMonoMarshal::M_Vector2i from = MARSHALLED_OUT(Vector2i, p_value.operator ::Vector2i()); - mono_field_set_value(p_object, mono_field, &from); - break; - } - - if (tclass == CACHED_CLASS(Rect2)) { - GDMonoMarshal::M_Rect2 from = MARSHALLED_OUT(Rect2, p_value.operator ::Rect2()); - mono_field_set_value(p_object, mono_field, &from); - break; - } - - if (tclass == CACHED_CLASS(Rect2i)) { - GDMonoMarshal::M_Rect2i from = MARSHALLED_OUT(Rect2i, p_value.operator ::Rect2i()); - mono_field_set_value(p_object, mono_field, &from); - break; - } - - if (tclass == CACHED_CLASS(Transform2D)) { - GDMonoMarshal::M_Transform2D from = MARSHALLED_OUT(Transform2D, p_value.operator ::Transform2D()); - mono_field_set_value(p_object, mono_field, &from); - break; - } - - if (tclass == CACHED_CLASS(Vector3)) { - GDMonoMarshal::M_Vector3 from = MARSHALLED_OUT(Vector3, p_value.operator ::Vector3()); - mono_field_set_value(p_object, mono_field, &from); - break; - } - - if (tclass == CACHED_CLASS(Vector3i)) { - GDMonoMarshal::M_Vector3i from = MARSHALLED_OUT(Vector3i, p_value.operator ::Vector3i()); - mono_field_set_value(p_object, mono_field, &from); - break; - } - - if (tclass == CACHED_CLASS(Vector4)) { - GDMonoMarshal::M_Vector4 from = MARSHALLED_OUT(Vector4, p_value.operator ::Vector4()); - mono_field_set_value(p_object, mono_field, &from); - break; - } - - if (tclass == CACHED_CLASS(Vector4i)) { - GDMonoMarshal::M_Vector4i from = MARSHALLED_OUT(Vector4i, p_value.operator ::Vector4i()); - mono_field_set_value(p_object, mono_field, &from); - break; - } - - if (tclass == CACHED_CLASS(Basis)) { - GDMonoMarshal::M_Basis from = MARSHALLED_OUT(Basis, p_value.operator ::Basis()); - mono_field_set_value(p_object, mono_field, &from); - break; - } - - if (tclass == CACHED_CLASS(Quaternion)) { - GDMonoMarshal::M_Quaternion from = MARSHALLED_OUT(Quaternion, p_value.operator ::Quaternion()); - mono_field_set_value(p_object, mono_field, &from); - break; - } - - if (tclass == CACHED_CLASS(Transform3D)) { - GDMonoMarshal::M_Transform3D from = MARSHALLED_OUT(Transform3D, p_value.operator ::Transform3D()); - mono_field_set_value(p_object, mono_field, &from); - break; - } - - if (tclass == CACHED_CLASS(Projection)) { - GDMonoMarshal::M_Projection from = MARSHALLED_OUT(Projection, p_value.operator ::Projection()); - mono_field_set_value(p_object, mono_field, &from); - break; - } - - if (tclass == CACHED_CLASS(AABB)) { - GDMonoMarshal::M_AABB from = MARSHALLED_OUT(AABB, p_value.operator ::AABB()); - mono_field_set_value(p_object, mono_field, &from); - break; - } - - if (tclass == CACHED_CLASS(Color)) { - GDMonoMarshal::M_Color from = MARSHALLED_OUT(Color, p_value.operator ::Color()); - mono_field_set_value(p_object, mono_field, &from); - break; - } - - if (tclass == CACHED_CLASS(Plane)) { - GDMonoMarshal::M_Plane from = MARSHALLED_OUT(Plane, p_value.operator ::Plane()); - mono_field_set_value(p_object, mono_field, &from); - break; - } - - if (tclass == CACHED_CLASS(Callable)) { - GDMonoMarshal::M_Callable val = GDMonoMarshal::callable_to_managed(p_value.operator Callable()); - mono_field_set_value(p_object, mono_field, &val); - break; - } - - if (tclass == CACHED_CLASS(SignalInfo)) { - GDMonoMarshal::M_SignalInfo val = GDMonoMarshal::signal_info_to_managed(p_value.operator Signal()); - mono_field_set_value(p_object, mono_field, &val); - break; - } - - if (mono_class_is_enum(tclass->get_mono_ptr())) { - MonoType *enum_basetype = mono_class_enum_basetype(tclass->get_mono_ptr()); - switch (mono_type_get_type(enum_basetype)) { - case MONO_TYPE_BOOLEAN: { - MonoBoolean val = p_value.operator bool(); - mono_field_set_value(p_object, mono_field, &val); - break; - } - case MONO_TYPE_CHAR: { - uint16_t val = p_value.operator unsigned short(); - mono_field_set_value(p_object, mono_field, &val); - break; - } - case MONO_TYPE_I1: { - int8_t val = p_value.operator signed char(); - mono_field_set_value(p_object, mono_field, &val); - break; - } - case MONO_TYPE_I2: { - int16_t val = p_value.operator signed short(); - mono_field_set_value(p_object, mono_field, &val); - break; - } - case MONO_TYPE_I4: { - int32_t val = p_value.operator signed int(); - mono_field_set_value(p_object, mono_field, &val); - break; - } - case MONO_TYPE_I8: { - int64_t val = p_value.operator int64_t(); - mono_field_set_value(p_object, mono_field, &val); - break; - } - case MONO_TYPE_U1: { - uint8_t val = p_value.operator unsigned char(); - mono_field_set_value(p_object, mono_field, &val); - break; - } - case MONO_TYPE_U2: { - uint16_t val = p_value.operator unsigned short(); - mono_field_set_value(p_object, mono_field, &val); - break; - } - case MONO_TYPE_U4: { - uint32_t val = p_value.operator unsigned int(); - mono_field_set_value(p_object, mono_field, &val); - break; - } - case MONO_TYPE_U8: { - uint64_t val = p_value.operator uint64_t(); - mono_field_set_value(p_object, mono_field, &val); - break; - } - default: { - ERR_FAIL_MSG("Attempted to convert Variant to a managed enum value of unmarshallable base type."); - } - } - - break; - } - - ERR_FAIL_MSG("Attempted to set the value of a field of unmarshallable type: '" + tclass->get_name() + "'."); - } break; - case MONO_TYPE_STRING: { - if (p_value.get_type() == Variant::NIL) { - // Otherwise, Variant -> String would return the string "Null" - MonoString *mono_string = nullptr; - mono_field_set_value(p_object, mono_field, mono_string); - } else { - MonoString *mono_string = GDMonoMarshal::mono_string_from_godot(p_value); - mono_field_set_value(p_object, mono_field, mono_string); - } - } break; - case MONO_TYPE_ARRAY: - case MONO_TYPE_SZARRAY: { - MonoArray *managed = GDMonoMarshal::variant_to_mono_array(p_value, type.type_class); - if (likely(managed != nullptr)) { - mono_field_set_value(p_object, mono_field, managed); - } - } break; - case MONO_TYPE_CLASS: { - MonoObject *managed = GDMonoMarshal::variant_to_mono_object_of_class(p_value, type.type_class); - if (likely(managed != nullptr)) { - mono_field_set_value(p_object, mono_field, managed); - } - } break; - case MONO_TYPE_GENERICINST: { - MonoObject *managed = GDMonoMarshal::variant_to_mono_object_of_genericinst(p_value, type.type_class); - if (likely(managed != nullptr)) { - mono_field_set_value(p_object, mono_field, managed); - } - } break; - case MONO_TYPE_OBJECT: { - // Variant - switch (p_value.get_type()) { - case Variant::BOOL: { - MonoBoolean val = p_value.operator bool(); - mono_field_set_value(p_object, mono_field, &val); - } break; - case Variant::INT: { - int32_t val = p_value.operator signed int(); - mono_field_set_value(p_object, mono_field, &val); - } break; - case Variant::FLOAT: { -#ifdef REAL_T_IS_DOUBLE - double val = p_value.operator double(); - mono_field_set_value(p_object, mono_field, &val); -#else - float val = p_value.operator float(); - mono_field_set_value(p_object, mono_field, &val); -#endif - } break; - case Variant::STRING: { - MonoString *mono_string = GDMonoMarshal::mono_string_from_godot(p_value); - mono_field_set_value(p_object, mono_field, mono_string); - } break; - case Variant::VECTOR2: { - GDMonoMarshal::M_Vector2 from = MARSHALLED_OUT(Vector2, p_value.operator ::Vector2()); - mono_field_set_value(p_object, mono_field, &from); - } break; - case Variant::VECTOR2I: { - GDMonoMarshal::M_Vector2i from = MARSHALLED_OUT(Vector2i, p_value.operator ::Vector2i()); - mono_field_set_value(p_object, mono_field, &from); - } break; - case Variant::RECT2: { - GDMonoMarshal::M_Rect2 from = MARSHALLED_OUT(Rect2, p_value.operator ::Rect2()); - mono_field_set_value(p_object, mono_field, &from); - } break; - case Variant::RECT2I: { - GDMonoMarshal::M_Rect2i from = MARSHALLED_OUT(Rect2i, p_value.operator ::Rect2i()); - mono_field_set_value(p_object, mono_field, &from); - } break; - case Variant::VECTOR3: { - GDMonoMarshal::M_Vector3 from = MARSHALLED_OUT(Vector3, p_value.operator ::Vector3()); - mono_field_set_value(p_object, mono_field, &from); - } break; - case Variant::VECTOR3I: { - GDMonoMarshal::M_Vector3i from = MARSHALLED_OUT(Vector3i, p_value.operator ::Vector3i()); - mono_field_set_value(p_object, mono_field, &from); - } break; - case Variant::VECTOR4: { - GDMonoMarshal::M_Vector4 from = MARSHALLED_OUT(Vector4, p_value.operator ::Vector4()); - mono_field_set_value(p_object, mono_field, &from); - } break; - case Variant::VECTOR4I: { - GDMonoMarshal::M_Vector4i from = MARSHALLED_OUT(Vector4i, p_value.operator ::Vector4i()); - mono_field_set_value(p_object, mono_field, &from); - } break; - case Variant::TRANSFORM2D: { - GDMonoMarshal::M_Transform2D from = MARSHALLED_OUT(Transform2D, p_value.operator ::Transform2D()); - mono_field_set_value(p_object, mono_field, &from); - } break; - case Variant::PLANE: { - GDMonoMarshal::M_Plane from = MARSHALLED_OUT(Plane, p_value.operator ::Plane()); - mono_field_set_value(p_object, mono_field, &from); - } break; - case Variant::QUATERNION: { - GDMonoMarshal::M_Quaternion from = MARSHALLED_OUT(Quaternion, p_value.operator ::Quaternion()); - mono_field_set_value(p_object, mono_field, &from); - } break; - case Variant::AABB: { - GDMonoMarshal::M_AABB from = MARSHALLED_OUT(AABB, p_value.operator ::AABB()); - mono_field_set_value(p_object, mono_field, &from); - } break; - case Variant::BASIS: { - GDMonoMarshal::M_Basis from = MARSHALLED_OUT(Basis, p_value.operator ::Basis()); - mono_field_set_value(p_object, mono_field, &from); - } break; - case Variant::TRANSFORM3D: { - GDMonoMarshal::M_Transform3D from = MARSHALLED_OUT(Transform3D, p_value.operator ::Transform3D()); - mono_field_set_value(p_object, mono_field, &from); - } break; - case Variant::PROJECTION: { - GDMonoMarshal::M_Projection from = MARSHALLED_OUT(Projection, p_value.operator ::Projection()); - mono_field_set_value(p_object, mono_field, &from); - } break; - case Variant::COLOR: { - GDMonoMarshal::M_Color from = MARSHALLED_OUT(Color, p_value.operator ::Color()); - mono_field_set_value(p_object, mono_field, &from); - } break; - case Variant::STRING_NAME: { - MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator StringName()); - mono_field_set_value(p_object, mono_field, managed); - } break; - case Variant::NODE_PATH: { - MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator NodePath()); - mono_field_set_value(p_object, mono_field, managed); - } break; - case Variant::RID: { - MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator ::RID()); - mono_field_set_value(p_object, mono_field, managed); - } break; - case Variant::OBJECT: { - MonoObject *managed = GDMonoUtils::unmanaged_get_managed(p_value.operator Object *()); - mono_field_set_value(p_object, mono_field, managed); - } break; - case Variant::CALLABLE: { - GDMonoMarshal::M_Callable val = GDMonoMarshal::callable_to_managed(p_value.operator Callable()); - mono_field_set_value(p_object, mono_field, &val); - } break; - case Variant::SIGNAL: { - GDMonoMarshal::M_SignalInfo val = GDMonoMarshal::signal_info_to_managed(p_value.operator Signal()); - mono_field_set_value(p_object, mono_field, &val); - } break; - case Variant::DICTIONARY: { - MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary)); - mono_field_set_value(p_object, mono_field, managed); - } break; - case Variant::ARRAY: { - MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array)); - mono_field_set_value(p_object, mono_field, managed); - } break; - case Variant::PACKED_BYTE_ARRAY: { - MonoArray *managed = GDMonoMarshal::PackedByteArray_to_mono_array(p_value.operator ::PackedByteArray()); - mono_field_set_value(p_object, mono_field, managed); - } break; - case Variant::PACKED_INT32_ARRAY: { - MonoArray *managed = GDMonoMarshal::PackedInt32Array_to_mono_array(p_value.operator ::PackedInt32Array()); - mono_field_set_value(p_object, mono_field, managed); - } break; - case Variant::PACKED_INT64_ARRAY: { - MonoArray *managed = GDMonoMarshal::PackedInt64Array_to_mono_array(p_value.operator ::PackedInt64Array()); - mono_field_set_value(p_object, mono_field, managed); - } break; - case Variant::PACKED_FLOAT32_ARRAY: { - MonoArray *managed = GDMonoMarshal::PackedFloat32Array_to_mono_array(p_value.operator ::PackedFloat32Array()); - mono_field_set_value(p_object, mono_field, managed); - } break; - case Variant::PACKED_FLOAT64_ARRAY: { - MonoArray *managed = GDMonoMarshal::PackedFloat64Array_to_mono_array(p_value.operator ::PackedFloat64Array()); - mono_field_set_value(p_object, mono_field, managed); - } break; - case Variant::PACKED_STRING_ARRAY: { - MonoArray *managed = GDMonoMarshal::PackedStringArray_to_mono_array(p_value.operator ::PackedStringArray()); - mono_field_set_value(p_object, mono_field, managed); - } break; - case Variant::PACKED_VECTOR2_ARRAY: { - MonoArray *managed = GDMonoMarshal::PackedVector2Array_to_mono_array(p_value.operator ::PackedVector2Array()); - mono_field_set_value(p_object, mono_field, managed); - } break; - case Variant::PACKED_VECTOR3_ARRAY: { - MonoArray *managed = GDMonoMarshal::PackedVector3Array_to_mono_array(p_value.operator ::PackedVector3Array()); - mono_field_set_value(p_object, mono_field, managed); - } break; - case Variant::PACKED_COLOR_ARRAY: { - MonoArray *managed = GDMonoMarshal::PackedColorArray_to_mono_array(p_value.operator ::PackedColorArray()); - mono_field_set_value(p_object, mono_field, managed); - } break; - default: - break; - } - } break; - default: { - ERR_PRINT("Attempted to set the value of a field of unexpected type encoding: " + itos(type.type_encoding) + "."); - } break; + if (exc) { + GDMonoUtils::debug_print_unhandled_exception(exc); } } diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp index a8604427647..ef6a008a25d 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.cpp +++ b/modules/mono/mono_gd/gd_mono_marshal.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -37,1282 +37,104 @@ namespace GDMonoMarshal { +// 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; + if (p_type.type_encoding == MONO_TYPE_VOID) { + return Variant::NIL; + } - 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; + MonoReflectionType *refltype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type()); + MonoBoolean nil_is_variant = false; - 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; + MonoException *exc = nullptr; + int32_t ret = CACHED_METHOD_THUNK(Marshaling, managed_to_variant_type) + .invoke(refltype, &nil_is_variant, &exc); - case MONO_TYPE_R4: - return Variant::FLOAT; - case MONO_TYPE_R8: - return Variant::FLOAT; - - case MONO_TYPE_STRING: { - return Variant::STRING; - } break; - - case MONO_TYPE_VALUETYPE: { - GDMonoClass *vtclass = p_type.type_class; - - if (vtclass == CACHED_CLASS(Vector2)) { - return Variant::VECTOR2; - } - - if (vtclass == CACHED_CLASS(Vector2i)) { - return Variant::VECTOR2I; - } - - if (vtclass == CACHED_CLASS(Rect2)) { - return Variant::RECT2; - } - - if (vtclass == CACHED_CLASS(Rect2i)) { - return Variant::RECT2I; - } - - if (vtclass == CACHED_CLASS(Transform2D)) { - return Variant::TRANSFORM2D; - } - - if (vtclass == CACHED_CLASS(Vector3)) { - return Variant::VECTOR3; - } - - if (vtclass == CACHED_CLASS(Vector3i)) { - return Variant::VECTOR3I; - } - if (vtclass == CACHED_CLASS(Vector4)) { - return Variant::VECTOR4; - } - - if (vtclass == CACHED_CLASS(Vector4i)) { - return Variant::VECTOR4I; - } - - if (vtclass == CACHED_CLASS(Basis)) { - return Variant::BASIS; - } - - if (vtclass == CACHED_CLASS(Quaternion)) { - return Variant::QUATERNION; - } - - if (vtclass == CACHED_CLASS(Transform3D)) { - return Variant::TRANSFORM3D; - } - if (vtclass == CACHED_CLASS(Projection)) { - return Variant::PROJECTION; - } - if (vtclass == CACHED_CLASS(AABB)) { - return Variant::AABB; - } - - if (vtclass == CACHED_CLASS(Color)) { - return Variant::COLOR; - } - - if (vtclass == CACHED_CLASS(Plane)) { - return Variant::PLANE; - } - - if (vtclass == CACHED_CLASS(Callable)) { - return Variant::CALLABLE; - } - - if (vtclass == CACHED_CLASS(SignalInfo)) { - return Variant::SIGNAL; - } - - if (mono_class_is_enum(vtclass->get_mono_ptr())) { - return Variant::INT; - } - } break; - - case MONO_TYPE_ARRAY: - case MONO_TYPE_SZARRAY: { - MonoClass *elem_class = mono_class_get_element_class(p_type.type_class->get_mono_ptr()); - - if (elem_class == CACHED_CLASS_RAW(MonoObject)) { - return Variant::ARRAY; - } - - if (elem_class == CACHED_CLASS_RAW(uint8_t)) { - return Variant::PACKED_BYTE_ARRAY; - } - - if (elem_class == CACHED_CLASS_RAW(int32_t)) { - return Variant::PACKED_INT32_ARRAY; - } - - if (elem_class == CACHED_CLASS_RAW(int64_t)) { - return Variant::PACKED_INT64_ARRAY; - } - - if (elem_class == CACHED_CLASS_RAW(float)) { - return Variant::PACKED_FLOAT32_ARRAY; - } - - if (elem_class == CACHED_CLASS_RAW(double)) { - return Variant::PACKED_FLOAT64_ARRAY; - } - - if (elem_class == CACHED_CLASS_RAW(String)) { - return Variant::PACKED_STRING_ARRAY; - } - - if (elem_class == CACHED_CLASS_RAW(Vector2)) { - return Variant::PACKED_VECTOR2_ARRAY; - } - - if (elem_class == CACHED_CLASS_RAW(Vector3)) { - return Variant::PACKED_VECTOR3_ARRAY; - } - - if (elem_class == CACHED_CLASS_RAW(Color)) { - return Variant::PACKED_COLOR_ARRAY; - } - - if (elem_class == CACHED_CLASS_RAW(StringName)) { - return Variant::ARRAY; - } - - if (elem_class == CACHED_CLASS_RAW(NodePath)) { - return Variant::ARRAY; - } - - if (elem_class == CACHED_CLASS_RAW(RID)) { - return Variant::ARRAY; - } - - if (mono_class_is_enum(elem_class)) { - return Variant::ARRAY; - } - - GDMonoClass *array_type_class = GDMono::get_singleton()->get_class(elem_class); - if (CACHED_CLASS(GodotObject)->is_assignable_from(array_type_class)) { - return Variant::ARRAY; - } - } break; - - case MONO_TYPE_CLASS: { - GDMonoClass *type_class = p_type.type_class; - - // 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; - } - - // GodotObject - GDMonoClass *type_class = p_type.type_class; - if (CACHED_CLASS(GodotObject)->is_assignable_from(type_class)) { - return Variant::OBJECT; - } - } 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: { - MonoClass *elem_class = mono_class_get_element_class(p_array_type.type_class->get_mono_ptr()); - r_elem_type = ManagedType::from_class(elem_class); - return true; - } break; - case MONO_TYPE_GENERICINST: { - MonoReflectionType *array_reftype = mono_type_get_object(mono_domain_get(), p_array_type.type_class->get_mono_type()); + 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 (array_type->eklass == CACHED_CLASS_RAW(StringName)) { - return Array_to_mono_array(p_var.operator Array()); - } - - if (array_type->eklass == CACHED_CLASS_RAW(NodePath)) { - return Array_to_mono_array(p_var.operator Array()); - } - - if (array_type->eklass == CACHED_CLASS_RAW(RID)) { - return Array_to_mono_array(p_var.operator Array()); - } - - if (mono_class_is_assignable_from(CACHED_CLASS(GodotObject)->get_mono_ptr(), array_type->eklass)) { - return Array_to_mono_array(p_var.operator ::Array(), array_type->eklass); - } - - 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); - } - - // GodotObject - if (CACHED_CLASS(GodotObject)->is_assignable_from(p_type_class)) { - return GDMonoUtils::unmanaged_get_managed(p_var.operator Object *()); - } - - ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported generic type: '" + p_type_class->get_full_name() + "'."); + return ret; } MonoObject *variant_to_mono_object(const Variant &p_var) { - // Variant - switch (p_var.get_type()) { - case Variant::BOOL: { - MonoBoolean val = p_var.operator bool(); - return BOX_BOOLEAN(val); - } - case Variant::INT: { - int64_t val = p_var.operator int64_t(); - return BOX_INT64(val); - } - case Variant::FLOAT: { -#ifdef REAL_T_IS_DOUBLE - double val = p_var.operator double(); - return BOX_DOUBLE(val); -#else - float val = p_var.operator float(); - return BOX_FLOAT(val); -#endif - } - case Variant::STRING: - return (MonoObject *)mono_string_from_godot(p_var.operator String()); - case Variant::VECTOR2: { - GDMonoMarshal::M_Vector2 from = MARSHALLED_OUT(Vector2, p_var.operator ::Vector2()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector2), &from); - } - case Variant::VECTOR2I: { - GDMonoMarshal::M_Vector2i from = MARSHALLED_OUT(Vector2i, p_var.operator ::Vector2i()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector2i), &from); - } - case Variant::RECT2: { - GDMonoMarshal::M_Rect2 from = MARSHALLED_OUT(Rect2, p_var.operator ::Rect2()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Rect2), &from); - } - case Variant::RECT2I: { - GDMonoMarshal::M_Rect2i from = MARSHALLED_OUT(Rect2i, p_var.operator ::Rect2i()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Rect2i), &from); - } - case Variant::VECTOR3: { - GDMonoMarshal::M_Vector3 from = MARSHALLED_OUT(Vector3, p_var.operator ::Vector3()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector3), &from); - } - case Variant::VECTOR3I: { - GDMonoMarshal::M_Vector3i from = MARSHALLED_OUT(Vector3i, p_var.operator ::Vector3i()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector3i), &from); - } - case Variant::TRANSFORM2D: { - GDMonoMarshal::M_Transform2D from = MARSHALLED_OUT(Transform2D, p_var.operator ::Transform2D()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform2D), &from); - } - case Variant::VECTOR4: { - GDMonoMarshal::M_Vector4 from = MARSHALLED_OUT(Vector4, p_var.operator ::Vector4()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector4), &from); - } - case Variant::VECTOR4I: { - GDMonoMarshal::M_Vector4i from = MARSHALLED_OUT(Vector4i, p_var.operator ::Vector4i()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector4i), &from); - } - case Variant::PLANE: { - GDMonoMarshal::M_Plane from = MARSHALLED_OUT(Plane, p_var.operator ::Plane()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Plane), &from); - } - case Variant::QUATERNION: { - GDMonoMarshal::M_Quaternion from = MARSHALLED_OUT(Quaternion, p_var.operator ::Quaternion()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Quaternion), &from); - } - case Variant::AABB: { - GDMonoMarshal::M_AABB from = MARSHALLED_OUT(AABB, p_var.operator ::AABB()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(AABB), &from); - } - case Variant::BASIS: { - GDMonoMarshal::M_Basis from = MARSHALLED_OUT(Basis, p_var.operator ::Basis()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Basis), &from); - } - case Variant::TRANSFORM3D: { - GDMonoMarshal::M_Transform3D from = MARSHALLED_OUT(Transform3D, p_var.operator ::Transform3D()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform3D), &from); - } - case Variant::PROJECTION: { - GDMonoMarshal::M_Projection from = MARSHALLED_OUT(Projection, p_var.operator ::Projection()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Projection), &from); - } - case Variant::COLOR: { - GDMonoMarshal::M_Color from = MARSHALLED_OUT(Color, p_var.operator ::Color()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Color), &from); - } - case Variant::STRING_NAME: - return GDMonoUtils::create_managed_from(p_var.operator StringName()); - case Variant::NODE_PATH: - return GDMonoUtils::create_managed_from(p_var.operator NodePath()); - case Variant::RID: - return GDMonoUtils::create_managed_from(p_var.operator ::RID()); - case Variant::OBJECT: - return GDMonoUtils::unmanaged_get_managed(p_var.operator Object *()); - case Variant::CALLABLE: { - GDMonoMarshal::M_Callable from = GDMonoMarshal::callable_to_managed(p_var.operator Callable()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Callable), &from); - } - case Variant::SIGNAL: { - GDMonoMarshal::M_SignalInfo from = GDMonoMarshal::signal_info_to_managed(p_var.operator Signal()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(SignalInfo), &from); - } - case Variant::DICTIONARY: - return GDMonoUtils::create_managed_from(p_var.operator Dictionary(), CACHED_CLASS(Dictionary)); - case Variant::ARRAY: - return GDMonoUtils::create_managed_from(p_var.operator Array(), CACHED_CLASS(Array)); - case Variant::PACKED_BYTE_ARRAY: - return (MonoObject *)PackedByteArray_to_mono_array(p_var.operator PackedByteArray()); - case Variant::PACKED_INT32_ARRAY: - return (MonoObject *)PackedInt32Array_to_mono_array(p_var.operator PackedInt32Array()); - case Variant::PACKED_INT64_ARRAY: - return (MonoObject *)PackedInt64Array_to_mono_array(p_var.operator PackedInt64Array()); - case Variant::PACKED_FLOAT32_ARRAY: - return (MonoObject *)PackedFloat32Array_to_mono_array(p_var.operator PackedFloat32Array()); - case Variant::PACKED_FLOAT64_ARRAY: - return (MonoObject *)PackedFloat64Array_to_mono_array(p_var.operator PackedFloat64Array()); - case Variant::PACKED_STRING_ARRAY: - return (MonoObject *)PackedStringArray_to_mono_array(p_var.operator PackedStringArray()); - case Variant::PACKED_VECTOR2_ARRAY: - return (MonoObject *)PackedVector2Array_to_mono_array(p_var.operator PackedVector2Array()); - case Variant::PACKED_VECTOR3_ARRAY: - return (MonoObject *)PackedVector3Array_to_mono_array(p_var.operator PackedVector3Array()); - case Variant::PACKED_COLOR_ARRAY: - return (MonoObject *)PackedColorArray_to_mono_array(p_var.operator PackedColorArray()); - default: - return nullptr; + 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); - } - - if (array_type->eklass == CACHED_CLASS_RAW(StringName)) { - return mono_array_to_Array((MonoArray *)p_obj); - } - - if (array_type->eklass == CACHED_CLASS_RAW(NodePath)) { - return mono_array_to_Array((MonoArray *)p_obj); - } - - if (array_type->eklass == CACHED_CLASS_RAW(RID)) { - return mono_array_to_Array((MonoArray *)p_obj); - } - - GDMonoClass *array_type_class = GDMono::get_singleton()->get_class(array_type->eklass); - if (CACHED_CLASS(GodotObject)->is_assignable_from(array_type_class)) { - return mono_array_to_Array((MonoArray *)p_obj); - } - - if (p_fail_with_err) { - ERR_FAIL_V_MSG(Variant(), "Attempted to convert a managed array of unmarshallable element type to Variant."); - } else { - return Variant(); - } - } break; - case MONO_TYPE_CLASS: { - GDMonoClass *type_class = p_type.type_class; - - // GodotObject - if (CACHED_CLASS(GodotObject)->is_assignable_from(type_class)) { - Object *ptr = unbox(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); - } - - // GodotObject - GDMonoClass *type_class = p_type.type_class; - 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(); - } - } 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) { @@ -1320,8 +142,7 @@ String mono_object_to_variant_string(MonoObject *p_obj, MonoException **r_exc) { return String("null"); } - ManagedType type = ManagedType::from_class(mono_object_get_class(p_obj)); - Variant var = GDMonoMarshal::mono_object_to_variant_no_err(p_obj, type); + Variant var = GDMonoMarshal::mono_object_to_variant_no_err(p_obj); if (var.get_type() == Variant::NIL) { // `&& p_obj != nullptr` but omitted because always true // Cannot convert MonoObject* to Variant; fallback to 'ToString()'. @@ -1341,90 +162,6 @@ String mono_object_to_variant_string(MonoObject *p_obj, MonoException **r_exc) { } } -MonoObject *Dictionary_to_system_generic_dict(const Dictionary &p_dict, GDMonoClass *p_class, MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype) { - String ctor_desc = ":.ctor(System.Collections.Generic.IDictionary`2<" + GDMonoUtils::get_type_desc(p_key_reftype) + - ", " + GDMonoUtils::get_type_desc(p_value_reftype) + ">)"; - GDMonoMethod *ctor = p_class->get_method_with_desc(ctor_desc, true); - CRASH_COND(ctor == nullptr); - - MonoObject *mono_object = mono_object_new(mono_domain_get(), p_class->get_mono_ptr()); - ERR_FAIL_NULL_V(mono_object, nullptr); - - GDMonoClass *godot_dict_class = GDMonoUtils::Marshal::make_generic_dictionary_type(p_key_reftype, p_value_reftype); - MonoObject *godot_dict = GDMonoUtils::create_managed_from(p_dict, godot_dict_class); - - void *ctor_args[1] = { godot_dict }; - - MonoException *exc = nullptr; - ctor->invoke_raw(mono_object, ctor_args, &exc); - UNHANDLED_EXCEPTION(exc); - - return mono_object; -} - -Dictionary system_generic_dict_to_Dictionary(MonoObject *p_obj, [[maybe_unused]] GDMonoClass *p_class, MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype) { - GDMonoClass *godot_dict_class = GDMonoUtils::Marshal::make_generic_dictionary_type(p_key_reftype, p_value_reftype); - String ctor_desc = ":.ctor(System.Collections.Generic.IDictionary`2<" + GDMonoUtils::get_type_desc(p_key_reftype) + - ", " + GDMonoUtils::get_type_desc(p_value_reftype) + ">)"; - GDMonoMethod *godot_dict_ctor = godot_dict_class->get_method_with_desc(ctor_desc, true); - CRASH_COND(godot_dict_ctor == nullptr); - - MonoObject *godot_dict = mono_object_new(mono_domain_get(), godot_dict_class->get_mono_ptr()); - ERR_FAIL_NULL_V(godot_dict, Dictionary()); - - void *ctor_args[1] = { p_obj }; - - MonoException *exc = nullptr; - godot_dict_ctor->invoke_raw(godot_dict, ctor_args, &exc); - UNHANDLED_EXCEPTION(exc); - - exc = nullptr; - MonoObject *ret = godot_dict_class->get_method("GetPtr")->invoke(godot_dict, &exc); - UNHANDLED_EXCEPTION(exc); - - return *unbox(ret); -} - -MonoObject *Array_to_system_generic_list(const Array &p_array, GDMonoClass *p_class, MonoReflectionType *p_elem_reftype) { - MonoType *elem_type = mono_reflection_type_get_type(p_elem_reftype); - - String ctor_desc = ":.ctor(System.Collections.Generic.IEnumerable`1<" + GDMonoUtils::get_type_desc(elem_type) + ">)"; - GDMonoMethod *ctor = p_class->get_method_with_desc(ctor_desc, true); - CRASH_COND(ctor == nullptr); - - MonoObject *mono_object = mono_object_new(mono_domain_get(), p_class->get_mono_ptr()); - ERR_FAIL_NULL_V(mono_object, nullptr); - - GDMonoClass *godot_array_class = GDMonoUtils::Marshal::make_generic_array_type(p_elem_reftype); - MonoObject *godot_array = GDMonoUtils::create_managed_from(p_array, godot_array_class); - - void *ctor_args[1] = { godot_array }; - - MonoException *exc = nullptr; - ctor->invoke_raw(mono_object, ctor_args, &exc); - UNHANDLED_EXCEPTION(exc); - - return mono_object; -} - -Variant system_generic_list_to_Array_variant(MonoObject *p_obj, GDMonoClass *p_class, [[maybe_unused]] MonoReflectionType *p_elem_reftype) { - GDMonoMethod *to_array = p_class->get_method("ToArray", 0); - CRASH_COND(to_array == nullptr); - - MonoException *exc = nullptr; - MonoObject *array = to_array->invoke_raw(p_obj, nullptr, &exc); - UNHANDLED_EXCEPTION(exc); - - ERR_FAIL_NULL_V(array, Variant()); - - ManagedType type = ManagedType::from_class(mono_object_get_class(array)); - - bool result_is_array = type.type_encoding != MONO_TYPE_SZARRAY && type.type_encoding != MONO_TYPE_ARRAY; - ERR_FAIL_COND_V(result_is_array, Variant()); - - return mono_object_to_variant(array, type); -} - MonoArray *Array_to_mono_array(const Array &p_array) { int length = p_array.size(); MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), length); @@ -1437,18 +174,6 @@ MonoArray *Array_to_mono_array(const Array &p_array) { return ret; } -MonoArray *Array_to_mono_array(const Array &p_array, MonoClass *p_array_type_class) { - int length = p_array.size(); - MonoArray *ret = mono_array_new(mono_domain_get(), p_array_type_class, length); - - for (int i = 0; i < length; i++) { - MonoObject *boxed = variant_to_mono_object(p_array[i]); - mono_array_setref(ret, i, boxed); - } - - return ret; -} - Array mono_array_to_Array(MonoArray *p_array) { Array ret; if (!p_array) { @@ -1465,141 +190,6 @@ Array mono_array_to_Array(MonoArray *p_array) { return ret; } -MonoArray *PackedInt32Array_to_mono_array(const PackedInt32Array &p_array) { - const int32_t *src = p_array.ptr(); - int length = p_array.size(); - - MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(int32_t), length); - - int32_t *dst = mono_array_addr(ret, int32_t, 0); - memcpy(dst, src, length * sizeof(int32_t)); - - return ret; -} - -PackedInt32Array mono_array_to_PackedInt32Array(MonoArray *p_array) { - PackedInt32Array ret; - if (!p_array) { - return ret; - } - int length = mono_array_length(p_array); - ret.resize(length); - int32_t *dst = ret.ptrw(); - - const int32_t *src = mono_array_addr(p_array, int32_t, 0); - memcpy(dst, src, length * sizeof(int32_t)); - - return ret; -} - -MonoArray *PackedInt64Array_to_mono_array(const PackedInt64Array &p_array) { - const int64_t *src = p_array.ptr(); - int length = p_array.size(); - - MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(int64_t), length); - - int64_t *dst = mono_array_addr(ret, int64_t, 0); - memcpy(dst, src, length * sizeof(int64_t)); - - return ret; -} - -PackedInt64Array mono_array_to_PackedInt64Array(MonoArray *p_array) { - PackedInt64Array ret; - if (!p_array) { - return ret; - } - int length = mono_array_length(p_array); - ret.resize(length); - int64_t *dst = ret.ptrw(); - - const int64_t *src = mono_array_addr(p_array, int64_t, 0); - memcpy(dst, src, length * sizeof(int64_t)); - - return ret; -} - -MonoArray *PackedByteArray_to_mono_array(const PackedByteArray &p_array) { - const uint8_t *src = p_array.ptr(); - int length = p_array.size(); - - MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(uint8_t), length); - - uint8_t *dst = mono_array_addr(ret, uint8_t, 0); - memcpy(dst, src, length * sizeof(uint8_t)); - - return ret; -} - -PackedByteArray mono_array_to_PackedByteArray(MonoArray *p_array) { - PackedByteArray ret; - if (!p_array) { - return ret; - } - int length = mono_array_length(p_array); - ret.resize(length); - uint8_t *dst = ret.ptrw(); - - const uint8_t *src = mono_array_addr(p_array, uint8_t, 0); - memcpy(dst, src, length * sizeof(uint8_t)); - - return ret; -} - -MonoArray *PackedFloat32Array_to_mono_array(const PackedFloat32Array &p_array) { - const float *src = p_array.ptr(); - int length = p_array.size(); - - MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(float), length); - - float *dst = mono_array_addr(ret, float, 0); - memcpy(dst, src, length * sizeof(float)); - - return ret; -} - -PackedFloat32Array mono_array_to_PackedFloat32Array(MonoArray *p_array) { - PackedFloat32Array ret; - if (!p_array) { - return ret; - } - int length = mono_array_length(p_array); - ret.resize(length); - float *dst = ret.ptrw(); - - const float *src = mono_array_addr(p_array, float, 0); - memcpy(dst, src, length * sizeof(float)); - - return ret; -} - -MonoArray *PackedFloat64Array_to_mono_array(const PackedFloat64Array &p_array) { - const double *src = p_array.ptr(); - int length = p_array.size(); - - MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(double), length); - - double *dst = mono_array_addr(ret, double, 0); - memcpy(dst, src, length * sizeof(double)); - - return ret; -} - -PackedFloat64Array mono_array_to_PackedFloat64Array(MonoArray *p_array) { - PackedFloat64Array ret; - if (!p_array) { - return ret; - } - int length = mono_array_length(p_array); - ret.resize(length); - double *dst = ret.ptrw(); - - const double *src = mono_array_addr(p_array, double, 0); - memcpy(dst, src, length * sizeof(double)); - - return ret; -} - MonoArray *PackedStringArray_to_mono_array(const PackedStringArray &p_array) { const String *r = p_array.ptr(); int length = p_array.size(); @@ -1613,212 +203,4 @@ MonoArray *PackedStringArray_to_mono_array(const PackedStringArray &p_array) { return ret; } - -PackedStringArray mono_array_to_PackedStringArray(MonoArray *p_array) { - PackedStringArray ret; - if (!p_array) { - return ret; - } - int length = mono_array_length(p_array); - ret.resize(length); - String *w = ret.ptrw(); - - for (int i = 0; i < length; i++) { - MonoString *elem = mono_array_get(p_array, MonoString *, i); - w[i] = mono_string_to_godot(elem); - } - - return ret; -} - -MonoArray *PackedColorArray_to_mono_array(const PackedColorArray &p_array) { - const Color *src = p_array.ptr(); - int length = p_array.size(); - - MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Color), length); - - if constexpr (InteropLayout::MATCHES_Color) { - Color *dst = mono_array_addr(ret, Color, 0); - memcpy(dst, src, length * sizeof(Color)); - } else { - for (int i = 0; i < length; i++) { - M_Color *raw = (M_Color *)mono_array_addr_with_size(ret, sizeof(M_Color), i); - *raw = MARSHALLED_OUT(Color, src[i]); - } - } - - return ret; -} - -PackedColorArray mono_array_to_PackedColorArray(MonoArray *p_array) { - PackedColorArray ret; - if (!p_array) { - return ret; - } - int length = mono_array_length(p_array); - ret.resize(length); - Color *dst = ret.ptrw(); - - if constexpr (InteropLayout::MATCHES_Color) { - const Color *src = mono_array_addr(p_array, Color, 0); - memcpy(dst, src, length * sizeof(Color)); - } else { - for (int i = 0; i < length; i++) { - dst[i] = MARSHALLED_IN(Color, (M_Color *)mono_array_addr_with_size(p_array, sizeof(M_Color), i)); - } - } - - return ret; -} - -MonoArray *PackedVector2Array_to_mono_array(const PackedVector2Array &p_array) { - const Vector2 *src = p_array.ptr(); - int length = p_array.size(); - - MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Vector2), length); - - if constexpr (InteropLayout::MATCHES_Vector2) { - Vector2 *dst = mono_array_addr(ret, Vector2, 0); - memcpy(dst, src, length * sizeof(Vector2)); - } else { - for (int i = 0; i < length; i++) { - M_Vector2 *raw = (M_Vector2 *)mono_array_addr_with_size(ret, sizeof(M_Vector2), i); - *raw = MARSHALLED_OUT(Vector2, src[i]); - } - } - - return ret; -} - -PackedVector2Array mono_array_to_PackedVector2Array(MonoArray *p_array) { - PackedVector2Array ret; - if (!p_array) { - return ret; - } - int length = mono_array_length(p_array); - ret.resize(length); - Vector2 *dst = ret.ptrw(); - - if constexpr (InteropLayout::MATCHES_Vector2) { - const Vector2 *src = mono_array_addr(p_array, Vector2, 0); - memcpy(dst, src, length * sizeof(Vector2)); - } else { - for (int i = 0; i < length; i++) { - dst[i] = MARSHALLED_IN(Vector2, (M_Vector2 *)mono_array_addr_with_size(p_array, sizeof(M_Vector2), i)); - } - } - - return ret; -} - -MonoArray *PackedVector3Array_to_mono_array(const PackedVector3Array &p_array) { - const Vector3 *src = p_array.ptr(); - int length = p_array.size(); - - MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Vector3), length); - - if constexpr (InteropLayout::MATCHES_Vector3) { - Vector3 *dst = mono_array_addr(ret, Vector3, 0); - memcpy(dst, src, length * sizeof(Vector3)); - } else { - for (int i = 0; i < length; i++) { - M_Vector3 *raw = (M_Vector3 *)mono_array_addr_with_size(ret, sizeof(M_Vector3), i); - *raw = MARSHALLED_OUT(Vector3, src[i]); - } - } - - return ret; -} - -PackedVector3Array mono_array_to_PackedVector3Array(MonoArray *p_array) { - PackedVector3Array ret; - if (!p_array) { - return ret; - } - int length = mono_array_length(p_array); - ret.resize(length); - Vector3 *dst = ret.ptrw(); - - if constexpr (InteropLayout::MATCHES_Vector3) { - const Vector3 *src = mono_array_addr(p_array, Vector3, 0); - memcpy(dst, src, length * sizeof(Vector3)); - } else { - for (int i = 0; i < length; i++) { - dst[i] = MARSHALLED_IN(Vector3, (M_Vector3 *)mono_array_addr_with_size(p_array, sizeof(M_Vector3), i)); - } - } - - return ret; -} - -Callable managed_to_callable(const M_Callable &p_managed_callable) { - if (p_managed_callable.delegate) { - // TODO: Use pooling for ManagedCallable instances. - CallableCustom *managed_callable = memnew(ManagedCallable(p_managed_callable.delegate)); - return Callable(managed_callable); - } else { - Object *target = p_managed_callable.target - ? unbox(CACHED_FIELD(GodotObject, ptr)->get_value(p_managed_callable.target)) - : nullptr; - StringName *method_ptr = p_managed_callable.method_string_name - ? unbox(CACHED_FIELD(StringName, ptr)->get_value(p_managed_callable.method_string_name)) - : nullptr; - StringName method = method_ptr ? *method_ptr : StringName(); - return Callable(target, method); - } -} - -M_Callable callable_to_managed(const Callable &p_callable) { - if (p_callable.is_custom()) { - CallableCustom *custom = p_callable.get_custom(); - CallableCustom::CompareEqualFunc compare_equal_func = custom->get_compare_equal_func(); - - if (compare_equal_func == ManagedCallable::compare_equal_func_ptr) { - ManagedCallable *managed_callable = static_cast(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 = p_managed_signal.name_string_name - ? unbox(CACHED_FIELD(StringName, ptr)->get_value(p_managed_signal.name_string_name)) - : nullptr; - StringName name = name_ptr ? *name_ptr : StringName(); - return Signal(owner, name); -} - -M_SignalInfo signal_info_to_managed(const Signal &p_signal) { - Object *owner = p_signal.get_object(); - MonoObject *owner_managed = GDMonoUtils::unmanaged_get_managed(owner); - MonoObject *name_string_name_managed = GDMonoUtils::create_managed_from(p_signal.get_name()); - return { owner_managed, name_string_name_managed }; -} } // namespace GDMonoMarshal diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h index 51f11ab18a8..16683de51aa 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.h +++ b/modules/mono/mono_gd/gd_mono_marshal.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,7 +33,6 @@ #include "core/variant/variant.h" -#include "../managed_callable.h" #include "gd_mono.h" #include "gd_mono_utils.h" @@ -44,25 +43,6 @@ T unbox(MonoObject *p_obj) { return *(T *)mono_object_unbox(p_obj); } -template -T *unbox_addr(MonoObject *p_obj) { - return (T *)mono_object_unbox(p_obj); -} - -#define BOX_DOUBLE(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(double), &x) -#define BOX_FLOAT(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(float), &x) -#define BOX_INT64(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(int64_t), &x) -#define BOX_INT32(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(int32_t), &x) -#define BOX_INT16(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(int16_t), &x) -#define BOX_INT8(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(int8_t), &x) -#define BOX_UINT64(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(uint64_t), &x) -#define BOX_UINT32(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(uint32_t), &x) -#define BOX_UINT16(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(uint16_t), &x) -#define BOX_UINT8(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(uint8_t), &x) -#define BOX_BOOLEAN(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(bool), &x) -#define BOX_PTR(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(IntPtr), x) -#define BOX_ENUM(m_enum_class, x) mono_value_box(mono_domain_get(), m_enum_class, &x) - Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_variant = nullptr); bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_elem_type); @@ -90,516 +70,37 @@ _FORCE_INLINE_ MonoString *mono_string_from_godot(const String &p_string) { // Variant -size_t variant_get_managed_unboxed_size(const ManagedType &p_type); -void *variant_to_managed_unboxed(const Variant &p_var, const ManagedType &p_type, void *r_buffer, unsigned int &r_offset); -MonoObject *variant_to_mono_object(const Variant &p_var, const ManagedType &p_type); +MonoObject *variant_to_mono_object_of_type(const Variant &p_var, const ManagedType &p_type); MonoObject *variant_to_mono_object(const Variant &p_var); -MonoArray *variant_to_mono_array(const Variant &p_var, GDMonoClass *p_type_class); -MonoObject *variant_to_mono_object_of_class(const Variant &p_var, GDMonoClass *p_type_class); -MonoObject *variant_to_mono_object_of_genericinst(const Variant &p_var, GDMonoClass *p_type_class); -MonoString *variant_to_mono_string(const Variant &p_var); // These overloads were added to avoid passing a `const Variant *` to the `const Variant &` // parameter. That would result in the `Variant(bool)` copy constructor being called as // pointers are implicitly converted to bool. Implicit conversions are f-ing evil. -_FORCE_INLINE_ void *variant_to_managed_unboxed(const Variant *p_var, const ManagedType &p_type, void *r_buffer, unsigned int &r_offset) { - return variant_to_managed_unboxed(*p_var, p_type, r_buffer, r_offset); -} -_FORCE_INLINE_ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_type) { - return variant_to_mono_object(*p_var, p_type); +_FORCE_INLINE_ MonoObject *variant_to_mono_object_of_type(const Variant *p_var, const ManagedType &p_type) { + return variant_to_mono_object_of_type(*p_var, p_type); } _FORCE_INLINE_ MonoObject *variant_to_mono_object(const Variant *p_var) { return variant_to_mono_object(*p_var); } -_FORCE_INLINE_ MonoArray *variant_to_mono_array(const Variant *p_var, GDMonoClass *p_type_class) { - return variant_to_mono_array(*p_var, p_type_class); -} -_FORCE_INLINE_ MonoObject *variant_to_mono_object_of_class(const Variant *p_var, GDMonoClass *p_type_class) { - return variant_to_mono_object_of_class(*p_var, p_type_class); -} -_FORCE_INLINE_ MonoObject *variant_to_mono_object_of_genericinst(const Variant *p_var, GDMonoClass *p_type_class) { - return variant_to_mono_object_of_genericinst(*p_var, p_type_class); -} -_FORCE_INLINE_ MonoString *variant_to_mono_string(const Variant *p_var) { - return variant_to_mono_string(*p_var); -} Variant mono_object_to_variant(MonoObject *p_obj); -Variant mono_object_to_variant(MonoObject *p_obj, const ManagedType &p_type); -Variant mono_object_to_variant_no_err(MonoObject *p_obj, const ManagedType &p_type); +Variant mono_object_to_variant_no_err(MonoObject *p_obj); /// Tries to convert the MonoObject* to Variant and then convert the Variant to String. /// If the MonoObject* cannot be converted to Variant, then 'ToString()' is called instead. String mono_object_to_variant_string(MonoObject *p_obj, MonoException **r_exc); -// System.Collections.Generic - -MonoObject *Dictionary_to_system_generic_dict(const Dictionary &p_dict, GDMonoClass *p_class, MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype); -Dictionary system_generic_dict_to_Dictionary(MonoObject *p_obj, GDMonoClass *p_class, MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype); - -MonoObject *Array_to_system_generic_list(const Array &p_array, GDMonoClass *p_class, MonoReflectionType *p_elem_reftype); -Variant system_generic_list_to_Array_variant(MonoObject *p_obj, GDMonoClass *p_class, MonoReflectionType *p_elem_reftype); - // Array MonoArray *Array_to_mono_array(const Array &p_array); -MonoArray *Array_to_mono_array(const Array &p_array, MonoClass *p_array_type_class); Array mono_array_to_Array(MonoArray *p_array); -// PackedInt32Array - -MonoArray *PackedInt32Array_to_mono_array(const PackedInt32Array &p_array); -PackedInt32Array mono_array_to_PackedInt32Array(MonoArray *p_array); - -// PackedInt64Array - -MonoArray *PackedInt64Array_to_mono_array(const PackedInt64Array &p_array); -PackedInt64Array mono_array_to_PackedInt64Array(MonoArray *p_array); - -// PackedByteArray - -MonoArray *PackedByteArray_to_mono_array(const PackedByteArray &p_array); -PackedByteArray mono_array_to_PackedByteArray(MonoArray *p_array); - -// PackedFloat32Array - -MonoArray *PackedFloat32Array_to_mono_array(const PackedFloat32Array &p_array); -PackedFloat32Array mono_array_to_PackedFloat32Array(MonoArray *p_array); - -// PackedFloat64Array - -MonoArray *PackedFloat64Array_to_mono_array(const PackedFloat64Array &p_array); -PackedFloat64Array mono_array_to_PackedFloat64Array(MonoArray *p_array); - // PackedStringArray MonoArray *PackedStringArray_to_mono_array(const PackedStringArray &p_array); -PackedStringArray mono_array_to_PackedStringArray(MonoArray *p_array); -// PackedColorArray - -MonoArray *PackedColorArray_to_mono_array(const PackedColorArray &p_array); -PackedColorArray mono_array_to_PackedColorArray(MonoArray *p_array); - -// PackedVector2Array - -MonoArray *PackedVector2Array_to_mono_array(const PackedVector2Array &p_array); -PackedVector2Array mono_array_to_PackedVector2Array(MonoArray *p_array); - -// PackedVector3Array - -MonoArray *PackedVector3Array_to_mono_array(const PackedVector3Array &p_array); -PackedVector3Array mono_array_to_PackedVector3Array(MonoArray *p_array); - -#pragma pack(push, 1) - -struct M_Callable { - MonoObject *target = nullptr; - MonoObject *method_string_name = nullptr; - MonoDelegate *delegate = nullptr; -}; - -struct M_SignalInfo { - MonoObject *owner = nullptr; - MonoObject *name_string_name = nullptr; -}; - -#pragma pack(pop) - -// Callable -Callable managed_to_callable(const M_Callable &p_managed_callable); -M_Callable callable_to_managed(const Callable &p_callable); - -// SignalInfo -Signal managed_to_signal_info(const M_SignalInfo &p_managed_signal); -M_SignalInfo signal_info_to_managed(const Signal &p_signal); - -// Structures - -namespace InteropLayout { - -enum { - MATCHES_int = (sizeof(int32_t) == sizeof(uint32_t)), - - MATCHES_float = (sizeof(float) == sizeof(uint32_t)), - - MATCHES_double = (sizeof(double) == sizeof(uint64_t)), - -#ifdef REAL_T_IS_DOUBLE - MATCHES_real_t = (sizeof(real_t) == sizeof(uint64_t)), -#else - MATCHES_real_t = (sizeof(real_t) == sizeof(uint32_t)), -#endif - - MATCHES_Vector2 = (MATCHES_real_t && (sizeof(Vector2) == (sizeof(real_t) * 2)) && - offsetof(Vector2, x) == (sizeof(real_t) * 0) && - offsetof(Vector2, y) == (sizeof(real_t) * 1)), - - MATCHES_Vector2i = (MATCHES_int && (sizeof(Vector2i) == (sizeof(int32_t) * 2)) && - offsetof(Vector2i, x) == (sizeof(int32_t) * 0) && - offsetof(Vector2i, y) == (sizeof(int32_t) * 1)), - - MATCHES_Rect2 = (MATCHES_Vector2 && (sizeof(Rect2) == (sizeof(Vector2) * 2)) && - offsetof(Rect2, position) == (sizeof(Vector2) * 0) && - offsetof(Rect2, size) == (sizeof(Vector2) * 1)), - - MATCHES_Rect2i = (MATCHES_Vector2i && (sizeof(Rect2i) == (sizeof(Vector2i) * 2)) && - offsetof(Rect2i, position) == (sizeof(Vector2i) * 0) && - offsetof(Rect2i, size) == (sizeof(Vector2i) * 1)), - - MATCHES_Transform2D = (MATCHES_Vector2 && (sizeof(Transform2D) == (sizeof(Vector2) * 3))), // No field offset required, it stores an array - - MATCHES_Vector3 = (MATCHES_real_t && (sizeof(Vector3) == (sizeof(real_t) * 3)) && - offsetof(Vector3, x) == (sizeof(real_t) * 0) && - offsetof(Vector3, y) == (sizeof(real_t) * 1) && - offsetof(Vector3, z) == (sizeof(real_t) * 2)), - - MATCHES_Vector4 = (MATCHES_real_t && (sizeof(Vector4) == (sizeof(real_t) * 4)) && - offsetof(Vector4, x) == (sizeof(real_t) * 0) && - offsetof(Vector4, y) == (sizeof(real_t) * 1) && - offsetof(Vector4, z) == (sizeof(real_t) * 2) && - offsetof(Vector4, w) == (sizeof(real_t) * 3)), - - MATCHES_Vector4i = (MATCHES_int && (sizeof(Vector4i) == (sizeof(int32_t) * 4)) && - offsetof(Vector4i, x) == (sizeof(int32_t) * 0) && - offsetof(Vector4i, y) == (sizeof(int32_t) * 1) && - offsetof(Vector4i, z) == (sizeof(int32_t) * 2) && - offsetof(Vector4i, w) == (sizeof(int32_t) * 3)), - - MATCHES_Vector3i = (MATCHES_int && (sizeof(Vector3i) == (sizeof(int32_t) * 3)) && - offsetof(Vector3i, x) == (sizeof(int32_t) * 0) && - offsetof(Vector3i, y) == (sizeof(int32_t) * 1) && - offsetof(Vector3i, z) == (sizeof(int32_t) * 2)), - - MATCHES_Basis = (MATCHES_Vector3 && (sizeof(Basis) == (sizeof(Vector3) * 3))), // No field offset required, it stores an array - - MATCHES_Quaternion = (MATCHES_real_t && (sizeof(Quaternion) == (sizeof(real_t) * 4)) && - offsetof(Quaternion, x) == (sizeof(real_t) * 0) && - offsetof(Quaternion, y) == (sizeof(real_t) * 1) && - offsetof(Quaternion, z) == (sizeof(real_t) * 2) && - offsetof(Quaternion, w) == (sizeof(real_t) * 3)), - - MATCHES_Transform3D = (MATCHES_Basis && MATCHES_Vector3 && (sizeof(Transform3D) == (sizeof(Basis) + sizeof(Vector3))) && - offsetof(Transform3D, basis) == 0 && - offsetof(Transform3D, origin) == sizeof(Basis)), - - MATCHES_Projection = (MATCHES_Vector4 && (sizeof(Projection) == (sizeof(Vector4) * 4))), - - MATCHES_AABB = (MATCHES_Vector3 && (sizeof(AABB) == (sizeof(Vector3) * 2)) && - offsetof(AABB, position) == (sizeof(Vector3) * 0) && - offsetof(AABB, size) == (sizeof(Vector3) * 1)), - - MATCHES_Color = (MATCHES_float && (sizeof(Color) == (sizeof(float) * 4)) && - offsetof(Color, r) == (sizeof(float) * 0) && - offsetof(Color, g) == (sizeof(float) * 1) && - offsetof(Color, b) == (sizeof(float) * 2) && - offsetof(Color, a) == (sizeof(float) * 3)), - - MATCHES_Plane = (MATCHES_Vector3 && MATCHES_real_t && (sizeof(Plane) == (sizeof(Vector3) + sizeof(real_t))) && - offsetof(Plane, normal) == 0 && - offsetof(Plane, d) == sizeof(Vector3)) -}; - -// In the future we may force this if we want to ref return these structs -#ifdef GD_MONO_FORCE_INTEROP_STRUCT_COPY -/* clang-format off */ -static_assert(MATCHES_Vector2 && MATCHES_Rect2 && MATCHES_Transform2D && MATCHES_Vector3 && MATCHES_Vector4 && - MATCHES_Basis && MATCHES_Quaternion && MATCHES_Transform3D && MATCHES_Projection && MATCHES_AABB && MATCHES_Color && - MATCHES_Plane && MATCHES_Vector2i && MATCHES_Rect2i && MATCHES_Vector3i && MATCHES_Vector4i); -/* clang-format on */ -#endif -} // namespace InteropLayout - -#pragma pack(push, 1) - -struct M_Vector2 { - real_t x, y; - - static _FORCE_INLINE_ Vector2 convert_to(const M_Vector2 &p_from) { - return Vector2(p_from.x, p_from.y); - } - - static _FORCE_INLINE_ M_Vector2 convert_from(const Vector2 &p_from) { - M_Vector2 ret = { p_from.x, p_from.y }; - return ret; - } -}; - -struct M_Vector2i { - int32_t x, y; - - static _FORCE_INLINE_ Vector2i convert_to(const M_Vector2i &p_from) { - return Vector2i(p_from.x, p_from.y); - } - - static _FORCE_INLINE_ M_Vector2i convert_from(const Vector2i &p_from) { - M_Vector2i ret = { p_from.x, p_from.y }; - return ret; - } -}; - -struct M_Rect2 { - M_Vector2 position; - M_Vector2 size; - - static _FORCE_INLINE_ Rect2 convert_to(const M_Rect2 &p_from) { - return Rect2(M_Vector2::convert_to(p_from.position), - M_Vector2::convert_to(p_from.size)); - } - - static _FORCE_INLINE_ M_Rect2 convert_from(const Rect2 &p_from) { - M_Rect2 ret = { M_Vector2::convert_from(p_from.position), M_Vector2::convert_from(p_from.size) }; - return ret; - } -}; - -struct M_Rect2i { - M_Vector2i position; - M_Vector2i size; - - static _FORCE_INLINE_ Rect2i convert_to(const M_Rect2i &p_from) { - return Rect2i(M_Vector2i::convert_to(p_from.position), - M_Vector2i::convert_to(p_from.size)); - } - - static _FORCE_INLINE_ M_Rect2i convert_from(const Rect2i &p_from) { - M_Rect2i ret = { M_Vector2i::convert_from(p_from.position), M_Vector2i::convert_from(p_from.size) }; - return ret; - } -}; - -struct M_Transform2D { - M_Vector2 elements[3]; - - static _FORCE_INLINE_ Transform2D convert_to(const M_Transform2D &p_from) { - return Transform2D(p_from.elements[0].x, p_from.elements[0].y, - p_from.elements[1].x, p_from.elements[1].y, - p_from.elements[2].x, p_from.elements[2].y); - } - - static _FORCE_INLINE_ M_Transform2D convert_from(const Transform2D &p_from) { - M_Transform2D ret = { - M_Vector2::convert_from(p_from.columns[0]), - M_Vector2::convert_from(p_from.columns[1]), - M_Vector2::convert_from(p_from.columns[2]) - }; - return ret; - } -}; - -struct M_Vector3 { - real_t x, y, z; - - static _FORCE_INLINE_ Vector3 convert_to(const M_Vector3 &p_from) { - return Vector3(p_from.x, p_from.y, p_from.z); - } - - static _FORCE_INLINE_ M_Vector3 convert_from(const Vector3 &p_from) { - M_Vector3 ret = { p_from.x, p_from.y, p_from.z }; - return ret; - } -}; - -struct M_Vector3i { - int32_t x, y, z; - - static _FORCE_INLINE_ Vector3i convert_to(const M_Vector3i &p_from) { - return Vector3i(p_from.x, p_from.y, p_from.z); - } - - static _FORCE_INLINE_ M_Vector3i convert_from(const Vector3i &p_from) { - M_Vector3i ret = { p_from.x, p_from.y, p_from.z }; - return ret; - } -}; - -struct M_Vector4 { - real_t x, y, z, w; - - static _FORCE_INLINE_ Vector4 convert_to(const M_Vector4 &p_from) { - return Vector4(p_from.x, p_from.y, p_from.z, p_from.w); - } - - static _FORCE_INLINE_ M_Vector4 convert_from(const Vector4 &p_from) { - M_Vector4 ret = { p_from.x, p_from.y, p_from.z, p_from.w }; - return ret; - } -}; - -struct M_Vector4i { - int32_t x, y, z, w; - - static _FORCE_INLINE_ Vector4i convert_to(const M_Vector4i &p_from) { - return Vector4i(p_from.x, p_from.y, p_from.z, p_from.w); - } - - static _FORCE_INLINE_ M_Vector4i convert_from(const Vector4i &p_from) { - M_Vector4i ret = { p_from.x, p_from.y, p_from.z, p_from.w }; - return ret; - } -}; - -struct M_Basis { - M_Vector3 elements[3]; - - static _FORCE_INLINE_ Basis convert_to(const M_Basis &p_from) { - return Basis(M_Vector3::convert_to(p_from.elements[0]), - M_Vector3::convert_to(p_from.elements[1]), - M_Vector3::convert_to(p_from.elements[2])); - } - - static _FORCE_INLINE_ M_Basis convert_from(const Basis &p_from) { - M_Basis ret = { - M_Vector3::convert_from(p_from.rows[0]), - M_Vector3::convert_from(p_from.rows[1]), - M_Vector3::convert_from(p_from.rows[2]) - }; - return ret; - } -}; - -struct M_Quaternion { - real_t x, y, z, w; - - static _FORCE_INLINE_ Quaternion convert_to(const M_Quaternion &p_from) { - return Quaternion(p_from.x, p_from.y, p_from.z, p_from.w); - } - - static _FORCE_INLINE_ M_Quaternion convert_from(const Quaternion &p_from) { - M_Quaternion ret = { p_from.x, p_from.y, p_from.z, p_from.w }; - return ret; - } -}; - -struct M_Transform3D { - M_Basis basis; - M_Vector3 origin; - - static _FORCE_INLINE_ Transform3D convert_to(const M_Transform3D &p_from) { - return Transform3D(M_Basis::convert_to(p_from.basis), M_Vector3::convert_to(p_from.origin)); - } - - static _FORCE_INLINE_ M_Transform3D convert_from(const Transform3D &p_from) { - M_Transform3D ret = { M_Basis::convert_from(p_from.basis), M_Vector3::convert_from(p_from.origin) }; - return ret; - } -}; - -struct M_Projection { - M_Vector4 vec1; - M_Vector4 vec2; - M_Vector4 vec3; - M_Vector4 vec4; - - static _FORCE_INLINE_ Projection convert_to(const M_Projection &p_from) { - return Projection(M_Vector4::convert_to(p_from.vec1), M_Vector4::convert_to(p_from.vec2), M_Vector4::convert_to(p_from.vec3), M_Vector4::convert_to(p_from.vec4)); - } - - static _FORCE_INLINE_ M_Projection convert_from(const Projection &p_from) { - M_Projection ret = { M_Vector4::convert_from(p_from.matrix[0]), M_Vector4::convert_from(p_from.matrix[1]), M_Vector4::convert_from(p_from.matrix[2]), M_Vector4::convert_from(p_from.matrix[3]) }; - return ret; - } -}; - -struct M_AABB { - M_Vector3 position; - M_Vector3 size; - - static _FORCE_INLINE_ AABB convert_to(const M_AABB &p_from) { - return AABB(M_Vector3::convert_to(p_from.position), M_Vector3::convert_to(p_from.size)); - } - - static _FORCE_INLINE_ M_AABB convert_from(const AABB &p_from) { - M_AABB ret = { M_Vector3::convert_from(p_from.position), M_Vector3::convert_from(p_from.size) }; - return ret; - } -}; - -struct M_Color { - float r, g, b, a; - - static _FORCE_INLINE_ Color convert_to(const M_Color &p_from) { - return Color(p_from.r, p_from.g, p_from.b, p_from.a); - } - - static _FORCE_INLINE_ M_Color convert_from(const Color &p_from) { - M_Color ret = { p_from.r, p_from.g, p_from.b, p_from.a }; - return ret; - } -}; - -struct M_Plane { - M_Vector3 normal; - real_t d; - - static _FORCE_INLINE_ Plane convert_to(const M_Plane &p_from) { - return Plane(M_Vector3::convert_to(p_from.normal), p_from.d); - } - - static _FORCE_INLINE_ M_Plane convert_from(const Plane &p_from) { - M_Plane ret = { M_Vector3::convert_from(p_from.normal), p_from.d }; - return ret; - } -}; - -#pragma pack(pop) - -#define DECL_TYPE_MARSHAL_TEMPLATES(m_type) \ - template \ - _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(Vector4) -DECL_TYPE_MARSHAL_TEMPLATES(Vector4i) -DECL_TYPE_MARSHAL_TEMPLATES(Quaternion) -DECL_TYPE_MARSHAL_TEMPLATES(Transform3D) -DECL_TYPE_MARSHAL_TEMPLATES(Projection) -DECL_TYPE_MARSHAL_TEMPLATES(AABB) -DECL_TYPE_MARSHAL_TEMPLATES(Color) -DECL_TYPE_MARSHAL_TEMPLATES(Plane) - -#define MARSHALLED_IN(m_type, m_from_ptr) (GDMonoMarshal::marshalled_in_##m_type(m_from_ptr)) -#define MARSHALLED_OUT(m_type, m_from) (GDMonoMarshal::marshalled_out_##m_type(m_from)) } // namespace GDMonoMarshal #endif // GD_MONO_MARSHAL_H diff --git a/modules/mono/mono_gd/gd_mono_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp index 6734b44783e..04f6005338c 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 be11ef5bfe0..5398f34103c 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_property.cpp b/modules/mono/mono_gd/gd_mono_property.cpp index c9775ae9cb8..7cbf5be1516 100644 --- a/modules/mono/mono_gd/gd_mono_property.cpp +++ b/modules/mono/mono_gd/gd_mono_property.cpp @@ -65,8 +65,6 @@ GDMonoProperty::GDMonoProperty(MonoProperty *p_mono_property, GDMonoClass *p_own type.type_class = GDMono::get_singleton()->get_class(param_type_class); } - param_buffer_size = GDMonoMarshal::variant_get_managed_unboxed_size(type); - attrs_fetched = false; attributes = nullptr; } @@ -150,19 +148,16 @@ bool GDMonoProperty::has_setter() { } void GDMonoProperty::set_value_from_variant(MonoObject *p_object, const Variant &p_value, MonoException **r_exc) { - uint8_t *buffer = (uint8_t *)alloca(param_buffer_size); - unsigned int offset = 0; + MonoMethod *set_method = mono_property_get_set_method(mono_property); + ERR_FAIL_COND(set_method == nullptr); - void *params[1] = { - GDMonoMarshal::variant_to_managed_unboxed(p_value, type, buffer, offset) - }; - -#ifdef DEBUG_ENABLED - CRASH_COND(offset != param_buffer_size); -#endif + // Temporary solution, while moving code to C# + MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), 1); + MonoObject *boxed_param = GDMonoMarshal::variant_to_mono_object_of_type(p_value, type); + mono_array_setref(params, 0, boxed_param); MonoException *exc = nullptr; - GDMonoUtils::property_set_value(mono_property, p_object, params, &exc); + GDMonoUtils::runtime_invoke_array(set_method, p_object, params, &exc); if (exc) { if (r_exc) { *r_exc = exc; diff --git a/modules/mono/mono_gd/gd_mono_property.h b/modules/mono/mono_gd/gd_mono_property.h index 6fc681aeb58..885ea8f0118 100644 --- a/modules/mono/mono_gd/gd_mono_property.h +++ b/modules/mono/mono_gd/gd_mono_property.h @@ -45,8 +45,6 @@ class GDMonoProperty : public IMonoClassMember { bool attrs_fetched; MonoCustomAttrInfo *attributes = nullptr; - unsigned int param_buffer_size; - public: virtual GDMonoClass *get_enclosing_class() const final { return owner; } diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp index 1983d6ebe2f..e2403811120 100644 --- a/modules/mono/mono_gd/gd_mono_utils.cpp +++ b/modules/mono/mono_gd/gd_mono_utils.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -170,13 +170,6 @@ void runtime_object_init(MonoObject *p_this_obj, GDMonoClass *p_class, MonoExcep ctor->invoke_raw(p_this_obj, nullptr, r_exc); } -bool mono_delegate_equal(MonoDelegate *p_a, MonoDelegate *p_b) { - MonoException *exc = nullptr; - MonoBoolean res = CACHED_METHOD_THUNK(Delegate, Equals).invoke((MonoObject *)p_a, (MonoObject *)p_b, &exc); - UNHANDLED_EXCEPTION(exc); - return (bool)res; -} - GDMonoClass *get_object_class(MonoObject *p_object) { return GDMono::get_singleton()->get_class(mono_object_get_class(p_object)); } @@ -239,102 +232,6 @@ MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringNa return mono_object; } -MonoObject *create_managed_from(const StringName &p_from) { - MonoObject *mono_object = mono_object_new(mono_domain_get(), CACHED_CLASS_RAW(StringName)); - ERR_FAIL_NULL_V(mono_object, nullptr); - - // Construct - GDMonoUtils::runtime_object_init(mono_object, CACHED_CLASS(StringName)); - - CACHED_FIELD(StringName, ptr)->set_value_raw(mono_object, memnew(StringName(p_from))); - - return mono_object; -} - -MonoObject *create_managed_from(const NodePath &p_from) { - MonoObject *mono_object = mono_object_new(mono_domain_get(), CACHED_CLASS_RAW(NodePath)); - ERR_FAIL_NULL_V(mono_object, nullptr); - - // Construct - GDMonoUtils::runtime_object_init(mono_object, CACHED_CLASS(NodePath)); - - CACHED_FIELD(NodePath, ptr)->set_value_raw(mono_object, memnew(NodePath(p_from))); - - return mono_object; -} - -MonoObject *create_managed_from(const RID &p_from) { - MonoObject *mono_object = mono_object_new(mono_domain_get(), CACHED_CLASS_RAW(RID)); - ERR_FAIL_NULL_V(mono_object, nullptr); - - // Construct - GDMonoUtils::runtime_object_init(mono_object, CACHED_CLASS(RID)); - - CACHED_FIELD(RID, ptr)->set_value_raw(mono_object, memnew(RID(p_from))); - - return mono_object; -} - -MonoObject *create_managed_from(const Array &p_from, GDMonoClass *p_class) { - MonoObject *mono_object = mono_object_new(mono_domain_get(), p_class->get_mono_ptr()); - ERR_FAIL_NULL_V(mono_object, nullptr); - - // Search constructor that takes a pointer as parameter - MonoMethod *m; - void *iter = nullptr; - while ((m = mono_class_get_methods(p_class->get_mono_ptr(), &iter))) { - if (strcmp(mono_method_get_name(m), ".ctor") == 0) { - MonoMethodSignature *sig = mono_method_signature(m); - void *front = nullptr; - if (mono_signature_get_param_count(sig) == 1 && - mono_class_from_mono_type(mono_signature_get_params(sig, &front)) == CACHED_CLASS(IntPtr)->get_mono_ptr()) { - break; - } - } - } - - CRASH_COND(m == nullptr); - - Array *new_array = memnew(Array(p_from)); - void *args[1] = { &new_array }; - - MonoException *exc = nullptr; - GDMonoUtils::runtime_invoke(m, mono_object, args, &exc); - UNHANDLED_EXCEPTION(exc); - - return mono_object; -} - -MonoObject *create_managed_from(const Dictionary &p_from, GDMonoClass *p_class) { - MonoObject *mono_object = mono_object_new(mono_domain_get(), p_class->get_mono_ptr()); - ERR_FAIL_NULL_V(mono_object, nullptr); - - // Search constructor that takes a pointer as parameter - MonoMethod *m; - void *iter = nullptr; - while ((m = mono_class_get_methods(p_class->get_mono_ptr(), &iter))) { - if (strcmp(mono_method_get_name(m), ".ctor") == 0) { - MonoMethodSignature *sig = mono_method_signature(m); - void *front = nullptr; - if (mono_signature_get_param_count(sig) == 1 && - mono_class_from_mono_type(mono_signature_get_params(sig, &front)) == CACHED_CLASS(IntPtr)->get_mono_ptr()) { - break; - } - } - } - - CRASH_COND(m == nullptr); - - Dictionary *new_dict = memnew(Dictionary(p_from)); - void *args[1] = { &new_dict }; - - MonoException *exc = nullptr; - GDMonoUtils::runtime_invoke(m, mono_object, args, &exc); - UNHANDLED_EXCEPTION(exc); - - return mono_object; -} - MonoDomain *create_domain(const String &p_friendly_name) { print_verbose("Mono: Creating domain '" + p_friendly_name + "'..."); @@ -487,6 +384,13 @@ MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, M return ret; } +MonoObject *runtime_invoke_array(MonoMethod *p_method, void *p_obj, MonoArray *p_params, MonoException **r_exc) { + GD_MONO_BEGIN_RUNTIME_INVOKE; + MonoObject *ret = mono_runtime_invoke_array(p_method, p_obj, p_params, (MonoObject **)r_exc); + GD_MONO_END_RUNTIME_INVOKE; + return ret; +} + MonoString *object_to_string(MonoObject *p_obj, MonoException **r_exc) { GD_MONO_BEGIN_RUNTIME_INVOKE; MonoString *ret = mono_object_to_string(p_obj, (MonoObject **)r_exc); @@ -558,62 +462,6 @@ namespace Marshal { { return m_ret; } #endif -bool type_is_generic_array(MonoReflectionType *p_reftype) { - NO_GLUE_RET(false); - MonoException *exc = nullptr; - MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray).invoke(p_reftype, &exc); - UNHANDLED_EXCEPTION(exc); - return (bool)res; -} - -bool type_is_generic_dictionary(MonoReflectionType *p_reftype) { - NO_GLUE_RET(false); - MonoException *exc = nullptr; - MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary).invoke(p_reftype, &exc); - UNHANDLED_EXCEPTION(exc); - return (bool)res; -} - -bool type_is_system_generic_list(MonoReflectionType *p_reftype) { - NO_GLUE_RET(false); - MonoException *exc = nullptr; - MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsSystemGenericList).invoke(p_reftype, &exc); - UNHANDLED_EXCEPTION(exc); - return (bool)res; -} - -bool type_is_system_generic_dictionary(MonoReflectionType *p_reftype) { - NO_GLUE_RET(false); - MonoException *exc = nullptr; - MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsSystemGenericDictionary).invoke(p_reftype, &exc); - UNHANDLED_EXCEPTION(exc); - return (bool)res; -} - -bool type_is_generic_ienumerable(MonoReflectionType *p_reftype) { - NO_GLUE_RET(false); - MonoException *exc = nullptr; - MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericIEnumerable).invoke(p_reftype, &exc); - UNHANDLED_EXCEPTION(exc); - return (bool)res; -} - -bool type_is_generic_icollection(MonoReflectionType *p_reftype) { - NO_GLUE_RET(false); - MonoException *exc = nullptr; - MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericICollection).invoke(p_reftype, &exc); - UNHANDLED_EXCEPTION(exc); - return (bool)res; -} - -bool type_is_generic_idictionary(MonoReflectionType *p_reftype) { - NO_GLUE_RET(false); - MonoException *exc = nullptr; - MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericIDictionary).invoke(p_reftype, &exc); - UNHANDLED_EXCEPTION(exc); - return (bool)res; -} - bool type_has_flags_attribute(MonoReflectionType *p_reftype) { NO_GLUE_RET(false); MonoException *exc = nullptr; @@ -622,39 +470,6 @@ bool type_has_flags_attribute(MonoReflectionType *p_reftype) { return (bool)res; } -void get_generic_type_definition(MonoReflectionType *p_reftype, MonoReflectionType **r_generic_reftype) { - MonoException *exc = nullptr; - CACHED_METHOD_THUNK(MarshalUtils, GetGenericTypeDefinition).invoke(p_reftype, r_generic_reftype, &exc); - UNHANDLED_EXCEPTION(exc); -} - -void array_get_element_type(MonoReflectionType *p_array_reftype, MonoReflectionType **r_elem_reftype) { - MonoException *exc = nullptr; - CACHED_METHOD_THUNK(MarshalUtils, ArrayGetElementType).invoke(p_array_reftype, r_elem_reftype, &exc); - UNHANDLED_EXCEPTION(exc); -} - -void dictionary_get_key_value_types(MonoReflectionType *p_dict_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype) { - MonoException *exc = nullptr; - CACHED_METHOD_THUNK(MarshalUtils, DictionaryGetKeyValueTypes).invoke(p_dict_reftype, r_key_reftype, r_value_reftype, &exc); - UNHANDLED_EXCEPTION(exc); -} - -GDMonoClass *make_generic_array_type(MonoReflectionType *p_elem_reftype) { - NO_GLUE_RET(nullptr); - MonoException *exc = nullptr; - MonoReflectionType *reftype = CACHED_METHOD_THUNK(MarshalUtils, MakeGenericArrayType).invoke(p_elem_reftype, &exc); - UNHANDLED_EXCEPTION(exc); - return GDMono::get_singleton()->get_class(mono_class_from_mono_type(mono_reflection_type_get_type(reftype))); -} - -GDMonoClass *make_generic_dictionary_type(MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype) { - NO_GLUE_RET(nullptr); - MonoException *exc = nullptr; - MonoReflectionType *reftype = CACHED_METHOD_THUNK(MarshalUtils, MakeGenericDictionaryType).invoke(p_key_reftype, p_value_reftype, &exc); - UNHANDLED_EXCEPTION(exc); - return GDMono::get_singleton()->get_class(mono_class_from_mono_type(mono_reflection_type_get_type(reftype))); -} } // namespace Marshal ScopeThreadAttach::ScopeThreadAttach() { @@ -671,7 +486,6 @@ ScopeThreadAttach::~ScopeThreadAttach() { StringName get_native_godot_class_name(GDMonoClass *p_class) { MonoObject *native_name_obj = p_class->get_field(BINDINGS_NATIVE_NAME_FIELD)->get_value(nullptr); - StringName *ptr = GDMonoMarshal::unbox(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 300cacfa4bf..ee1be979e73 100644 --- a/modules/mono/mono_gd/gd_mono_utils.h +++ b/modules/mono/mono_gd/gd_mono_utils.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -53,23 +53,7 @@ namespace GDMonoUtils { namespace Marshal { - -bool type_is_generic_array(MonoReflectionType *p_reftype); -bool type_is_generic_dictionary(MonoReflectionType *p_reftype); -bool type_is_system_generic_list(MonoReflectionType *p_reftype); -bool type_is_system_generic_dictionary(MonoReflectionType *p_reftype); -bool type_is_generic_ienumerable(MonoReflectionType *p_reftype); -bool type_is_generic_icollection(MonoReflectionType *p_reftype); -bool type_is_generic_idictionary(MonoReflectionType *p_reftype); bool type_has_flags_attribute(MonoReflectionType *p_reftype); - -void get_generic_type_definition(MonoReflectionType *p_reftype, MonoReflectionType **r_generic_reftype); - -void array_get_element_type(MonoReflectionType *p_array_reftype, MonoReflectionType **r_elem_reftype); -void dictionary_get_key_value_types(MonoReflectionType *p_dict_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype); - -GDMonoClass *make_generic_array_type(MonoReflectionType *p_elem_reftype); -GDMonoClass *make_generic_dictionary_type(MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype); } // namespace Marshal _FORCE_INLINE_ void hash_combine(uint32_t &p_hash, const uint32_t &p_with_hash) { @@ -97,20 +81,12 @@ void free_gchandle(uint32_t p_gchandle); void runtime_object_init(MonoObject *p_this_obj, GDMonoClass *p_class, MonoException **r_exc = nullptr); -bool mono_delegate_equal(MonoDelegate *p_a, MonoDelegate *p_b); - GDMonoClass *get_object_class(MonoObject *p_object); GDMonoClass *type_get_proxy_class(const StringName &p_type); GDMonoClass *get_class_native_base(GDMonoClass *p_class); MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringName &p_native, Object *p_object); -MonoObject *create_managed_from(const StringName &p_from); -MonoObject *create_managed_from(const NodePath &p_from); -MonoObject *create_managed_from(const RID &p_from); -MonoObject *create_managed_from(const Array &p_from, GDMonoClass *p_class); -MonoObject *create_managed_from(const Dictionary &p_from, GDMonoClass *p_class); - MonoDomain *create_domain(const String &p_friendly_name); String get_type_desc(MonoType *p_type); @@ -141,6 +117,7 @@ _FORCE_INLINE_ int &get_runtime_invoke_count_ref() { } MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, MonoException **r_exc); +MonoObject *runtime_invoke_array(MonoMethod *p_method, void *p_obj, MonoArray *p_params, MonoException **r_exc); MonoString *object_to_string(MonoObject *p_obj, MonoException **r_exc); diff --git a/modules/mono/signal_awaiter_utils.cpp b/modules/mono/signal_awaiter_utils.cpp index 437c4ca54af..f26459554c4 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 532aa3e3273..4f4986baf34 100644 --- a/modules/mono/signal_awaiter_utils.h +++ b/modules/mono/signal_awaiter_utils.h @@ -38,7 +38,12 @@ Error gd_mono_connect_signal_awaiter(Object *p_source, const StringName &p_signal, Object *p_target, MonoObject *p_awaiter); -class SignalAwaiterCallable : public CallableCustom { +class BaseSignalCallable : public CallableCustom { +public: + virtual StringName get_signal() const = 0; +}; + +class SignalAwaiterCallable : public BaseSignalCallable { ObjectID target_id; MonoGCHandleData awaiter_handle; StringName signal; @@ -59,7 +64,7 @@ public: ObjectID get_object() const override; - _FORCE_INLINE_ StringName get_signal() const { return signal; } + StringName get_signal() const override; void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override; @@ -67,7 +72,7 @@ public: ~SignalAwaiterCallable(); }; -class EventSignalCallable : public CallableCustom { +class EventSignalCallable : public BaseSignalCallable { Object *owner = nullptr; const CSharpScript::EventSignal *event_signal; @@ -87,7 +92,7 @@ public: ObjectID get_object() const override; - StringName get_signal() const; + StringName get_signal() const override; void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override; diff --git a/modules/mono/utils/string_utils.cpp b/modules/mono/utils/string_utils.cpp index 975f2d8332b..b0f94310b8b 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 fa4c5e89f47..b00dd9dde8b 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); From c4ccabd3fb81702820ff0943532c3d8a1f1d1c9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Rold=C3=A1n=20Etcheverry?= Date: Sun, 12 Sep 2021 19:49:23 +0200 Subject: [PATCH 02/31] C#: Remove DynamicGodotObject/Object.DynamicObject We are moving in the direction of no dynamic code generation, so this is no longer desired. The feature can still be easily implemented by any project that still want it. --- .../GodotSharp/Core/DynamicObject.cs | 220 ------------------ .../GodotSharp/GodotSharp/Core/Object.base.cs | 5 - .../GodotSharp/GodotSharp/GodotSharp.csproj | 1 - modules/mono/glue/arguments_vector.h | 67 ------ modules/mono/glue/base_object_glue.cpp | 66 ------ 5 files changed, 359 deletions(-) delete mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/DynamicObject.cs delete mode 100644 modules/mono/glue/arguments_vector.h diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DynamicObject.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DynamicObject.cs deleted file mode 100644 index 26d5f9c7963..00000000000 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DynamicObject.cs +++ /dev/null @@ -1,220 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Dynamic; -using System.Linq.Expressions; -using System.Runtime.CompilerServices; - -namespace Godot -{ - /// - /// Represents an whose members can be dynamically accessed at runtime through the Variant API. - /// - /// - /// - /// The class enables access to the Variant - /// members of a instance at runtime. - /// - /// - /// This allows accessing the class members using their original names in the engine as well as the members from the - /// script attached to the , regardless of the scripting language it was written in. - /// - /// - /// - /// This sample shows how to use to dynamically access the engine members of a . - /// - /// dynamic sprite = GetNode("Sprite2D").DynamicGodotObject; - /// sprite.add_child(this); - /// - /// if ((sprite.hframes * sprite.vframes) > 0) - /// sprite.frame = 0; - /// - /// - /// - /// This sample shows how to use to dynamically access the members of the script attached to a . - /// - /// dynamic childNode = GetNode("ChildNode").DynamicGodotObject; - /// - /// if (childNode.print_allowed) - /// { - /// childNode.message = "Hello from C#"; - /// childNode.print_message(3); - /// } - /// - /// The ChildNode node has the following GDScript script attached: - /// - /// // # ChildNode.gd - /// // var print_allowed = true - /// // var message = "" - /// // - /// // func print_message(times): - /// // for i in times: - /// // print(message) - /// - /// - public class DynamicGodotObject : DynamicObject - { - /// - /// Gets the associated with this . - /// - public Object Value { get; } - - /// - /// Initializes a new instance of the class. - /// - /// - /// The that will be associated with this . - /// - /// - /// Thrown when the parameter is . - /// - public DynamicGodotObject(Object godotObject) - { - if (godotObject == null) - throw new ArgumentNullException(nameof(godotObject)); - - Value = godotObject; - } - - /// - public override IEnumerable GetDynamicMemberNames() - { - return godot_icall_DynamicGodotObject_SetMemberList(Object.GetPtr(Value)); - } - - /// - public override bool TryBinaryOperation(BinaryOperationBinder binder, object arg, out object result) - { - switch (binder.Operation) - { - case ExpressionType.Equal: - case ExpressionType.NotEqual: - if (binder.ReturnType == typeof(bool) || binder.ReturnType.IsAssignableFrom(typeof(bool))) - { - if (arg == null) - { - bool boolResult = Object.IsInstanceValid(Value); - - if (binder.Operation == ExpressionType.Equal) - boolResult = !boolResult; - - result = boolResult; - return true; - } - - if (arg is Object other) - { - bool boolResult = (Value == other); - - if (binder.Operation == ExpressionType.NotEqual) - boolResult = !boolResult; - - result = boolResult; - return true; - } - } - - break; - default: - // We're not implementing operators <, <=, >, and >= (LessThan, LessThanOrEqual, GreaterThan, GreaterThanOrEqual). - // These are used on the actual pointers in variant_op.cpp. It's better to let the user do that explicitly. - break; - } - - return base.TryBinaryOperation(binder, arg, out result); - } - - /// - public override bool TryConvert(ConvertBinder binder, out object result) - { - if (binder.Type == typeof(Object)) - { - result = Value; - return true; - } - - if (typeof(Object).IsAssignableFrom(binder.Type)) - { - // Throws InvalidCastException when the cast fails - result = Convert.ChangeType(Value, binder.Type); - return true; - } - - return base.TryConvert(binder, out result); - } - - /// - public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) - { - if (indexes.Length == 1) - { - if (indexes[0] is string name) - { - return godot_icall_DynamicGodotObject_GetMember(Object.GetPtr(Value), name, out result); - } - } - - return base.TryGetIndex(binder, indexes, out result); - } - - /// - public override bool TryGetMember(GetMemberBinder binder, out object result) - { - return godot_icall_DynamicGodotObject_GetMember(Object.GetPtr(Value), binder.Name, out result); - } - - /// - public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) - { - return godot_icall_DynamicGodotObject_InvokeMember(Object.GetPtr(Value), binder.Name, args, out result); - } - - /// - public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value) - { - if (indexes.Length == 1) - { - if (indexes[0] is string name) - { - return godot_icall_DynamicGodotObject_SetMember(Object.GetPtr(Value), name, value); - } - } - - return base.TrySetIndex(binder, indexes, value); - } - - /// - public override bool TrySetMember(SetMemberBinder binder, object value) - { - return godot_icall_DynamicGodotObject_SetMember(Object.GetPtr(Value), binder.Name, value); - } - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern string[] godot_icall_DynamicGodotObject_SetMemberList(IntPtr godotObject); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_DynamicGodotObject_InvokeMember(IntPtr godotObject, string name, object[] args, out object result); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_DynamicGodotObject_GetMember(IntPtr godotObject, string name, out object result); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_DynamicGodotObject_SetMember(IntPtr godotObject, string name, object value); - - #region We don't override these methods - - // Looks like this is not usable from C# - //public override bool TryCreateInstance(CreateInstanceBinder binder, object[] args, out object result); - - // Object members cannot be deleted - //public override bool TryDeleteIndex(DeleteIndexBinder binder, object[] indexes); - //public override bool TryDeleteMember(DeleteMemberBinder binder); - - // Invocation on the object itself, e.g.: obj(param) - //public override bool TryInvoke(InvokeBinder binder, object[] args, out object result); - - // No unnary operations to handle - //public override bool TryUnaryOperation(UnaryOperationBinder binder, out object result); - - #endregion - } -} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs index 80d63581c19..9c4265517ec 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs @@ -138,11 +138,6 @@ namespace Godot return new SignalAwaiter(source, signal, this); } - /// - /// Gets a new associated with this instance. - /// - public dynamic DynamicObject => new DynamicGodotObject(this); - internal static unsafe IntPtr ClassDB_get_method(StringName type, string method) { IntPtr methodBind; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj index 9683d8e812d..0709723da08 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj @@ -39,7 +39,6 @@ - diff --git a/modules/mono/glue/arguments_vector.h b/modules/mono/glue/arguments_vector.h deleted file mode 100644 index 4405809887a..00000000000 --- a/modules/mono/glue/arguments_vector.h +++ /dev/null @@ -1,67 +0,0 @@ -/*************************************************************************/ -/* arguments_vector.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef ARGUMENTS_VECTOR_H -#define ARGUMENTS_VECTOR_H - -#include "core/os/memory.h" - -template -struct ArgumentsVector { -private: - T pool[POOL_SIZE]; - T *_ptr = nullptr; - int size; - - ArgumentsVector() = delete; - ArgumentsVector(const ArgumentsVector &) = delete; - -public: - T *ptr() { return _ptr; } - T &get(int p_idx) { return _ptr[p_idx]; } - void set(int p_idx, const T &p_value) { _ptr[p_idx] = p_value; } - - explicit ArgumentsVector(int p_size) : - size(p_size) { - if (p_size <= POOL_SIZE) { - _ptr = pool; - } else { - _ptr = memnew_arr(T, p_size); - } - } - - ~ArgumentsVector() { - if (size > POOL_SIZE) { - memdelete_arr(_ptr); - } - } -}; - -#endif // ARGUMENTS_VECTOR_H diff --git a/modules/mono/glue/base_object_glue.cpp b/modules/mono/glue/base_object_glue.cpp index 05a8ef20f93..c252ee47f4a 100644 --- a/modules/mono/glue/base_object_glue.cpp +++ b/modules/mono/glue/base_object_glue.cpp @@ -39,7 +39,6 @@ #include "../mono_gd/gd_mono_marshal.h" #include "../mono_gd/gd_mono_utils.h" #include "../signal_awaiter_utils.h" -#include "arguments_vector.h" void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr) { #ifdef DEBUG_ENABLED @@ -154,67 +153,6 @@ int32_t godot_icall_SignalAwaiter_connect(Object *p_source, StringName *p_signal return (int32_t)gd_mono_connect_signal_awaiter(p_source, signal, p_target, p_awaiter); } -MonoArray *godot_icall_DynamicGodotObject_SetMemberList(Object *p_ptr) { - List property_list; - p_ptr->get_property_list(&property_list); - - MonoArray *result = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(String), property_list.size()); - - int i = 0; - for (const PropertyInfo &E : property_list) { - MonoString *boxed = GDMonoMarshal::mono_string_from_godot(E.name); - mono_array_setref(result, i, boxed); - i++; - } - - return result; -} - -MonoBoolean godot_icall_DynamicGodotObject_InvokeMember(Object *p_ptr, MonoString *p_name, MonoArray *p_args, MonoObject **r_result) { - String name = GDMonoMarshal::mono_string_to_godot(p_name); - - int argc = mono_array_length(p_args); - - ArgumentsVector arg_store(argc); - ArgumentsVector args(argc); - - for (int i = 0; i < argc; i++) { - MonoObject *elem = mono_array_get(p_args, MonoObject *, i); - arg_store.set(i, GDMonoMarshal::mono_object_to_variant(elem)); - args.set(i, &arg_store.get(i)); - } - - Callable::CallError error; - Variant result = p_ptr->callp(StringName(name), args.ptr(), argc, error); - - *r_result = GDMonoMarshal::variant_to_mono_object(result); - - return error.error == Callable::CallError::CALL_OK; -} - -MonoBoolean godot_icall_DynamicGodotObject_GetMember(Object *p_ptr, MonoString *p_name, MonoObject **r_result) { - String name = GDMonoMarshal::mono_string_to_godot(p_name); - - bool valid; - Variant value = p_ptr->get(StringName(name), &valid); - - if (valid) { - *r_result = GDMonoMarshal::variant_to_mono_object(value); - } - - return valid; -} - -MonoBoolean godot_icall_DynamicGodotObject_SetMember(Object *p_ptr, MonoString *p_name, MonoObject *p_value) { - String name = GDMonoMarshal::mono_string_to_godot(p_name); - Variant value = GDMonoMarshal::mono_object_to_variant(p_value); - - bool valid; - p_ptr->set(StringName(name), value, &valid); - - return valid; -} - MonoString *godot_icall_Object_ToString(Object *p_ptr) { #ifdef DEBUG_ENABLED // Cannot happen in C#; would get an ObjectDisposedException instead. @@ -232,8 +170,4 @@ void godot_register_object_icalls() { 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); - GDMonoUtils::add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_SetMemberList", godot_icall_DynamicGodotObject_SetMemberList); - GDMonoUtils::add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_InvokeMember", godot_icall_DynamicGodotObject_InvokeMember); - 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); } From 9a51430441eecafbd07a7b9eb46967e2c3dd8b5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Rold=C3=A1n=20Etcheverry?= Date: Sun, 12 Sep 2021 19:49:39 +0200 Subject: [PATCH 03/31] C#: Re-write Array, Dictionary, NodePath, String icalls as P/Invoke --- modules/mono/csharp_script.cpp | 2 +- .../glue/GodotSharp/GodotSharp/Core/Array.cs | 222 +++++++------ .../GodotSharp/GodotSharp/Core/Dictionary.cs | 245 +++++++------- .../Core/NativeInterop/InteropStructs.cs | 121 ++++--- .../Core/NativeInterop/Marshaling.cs | 64 ++-- .../Core/NativeInterop/NativeFuncs.cs | 224 +++++++++++-- .../NativeInterop/NativeFuncs.extended.cs | 14 + .../GodotSharp/GodotSharp/Core/NodePath.cs | 56 ++-- .../GodotSharp/Core/StringExtensions.cs | 93 +++--- modules/mono/glue/collections_glue.cpp | 300 ------------------ modules/mono/glue/node_path_glue.cpp | 77 ----- modules/mono/glue/runtime_interop.cpp | 218 ++++++++++++- modules/mono/glue/string_glue.cpp | 81 ----- modules/mono/mono_gd/gd_mono.cpp | 7 - 14 files changed, 858 insertions(+), 866 deletions(-) delete mode 100644 modules/mono/glue/collections_glue.cpp delete mode 100644 modules/mono/glue/node_path_glue.cpp delete mode 100644 modules/mono/glue/string_glue.cpp diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 2e6a6ef7e05..9520598f5cd 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -109,7 +109,7 @@ Error CSharpLanguage::execute_file(const String &p_path) { return OK; } -extern void *godotsharp_pinvoke_funcs[101]; +extern void *godotsharp_pinvoke_funcs[138]; [[maybe_unused]] volatile void **do_not_strip_godotsharp_pinvoke_funcs; void CSharpLanguage::init() { diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs index c32895baab1..893ab81e5d0 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.Collections; using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; using Godot.NativeInterop; namespace Godot.Collections @@ -22,7 +21,7 @@ namespace Godot.Collections /// public Array() { - godot_icall_Array_Ctor(out NativeValue); + NativeValue = NativeFuncs.godotsharp_array_new(); } /// @@ -48,11 +47,15 @@ namespace Godot.Collections public Array(params object[] array) : this() { if (array == null) - { throw new NullReferenceException($"Parameter '{nameof(array)} cannot be null.'"); - } - godot_icall_Array_Ctor_MonoArray(array, out NativeValue); + NativeValue = NativeFuncs.godotsharp_array_new(); + int length = array.Length; + + Resize(length); + + for (int i = 0; i < length; i++) + this[i] = array[i]; } private Array(godot_array nativeValueToOwn) @@ -92,7 +95,7 @@ namespace Godot.Collections public Array Duplicate(bool deep = false) { godot_array newArray; - godot_icall_Array_Duplicate(ref NativeValue, deep, out newArray); + NativeFuncs.godotsharp_array_duplicate(ref NativeValue, deep, out newArray); return CreateTakingOwnershipOfDisposableValue(newArray); } @@ -101,18 +104,12 @@ namespace Godot.Collections /// /// The new size of the array. /// if successful, or an error code. - public Error Resize(int newSize) - { - return godot_icall_Array_Resize(ref NativeValue, newSize); - } + public Error Resize(int newSize) => NativeFuncs.godotsharp_array_resize(ref NativeValue, newSize); /// /// Shuffles the contents of this into a random order. /// - public void Shuffle() - { - godot_icall_Array_Shuffle(ref NativeValue); - } + public void Shuffle() => NativeFuncs.godotsharp_array_shuffle(ref NativeValue); /// /// Concatenates these two s. @@ -122,9 +119,16 @@ namespace Godot.Collections /// A new Godot Array with the contents of both arrays. public static Array operator +(Array left, Array right) { - godot_array newArray; - godot_icall_Array_Concatenate(ref left.NativeValue, ref right.NativeValue, out newArray); - return CreateTakingOwnershipOfDisposableValue(newArray); + int leftCount = left.Count; + int rightCount = right.Count; + + Array newArray = left.Duplicate(deep: false); + newArray.Resize(leftCount + rightCount); + + for (int i = 0; i < rightCount; i++) + newArray[i + leftCount] = right[i]; + + return newArray; } // IList @@ -137,18 +141,20 @@ namespace Godot.Collections /// Returns the object at the given . /// /// The object at the given . - public object this[int index] + public unsafe object this[int index] { get { - godot_icall_Array_At(ref NativeValue, index, out godot_variant elem); - unsafe - { - using (elem) - return Marshaling.variant_to_mono_object(&elem); - } + GetVariantBorrowElementAt(index, out godot_variant borrowElem); + return Marshaling.variant_to_mono_object(&borrowElem); + } + set + { + if (index < 0 || index >= Count) + throw new IndexOutOfRangeException(); + godot_variant* ptrw = NativeFuncs.godotsharp_array_ptrw(ref NativeValue); + ptrw[index] = Marshaling.mono_object_to_variant(value); } - set => godot_icall_Array_SetAt(ref NativeValue, index, value); } /// @@ -157,19 +163,23 @@ namespace Godot.Collections /// /// The object to add. /// The new size after adding the object. - public int Add(object value) => godot_icall_Array_Add(ref NativeValue, value); + public unsafe int Add(object value) + { + using godot_variant variantValue = Marshaling.mono_object_to_variant(value); + return NativeFuncs.godotsharp_array_add(ref NativeValue, &variantValue); + } /// /// 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(ref NativeValue, value); + public bool Contains(object value) => IndexOf(value) != -1; /// /// Erases all items from this . /// - public void Clear() => godot_icall_Array_Clear(ref NativeValue); + public void Clear() => Resize(0); /// /// Searches this for an object @@ -177,7 +187,11 @@ 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(ref NativeValue, value); + public unsafe int IndexOf(object value) + { + using godot_variant variantValue = Marshaling.mono_object_to_variant(value); + return NativeFuncs.godotsharp_array_index_of(ref NativeValue, &variantValue); + } /// /// Inserts a new object at a given position in the array. @@ -187,20 +201,38 @@ namespace Godot.Collections /// /// The index to insert at. /// The object to insert. - public void Insert(int index, object value) => godot_icall_Array_Insert(ref NativeValue, index, value); + public unsafe void Insert(int index, object value) + { + if (index < 0 || index > Count) + throw new IndexOutOfRangeException(); + + using godot_variant variantValue = Marshaling.mono_object_to_variant(value); + NativeFuncs.godotsharp_array_insert(ref NativeValue, index, &variantValue); + } /// /// Removes the first occurrence of the specified /// from this . /// /// The value to remove. - public void Remove(object value) => godot_icall_Array_Remove(ref NativeValue, value); + public void Remove(object value) + { + int index = IndexOf(value); + if (index >= 0) + RemoveAt(index); + } /// /// Removes an element from this by index. /// /// The index of the element to remove. - public void RemoveAt(int index) => godot_icall_Array_RemoveAt(ref NativeValue, index); + public void RemoveAt(int index) + { + if (index < 0 || index > Count) + throw new IndexOutOfRangeException(); + + NativeFuncs.godotsharp_array_remove_at(ref NativeValue, index); + } // ICollection @@ -209,7 +241,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(ref NativeValue); + public int Count => NativeValue.Size; object ICollection.SyncRoot => this; @@ -220,17 +252,35 @@ namespace Godot.Collections /// untyped C# array, starting at the given index. /// /// The array to copy to. - /// The index to start at. - public void CopyTo(System.Array array, int index) + /// The index to start at. + public void CopyTo(System.Array array, int destIndex) { if (array == null) throw new ArgumentNullException(nameof(array), "Value cannot be null."); - if (index < 0) - throw new ArgumentOutOfRangeException(nameof(index), "Number was less than the array's lower bound in the first dimension."); + if (destIndex < 0) + { + throw new ArgumentOutOfRangeException(nameof(destIndex), + "Number was less than the array's lower bound in the first dimension."); + } - // Internal call may throw ArgumentException - godot_icall_Array_CopyTo(ref NativeValue, array, index); + int count = Count; + + if (array.Length < (destIndex + count)) + { + throw new ArgumentException( + "Destination array was not long enough. Check destIndex and length, and the array's lower bounds."); + } + + unsafe + { + for (int i = 0; i < count; i++) + { + object obj = Marshaling.variant_to_mono_object(&(*NativeValue._p)._arrayVector._ptr[i]); + array.SetValue(obj, destIndex); + destIndex++; + } + } } // IEnumerable @@ -253,64 +303,30 @@ namespace Godot.Collections /// Converts this to a string. /// /// A string representation of this array. - public override string ToString() + public override unsafe string ToString() { - return godot_icall_Array_ToString(ref NativeValue); + using godot_string str = default; + NativeFuncs.godotsharp_array_to_string(ref NativeValue, &str); + return Marshaling.mono_string_from_godot(&str); } - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_Ctor(out godot_array dest); + /// + /// The variant returned via the parameter is owned by the Array and must not be disposed. + /// + internal void GetVariantBorrowElementAt(int index, out godot_variant elem) + { + if (index < 0 || index >= Count) + throw new IndexOutOfRangeException(); + GetVariantBorrowElementAtUnchecked(index, out elem); + } - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_Ctor_MonoArray(System.Array array, out godot_array dest); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_At(ref godot_array ptr, int index, out godot_variant elem); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_SetAt(ref godot_array ptr, int index, object value); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern int godot_icall_Array_Count(ref godot_array ptr); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern int godot_icall_Array_Add(ref godot_array ptr, object item); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_Clear(ref godot_array ptr); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_Concatenate(ref godot_array left, ref godot_array right, out godot_array dest); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_Array_Contains(ref godot_array ptr, object item); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_CopyTo(ref godot_array ptr, System.Array array, int arrayIndex); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_Duplicate(ref godot_array ptr, bool deep, out godot_array dest); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern int godot_icall_Array_IndexOf(ref godot_array ptr, object item); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_Insert(ref godot_array ptr, int index, object item); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_Array_Remove(ref godot_array ptr, object item); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Array_RemoveAt(ref godot_array ptr, int index); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern Error godot_icall_Array_Resize(ref godot_array ptr, int newSize); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern Error godot_icall_Array_Shuffle(ref godot_array ptr); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern string godot_icall_Array_ToString(ref godot_array ptr); + /// + /// The variant returned via the parameter is owned by the Array and must not be disposed. + /// + internal unsafe void GetVariantBorrowElementAtUnchecked(int index, out godot_variant elem) + { + elem = (*NativeValue._p)._arrayVector._ptr[index]; + } } internal interface IGenericGodotArray @@ -451,11 +467,10 @@ namespace Godot.Collections { get { - Array.godot_icall_Array_At(ref _underlyingArray.NativeValue, index, out godot_variant elem); + _underlyingArray.GetVariantBorrowElementAt(index, out godot_variant borrowElem); unsafe { - using (elem) - return (T)Marshaling.variant_to_mono_object_of_type(&elem, TypeOfElements); + return (T)Marshaling.variant_to_mono_object_of_type(&borrowElem, TypeOfElements); } } set => _underlyingArray[index] = value; @@ -546,12 +561,14 @@ namespace Godot.Collections throw new ArgumentNullException(nameof(array), "Value cannot be null."); if (arrayIndex < 0) - throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Number was less than the array's lower bound in the first dimension."); + throw new ArgumentOutOfRangeException(nameof(arrayIndex), + "Number was less than the array's lower bound in the first dimension."); 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."); + 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++) { @@ -568,7 +585,14 @@ namespace Godot.Collections /// A indicating success or failure. public bool Remove(T item) { - return Array.godot_icall_Array_Remove(ref _underlyingArray.NativeValue, item); + int index = IndexOf(item); + if (index >= 0) + { + RemoveAt(index); + return true; + } + + return false; } // IEnumerable diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs index d0c7e4523b7..932ee33fe3e 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Collections; -using System.Runtime.CompilerServices; using Godot.NativeInterop; using System.Diagnostics.CodeAnalysis; @@ -23,7 +22,7 @@ namespace Godot.Collections /// public Dictionary() { - godot_icall_Dictionary_Ctor(out NativeValue); + NativeValue = NativeFuncs.godotsharp_dictionary_new(); } /// @@ -77,7 +76,7 @@ namespace Godot.Collections public Dictionary Duplicate(bool deep = false) { godot_dictionary newDictionary; - godot_icall_Dictionary_Duplicate(ref NativeValue, deep, out newDictionary); + NativeFuncs.godotsharp_dictionary_duplicate(ref NativeValue, deep, out newDictionary); return CreateTakingOwnershipOfDisposableValue(newDictionary); } @@ -91,7 +90,7 @@ namespace Godot.Collections get { godot_array keysArray; - godot_icall_Dictionary_Keys(ref NativeValue, out keysArray); + NativeFuncs.godotsharp_dictionary_keys(ref NativeValue, out keysArray); return Array.CreateTakingOwnershipOfDisposableValue(keysArray); } } @@ -104,7 +103,7 @@ namespace Godot.Collections get { godot_array valuesArray; - godot_icall_Dictionary_Values(ref NativeValue, out valuesArray); + NativeFuncs.godotsharp_dictionary_values(ref NativeValue, out valuesArray); return Array.CreateTakingOwnershipOfDisposableValue(valuesArray); } } @@ -112,10 +111,15 @@ namespace Godot.Collections private (Array keys, Array values, int count) GetKeyValuePairs() { godot_array keysArray; - godot_array valuesArray; - int count = godot_icall_Dictionary_KeyValuePairs(ref NativeValue, out keysArray, out valuesArray); + NativeFuncs.godotsharp_dictionary_keys(ref NativeValue, out keysArray); var keys = Array.CreateTakingOwnershipOfDisposableValue(keysArray); + + godot_array valuesArray; + NativeFuncs.godotsharp_dictionary_keys(ref NativeValue, out valuesArray); var values = Array.CreateTakingOwnershipOfDisposableValue(valuesArray); + + int count = NativeFuncs.godotsharp_dictionary_count(ref NativeValue); + return (keys, values, count); } @@ -127,18 +131,28 @@ namespace Godot.Collections /// Returns the object at the given . /// /// The object at the given . - public object this[object key] + public unsafe object this[object key] { get { - godot_icall_Dictionary_GetValue(ref NativeValue, key, out godot_variant value); - unsafe + using godot_variant variantKey = Marshaling.mono_object_to_variant(key); + if (NativeFuncs.godotsharp_dictionary_try_get_value(ref NativeValue, &variantKey, + out godot_variant value)) { using (value) return Marshaling.variant_to_mono_object(&value); } + else + { + throw new KeyNotFoundException(); + } + } + set + { + using godot_variant variantKey = Marshaling.mono_object_to_variant(key); + using godot_variant variantValue = Marshaling.mono_object_to_variant(value); + NativeFuncs.godotsharp_dictionary_set_value(ref NativeValue, &variantKey, &variantValue); } - set => godot_icall_Dictionary_SetValue(ref NativeValue, key, value); } /// @@ -147,19 +161,32 @@ 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(ref NativeValue, key, value); + public unsafe void Add(object key, object value) + { + using godot_variant variantKey = Marshaling.mono_object_to_variant(key); + + if (NativeFuncs.godotsharp_dictionary_contains_key(ref NativeValue, &variantKey)) + throw new ArgumentException("An element with the same key already exists", nameof(key)); + + using godot_variant variantValue = Marshaling.mono_object_to_variant(value); + NativeFuncs.godotsharp_dictionary_add(ref NativeValue, &variantKey, &variantValue); + } /// /// Erases all items from this . /// - public void Clear() => godot_icall_Dictionary_Clear(ref NativeValue); + public void Clear() => NativeFuncs.godotsharp_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(ref NativeValue, key); + public unsafe bool Contains(object key) + { + using godot_variant variantKey = Marshaling.mono_object_to_variant(key); + return NativeFuncs.godotsharp_dictionary_contains_key(ref NativeValue, &variantKey); + } /// /// Gets an enumerator for this . @@ -171,7 +198,11 @@ 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(ref NativeValue, key); + public unsafe void Remove(object key) + { + using godot_variant variantKey = Marshaling.mono_object_to_variant(key); + NativeFuncs.godotsharp_dictionary_remove_key(ref NativeValue, &variantKey); + } // ICollection @@ -184,7 +215,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(ref NativeValue); + public int Count => NativeFuncs.godotsharp_dictionary_count(ref NativeValue); /// /// Copies the elements of this to the given @@ -198,12 +229,14 @@ namespace Godot.Collections throw new ArgumentNullException(nameof(array), "Value cannot be null."); if (index < 0) - throw new ArgumentOutOfRangeException(nameof(index), "Number was less than the array's lower bound in the first dimension."); + throw new ArgumentOutOfRangeException(nameof(index), + "Number was less than the array's lower bound in the first dimension."); var (keys, values, count) = GetKeyValuePairs(); if (array.Length < (index + count)) - throw new ArgumentException("Destination array was not long enough. Check destIndex and length, and the array's lower bounds."); + 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++) { @@ -241,23 +274,22 @@ namespace Godot.Collections { UpdateEntry(); } + return _entry; } } - private void UpdateEntry() + private unsafe void UpdateEntry() { _dirty = false; - godot_icall_Dictionary_KeyValuePairAt(ref _dictionary.NativeValue, _index, out var vKey, out var vValue); - unsafe + NativeFuncs.godotsharp_dictionary_key_value_pair_at(ref _dictionary.NativeValue, _index, + out godot_variant key, + out godot_variant value); + using (key) + using (value) { - using (vKey) - using (vValue) - { - var key = Marshaling.variant_to_mono_object(&vKey); - var value = Marshaling.variant_to_mono_object(&vValue); - _entry = new DictionaryEntry(key, value); - } + _entry = new DictionaryEntry(Marshaling.variant_to_mono_object(&key), + Marshaling.variant_to_mono_object(&value)); } } @@ -283,61 +315,12 @@ namespace Godot.Collections /// Converts this to a string. /// /// A string representation of this dictionary. - public override string ToString() + public override unsafe string ToString() { - return godot_icall_Dictionary_ToString(ref NativeValue); + using godot_string str = default; + NativeFuncs.godotsharp_dictionary_to_string(ref NativeValue, &str); + return Marshaling.mono_string_from_godot(&str); } - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_Ctor(out godot_dictionary dest); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_GetValue(ref godot_dictionary ptr, object key, out godot_variant value); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_SetValue(ref godot_dictionary ptr, object key, object value); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_Keys(ref godot_dictionary ptr, out godot_array dest); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_Values(ref godot_dictionary ptr, out godot_array dest); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern int godot_icall_Dictionary_KeyValuePairs(ref godot_dictionary ptr, out godot_array keys, out godot_array values); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_KeyValuePairAt(ref godot_dictionary ptr, int index, out godot_variant key, out godot_variant value); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_Add(ref godot_dictionary ptr, object key, object value); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern int godot_icall_Dictionary_Count(ref godot_dictionary ptr); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_Clear(ref godot_dictionary ptr); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_Dictionary_Contains(ref godot_dictionary ptr, object key, object value); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_Dictionary_ContainsKey(ref godot_dictionary ptr, object key); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_Dictionary_Duplicate(ref godot_dictionary ptr, bool deep, out godot_dictionary dest); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_Dictionary_RemoveKey(ref godot_dictionary ptr, object key); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_Dictionary_Remove(ref godot_dictionary ptr, object key, object value); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_Dictionary_TryGetValue(ref godot_dictionary ptr, object key, out godot_variant value); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern string godot_icall_Dictionary_ToString(ref godot_dictionary ptr); } internal interface IGenericGodotDictionary @@ -412,7 +395,8 @@ namespace Godot.Collections } // Explicit name to make it very clear - internal static Dictionary CreateTakingOwnershipOfDisposableValue(godot_dictionary nativeValueToOwn) + internal static Dictionary CreateTakingOwnershipOfDisposableValue( + godot_dictionary nativeValueToOwn) => new Dictionary(Dictionary.CreateTakingOwnershipOfDisposableValue(nativeValueToOwn)); /// @@ -444,11 +428,19 @@ namespace Godot.Collections { 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); + using godot_variant variantKey = Marshaling.mono_object_to_variant(key); + if (NativeFuncs.godotsharp_dictionary_try_get_value(ref _underlyingDict.NativeValue, + &variantKey, out godot_variant value)) + { + using (value) + return (TValue)Marshaling.variant_to_mono_object_of_type(&value, TypeOfValues); + } + else + { + throw new KeyNotFoundException(); + } } } set => _underlyingDict[key] = value; @@ -462,7 +454,7 @@ namespace Godot.Collections get { godot_array keyArray; - Dictionary.godot_icall_Dictionary_Keys(ref _underlyingDict.NativeValue, out keyArray); + NativeFuncs.godotsharp_dictionary_keys(ref _underlyingDict.NativeValue, out keyArray); return Array.CreateTakingOwnershipOfDisposableValue(keyArray); } } @@ -475,23 +467,21 @@ namespace Godot.Collections get { godot_array valuesArray; - Dictionary.godot_icall_Dictionary_Values(ref _underlyingDict.NativeValue, out valuesArray); + NativeFuncs.godotsharp_dictionary_values(ref _underlyingDict.NativeValue, out valuesArray); return Array.CreateTakingOwnershipOfDisposableValue(valuesArray); } } - private KeyValuePair GetKeyValuePair(int index) + private unsafe KeyValuePair GetKeyValuePair(int index) { - Dictionary.godot_icall_Dictionary_KeyValuePairAt(ref _underlyingDict.NativeValue, index, out var vKey, out var vValue); - unsafe + NativeFuncs.godotsharp_dictionary_key_value_pair_at(ref _underlyingDict.NativeValue, index, + out godot_variant key, + out godot_variant value); + using (key) + using (value) { - using (vKey) - using (vValue) - { - var key = Marshaling.variant_to_mono_object_of_type(&vKey, TypeOfKeys); - var value = Marshaling.variant_to_mono_object_of_type(&vValue, TypeOfValues); - return new KeyValuePair((TKey)key, (TValue)value); - } + return new KeyValuePair((TKey)Marshaling.variant_to_mono_object(&key), + (TValue)Marshaling.variant_to_mono_object(&value)); } } @@ -520,9 +510,10 @@ namespace Godot.Collections /// Removes an element from this by key. /// /// The key of the element to remove. - public bool Remove(TKey key) + public unsafe bool Remove(TKey key) { - return Dictionary.godot_icall_Dictionary_RemoveKey(ref _underlyingDict.NativeValue, key); + using godot_variant variantKey = Marshaling.mono_object_to_variant(key); + return NativeFuncs.godotsharp_dictionary_remove_key(ref _underlyingDict.NativeValue, &variantKey); } /// @@ -531,18 +522,17 @@ namespace Godot.Collections /// The key of the element to get. /// The value at the given . /// If an object was found for the given . - public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) + public unsafe bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) { - bool found = Dictionary.godot_icall_Dictionary_TryGetValue(ref _underlyingDict.NativeValue, key, out godot_variant retValue); + using godot_variant variantKey = Marshaling.mono_object_to_variant(key); + bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref _underlyingDict.NativeValue, + &variantKey, out godot_variant retValue); - unsafe + using (retValue) { - using (retValue) - { - value = found ? - (TValue)Marshaling.variant_to_mono_object_of_type(&retValue, TypeOfValues) : - default; - } + value = found ? + (TValue)Marshaling.variant_to_mono_object_of_type(&retValue, TypeOfValues) : + default; } return found; @@ -572,9 +562,20 @@ namespace Godot.Collections _underlyingDict.Clear(); } - bool ICollection>.Contains(KeyValuePair item) + unsafe bool ICollection>.Contains(KeyValuePair item) { - return _underlyingDict.Contains(new KeyValuePair(item.Key, item.Value)); + using godot_variant variantKey = Marshaling.mono_object_to_variant(item.Key); + bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref _underlyingDict.NativeValue, + &variantKey, out godot_variant retValue); + + using (retValue) + { + if (!found) + return false; + + using godot_variant variantValue = Marshaling.mono_object_to_variant(item.Value); + return NativeFuncs.godotsharp_variant_equals(&variantValue, &retValue); + } } /// @@ -589,12 +590,14 @@ namespace Godot.Collections throw new ArgumentNullException(nameof(array), "Value cannot be null."); if (arrayIndex < 0) - throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Number was less than the array's lower bound in the first dimension."); + throw new ArgumentOutOfRangeException(nameof(arrayIndex), + "Number was less than the array's lower bound in the first dimension."); int count = Count; if (array.Length < (arrayIndex + count)) - throw new ArgumentException("Destination array was not long enough. Check destIndex and length, and the array's lower bounds."); + 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++) { @@ -603,9 +606,23 @@ namespace Godot.Collections } } - bool ICollection>.Remove(KeyValuePair item) + unsafe bool ICollection>.Remove(KeyValuePair item) { - return Dictionary.godot_icall_Dictionary_Remove(ref _underlyingDict.NativeValue, item.Key, item.Value); + using godot_variant variantKey = Marshaling.mono_object_to_variant(item.Key); + bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref _underlyingDict.NativeValue, + &variantKey, out godot_variant retValue); + + using (retValue) + { + if (!found) + return false; + + using godot_variant variantValue = Marshaling.mono_object_to_variant(item.Value); + if (NativeFuncs.godotsharp_variant_equals(&variantValue, &retValue)) + return NativeFuncs.godotsharp_dictionary_remove_key(ref _underlyingDict.NativeValue, &variantKey); + + return false; + } } // IEnumerable> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs index 6ee8cbc0bc6..ddcb6b90919 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs @@ -171,6 +171,9 @@ namespace Godot.NativeInterop NativeFuncs.godotsharp_string_destroy(ref this); _ptr = IntPtr.Zero; } + + // Size including the null termination character + public unsafe int Size => _ptr != IntPtr.Zero ? *((int*)_ptr - 1) : 0; } [StructLayout(LayoutKind.Sequential)] @@ -262,14 +265,34 @@ namespace Godot.NativeInterop // ReSharper disable once InconsistentNaming internal struct godot_array : IDisposable { - internal IntPtr _p; + internal unsafe ArrayPrivate* _p; - public void Dispose() + [StructLayout(LayoutKind.Sequential)] + internal struct ArrayPrivate { - if (_p == IntPtr.Zero) + private uint _safeRefCount; + + internal VariantVector _arrayVector; + // There's more here, but we don't care as we never store this in C# + } + + [StructLayout(LayoutKind.Sequential)] + internal struct VariantVector + { + internal IntPtr _writeProxy; + internal unsafe godot_variant* _ptr; + + public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0; + } + + public unsafe int Size => _p != null ? _p->_arrayVector.Size : 0; + + public unsafe void Dispose() + { + if (_p == null) return; NativeFuncs.godotsharp_array_destroy(ref this); - _p = IntPtr.Zero; + _p = null; } } @@ -297,15 +320,17 @@ namespace Godot.NativeInterop internal struct godot_packed_byte_array : IDisposable { internal IntPtr _writeProxy; - internal IntPtr _ptr; + internal unsafe byte* _ptr; - public void Dispose() + public unsafe void Dispose() { - if (_ptr == IntPtr.Zero) + if (_ptr == null) return; NativeFuncs.godotsharp_packed_byte_array_destroy(ref this); - _ptr = IntPtr.Zero; + _ptr = null; } + + public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0; } [StructLayout(LayoutKind.Sequential)] @@ -313,15 +338,17 @@ namespace Godot.NativeInterop internal struct godot_packed_int32_array : IDisposable { internal IntPtr _writeProxy; - internal IntPtr _ptr; + internal unsafe int* _ptr; - public void Dispose() + public unsafe void Dispose() { - if (_ptr == IntPtr.Zero) + if (_ptr == null) return; NativeFuncs.godotsharp_packed_int32_array_destroy(ref this); - _ptr = IntPtr.Zero; + _ptr = null; } + + public unsafe int Size => _ptr != null ? *(_ptr - 1) : 0; } [StructLayout(LayoutKind.Sequential)] @@ -329,15 +356,17 @@ namespace Godot.NativeInterop internal struct godot_packed_int64_array : IDisposable { internal IntPtr _writeProxy; - internal IntPtr _ptr; + internal unsafe long* _ptr; - public void Dispose() + public unsafe void Dispose() { - if (_ptr == IntPtr.Zero) + if (_ptr == null) return; NativeFuncs.godotsharp_packed_int64_array_destroy(ref this); - _ptr = IntPtr.Zero; + _ptr = null; } + + public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0; } [StructLayout(LayoutKind.Sequential)] @@ -345,15 +374,17 @@ namespace Godot.NativeInterop internal struct godot_packed_float32_array : IDisposable { internal IntPtr _writeProxy; - internal IntPtr _ptr; + internal unsafe float* _ptr; - public void Dispose() + public unsafe void Dispose() { - if (_ptr == IntPtr.Zero) + if (_ptr == null) return; NativeFuncs.godotsharp_packed_float32_array_destroy(ref this); - _ptr = IntPtr.Zero; + _ptr = null; } + + public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0; } [StructLayout(LayoutKind.Sequential)] @@ -361,15 +392,17 @@ namespace Godot.NativeInterop internal struct godot_packed_float64_array : IDisposable { internal IntPtr _writeProxy; - internal IntPtr _ptr; + internal unsafe double* _ptr; - public void Dispose() + public unsafe void Dispose() { - if (_ptr == IntPtr.Zero) + if (_ptr == null) return; NativeFuncs.godotsharp_packed_float64_array_destroy(ref this); - _ptr = IntPtr.Zero; + _ptr = null; } + + public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0; } [StructLayout(LayoutKind.Sequential)] @@ -377,15 +410,17 @@ namespace Godot.NativeInterop internal struct godot_packed_string_array : IDisposable { internal IntPtr _writeProxy; - internal IntPtr _ptr; + internal unsafe godot_string* _ptr; - public void Dispose() + public unsafe void Dispose() { - if (_ptr == IntPtr.Zero) + if (_ptr == null) return; NativeFuncs.godotsharp_packed_string_array_destroy(ref this); - _ptr = IntPtr.Zero; + _ptr = null; } + + public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0; } [StructLayout(LayoutKind.Sequential)] @@ -393,15 +428,17 @@ namespace Godot.NativeInterop internal struct godot_packed_vector2_array : IDisposable { internal IntPtr _writeProxy; - internal IntPtr _ptr; + internal unsafe Vector2* _ptr; - public void Dispose() + public unsafe void Dispose() { - if (_ptr == IntPtr.Zero) + if (_ptr == null) return; NativeFuncs.godotsharp_packed_vector2_array_destroy(ref this); - _ptr = IntPtr.Zero; + _ptr = null; } + + public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0; } [StructLayout(LayoutKind.Sequential)] @@ -409,15 +446,17 @@ namespace Godot.NativeInterop internal struct godot_packed_vector3_array : IDisposable { internal IntPtr _writeProxy; - internal IntPtr _ptr; + internal unsafe Vector3* _ptr; - public void Dispose() + public unsafe void Dispose() { - if (_ptr == IntPtr.Zero) + if (_ptr == null) return; NativeFuncs.godotsharp_packed_vector3_array_destroy(ref this); - _ptr = IntPtr.Zero; + _ptr = null; } + + public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0; } [StructLayout(LayoutKind.Sequential)] @@ -425,14 +464,16 @@ namespace Godot.NativeInterop internal struct godot_packed_color_array : IDisposable { internal IntPtr _writeProxy; - internal IntPtr _ptr; + internal unsafe Color* _ptr; - public void Dispose() + public unsafe void Dispose() { - if (_ptr == IntPtr.Zero) + if (_ptr == null) return; NativeFuncs.godotsharp_packed_color_array_destroy(ref this); - _ptr = IntPtr.Zero; + _ptr = null; } + + public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0; } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs index 9b6f9633d1c..590fb082e83 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs @@ -161,7 +161,8 @@ namespace Godot.NativeInterop if (genericTypeDefinition == typeof(IDictionary<,>)) return Variant.Type.Dictionary; - if (genericTypeDefinition == typeof(ICollection<>) || genericTypeDefinition == typeof(IEnumerable<>)) + if (genericTypeDefinition == typeof(ICollection<>) || + genericTypeDefinition == typeof(IEnumerable<>)) return Variant.Type.Array; } else if (type == typeof(object)) @@ -430,7 +431,8 @@ namespace Godot.NativeInterop { // TODO: Validate key and value types are compatible with Variant #if NET - Collections.IGenericGodotDictionary genericGodotDictionary = IDictionaryToGenericGodotDictionary((dynamic)p_obj); + Collections.IGenericGodotDictionary genericGodotDictionary = + IDictionaryToGenericGodotDictionary((dynamic)p_obj); #else var genericArguments = type.GetGenericArguments(); @@ -441,7 +443,7 @@ namespace Godot.NativeInterop .MakeGenericMethod(genericArguments[0], genericArguments[1]); var genericGodotDictionary = (Collections.IGenericGodotDictionary)method - .Invoke(null, new[] {p_obj}); + .Invoke(null, new[] { p_obj }); #endif var godotDict = genericGodotDictionary.UnderlyingDictionary; @@ -454,7 +456,8 @@ namespace Godot.NativeInterop { // TODO: Validate element type is compatible with Variant #if NET - var nativeGodotArray = mono_array_to_Array(System.Runtime.InteropServices.CollectionsMarshal.AsSpan((dynamic)p_obj)); + 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. @@ -623,8 +626,9 @@ namespace Godot.NativeInterop return VariantUtils.ConvertToUInt64(p_var); default: { - GD.PushError("Attempted to convert Variant to enum value of unsupported underlying type. Name: " + - type.FullName + " : " + enumUnderlyingType.FullName + "."); + GD.PushError( + "Attempted to convert Variant to enum value of unsupported underlying type. Name: " + + type.FullName + " : " + enumUnderlyingType.FullName + "."); return null; } } @@ -777,7 +781,7 @@ namespace Godot.NativeInterop VariantUtils.ConvertToDictionary(p_var)); return Activator.CreateInstance(fullType, BindingFlags.Public | BindingFlags.Instance, null, - args: new object[] {underlyingDict}, null); + args: new object[] { underlyingDict }, null); } static object variant_to_generic_godot_collections_array(godot_variant* p_var, Type fullType) @@ -786,7 +790,7 @@ namespace Godot.NativeInterop VariantUtils.ConvertToArray(p_var)); return Activator.CreateInstance(fullType, BindingFlags.Public | BindingFlags.Instance, null, - args: new object[] {underlyingArray}, null); + args: new object[] { underlyingArray }, null); } var genericTypeDefinition = type.GetGenericTypeDefinition(); @@ -1010,7 +1014,7 @@ namespace Godot.NativeInterop const int sizeOfChar32 = 4; byte* bytes = (byte*)(*p_string)._ptr; - int size = *((int*)(*p_string)._ptr - 1); + int size = (*p_string).Size; if (size == 0) return string.Empty; size -= 1; // zero at the end @@ -1148,11 +1152,7 @@ namespace Godot.NativeInterop 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; - } + return NativeFuncs.godotsharp_array_new(); using var array = new Collections.Array(); array.Resize(p_array.Length); @@ -1171,8 +1171,8 @@ namespace Godot.NativeInterop 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); + byte* buffer = (*p_array)._ptr; + int size = (*p_array).Size; var array = new byte[size]; fixed (byte* dest = array) Buffer.MemoryCopy(buffer, dest, size, size); @@ -1191,8 +1191,8 @@ namespace Godot.NativeInterop 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* buffer = (*p_array)._ptr; + int size = (*p_array).Size; int sizeInBytes = size * sizeof(int); var array = new int[size]; fixed (int* dest = array) @@ -1212,8 +1212,8 @@ namespace Godot.NativeInterop 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); + long* buffer = (*p_array)._ptr; + int size = (*p_array).Size; int sizeInBytes = size * sizeof(long); var array = new long[size]; fixed (long* dest = array) @@ -1233,8 +1233,8 @@ namespace Godot.NativeInterop 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); + float* buffer = (*p_array)._ptr; + int size = (*p_array).Size; int sizeInBytes = size * sizeof(float); var array = new float[size]; fixed (float* dest = array) @@ -1254,8 +1254,8 @@ namespace Godot.NativeInterop 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); + double* buffer = (*p_array)._ptr; + int size = (*p_array).Size; int sizeInBytes = size * sizeof(double); var array = new double[size]; fixed (double* dest = array) @@ -1275,10 +1275,10 @@ namespace Godot.NativeInterop public static unsafe string[] PackedStringArray_to_mono_array(godot_packed_string_array* p_array) { - godot_string* buffer = (godot_string*)(*p_array)._ptr; + godot_string* buffer = (*p_array)._ptr; if (buffer == null) return new string[] { }; - int size = *((int*)(*p_array)._ptr - 1); + int size = (*p_array).Size; var array = new string[size]; for (int i = 0; i < size; i++) array[i] = mono_string_from_godot(&buffer[i]); @@ -1308,8 +1308,8 @@ namespace Godot.NativeInterop 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); + Vector2* buffer = (*p_array)._ptr; + int size = (*p_array).Size; int sizeInBytes = size * sizeof(Vector2); var array = new Vector2[size]; fixed (Vector2* dest = array) @@ -1329,8 +1329,8 @@ namespace Godot.NativeInterop 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); + Vector3* buffer = (*p_array)._ptr; + int size = (*p_array).Size; int sizeInBytes = size * sizeof(Vector3); var array = new Vector3[size]; fixed (Vector3* dest = array) @@ -1350,8 +1350,8 @@ namespace Godot.NativeInterop 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); + Color* buffer = (*p_array)._ptr; + int size = (*p_array).Size; int sizeInBytes = size * sizeof(Color); var array = new Color[size]; fixed (Color* dest = array) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs index 2f1056219d3..0680ec7be5d 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs @@ -17,7 +17,8 @@ namespace Godot.NativeInterop // Custom functions [DllImport(GodotDllName)] - public static extern IntPtr godotsharp_method_bind_get_method(ref godot_string_name p_classname, char* p_methodname); + public static extern IntPtr godotsharp_method_bind_get_method(ref godot_string_name p_classname, + char* p_methodname); #if NET [DllImport(GodotDllName)] @@ -50,47 +51,60 @@ namespace Godot.NativeInterop 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + public static extern godot_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); + 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); + 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 @@ -131,34 +145,43 @@ namespace Godot.NativeInterop 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + public static extern godot_bool godotsharp_variant_as_bool(godot_variant* p_self); [DllImport(GodotDllName)] public static extern Int64 godotsharp_variant_as_int(godot_variant* p_self); @@ -248,23 +271,30 @@ namespace Godot.NativeInterop 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); + 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); + 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); + 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); + 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); + [DllImport(GodotDllName)] + public static extern godot_bool godotsharp_variant_equals(godot_variant* p_a, godot_variant* p_b); + // string.h [DllImport(GodotDllName)] @@ -282,11 +312,20 @@ namespace Godot.NativeInterop // array.h + [DllImport(GodotDllName)] + public static extern void godotsharp_array_new(godot_array* p_self); + [DllImport(GodotDllName)] public static extern void godotsharp_array_new_copy(godot_array* r_dest, godot_array* p_src); + [DllImport(GodotDllName)] + public static extern godot_variant* godotsharp_array_ptrw(ref godot_array p_self); + // dictionary.h + [DllImport(GodotDllName)] + public static extern void godotsharp_dictionary_new(godot_dictionary* p_self); + [DllImport(GodotDllName)] public static extern void godotsharp_dictionary_new_copy(godot_dictionary* r_dest, godot_dictionary* p_src); @@ -342,5 +381,134 @@ namespace Godot.NativeInterop [DllImport(GodotDllName)] public static extern void godotsharp_dictionary_destroy(ref godot_dictionary p_self); + + // Array + + [DllImport(GodotDllName)] + public static extern int godotsharp_array_add(ref godot_array p_self, godot_variant* p_item); + + [DllImport(GodotDllName)] + public static extern void + godotsharp_array_duplicate(ref godot_array p_self, godot_bool p_deep, out godot_array r_dest); + + [DllImport(GodotDllName)] + public static extern int godotsharp_array_index_of(ref godot_array p_self, godot_variant* p_item); + + [DllImport(GodotDllName)] + public static extern void godotsharp_array_insert(ref godot_array p_self, int p_index, godot_variant* p_item); + + [DllImport(GodotDllName)] + public static extern void godotsharp_array_remove_at(ref godot_array p_self, int p_index); + + [DllImport(GodotDllName)] + public static extern Error godotsharp_array_resize(ref godot_array p_self, int p_new_size); + + [DllImport(GodotDllName)] + public static extern Error godotsharp_array_shuffle(ref godot_array p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_array_to_string(ref godot_array p_self, godot_string* r_str); + + // Dictionary + + [DllImport(GodotDllName)] + public static extern godot_bool godotsharp_dictionary_try_get_value(ref godot_dictionary p_self, + godot_variant* p_key, + out godot_variant r_value); + + [DllImport(GodotDllName)] + public static extern void godotsharp_dictionary_set_value(ref godot_dictionary p_self, godot_variant* p_key, + godot_variant* p_value); + + [DllImport(GodotDllName)] + public static extern void godotsharp_dictionary_keys(ref godot_dictionary p_self, out godot_array r_dest); + + [DllImport(GodotDllName)] + public static extern void godotsharp_dictionary_values(ref godot_dictionary p_self, out godot_array r_dest); + + [DllImport(GodotDllName)] + public static extern int godotsharp_dictionary_count(ref godot_dictionary p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_dictionary_key_value_pair_at(ref godot_dictionary p_self, int p_index, + out godot_variant r_key, out godot_variant r_value); + + [DllImport(GodotDllName)] + public static extern void godotsharp_dictionary_add(ref godot_dictionary p_self, godot_variant* p_key, + godot_variant* p_value); + + [DllImport(GodotDllName)] + public static extern void godotsharp_dictionary_clear(ref godot_dictionary p_self); + + [DllImport(GodotDllName)] + public static extern godot_bool godotsharp_dictionary_contains_key(ref godot_dictionary p_self, + godot_variant* p_key); + + [DllImport(GodotDllName)] + public static extern void godotsharp_dictionary_duplicate(ref godot_dictionary p_self, godot_bool p_deep, + out godot_dictionary r_dest); + + [DllImport(GodotDllName)] + public static extern godot_bool godotsharp_dictionary_remove_key(ref godot_dictionary p_self, + godot_variant* p_key); + + [DllImport(GodotDllName)] + public static extern void godotsharp_dictionary_to_string(ref godot_dictionary p_self, godot_string* r_str); + + // StringExtensions + + [DllImport(GodotDllName)] + public static extern void godotsharp_string_md5_buffer(godot_string* p_self, + godot_packed_byte_array* r_md5_buffer); + + [DllImport(GodotDllName)] + public static extern void godotsharp_string_md5_text(godot_string* p_self, godot_string* r_md5_text); + + [DllImport(GodotDllName)] + public static extern int godotsharp_string_rfind(godot_string* p_self, godot_string* p_what, int p_from); + + [DllImport(GodotDllName)] + public static extern int godotsharp_string_rfindn(godot_string* p_self, godot_string* p_what, int p_from); + + [DllImport(GodotDllName)] + public static extern void godotsharp_string_sha256_buffer(godot_string* p_self, + godot_packed_byte_array* r_sha256_buffer); + + [DllImport(GodotDllName)] + public static extern void godotsharp_string_sha256_text(godot_string* p_self, godot_string* r_sha256_text); + + [DllImport(GodotDllName)] + public static extern void godotsharp_string_simplify_path(godot_string* p_self, godot_string* r_simplified_path); + + // NodePath + + [DllImport(GodotDllName)] + public static extern void godotsharp_node_path_get_as_property_path(ref godot_node_path p_self, + ref godot_node_path r_dest); + + [DllImport(GodotDllName)] + public static extern void godotsharp_node_path_get_concatenated_names(ref godot_node_path p_self, + godot_string* r_names); + + [DllImport(GodotDllName)] + public static extern void godotsharp_node_path_get_concatenated_subnames(ref godot_node_path p_self, + godot_string* r_subnames); + + [DllImport(GodotDllName)] + public static extern void godotsharp_node_path_get_name(ref godot_node_path p_self, int p_idx, + godot_string* r_name); + + [DllImport(GodotDllName)] + public static extern int godotsharp_node_path_get_name_count(ref godot_node_path p_self); + + [DllImport(GodotDllName)] + public static extern void godotsharp_node_path_get_subname(ref godot_node_path p_self, int p_idx, + godot_string* r_subname); + + [DllImport(GodotDllName)] + public static extern int godotsharp_node_path_get_subname_count(ref godot_node_path p_self); + + [DllImport(GodotDllName)] + public static extern bool godotsharp_node_path_is_absolute(ref godot_node_path 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 index 70df79c1de3..6001b3a0de6 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs @@ -29,6 +29,13 @@ namespace Godot.NativeInterop 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() + { + godot_array ret; + godotsharp_array_new(&ret); + return ret; + } + public static godot_array godotsharp_array_new_copy(godot_array* src) { godot_array ret; @@ -40,6 +47,13 @@ namespace Godot.NativeInterop public static godot_array godotsharp_array_new_copy(godot_array src) => godotsharp_array_new_copy(&src); + public static godot_dictionary godotsharp_dictionary_new() + { + godot_dictionary ret; + godotsharp_dictionary_new(&ret); + return ret; + } + public static godot_dictionary godotsharp_dictionary_new_copy(godot_dictionary* src) { godot_dictionary ret; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs index 90e51e8a1a7..541364b2810 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs @@ -161,7 +161,7 @@ namespace Godot public NodePath GetAsPropertyPath() { godot_node_path propertyPath = default; - godot_icall_NodePath_get_as_property_path(ref NativeValue, ref propertyPath); + NativeFuncs.godotsharp_node_path_get_as_property_path(ref NativeValue, ref propertyPath); return CreateTakingOwnershipOfDisposableValue(propertyPath); } @@ -175,9 +175,11 @@ namespace Godot /// /// /// The names concatenated with /. - public string GetConcatenatedNames() + public unsafe string GetConcatenatedNames() { - return godot_icall_NodePath_get_concatenated_names(ref NativeValue); + using godot_string names = default; + NativeFuncs.godotsharp_node_path_get_concatenated_names(ref NativeValue, &names); + return Marshaling.mono_string_from_godot(&names); } /// @@ -191,9 +193,11 @@ namespace Godot /// /// /// The subnames concatenated with :. - public string GetConcatenatedSubNames() + public unsafe string GetConcatenatedSubNames() { - return godot_icall_NodePath_get_concatenated_subnames(ref NativeValue); + using godot_string subNames = default; + NativeFuncs.godotsharp_node_path_get_concatenated_subnames(ref NativeValue, &subNames); + return Marshaling.mono_string_from_godot(&subNames); } /// @@ -209,9 +213,11 @@ namespace Godot /// /// The name index. /// The name at the given index . - public string GetName(int idx) + public unsafe string GetName(int idx) { - return godot_icall_NodePath_get_name(ref NativeValue, idx); + using godot_string name = default; + NativeFuncs.godotsharp_node_path_get_name(ref NativeValue, idx, &name); + return Marshaling.mono_string_from_godot(&name); } /// @@ -222,7 +228,7 @@ namespace Godot /// The number of node names which make up the path. public int GetNameCount() { - return godot_icall_NodePath_get_name_count(ref NativeValue); + return NativeFuncs.godotsharp_node_path_get_name_count(ref NativeValue); } /// @@ -230,9 +236,11 @@ namespace Godot /// /// The subname index. /// The subname at the given index . - public string GetSubName(int idx) + public unsafe string GetSubName(int idx) { - return godot_icall_NodePath_get_subname(ref NativeValue, idx); + using godot_string subName = default; + NativeFuncs.godotsharp_node_path_get_subname(ref NativeValue, idx, &subName); + return Marshaling.mono_string_from_godot(&subName); } /// @@ -243,7 +251,7 @@ namespace Godot /// The number of subnames in the path. public int GetSubNameCount() { - return godot_icall_NodePath_get_subname_count(ref NativeValue); + return NativeFuncs.godotsharp_node_path_get_subname_count(ref NativeValue); } /// @@ -255,7 +263,7 @@ namespace Godot /// If the is an absolute path. public bool IsAbsolute() { - return godot_icall_NodePath_is_absolute(ref NativeValue); + return NativeFuncs.godotsharp_node_path_is_absolute(ref NativeValue); } /// @@ -263,29 +271,5 @@ namespace Godot /// /// If the is empty. public bool IsEmpty => godot_node_path.IsEmpty(in NativeValue); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void godot_icall_NodePath_get_as_property_path(ref godot_node_path ptr, ref godot_node_path dest); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string godot_icall_NodePath_get_concatenated_names(ref godot_node_path ptr); - - [MethodImpl(MethodImplOptions.InternalCall)] - 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_get_name(ref godot_node_path ptr, int arg1); - - [MethodImpl(MethodImplOptions.InternalCall)] - 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_subname(ref godot_node_path ptr, int arg1); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern int godot_icall_NodePath_get_subname_count(ref godot_node_path ptr); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern bool godot_icall_NodePath_is_absolute(ref godot_node_path ptr); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs index 058c5447e2e..6c3d673fdce 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs @@ -5,6 +5,7 @@ using System.Runtime.CompilerServices; using System.Security; using System.Text; using System.Text.RegularExpressions; +using Godot.NativeInterop; namespace Godot { @@ -177,6 +178,7 @@ namespace Godot { return 0; } + if (from == 0 && to == len) { str = instance; @@ -471,7 +473,8 @@ namespace Godot /// The starting position of the substring, or -1 if not found. public static int Find(this string instance, string what, int from = 0, bool caseSensitive = true) { - return instance.IndexOf(what, from, caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase); + return instance.IndexOf(what, from, + caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase); } /// @@ -490,7 +493,8 @@ namespace Godot { // TODO: Could be more efficient if we get a char version of `IndexOf`. // See https://github.com/dotnet/runtime/issues/44116 - return instance.IndexOf(what.ToString(), from, caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase); + return instance.IndexOf(what.ToString(), from, + caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase); } /// Find the last occurrence of a substring. @@ -519,7 +523,8 @@ namespace Godot /// The starting position of the substring, or -1 if not found. public static int FindLast(this string instance, string what, int from, bool caseSensitive = true) { - return instance.LastIndexOf(what, from, caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase); + return instance.LastIndexOf(what, from, + caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase); } /// @@ -804,6 +809,7 @@ namespace Godot { match = instance[source] == text[target]; } + if (match) { source++; @@ -1015,15 +1021,18 @@ namespace Godot switch (expr[0]) { case '*': - return ExprMatch(instance, expr.Substring(1), caseSensitive) || (instance.Length > 0 && ExprMatch(instance.Substring(1), expr, caseSensitive)); + return ExprMatch(instance, expr.Substring(1), caseSensitive) || (instance.Length > 0 && + ExprMatch(instance.Substring(1), expr, caseSensitive)); case '?': - return instance.Length > 0 && instance[0] != '.' && ExprMatch(instance.Substring(1), expr.Substring(1), caseSensitive); + return instance.Length > 0 && instance[0] != '.' && + ExprMatch(instance.Substring(1), expr.Substring(1), caseSensitive); default: if (instance.Length == 0) return false; if (caseSensitive) return instance[0] == expr[0]; - return (char.ToUpper(instance[0]) == char.ToUpper(expr[0])) && ExprMatch(instance.Substring(1), expr.Substring(1), caseSensitive); + return (char.ToUpper(instance[0]) == char.ToUpper(expr[0])) && + ExprMatch(instance.Substring(1), expr.Substring(1), caseSensitive); } } @@ -1068,28 +1077,28 @@ namespace Godot /// /// The string to hash. /// The MD5 hash of the string. - public static byte[] MD5Buffer(this string instance) + public static unsafe byte[] MD5Buffer(this string instance) { - return godot_icall_String_md5_buffer(instance); + using godot_string instanceStr = Marshaling.mono_string_to_godot(instance); + using godot_packed_byte_array md5Buffer = default; + NativeFuncs.godotsharp_string_md5_buffer(&instanceStr, &md5Buffer); + return Marshaling.PackedByteArray_to_mono_array(&md5Buffer); } - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern byte[] godot_icall_String_md5_buffer(string str); - /// /// Returns the MD5 hash of the string as a string. /// /// /// The string to hash. /// The MD5 hash of the string. - public static string MD5Text(this string instance) + public static unsafe string MD5Text(this string instance) { - return godot_icall_String_md5_text(instance); + using godot_string instanceStr = Marshaling.mono_string_to_godot(instance); + using godot_string md5Text = default; + NativeFuncs.godotsharp_string_md5_text(&instanceStr, &md5Text); + return Marshaling.mono_string_from_godot(&md5Text); } - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern string godot_icall_String_md5_text(string str); - /// /// Perform a case-insensitive comparison to another string, return -1 if less, 0 if equal and +1 if greater. /// @@ -1242,14 +1251,13 @@ namespace Godot /// The substring to search in the string. /// The position at which to start searching. /// The position at which the substring was found, or -1 if not found. - public static int RFind(this string instance, string what, int from = -1) + public static unsafe int RFind(this string instance, string what, int from = -1) { - return godot_icall_String_rfind(instance, what, from); + using godot_string instanceStr = Marshaling.mono_string_to_godot(instance); + using godot_string whatStr = Marshaling.mono_string_to_godot(instance); + return NativeFuncs.godotsharp_string_rfind(&instanceStr, &whatStr, from); } - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern int godot_icall_String_rfind(string str, string what, int from); - /// /// Perform a search for a substring, but start from the end of the string instead of the beginning. /// Also search case-insensitive. @@ -1259,14 +1267,13 @@ namespace Godot /// The substring to search in the string. /// The position at which to start searching. /// The position at which the substring was found, or -1 if not found. - public static int RFindN(this string instance, string what, int from = -1) + public static unsafe int RFindN(this string instance, string what, int from = -1) { - return godot_icall_String_rfindn(instance, what, from); + using godot_string instanceStr = Marshaling.mono_string_to_godot(instance); + using godot_string whatStr = Marshaling.mono_string_to_godot(instance); + return NativeFuncs.godotsharp_string_rfindn(&instanceStr, &whatStr, from); } - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern int godot_icall_String_rfindn(string str, string what, int from); - /// /// Returns the right side of the string from a given position. /// @@ -1319,28 +1326,28 @@ namespace Godot /// /// The string to hash. /// The SHA-256 hash of the string. - public static byte[] SHA256Buffer(this string instance) + public static unsafe byte[] SHA256Buffer(this string instance) { - return godot_icall_String_sha256_buffer(instance); + using godot_string instanceStr = Marshaling.mono_string_to_godot(instance); + using godot_packed_byte_array sha256Buffer = default; + NativeFuncs.godotsharp_string_sha256_buffer(&instanceStr, &sha256Buffer); + return Marshaling.PackedByteArray_to_mono_array(&sha256Buffer); } - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern byte[] godot_icall_String_sha256_buffer(string str); - /// /// Returns the SHA-256 hash of the string as a string. /// /// /// The string to hash. /// The SHA-256 hash of the string. - public static string SHA256Text(this string instance) + public static unsafe string SHA256Text(this string instance) { - return godot_icall_String_sha256_text(instance); + using godot_string instanceStr = Marshaling.mono_string_to_godot(instance); + using godot_string sha256Text = default; + NativeFuncs.godotsharp_string_sha256_text(&instanceStr, &sha256Text); + return Marshaling.mono_string_from_godot(&sha256Text); } - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern string godot_icall_String_sha256_text(string str); - /// /// Returns the similarity index of the text compared to this string. /// 1 means totally similar and 0 means totally dissimilar. @@ -1355,6 +1362,7 @@ namespace Godot // Equal strings are totally similar return 1.0f; } + if (instance.Length < 2 || text.Length < 2) { // No way to calculate similarity without a single bigram @@ -1388,14 +1396,14 @@ namespace Godot /// /// Returns a simplified canonical path. /// - public static string SimplifyPath(this string instance) + public static unsafe string SimplifyPath(this string instance) { - return godot_icall_String_simplify_path(instance); + using godot_string instanceStr = Marshaling.mono_string_to_godot(instance); + using godot_string simplifiedPath = default; + NativeFuncs.godotsharp_string_simplify_path(&instanceStr, &simplifiedPath); + return Marshaling.mono_string_from_godot(&simplifiedPath); } - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern string godot_icall_String_simplify_path(string str); - /// /// Split the string by a divisor string, return an array of the substrings. /// Example "One,Two,Three" will return ["One","Two","Three"] if split by ",". @@ -1409,7 +1417,8 @@ namespace Godot /// The array of strings split from the string. public static string[] Split(this string instance, string divisor, bool allowEmpty = true) { - return instance.Split(new[] { divisor }, allowEmpty ? StringSplitOptions.None : StringSplitOptions.RemoveEmptyEntries); + return instance.Split(new[] { divisor }, + allowEmpty ? StringSplitOptions.None : StringSplitOptions.RemoveEmptyEntries); } /// diff --git a/modules/mono/glue/collections_glue.cpp b/modules/mono/glue/collections_glue.cpp deleted file mode 100644 index 30adea60cc8..00000000000 --- a/modules/mono/glue/collections_glue.cpp +++ /dev/null @@ -1,300 +0,0 @@ -/*************************************************************************/ -/* collections_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. */ -/*************************************************************************/ - -#include - -#include "core/variant/array.h" - -#include "../mono_gd/gd_mono_cache.h" -#include "../mono_gd/gd_mono_class.h" -#include "../mono_gd/gd_mono_marshal.h" -#include "../mono_gd/gd_mono_utils.h" - -void godot_icall_Array_Ctor(Array *r_dest) { - memnew_placement(r_dest, Array); -} - -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()); - *r_elem = Variant(); - return; - } - *r_elem = ptr->operator[](index); -} - -void godot_icall_Array_SetAt(Array *ptr, int32_t index, MonoObject *value) { - if (index < 0 || index >= ptr->size()) { - GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range()); - return; - } - ptr->operator[](index) = GDMonoMarshal::mono_object_to_variant(value); -} - -int32_t godot_icall_Array_Count(Array *ptr) { - return ptr->size(); -} - -int32_t godot_icall_Array_Add(Array *ptr, MonoObject *item) { - ptr->append(GDMonoMarshal::mono_object_to_variant(item)); - return ptr->size(); -} - -void godot_icall_Array_Clear(Array *ptr) { - ptr->clear(); -} - -MonoBoolean godot_icall_Array_Contains(Array *ptr, MonoObject *item) { - return ptr->find(GDMonoMarshal::mono_object_to_variant(item)) != -1; -} - -void godot_icall_Array_CopyTo(Array *ptr, MonoArray *array, int32_t array_index) { - unsigned int count = ptr->size(); - - if (mono_array_length(array) < (array_index + count)) { - MonoException *exc = mono_get_exception_argument("", "Destination array was not long enough. Check destIndex and length, and the array's lower bounds."); - GDMonoUtils::set_pending_exception(exc); - return; - } - - for (unsigned int i = 0; i < count; i++) { - MonoObject *boxed = GDMonoMarshal::variant_to_mono_object(ptr->operator[](i)); - mono_array_setref(array, array_index, boxed); - array_index++; - } -} - -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); - 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(r_dest, i, item); - } -} - -void godot_icall_Array_Duplicate(Array *ptr, MonoBoolean deep, Array *r_dest) { - memnew_placement(r_dest, Array(ptr->duplicate(deep))); -} - -void godot_icall_Array_Concatenate(Array *left, Array *right, Array *r_dest) { - int count = left->size() + right->size(); - memnew_placement(r_dest, Array(left->duplicate(false))); - r_dest->resize(count); - for (unsigned int i = 0; i < (unsigned int)right->size(); i++) { - r_dest->operator[](i + left->size()) = right->operator[](i); - } -} - -int32_t godot_icall_Array_IndexOf(Array *ptr, MonoObject *item) { - return ptr->find(GDMonoMarshal::mono_object_to_variant(item)); -} - -void godot_icall_Array_Insert(Array *ptr, int32_t index, MonoObject *item) { - if (index < 0 || index > ptr->size()) { - GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range()); - return; - } - ptr->insert(index, GDMonoMarshal::mono_object_to_variant(item)); -} - -MonoBoolean godot_icall_Array_Remove(Array *ptr, MonoObject *item) { - int idx = ptr->find(GDMonoMarshal::mono_object_to_variant(item)); - if (idx >= 0) { - ptr->remove_at(idx); - return true; - } - return false; -} - -void godot_icall_Array_RemoveAt(Array *ptr, int32_t index) { - if (index < 0 || index >= ptr->size()) { - GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range()); - return; - } - ptr->remove_at(index); -} - -int32_t godot_icall_Array_Resize(Array *ptr, int32_t new_size) { - return (int32_t)ptr->resize(new_size); -} - -void godot_icall_Array_Shuffle(Array *ptr) { - ptr->shuffle(); -} - -MonoString *godot_icall_Array_ToString(Array *ptr) { - return GDMonoMarshal::mono_string_from_godot(Variant(*ptr).operator String()); -} - -void godot_icall_Dictionary_Ctor(Dictionary *r_dest) { - memnew_placement(r_dest, Dictionary); -} - -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()); -#ifdef DEBUG_ENABLED - CRASH_COND(!exc); -#endif - GDMonoUtils::runtime_object_init(exc, CACHED_CLASS(KeyNotFoundException)); - GDMonoUtils::set_pending_exception((MonoException *)exc); - *r_value = Variant(); - return; - } - *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); -} - -void godot_icall_Dictionary_Keys(Dictionary *ptr, Array *r_dest) { - memnew_placement(r_dest, Array(ptr->keys())); -} - -void godot_icall_Dictionary_Values(Dictionary *ptr, Array *r_dest) { - memnew_placement(r_dest, Array(ptr->values())); -} - -int32_t godot_icall_Dictionary_Count(Dictionary *ptr) { - return ptr->size(); -} - -int32_t godot_icall_Dictionary_KeyValuePairs(Dictionary *ptr, Array *keys, Array *values) { - memnew_placement(keys, Array(ptr->keys())); - memnew_placement(values, Array(ptr->values())); - return ptr->size(); -} - -void godot_icall_Dictionary_KeyValuePairAt(Dictionary *ptr, int index, Variant *r_key, Variant *r_value) { - memnew_placement(r_key, Variant(ptr->get_key_at_index(index))); - memnew_placement(r_value, Variant(ptr->get_value_at_index(index))); -} - -void godot_icall_Dictionary_Add(Dictionary *ptr, MonoObject *key, MonoObject *value) { - Variant varKey = GDMonoMarshal::mono_object_to_variant(key); - Variant *ret = ptr->getptr(varKey); - if (ret != nullptr) { - GDMonoUtils::set_pending_exception(mono_get_exception_argument("key", "An element with the same key already exists")); - return; - } - ptr->operator[](varKey) = GDMonoMarshal::mono_object_to_variant(value); -} - -void godot_icall_Dictionary_Clear(Dictionary *ptr) { - ptr->clear(); -} - -MonoBoolean godot_icall_Dictionary_Contains(Dictionary *ptr, MonoObject *key, MonoObject *value) { - // no dupes - Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key)); - return ret != nullptr && *ret == GDMonoMarshal::mono_object_to_variant(value); -} - -MonoBoolean godot_icall_Dictionary_ContainsKey(Dictionary *ptr, MonoObject *key) { - return ptr->has(GDMonoMarshal::mono_object_to_variant(key)); -} - -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) { - return ptr->erase(GDMonoMarshal::mono_object_to_variant(key)); -} - -MonoBoolean godot_icall_Dictionary_Remove(Dictionary *ptr, MonoObject *key, MonoObject *value) { - Variant varKey = GDMonoMarshal::mono_object_to_variant(key); - - // no dupes - Variant *ret = ptr->getptr(varKey); - if (ret != nullptr && *ret == GDMonoMarshal::mono_object_to_variant(value)) { - ptr->erase(varKey); - return true; - } - - return false; -} - -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 = Variant(); - return false; - } - *value = *ret; - return true; -} - -MonoString *godot_icall_Dictionary_ToString(Dictionary *ptr) { - return GDMonoMarshal::mono_string_from_godot(Variant(*ptr).operator String()); -} - -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_At", godot_icall_Array_At); - 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); - GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Clear", godot_icall_Array_Clear); - GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Concatenate", godot_icall_Array_Concatenate); - GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Contains", godot_icall_Array_Contains); - GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_CopyTo", godot_icall_Array_CopyTo); - GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Duplicate", godot_icall_Array_Duplicate); - GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_IndexOf", godot_icall_Array_IndexOf); - GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Insert", godot_icall_Array_Insert); - GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Remove", godot_icall_Array_Remove); - 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_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_GetValue", godot_icall_Dictionary_GetValue); - GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_SetValue", godot_icall_Dictionary_SetValue); - GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Keys", godot_icall_Dictionary_Keys); - GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Values", godot_icall_Dictionary_Values); - GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Count", godot_icall_Dictionary_Count); - GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_KeyValuePairs", godot_icall_Dictionary_KeyValuePairs); - GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_KeyValuePairAt", godot_icall_Dictionary_KeyValuePairAt); - GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Add", godot_icall_Dictionary_Add); - GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Clear", godot_icall_Dictionary_Clear); - GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Contains", godot_icall_Dictionary_Contains); - GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_ContainsKey", godot_icall_Dictionary_ContainsKey); - GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Duplicate", godot_icall_Dictionary_Duplicate); - 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_ToString", godot_icall_Dictionary_ToString); -} diff --git a/modules/mono/glue/node_path_glue.cpp b/modules/mono/glue/node_path_glue.cpp deleted file mode 100644 index 770ed312604..00000000000 --- a/modules/mono/glue/node_path_glue.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/*************************************************************************/ -/* node_path_glue.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "core/string/node_path.h" -#include "core/string/ustring.h" - -#include "../mono_gd/gd_mono_marshal.h" - -MonoBoolean godot_icall_NodePath_is_absolute(NodePath *p_ptr) { - return (MonoBoolean)p_ptr->is_absolute(); -} - -int32_t godot_icall_NodePath_get_name_count(NodePath *p_ptr) { - return p_ptr->get_name_count(); -} - -MonoString *godot_icall_NodePath_get_name(NodePath *p_ptr, uint32_t p_idx) { - return GDMonoMarshal::mono_string_from_godot(p_ptr->get_name(p_idx)); -} - -int32_t godot_icall_NodePath_get_subname_count(NodePath *p_ptr) { - return p_ptr->get_subname_count(); -} - -MonoString *godot_icall_NodePath_get_subname(NodePath *p_ptr, uint32_t p_idx) { - return GDMonoMarshal::mono_string_from_godot(p_ptr->get_subname(p_idx)); -} - -MonoString *godot_icall_NodePath_get_concatenated_names(NodePath *p_ptr) { - return GDMonoMarshal::mono_string_from_godot(p_ptr->get_concatenated_names()); -} - -MonoString *godot_icall_NodePath_get_concatenated_subnames(NodePath *p_ptr) { - return GDMonoMarshal::mono_string_from_godot(p_ptr->get_concatenated_subnames()); -} - -void godot_icall_NodePath_get_as_property_path(NodePath *p_ptr, NodePath *r_dest) { - *r_dest = p_ptr->get_as_property_path(); -} - -void godot_register_node_path_icalls() { - GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_as_property_path", godot_icall_NodePath_get_as_property_path); - GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_concatenated_names", godot_icall_NodePath_get_concatenated_names); - GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_concatenated_subnames", godot_icall_NodePath_get_concatenated_subnames); - GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_name", godot_icall_NodePath_get_name); - GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_name_count", godot_icall_NodePath_get_name_count); - 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); -} diff --git a/modules/mono/glue/runtime_interop.cpp b/modules/mono/glue/runtime_interop.cpp index a9c47f8880b..f5a04bf05c7 100644 --- a/modules/mono/glue/runtime_interop.cpp +++ b/modules/mono/glue/runtime_interop.cpp @@ -56,6 +56,9 @@ extern "C" { #define GD_PINVOKE_EXPORT MAYBE_UNUSED #endif +// For ArrayPrivate and DictionaryPrivate +static_assert(sizeof(SafeRefCount) == sizeof(uint32_t)); + typedef Object *(*godotsharp_class_creation_func)(); GD_PINVOKE_EXPORT MethodBind *godotsharp_method_bind_get_method(const StringName *p_classname, const char16_t *p_methodname) { @@ -185,7 +188,7 @@ GD_PINVOKE_EXPORT void godotsharp_packed_string_array_add(PackedStringArray *r_d 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); + memnew_placement(r_callable, Callable(managed_callable)); } GD_PINVOKE_EXPORT bool godotsharp_callable_get_data_for_marshalling(const Callable *p_callable, @@ -198,31 +201,31 @@ GD_PINVOKE_EXPORT bool godotsharp_callable_get_data_for_marshalling(const Callab ManagedCallable *managed_callable = static_cast(custom); *r_delegate_handle = managed_callable->get_delegate(); *r_object = nullptr; - *r_name = StringName(); + memnew_placement(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(); + memnew_placement(r_name, StringName(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(); + memnew_placement(r_name, StringName(event_signal_callable->get_signal())); return true; } // Some other CallableCustom. We only support ManagedCallable. *r_delegate_handle = nullptr; *r_object = nullptr; - *r_name = StringName(); + memnew_placement(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(); + memnew_placement(r_name, StringName(p_callable->get_method())); return true; } } @@ -235,7 +238,7 @@ GD_PINVOKE_EXPORT void godotsharp_method_bind_ptrcall(MethodBind *p_method_bind, p_method_bind->ptrcall(p_instance, p_args, p_ret); } -GD_PINVOKE_EXPORT godot_variant godotsharp_method_bind_call(MethodBind *p_method_bind, Object *p_instance, const godot_variant **p_args, const int p_arg_count, Callable::CallError *p_call_error) { +GD_PINVOKE_EXPORT godot_variant godotsharp_method_bind_call(MethodBind *p_method_bind, Object *p_instance, const godot_variant **p_args, const int32_t p_arg_count, Callable::CallError *p_call_error) { godot_variant ret; memnew_placement(&ret, Variant()); @@ -575,6 +578,10 @@ GD_PINVOKE_EXPORT godot_packed_array godotsharp_variant_as_packed_color_array(co return raw_dest; } +GD_PINVOKE_EXPORT bool godotsharp_variant_equals(const godot_variant *p_a, const godot_variant *p_b) { + return *reinterpret_cast(p_a) == *reinterpret_cast(p_b); +} + // string.h GD_PINVOKE_EXPORT void godotsharp_string_new_with_utf16_chars(String *r_dest, const char16_t *p_contents) { @@ -596,12 +603,24 @@ GD_PINVOKE_EXPORT void godotsharp_node_path_new_copy(NodePath *r_dest, const Nod // array.h +GD_PINVOKE_EXPORT void godotsharp_array_new(Array *r_dest) { + memnew_placement(r_dest, Array); +} + GD_PINVOKE_EXPORT void godotsharp_array_new_copy(Array *r_dest, const Array *p_src) { memnew_placement(r_dest, Array(*p_src)); } +GD_PINVOKE_EXPORT godot_variant *godotsharp_array_ptrw(godot_array *p_self) { + return reinterpret_cast(&reinterpret_cast(p_self)->operator[](0)); +} + // dictionary.h +GD_PINVOKE_EXPORT void godotsharp_dictionary_new(Dictionary *r_dest) { + memnew_placement(r_dest, Dictionary); +} + GD_PINVOKE_EXPORT void godotsharp_dictionary_new_copy(Dictionary *r_dest, const Dictionary *p_src) { memnew_placement(r_dest, Dictionary(*p_src)); } @@ -676,12 +695,156 @@ GD_PINVOKE_EXPORT void godotsharp_dictionary_destroy(Dictionary *p_self) { p_self->~Dictionary(); } +// Array + +GD_PINVOKE_EXPORT int32_t godotsharp_array_add(Array *p_self, const Variant *p_item) { + p_self->append(*p_item); + return p_self->size(); +} + +GD_PINVOKE_EXPORT void godotsharp_array_duplicate(const Array *p_self, bool p_deep, Array *r_dest) { + memnew_placement(r_dest, Array(p_self->duplicate(p_deep))); +} + +GD_PINVOKE_EXPORT int32_t godotsharp_array_index_of(const Array *p_self, const Variant *p_item) { + return p_self->find(*p_item); +} + +GD_PINVOKE_EXPORT void godotsharp_array_insert(Array *p_self, int32_t p_index, const Variant *p_item) { + p_self->insert(p_index, *p_item); +} + +GD_PINVOKE_EXPORT void godotsharp_array_remove_at(Array *p_self, int32_t p_index) { + p_self->remove_at(p_index); +} + +GD_PINVOKE_EXPORT int32_t godotsharp_array_resize(Array *p_self, int32_t p_new_size) { + return (int32_t)p_self->resize(p_new_size); +} + +GD_PINVOKE_EXPORT void godotsharp_array_shuffle(Array *p_self) { + p_self->shuffle(); +} + +// Dictionary + +GD_PINVOKE_EXPORT bool godotsharp_dictionary_try_get_value(const Dictionary *p_self, const Variant *p_key, Variant *r_value) { + const Variant *ret = p_self->getptr(*p_key); + if (ret == nullptr) { + memnew_placement(r_value, Variant()); + return false; + } + memnew_placement(r_value, Variant(*ret)); + return true; +} + +GD_PINVOKE_EXPORT void godotsharp_dictionary_set_value(Dictionary *p_self, const Variant *p_key, const Variant *p_value) { + p_self->operator[](*p_key) = *p_value; +} + +GD_PINVOKE_EXPORT void godotsharp_dictionary_keys(const Dictionary *p_self, Array *r_dest) { + memnew_placement(r_dest, Array(p_self->keys())); +} + +GD_PINVOKE_EXPORT void godotsharp_dictionary_values(const Dictionary *p_self, Array *r_dest) { + memnew_placement(r_dest, Array(p_self->values())); +} + +GD_PINVOKE_EXPORT int32_t godotsharp_dictionary_count(const Dictionary *p_self) { + return p_self->size(); +} + +GD_PINVOKE_EXPORT void godotsharp_dictionary_key_value_pair_at(const Dictionary *p_self, int32_t p_index, Variant *r_key, Variant *r_value) { + memnew_placement(r_key, Variant(p_self->get_key_at_index(p_index))); + memnew_placement(r_value, Variant(p_self->get_value_at_index(p_index))); +} + +GD_PINVOKE_EXPORT void godotsharp_dictionary_add(Dictionary *p_self, const Variant *p_key, const Variant *p_value) { + p_self->operator[](*p_key) = *p_value; +} + +GD_PINVOKE_EXPORT void godotsharp_dictionary_clear(Dictionary *p_self) { + p_self->clear(); +} + +GD_PINVOKE_EXPORT bool godotsharp_dictionary_contains_key(const Dictionary *p_self, const Variant *p_key) { + return p_self->has(*p_key); +} + +GD_PINVOKE_EXPORT void godotsharp_dictionary_duplicate(const Dictionary *p_self, bool p_deep, Dictionary *r_dest) { + memnew_placement(r_dest, Dictionary(p_self->duplicate(p_deep))); +} + +GD_PINVOKE_EXPORT bool godotsharp_dictionary_remove_key(Dictionary *p_self, const Variant *p_key) { + return p_self->erase(*p_key); +} + +GD_PINVOKE_EXPORT void godotsharp_string_md5_buffer(const String *p_self, PackedByteArray *r_md5_buffer) { + memnew_placement(r_md5_buffer, PackedByteArray(p_self->md5_buffer())); +} + +GD_PINVOKE_EXPORT void godotsharp_string_md5_text(const String *p_self, String *r_md5_text) { + memnew_placement(r_md5_text, String(p_self->md5_text())); +} + +GD_PINVOKE_EXPORT int32_t godotsharp_string_rfind(const String *p_self, const String *p_what, int32_t p_from) { + return p_self->rfind(*p_what, p_from); +} + +GD_PINVOKE_EXPORT int32_t godotsharp_string_rfindn(const String *p_self, const String *p_what, int32_t p_from) { + return p_self->rfindn(*p_what, p_from); +} + +GD_PINVOKE_EXPORT void godotsharp_string_sha256_buffer(const String *p_self, PackedByteArray *r_sha256_buffer) { + memnew_placement(r_sha256_buffer, PackedByteArray(p_self->sha256_buffer())); +} + +GD_PINVOKE_EXPORT void godotsharp_string_sha256_text(const String *p_self, String *r_sha256_text) { + memnew_placement(r_sha256_text, String(p_self->sha256_text())); +} + +GD_PINVOKE_EXPORT void godotsharp_string_simplify_path(const String *p_self, String *r_simplified_path) { + memnew_placement(r_simplified_path, String(p_self->simplify_path())); +} + +GD_PINVOKE_EXPORT void godotsharp_node_path_get_as_property_path(const NodePath *p_ptr, NodePath *r_dest) { + memnew_placement(r_dest, NodePath(p_ptr->get_as_property_path())); +} + +GD_PINVOKE_EXPORT void godotsharp_node_path_get_concatenated_names(const NodePath *p_self, String *r_subnames) { + memnew_placement(r_subnames, String(p_self->get_concatenated_names())); +} + +GD_PINVOKE_EXPORT void godotsharp_node_path_get_concatenated_subnames(const NodePath *p_self, String *r_subnames) { + memnew_placement(r_subnames, String(p_self->get_concatenated_subnames())); +} + +GD_PINVOKE_EXPORT void godotsharp_node_path_get_name(const NodePath *p_self, uint32_t p_idx, String *r_name) { + memnew_placement(r_name, String(p_self->get_name(p_idx))); +} + +GD_PINVOKE_EXPORT int32_t godotsharp_node_path_get_name_count(const NodePath *p_self) { + return p_self->get_name_count(); +} + +GD_PINVOKE_EXPORT void godotsharp_node_path_get_subname(const NodePath *p_self, uint32_t p_idx, String *r_subname) { + memnew_placement(r_subname, String(p_self->get_subname(p_idx))); +} + +GD_PINVOKE_EXPORT int32_t godotsharp_node_path_get_subname_count(const NodePath *p_self) { + return p_self->get_subname_count(); +} + +GD_PINVOKE_EXPORT bool godotsharp_node_path_is_absolute(const NodePath *p_self) { + return p_self->is_absolute(); +} + #ifdef __cplusplus } #endif // We need this to prevent the functions from being stripped. -void *godotsharp_pinvoke_funcs[101] = { +void *godotsharp_pinvoke_funcs[138] = { (void *)godotsharp_method_bind_get_method, (void *)godotsharp_get_class_constructor, (void *)godotsharp_invoke_class_constructor, @@ -761,10 +924,14 @@ void *godotsharp_pinvoke_funcs[101] = { (void *)godotsharp_variant_as_packed_vector2_array, (void *)godotsharp_variant_as_packed_vector3_array, (void *)godotsharp_variant_as_packed_color_array, + (void *)godotsharp_variant_equals, (void *)godotsharp_string_new_with_utf16_chars, (void *)godotsharp_string_name_new_copy, (void *)godotsharp_node_path_new_copy, + (void *)godotsharp_array_new, (void *)godotsharp_array_new_copy, + (void *)godotsharp_array_ptrw, + (void *)godotsharp_dictionary_new, (void *)godotsharp_dictionary_new_copy, (void *)godotsharp_packed_byte_array_destroy, (void *)godotsharp_packed_int32_array_destroy, @@ -782,5 +949,38 @@ void *godotsharp_pinvoke_funcs[101] = { (void *)godotsharp_signal_destroy, (void *)godotsharp_callable_destroy, (void *)godotsharp_array_destroy, - (void *)godotsharp_dictionary_destroy + (void *)godotsharp_dictionary_destroy, + (void *)godotsharp_array_add, + (void *)godotsharp_array_duplicate, + (void *)godotsharp_array_index_of, + (void *)godotsharp_array_insert, + (void *)godotsharp_array_remove_at, + (void *)godotsharp_array_resize, + (void *)godotsharp_array_shuffle, + (void *)godotsharp_dictionary_try_get_value, + (void *)godotsharp_dictionary_set_value, + (void *)godotsharp_dictionary_keys, + (void *)godotsharp_dictionary_values, + (void *)godotsharp_dictionary_count, + (void *)godotsharp_dictionary_key_value_pair_at, + (void *)godotsharp_dictionary_add, + (void *)godotsharp_dictionary_clear, + (void *)godotsharp_dictionary_contains_key, + (void *)godotsharp_dictionary_duplicate, + (void *)godotsharp_dictionary_remove_key, + (void *)godotsharp_string_md5_buffer, + (void *)godotsharp_string_md5_text, + (void *)godotsharp_string_rfind, + (void *)godotsharp_string_rfindn, + (void *)godotsharp_string_sha256_buffer, + (void *)godotsharp_string_sha256_text, + (void *)godotsharp_string_simplify_path, + (void *)godotsharp_node_path_get_as_property_path, + (void *)godotsharp_node_path_get_concatenated_names, + (void *)godotsharp_node_path_get_concatenated_subnames, + (void *)godotsharp_node_path_get_name, + (void *)godotsharp_node_path_get_name_count, + (void *)godotsharp_node_path_get_subname, + (void *)godotsharp_node_path_get_subname_count, + (void *)godotsharp_node_path_is_absolute }; diff --git a/modules/mono/glue/string_glue.cpp b/modules/mono/glue/string_glue.cpp deleted file mode 100644 index fe1c0b5f8cf..00000000000 --- a/modules/mono/glue/string_glue.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/*************************************************************************/ -/* string_glue.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "core/string/ustring.h" -#include "core/templates/vector.h" -#include "core/variant/variant.h" - -#include "../mono_gd/gd_mono_marshal.h" - -MonoArray *godot_icall_String_md5_buffer(MonoString *p_str) { - Vector ret = GDMonoMarshal::mono_string_to_godot(p_str).md5_buffer(); - // TODO Check possible Array/Vector problem? - return GDMonoMarshal::Array_to_mono_array(Variant(ret)); -} - -MonoString *godot_icall_String_md5_text(MonoString *p_str) { - String ret = GDMonoMarshal::mono_string_to_godot(p_str).md5_text(); - return GDMonoMarshal::mono_string_from_godot(ret); -} - -int godot_icall_String_rfind(MonoString *p_str, MonoString *p_what, int p_from) { - String what = GDMonoMarshal::mono_string_to_godot(p_what); - return GDMonoMarshal::mono_string_to_godot(p_str).rfind(what, p_from); -} - -int godot_icall_String_rfindn(MonoString *p_str, MonoString *p_what, int p_from) { - String what = GDMonoMarshal::mono_string_to_godot(p_what); - return GDMonoMarshal::mono_string_to_godot(p_str).rfindn(what, p_from); -} - -MonoArray *godot_icall_String_sha256_buffer(MonoString *p_str) { - Vector ret = GDMonoMarshal::mono_string_to_godot(p_str).sha256_buffer(); - return GDMonoMarshal::Array_to_mono_array(Variant(ret)); -} - -MonoString *godot_icall_String_sha256_text(MonoString *p_str) { - String ret = GDMonoMarshal::mono_string_to_godot(p_str).sha256_text(); - return GDMonoMarshal::mono_string_from_godot(ret); -} - -MonoString *godot_icall_String_simplify_path(MonoString *p_str) { - String ret = GDMonoMarshal::mono_string_to_godot(p_str).simplify_path(); - return GDMonoMarshal::mono_string_from_godot(ret); -} - -void godot_register_string_icalls() { - GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_md5_buffer", godot_icall_String_md5_buffer); - GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_md5_text", godot_icall_String_md5_text); - GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_rfind", godot_icall_String_rfind); - GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_rfindn", godot_icall_String_rfindn); - 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); - GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_simplify_path", godot_icall_String_simplify_path); -} diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index eeaec30d739..b45b71aea70 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -442,22 +442,15 @@ bool GDMono::_are_api_assemblies_out_of_sync() { return out_of_sync; } -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() { // 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(); } From 5e37d073bb86492e8c415964ffd554a2fa08920d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Rold=C3=A1n=20Etcheverry?= Date: Sun, 12 Sep 2021 19:50:13 +0200 Subject: [PATCH 04/31] C#: Re-write GD and some other icalls as P/Invoke --- modules/mono/csharp_script.cpp | 21 +- .../GodotSharp/GodotSharp/Core/Dispatcher.cs | 20 +- .../Core/Extensions/ObjectExtensions.cs | 20 +- .../glue/GodotSharp/GodotSharp/Core/GD.cs | 200 ++++------ .../Core/NativeInterop/InteropStructs.cs | 2 + .../Core/NativeInterop/Marshaling.cs | 14 +- .../Core/NativeInterop/NativeFuncs.cs | 84 +++++ .../GodotSharp/GodotSharp/Core/Object.base.cs | 10 +- .../GodotSharp/Core/ScriptManager.cs | 10 + .../GodotSharp/GodotSharp/GodotSharp.csproj | 1 + modules/mono/glue/base_object_glue.cpp | 36 -- modules/mono/glue/gd_glue.cpp | 342 ------------------ modules/mono/glue/runtime_interop.cpp | 214 ++++++++++- modules/mono/mono_gd/gd_mono.cpp | 6 +- modules/mono/mono_gd/gd_mono_cache.cpp | 18 +- modules/mono/mono_gd/gd_mono_cache.h | 3 - modules/mono/mono_gd/gd_mono_marshal.cpp | 57 +-- modules/mono/mono_gd/gd_mono_marshal.h | 9 - modules/mono/mono_gd/gd_mono_method.cpp | 10 +- 19 files changed, 442 insertions(+), 635 deletions(-) create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/ScriptManager.cs delete mode 100644 modules/mono/glue/gd_glue.cpp diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 9520598f5cd..4316f38bd2e 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -109,7 +109,7 @@ Error CSharpLanguage::execute_file(const String &p_path) { return OK; } -extern void *godotsharp_pinvoke_funcs[138]; +extern void *godotsharp_pinvoke_funcs[164]; [[maybe_unused]] volatile void **do_not_strip_godotsharp_pinvoke_funcs; void CSharpLanguage::init() { @@ -705,19 +705,14 @@ void CSharpLanguage::pre_unsafe_unreference(Object *p_obj) { void CSharpLanguage::frame() { if (gdmono && gdmono->is_runtime_initialized() && gdmono->get_core_api_assembly() != nullptr) { - const Ref &task_scheduler_handle = GDMonoCache::cached_data.task_scheduler_handle; + MonoException *exc = nullptr; + gdmono->get_core_api_assembly() + ->get_class("Godot", "ScriptManager") + ->get_method("FrameCallback") + ->invoke(nullptr, &exc); - if (task_scheduler_handle.is_valid()) { - MonoObject *task_scheduler = task_scheduler_handle->get_target(); - - if (task_scheduler) { - MonoException *exc = nullptr; - CACHED_METHOD_THUNK(GodotTaskScheduler, Activate).invoke(task_scheduler, &exc); - - if (exc) { - GDMonoUtils::debug_unhandled_exception(exc); - } - } + if (exc) { + GDMonoUtils::debug_unhandled_exception(exc); } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs index 64752370023..5f84bb530f3 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs @@ -1,20 +1,14 @@ -using System.Runtime.CompilerServices; - namespace Godot { public static class Dispatcher { - /// - /// Implements an external instance of GodotTaskScheduler. - /// - /// A GodotTaskScheduler instance. - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern GodotTaskScheduler godot_icall_DefaultGodotTaskScheduler(); + internal static GodotTaskScheduler DefaultGodotTaskScheduler; - /// - /// Initializes the synchronization context as the context of the GodotTaskScheduler. - /// - public static GodotSynchronizationContext SynchronizationContext => - godot_icall_DefaultGodotTaskScheduler().Context; + private static void InitializeDefaultGodotTaskScheduler() + { + DefaultGodotTaskScheduler = new GodotTaskScheduler(); + } + + public static GodotSynchronizationContext SynchronizationContext => DefaultGodotTaskScheduler.Context; } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs index 691fd859647..340780bb45a 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs @@ -1,5 +1,5 @@ using System; -using System.Runtime.CompilerServices; +using Godot.NativeInterop; namespace Godot { @@ -32,10 +32,20 @@ namespace Godot /// public static WeakRef WeakRef(Object obj) { - return godot_icall_Object_weakref(GetPtr(obj)); - } + if (!IsInstanceValid(obj)) + return null; - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern WeakRef godot_icall_Object_weakref(IntPtr obj); + using godot_ref weakRef = default; + + unsafe + { + NativeFuncs.godotsharp_weakref(GetPtr(obj), &weakRef); + } + + if (weakRef.IsNull) + return null; + + return (WeakRef)InteropUtils.UnmanagedGetManaged(weakRef._reference); + } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs index e8ea8f379bd..9d237b8d93f 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs @@ -5,7 +5,6 @@ using real_t = System.Single; #endif using System; using System.Collections.Generic; -using System.Runtime.CompilerServices; using Godot.NativeInterop; namespace Godot @@ -30,7 +29,9 @@ namespace Godot public static unsafe object Bytes2Var(byte[] bytes, bool allowObjects = false) { using var varBytes = Marshaling.mono_array_to_PackedByteArray(bytes); - return godot_icall_GD_bytes2var(&varBytes, allowObjects); + using godot_variant ret = default; + NativeFuncs.godotsharp_bytes2var(&varBytes, allowObjects, &ret); + return Marshaling.variant_to_mono_object(&ret); } /// @@ -48,9 +49,12 @@ namespace Godot /// /// /// The Variant converted to the given . - public static object Convert(object what, Variant.Type type) + public static unsafe object Convert(object what, Variant.Type type) { - return godot_icall_GD_convert(what, type); + using var whatVariant = Marshaling.mono_object_to_variant(what); + using godot_variant ret = default; + NativeFuncs.godotsharp_convert(&whatVariant, (int)type, &ret); + return Marshaling.variant_to_mono_object(&ret); } /// @@ -64,7 +68,7 @@ namespace Godot return (real_t)Math.Exp(db * 0.11512925464970228420089957273422); } - private static object[] GetPrintParams(object[] parameters) + private static string[] GetPrintParams(object[] parameters) { if (parameters == null) { @@ -84,9 +88,10 @@ namespace Godot /// /// Variable that will be hashed. /// Hash of the variable passed. - public static int Hash(object var) + public static unsafe int Hash(object var) { - return godot_icall_GD_hash(var); + using var variant = Marshaling.mono_object_to_variant(var); + return NativeFuncs.godotsharp_hash(&variant); } /// @@ -112,7 +117,7 @@ namespace Godot /// The instance. public static Object InstanceFromId(ulong instanceId) { - return godot_icall_GD_instance_from_id(instanceId); + return InteropUtils.UnmanagedGetManaged(NativeFuncs.godotsharp_instance_from_id(instanceId)); } /// @@ -202,9 +207,10 @@ namespace Godot /// /// /// Error message. - public static void PushError(string message) + public static unsafe void PushError(string message) { - godot_icall_GD_pusherror(message); + using var godotStr = Marshaling.mono_string_to_godot(message); + NativeFuncs.godotsharp_pusherror(&godotStr); } /// @@ -214,9 +220,10 @@ namespace Godot /// GD.PushWarning("test warning"); // Prints "test warning" to debugger and terminal as warning call /// /// Warning message. - public static void PushWarning(string message) + public static unsafe void PushWarning(string message) { - godot_icall_GD_pushwarning(message); + using var godotStr = Marshaling.mono_string_to_godot(message); + NativeFuncs.godotsharp_pushwarning(&godotStr); } /// @@ -235,9 +242,11 @@ namespace Godot /// /// /// Arguments that will be printed. - public static void Print(params object[] what) + public static unsafe void Print(params object[] what) { - godot_icall_GD_print(GetPrintParams(what)); + string str = string.Concat(GetPrintParams(what)); + using var godotStr = Marshaling.mono_string_to_godot(str); + NativeFuncs.godotsharp_print(&godotStr); } /// @@ -264,9 +273,11 @@ namespace Godot /// /// /// Arguments that will be printed. - public static void PrintRich(params object[] what) + public static unsafe void PrintRich(params object[] what) { - godot_icall_GD_print_rich(GetPrintParams(what)); + string str = string.Concat(GetPrintParams(what)); + using var godotStr = Marshaling.mono_string_to_godot(str); + NativeFuncs.godotsharp_print_rich(&godotStr); } /// @@ -286,9 +297,11 @@ namespace Godot /// /// /// Arguments that will be printed. - public static void PrintErr(params object[] what) + public static unsafe void PrintErr(params object[] what) { - godot_icall_GD_printerr(GetPrintParams(what)); + string str = string.Concat(GetPrintParams(what)); + using var godotStr = Marshaling.mono_string_to_godot(str); + NativeFuncs.godotsharp_printerr(&godotStr); } /// @@ -306,9 +319,11 @@ namespace Godot /// /// /// Arguments that will be printed. - public static void PrintRaw(params object[] what) + public static unsafe void PrintRaw(params object[] what) { - godot_icall_GD_printraw(GetPrintParams(what)); + string str = string.Concat(GetPrintParams(what)); + using var godotStr = Marshaling.mono_string_to_godot(str); + NativeFuncs.godotsharp_printraw(&godotStr); } /// @@ -320,9 +335,11 @@ namespace Godot /// /// /// Arguments that will be printed. - public static void PrintS(params object[] what) + public static unsafe void PrintS(params object[] what) { - godot_icall_GD_prints(GetPrintParams(what)); + string str = string.Join(' ', GetPrintParams(what)); + using var godotStr = Marshaling.mono_string_to_godot(str); + NativeFuncs.godotsharp_prints(&godotStr); } /// @@ -334,9 +351,11 @@ namespace Godot /// /// /// Arguments that will be printed. - public static void PrintT(params object[] what) + public static unsafe void PrintT(params object[] what) { - godot_icall_GD_printt(GetPrintParams(what)); + string str = string.Join('\t', GetPrintParams(what)); + using var godotStr = Marshaling.mono_string_to_godot(str); + NativeFuncs.godotsharp_printt(&godotStr); } /// @@ -350,7 +369,7 @@ namespace Godot /// A random number. public static float Randf() { - return godot_icall_GD_randf(); + return NativeFuncs.godotsharp_randf(); } /// @@ -360,7 +379,7 @@ namespace Godot /// A random normally-distributed number. public static double Randfn(double mean, double deviation) { - return godot_icall_GD_randfn(mean, deviation); + return NativeFuncs.godotsharp_randfn(mean, deviation); } /// @@ -378,7 +397,7 @@ namespace Godot /// A random number. public static uint Randi() { - return godot_icall_GD_randi(); + return NativeFuncs.godotsharp_randi(); } /// @@ -391,7 +410,7 @@ namespace Godot /// public static void Randomize() { - godot_icall_GD_randomize(); + NativeFuncs.godotsharp_randomize(); } /// @@ -406,7 +425,7 @@ namespace Godot /// A random number inside the given range. public static double RandRange(double from, double to) { - return godot_icall_GD_randf_range(from, to); + return NativeFuncs.godotsharp_randf_range(from, to); } /// @@ -423,7 +442,7 @@ namespace Godot /// A random number inside the given range. public static int RandRange(int from, int to) { - return godot_icall_GD_randi_range(from, to); + return NativeFuncs.godotsharp_randi_range(from, to); } /// @@ -436,7 +455,7 @@ namespace Godot /// A random number. public static uint RandFromSeed(ref ulong seed) { - return godot_icall_GD_rand_seed(seed, out seed); + return NativeFuncs.godotsharp_rand_from_seed(seed, out seed); } /// @@ -493,7 +512,7 @@ namespace Godot /// Seed that will be used. public static void Seed(ulong seed) { - godot_icall_GD_seed(seed); + NativeFuncs.godotsharp_seed(seed); } /// @@ -501,9 +520,12 @@ namespace Godot /// /// Arguments that will converted to string. /// The string formed by the given arguments. - public static string Str(params object[] what) + public static unsafe string Str(params object[] what) { - return godot_icall_GD_str(what); + using var whatGodotArray = Marshaling.mono_array_to_Array(what); + using godot_string ret = default; + NativeFuncs.godotsharp_str(&whatGodotArray, &ret); + return Marshaling.mono_string_from_godot(&ret); } /// @@ -518,18 +540,12 @@ namespace Godot /// /// String that will be converted to Variant. /// The decoded Variant. - public static object Str2Var(string str) + public static unsafe object Str2Var(string str) { - return godot_icall_GD_str2var(str); - } - - /// - /// Returns whether the given class exists in . - /// - /// If the class exists in . - public static bool TypeExists(StringName type) - { - return godot_icall_GD_type_exists(ref type.NativeValue); + using var godotStr = Marshaling.mono_string_to_godot(str); + using godot_variant ret = default; + NativeFuncs.godotsharp_str2var(&godotStr, &ret); + return Marshaling.variant_to_mono_object(&ret); } /// @@ -543,12 +559,11 @@ namespace Godot /// The Variant encoded as an array of bytes. public static unsafe byte[] Var2Bytes(object var, bool fullObjects = false) { - godot_packed_byte_array varBytes; - godot_icall_GD_var2bytes(var, fullObjects, &varBytes); + using var variant = Marshaling.mono_object_to_variant(var); + using godot_packed_byte_array varBytes = default; + NativeFuncs.godotsharp_var2bytes(&variant, fullObjects, &varBytes); using (varBytes) - { return Marshaling.PackedByteArray_to_mono_array(&varBytes); - } } /// @@ -568,9 +583,12 @@ namespace Godot /// /// Variant that will be converted to string. /// The Variant encoded as a string. - public static string Var2Str(object var) + public static unsafe string Var2Str(object var) { - return godot_icall_GD_var2str(var); + using var variant = Marshaling.mono_object_to_variant(var); + using godot_string ret = default; + NativeFuncs.godotsharp_var2str(&variant, &ret); + return Marshaling.mono_string_from_godot(&ret); } /// @@ -579,85 +597,7 @@ namespace Godot /// The for the given . public static Variant.Type TypeToVariantType(Type type) { - return godot_icall_TypeToVariantType(type); + return Marshaling.managed_to_variant_type(type, out bool _); } - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern unsafe object godot_icall_GD_bytes2var(godot_packed_byte_array* bytes, bool allowObjects); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern object godot_icall_GD_convert(object what, Variant.Type type); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern int godot_icall_GD_hash(object var); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern Object godot_icall_GD_instance_from_id(ulong instanceId); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_GD_print(object[] what); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_GD_print_rich(object[] what); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_GD_printerr(object[] what); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_GD_printraw(object[] what); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_GD_prints(object[] what); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_GD_printt(object[] what); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_GD_randomize(); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern uint godot_icall_GD_randi(); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern float godot_icall_GD_randf(); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern int godot_icall_GD_randi_range(int from, int to); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern double godot_icall_GD_randf_range(double from, double to); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern double godot_icall_GD_randfn(double mean, double deviation); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern uint godot_icall_GD_rand_seed(ulong seed, out ulong newSeed); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_GD_seed(ulong seed); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern string godot_icall_GD_str(object[] what); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern object godot_icall_GD_str2var(string str); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool godot_icall_GD_type_exists(ref godot_string_name type); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern unsafe void godot_icall_GD_var2bytes(object what, bool fullObjects, godot_packed_byte_array* bytes); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern string godot_icall_GD_var2str(object var); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void godot_icall_GD_pusherror(string type); - - [MethodImpl(MethodImplOptions.InternalCall)] - 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/NativeInterop/InteropStructs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs index ddcb6b90919..865863cd3ec 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs @@ -36,6 +36,8 @@ namespace Godot.NativeInterop NativeFuncs.godotsharp_ref_destroy(ref this); _reference = IntPtr.Zero; } + + public bool IsNull => _reference == IntPtr.Zero; } [SuppressMessage("ReSharper", "InconsistentNaming")] diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs index 590fb082e83..e0819b2857f 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs @@ -18,8 +18,10 @@ namespace Godot.NativeInterop fieldInfo.SetValue(obj, valueObj); } - public static Variant.Type managed_to_variant_type(Type type, ref bool r_nil_is_variant) + public static Variant.Type managed_to_variant_type(Type type, out bool r_nil_is_variant) { + r_nil_is_variant = false; + switch (Type.GetTypeCode(type)) { case TypeCode.Boolean: @@ -199,8 +201,6 @@ namespace Godot.NativeInterop } } - r_nil_is_variant = false; - // Unknown return Variant.Type.Nil; } @@ -711,7 +711,7 @@ namespace Godot.NativeInterop 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); + return Array_to_mono_array_of_godot_object_type(&godotArray, type); } if (type == typeof(object[])) @@ -1136,7 +1136,7 @@ namespace Godot.NativeInterop return ret; } - public static unsafe object Array_to_mono_array_of_type(godot_array* p_array, Type type) + public static unsafe object Array_to_mono_array_of_godot_object_type(godot_array* p_array, Type type) { var array = Collections.Array.CreateTakingOwnershipOfDisposableValue( NativeFuncs.godotsharp_array_new_copy(p_array)); @@ -1144,7 +1144,9 @@ namespace Godot.NativeInterop int length = array.Count; object ret = Activator.CreateInstance(type, length); - array.CopyTo((object[])ret, 0); // variant_to_mono_object handled by Collections.Array + // variant_to_mono_object handled by Collections.Array + // variant_to_mono_object_of_type is not needed because target element types are Godot.Object (or derived) + array.CopyTo((object[])ret, 0); return ret; } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs index 0680ec7be5d..adbf5eb9b68 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs @@ -510,5 +510,89 @@ namespace Godot.NativeInterop [DllImport(GodotDllName)] public static extern bool godotsharp_node_path_is_absolute(ref godot_node_path p_self); + + // GD, etc + + [DllImport(GodotDllName)] + public static extern void godotsharp_bytes2var(godot_packed_byte_array* p_bytes, bool p_allow_objects, + godot_variant* r_ret); + + [DllImport(GodotDllName)] + public static extern void godotsharp_convert(godot_variant* p_what, int p_type, godot_variant* r_ret); + + [DllImport(GodotDllName)] + public static extern int godotsharp_hash(godot_variant* var); + + [DllImport(GodotDllName)] + public static extern IntPtr godotsharp_instance_from_id(ulong instanceId); + + [DllImport(GodotDllName)] + public static extern void godotsharp_print(godot_string* p_what); + + [DllImport(GodotDllName)] + public static extern void godotsharp_print_rich(godot_string* p_what); + + [DllImport(GodotDllName)] + public static extern void godotsharp_printerr(godot_string* p_what); + + [DllImport(GodotDllName)] + public static extern void godotsharp_printraw(godot_string* p_what); + + [DllImport(GodotDllName)] + public static extern void godotsharp_prints(godot_string* p_what); + + [DllImport(GodotDllName)] + public static extern void godotsharp_printt(godot_string* p_what); + + [DllImport(GodotDllName)] + public static extern float godotsharp_randf(); + + [DllImport(GodotDllName)] + public static extern uint godotsharp_randi(); + + [DllImport(GodotDllName)] + public static extern void godotsharp_randomize(); + + [DllImport(GodotDllName)] + public static extern double godotsharp_randf_range(double from, double to); + + [DllImport(GodotDllName)] + public static extern double godotsharp_randfn(double mean, double deviation); + + [DllImport(GodotDllName)] + public static extern int godotsharp_randi_range(int from, int to); + + [DllImport(GodotDllName)] + public static extern uint godotsharp_rand_from_seed(ulong seed, out ulong newSeed); + + [DllImport(GodotDllName)] + public static extern void godotsharp_seed(ulong seed); + + [DllImport(GodotDllName)] + public static extern void godotsharp_weakref(IntPtr obj, godot_ref* r_weak_ref); + + [DllImport(GodotDllName)] + public static extern string godotsharp_str(godot_array* p_what, godot_string* r_ret); + + [DllImport(GodotDllName)] + public static extern void godotsharp_str2var(godot_string* p_str, godot_variant* r_ret); + + [DllImport(GodotDllName)] + public static extern void godotsharp_var2bytes(godot_variant* what, bool fullObjects, + godot_packed_byte_array* bytes); + + [DllImport(GodotDllName)] + public static extern void godotsharp_var2str(godot_variant* var, godot_string* r_ret); + + [DllImport(GodotDllName)] + public static extern void godotsharp_pusherror(godot_string* type); + + [DllImport(GodotDllName)] + public static extern void godotsharp_pushwarning(godot_string* type); + + // Object + + [DllImport(GodotDllName)] + public static extern string godotsharp_object_to_string(IntPtr ptr, godot_string* r_str); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs index 9c4265517ec..7bbaef62faa 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.CompilerServices; +using Godot.NativeInterop; namespace Godot { @@ -101,9 +102,11 @@ namespace Godot /// Converts this to a string. /// /// A string representation of this object. - public override string ToString() + public override unsafe string ToString() { - return godot_icall_Object_ToString(GetPtr(this)); + using godot_string str = default; + NativeFuncs.godotsharp_object_to_string(GetPtr(this), &str); + return Marshaling.mono_string_from_godot(&str); } /// @@ -190,8 +193,5 @@ namespace Godot [MethodImpl(MethodImplOptions.InternalCall)] internal static extern void godot_icall_Object_ConnectEventSignals(IntPtr obj); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern string godot_icall_Object_ToString(IntPtr ptr); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/ScriptManager.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/ScriptManager.cs new file mode 100644 index 00000000000..e92688f5bb1 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/ScriptManager.cs @@ -0,0 +1,10 @@ +namespace Godot +{ + internal class ScriptManager + { + internal static void FrameCallback() + { + Dispatcher.DefaultGodotTaskScheduler?.Activate(); + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj index 0709723da08..36faf92144c 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj @@ -71,6 +71,7 @@ + diff --git a/modules/mono/glue/base_object_glue.cpp b/modules/mono/glue/base_object_glue.cpp index c252ee47f4a..d56b70e7392 100644 --- a/modules/mono/glue/base_object_glue.cpp +++ b/modules/mono/glue/base_object_glue.cpp @@ -124,50 +124,14 @@ void godot_icall_Object_ConnectEventSignals(Object *p_ptr) { } } -MonoObject *godot_icall_Object_weakref(Object *p_ptr) { - if (!p_ptr) { - return nullptr; - } - - Ref wref; - RefCounted *rc = Object::cast_to(p_ptr); - - if (rc) { - Ref r = rc; - if (!r.is_valid()) { - return nullptr; - } - - wref.instantiate(); - wref->set_ref(r); - } else { - wref.instantiate(); - wref->set_obj(p_ptr); - } - - return GDMonoUtils::unmanaged_get_managed(wref.ptr()); -} - int32_t godot_icall_SignalAwaiter_connect(Object *p_source, StringName *p_signal, Object *p_target, MonoObject *p_awaiter) { StringName signal = p_signal ? *p_signal : StringName(); return (int32_t)gd_mono_connect_signal_awaiter(p_source, signal, p_target, p_awaiter); } -MonoString *godot_icall_Object_ToString(Object *p_ptr) { -#ifdef DEBUG_ENABLED - // Cannot happen in C#; would get an ObjectDisposedException instead. - CRASH_COND(p_ptr == nullptr); -#endif - // Can't call 'Object::to_string()' here, as that can end up calling 'ToString' again resulting in an endless circular loop. - String result = "[" + p_ptr->get_class() + ":" + itos(p_ptr->get_instance_id()) + "]"; - return GDMonoMarshal::mono_string_from_godot(result); -} - void godot_register_object_icalls() { 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_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); } diff --git a/modules/mono/glue/gd_glue.cpp b/modules/mono/glue/gd_glue.cpp deleted file mode 100644 index b3158318190..00000000000 --- a/modules/mono/glue/gd_glue.cpp +++ /dev/null @@ -1,342 +0,0 @@ -/*************************************************************************/ -/* gd_glue.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "core/io/marshalls.h" -#include "core/os/os.h" -#include "core/string/ustring.h" -#include "core/variant/array.h" -#include "core/variant/variant.h" -#include "core/variant/variant_parser.h" - -#include "../mono_gd/gd_mono_cache.h" -#include "../mono_gd/gd_mono_marshal.h" -#include "../mono_gd/gd_mono_utils.h" - -MonoObject *godot_icall_GD_bytes2var(PackedByteArray *p_bytes, MonoBoolean p_allow_objects) { - Variant ret; - 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."); - } - return GDMonoMarshal::variant_to_mono_object(ret); -} - -MonoObject *godot_icall_GD_convert(MonoObject *p_what, int32_t p_type) { - Variant what = GDMonoMarshal::mono_object_to_variant(p_what); - const Variant *args[1] = { &what }; - Callable::CallError ce; - Variant ret; - Variant::construct(Variant::Type(p_type), ret, args, 1, ce); - ERR_FAIL_COND_V(ce.error != Callable::CallError::CALL_OK, nullptr); - return GDMonoMarshal::variant_to_mono_object(ret); -} - -int godot_icall_GD_hash(MonoObject *p_var) { - return GDMonoMarshal::mono_object_to_variant(p_var).hash(); -} - -MonoObject *godot_icall_GD_instance_from_id(uint64_t p_instance_id) { - return GDMonoUtils::unmanaged_get_managed(ObjectDB::get_instance(ObjectID(p_instance_id))); -} - -void godot_icall_GD_print(MonoArray *p_what) { - String str; - int length = mono_array_length(p_what); - - for (int i = 0; i < length; i++) { - MonoObject *elem = mono_array_get(p_what, MonoObject *, i); - - MonoException *exc = nullptr; - String elem_str = GDMonoMarshal::mono_object_to_variant_string(elem, &exc); - - if (exc) { - GDMonoUtils::set_pending_exception(exc); - return; - } - - str += elem_str; - } - - print_line(str); -} - -void godot_icall_GD_print_rich(MonoArray *p_what) { - String str; - int length = mono_array_length(p_what); - - for (int i = 0; i < length; i++) { - MonoObject *elem = mono_array_get(p_what, MonoObject *, i); - - MonoException *exc = nullptr; - String elem_str = GDMonoMarshal::mono_object_to_variant_string(elem, &exc); - - if (exc) { - GDMonoUtils::set_pending_exception(exc); - return; - } - - str += elem_str; - } - - print_line_rich(str); -} - -void godot_icall_GD_printerr(MonoArray *p_what) { - String str; - int length = mono_array_length(p_what); - - for (int i = 0; i < length; i++) { - MonoObject *elem = mono_array_get(p_what, MonoObject *, i); - - MonoException *exc = nullptr; - String elem_str = GDMonoMarshal::mono_object_to_variant_string(elem, &exc); - - if (exc) { - GDMonoUtils::set_pending_exception(exc); - return; - } - - str += elem_str; - } - - print_error(str); -} - -void godot_icall_GD_printraw(MonoArray *p_what) { - String str; - int length = mono_array_length(p_what); - - for (int i = 0; i < length; i++) { - MonoObject *elem = mono_array_get(p_what, MonoObject *, i); - - MonoException *exc = nullptr; - String elem_str = GDMonoMarshal::mono_object_to_variant_string(elem, &exc); - - if (exc) { - GDMonoUtils::set_pending_exception(exc); - return; - } - - str += elem_str; - } - - OS::get_singleton()->print("%s", str.utf8().get_data()); -} - -void godot_icall_GD_prints(MonoArray *p_what) { - String str; - int length = mono_array_length(p_what); - - for (int i = 0; i < length; i++) { - MonoObject *elem = mono_array_get(p_what, MonoObject *, i); - - MonoException *exc = nullptr; - String elem_str = GDMonoMarshal::mono_object_to_variant_string(elem, &exc); - - if (exc) { - GDMonoUtils::set_pending_exception(exc); - return; - } - - if (i) { - str += " "; - } - - str += elem_str; - } - - print_line(str); -} - -void godot_icall_GD_printt(MonoArray *p_what) { - String str; - int length = mono_array_length(p_what); - - for (int i = 0; i < length; i++) { - MonoObject *elem = mono_array_get(p_what, MonoObject *, i); - - MonoException *exc = nullptr; - String elem_str = GDMonoMarshal::mono_object_to_variant_string(elem, &exc); - - if (exc) { - GDMonoUtils::set_pending_exception(exc); - return; - } - - if (i) { - str += "\t"; - } - - str += elem_str; - } - - print_line(str); -} - -void godot_icall_GD_randomize() { - Math::randomize(); -} - -uint32_t godot_icall_GD_randi() { - return Math::rand(); -} - -float godot_icall_GD_randf() { - return Math::randf(); -} - -int32_t godot_icall_GD_randi_range(int32_t from, int32_t to) { - return Math::random(from, to); -} - -double godot_icall_GD_randf_range(double from, double to) { - return Math::random(from, to); -} - -double godot_icall_GD_randfn(double mean, double deviation) { - return Math::randfn(mean, deviation); -} - -uint32_t godot_icall_GD_rand_seed(uint64_t seed, uint64_t *newSeed) { - uint32_t ret = Math::rand_from_seed(&seed); - *newSeed = seed; - return ret; -} - -void godot_icall_GD_seed(uint64_t p_seed) { - Math::seed(p_seed); -} - -MonoString *godot_icall_GD_str(MonoArray *p_what) { - String str; - Array what = GDMonoMarshal::mono_array_to_Array(p_what); - - for (int i = 0; i < what.size(); i++) { - String os = what[i].operator String(); - - if (i == 0) { - str = os; - } else { - str += os; - } - } - - return GDMonoMarshal::mono_string_from_godot(str); -} - -MonoObject *godot_icall_GD_str2var(MonoString *p_str) { - Variant ret; - - VariantParser::StreamString ss; - ss.s = GDMonoMarshal::mono_string_to_godot(p_str); - - String errs; - int line; - Error err = VariantParser::parse(&ss, ret, errs, line); - if (err != OK) { - String err_str = "Parse error at line " + itos(line) + ": " + errs + "."; - ERR_PRINT(err_str); - ret = err_str; - } - - return GDMonoMarshal::variant_to_mono_object(ret); -} - -MonoBoolean godot_icall_GD_type_exists(StringName *p_type) { - StringName type = p_type ? *p_type : StringName(); - return ClassDB::class_exists(type); -} - -void godot_icall_GD_pusherror(MonoString *p_str) { - ERR_PRINT(GDMonoMarshal::mono_string_to_godot(p_str)); -} - -void godot_icall_GD_pushwarning(MonoString *p_str) { - WARN_PRINT(GDMonoMarshal::mono_string_to_godot(p_str)); -} - -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); - - int len; - Error err = encode_variant(var, nullptr, len, p_full_objects); - ERR_FAIL_COND_MSG(err != OK, "Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID)."); - - r_bytes->resize(len); - encode_variant(var, r_bytes->ptrw(), len, p_full_objects); -} - -MonoString *godot_icall_GD_var2str(MonoObject *p_var) { - String vars; - VariantWriter::write_to_string(GDMonoMarshal::mono_object_to_variant(p_var), vars); - return GDMonoMarshal::mono_string_from_godot(vars); -} - -uint32_t godot_icall_TypeToVariantType(MonoReflectionType *p_refl_type) { - return (uint32_t)GDMonoMarshal::managed_to_variant_type(ManagedType::from_reftype(p_refl_type)); -} - -MonoObject *godot_icall_DefaultGodotTaskScheduler() { - return GDMonoCache::cached_data.task_scheduler_handle->get_target(); -} - -void godot_register_gd_icalls() { - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_bytes2var", godot_icall_GD_bytes2var); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_convert", godot_icall_GD_convert); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_hash", godot_icall_GD_hash); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_instance_from_id", godot_icall_GD_instance_from_id); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_pusherror", godot_icall_GD_pusherror); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_pushwarning", godot_icall_GD_pushwarning); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_print", godot_icall_GD_print); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_print_rich", godot_icall_GD_print_rich); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_printerr", godot_icall_GD_printerr); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_printraw", godot_icall_GD_printraw); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_prints", godot_icall_GD_prints); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_printt", godot_icall_GD_printt); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randomize", godot_icall_GD_randomize); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randi", godot_icall_GD_randi); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randf", godot_icall_GD_randf); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randi_range", godot_icall_GD_randi_range); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randf_range", godot_icall_GD_randf_range); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randfn", godot_icall_GD_randfn); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_rand_seed", godot_icall_GD_rand_seed); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_seed", godot_icall_GD_seed); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_str", godot_icall_GD_str); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_str2var", godot_icall_GD_str2var); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_type_exists", godot_icall_GD_type_exists); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_var2bytes", godot_icall_GD_var2bytes); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_var2str", godot_icall_GD_var2str); - GDMonoUtils::add_internal_call("Godot.GD::godot_icall_TypeToVariantType", godot_icall_TypeToVariantType); - - // Dispatcher - GDMonoUtils::add_internal_call("Godot.Dispatcher::godot_icall_DefaultGodotTaskScheduler", godot_icall_DefaultGodotTaskScheduler); -} diff --git a/modules/mono/glue/runtime_interop.cpp b/modules/mono/glue/runtime_interop.cpp index f5a04bf05c7..28603861271 100644 --- a/modules/mono/glue/runtime_interop.cpp +++ b/modules/mono/glue/runtime_interop.cpp @@ -29,8 +29,10 @@ /*************************************************************************/ #include "core/config/engine.h" +#include "core/io/marshalls.h" #include "core/object/class_db.h" #include "core/object/method_bind.h" +#include "core/os/os.h" #include "core/string/string_name.h" #include "../interop_types.h" @@ -839,12 +841,194 @@ GD_PINVOKE_EXPORT bool godotsharp_node_path_is_absolute(const NodePath *p_self) return p_self->is_absolute(); } +GD_PINVOKE_EXPORT void godotsharp_randomize() { + Math::randomize(); +} + +GD_PINVOKE_EXPORT uint32_t godotsharp_randi() { + return Math::rand(); +} + +GD_PINVOKE_EXPORT float godotsharp_randf() { + return Math::randf(); +} + +GD_PINVOKE_EXPORT int32_t godotsharp_randi_range(int32_t p_from, int32_t p_to) { + return Math::random(p_from, p_to); +} + +GD_PINVOKE_EXPORT double godotsharp_randf_range(double p_from, double p_to) { + return Math::random(p_from, p_to); +} + +GD_PINVOKE_EXPORT double godotsharp_randfn(double p_mean, double p_deviation) { + return Math::randfn(p_mean, p_deviation); +} + +GD_PINVOKE_EXPORT void godotsharp_seed(uint64_t p_seed) { + Math::seed(p_seed); +} + +GD_PINVOKE_EXPORT uint32_t godotsharp_rand_from_seed(uint64_t p_seed, uint64_t *r_new_seed) { + uint32_t ret = Math::rand_from_seed(&p_seed); + *r_new_seed = p_seed; + return ret; +} + +GD_PINVOKE_EXPORT void godotsharp_weakref(Object *p_ptr, Ref *r_weak_ref) { + if (!p_ptr) { + return; + } + + Ref wref; + RefCounted *rc = Object::cast_to(p_ptr); + + if (rc) { + Ref r = rc; + if (!r.is_valid()) { + return; + } + + wref.instantiate(); + wref->set_ref(r); + } else { + wref.instantiate(); + wref->set_obj(p_ptr); + } + + memnew_placement(r_weak_ref, Ref(wref)); +} + +GD_PINVOKE_EXPORT void godotsharp_str(const godot_array *p_what, godot_string *r_ret) { + String &str = *memnew_placement(r_ret, String); + const Array &what = *reinterpret_cast(p_what); + + for (int i = 0; i < what.size(); i++) { + String os = what[i].operator String(); + + if (i == 0) { + str = os; + } else { + str += os; + } + } +} + +GD_PINVOKE_EXPORT void godotsharp_print(const godot_string *p_what) { + print_line(*reinterpret_cast(p_what)); +} + +GD_PINVOKE_EXPORT void godotsharp_print_rich(const godot_string *p_what) { + print_line_rich(*reinterpret_cast(p_what)); +} + +GD_PINVOKE_EXPORT void godotsharp_printerr(const godot_string *p_what) { + print_error(*reinterpret_cast(p_what)); +} + +GD_PINVOKE_EXPORT void godotsharp_printt(const godot_string *p_what) { + print_line(*reinterpret_cast(p_what)); +} + +GD_PINVOKE_EXPORT void godotsharp_prints(const godot_string *p_what) { + print_line(*reinterpret_cast(p_what)); +} + +GD_PINVOKE_EXPORT void godotsharp_printraw(const godot_string *p_what) { + OS::get_singleton()->print("%s", reinterpret_cast(p_what)->utf8().get_data()); +} + +GD_PINVOKE_EXPORT void godotsharp_pusherror(const godot_string *p_str) { + ERR_PRINT(*reinterpret_cast(p_str)); +} + +GD_PINVOKE_EXPORT void godotsharp_pushwarning(const godot_string *p_str) { + WARN_PRINT(*reinterpret_cast(p_str)); +} + +GD_PINVOKE_EXPORT void godotsharp_var2str(const godot_variant *p_var, godot_string *r_ret) { + const Variant &var = *reinterpret_cast(p_var); + String &vars = *memnew_placement(r_ret, String); + VariantWriter::write_to_string(var, vars); +} + +GD_PINVOKE_EXPORT void godotsharp_str2var(const godot_string *p_str, godot_variant *r_ret) { + Variant ret; + + VariantParser::StreamString ss; + ss.s = *reinterpret_cast(p_str); + + String errs; + int line; + Error err = VariantParser::parse(&ss, ret, errs, line); + if (err != OK) { + String err_str = "Parse error at line " + itos(line) + ": " + errs + "."; + ERR_PRINT(err_str); + ret = err_str; + } + memnew_placement(r_ret, Variant(ret)); +} + +GD_PINVOKE_EXPORT void godotsharp_var2bytes(const godot_variant *p_var, bool p_full_objects, godot_packed_array *r_bytes) { + const Variant &var = *reinterpret_cast(p_var); + PackedByteArray &bytes = *memnew_placement(r_bytes, PackedByteArray); + + int len; + Error err = encode_variant(var, nullptr, len, p_full_objects); + ERR_FAIL_COND_MSG(err != OK, "Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID)."); + + bytes.resize(len); + encode_variant(var, bytes.ptrw(), len, p_full_objects); +} + +GD_PINVOKE_EXPORT void godotsharp_bytes2var(const godot_packed_array *p_bytes, bool p_allow_objects, godot_variant *r_ret) { + const PackedByteArray *bytes = reinterpret_cast(p_bytes); + Variant ret; + Error err = decode_variant(ret, bytes->ptr(), bytes->size(), nullptr, p_allow_objects); + if (err != OK) { + ret = RTR("Not enough bytes for decoding bytes, or invalid format."); + } + memnew_placement(r_ret, Variant(ret)); +} + +GD_PINVOKE_EXPORT int godotsharp_hash(const godot_variant *p_var) { + return reinterpret_cast(p_var)->hash(); +} + +GD_PINVOKE_EXPORT void godotsharp_convert(const godot_variant *p_what, int32_t p_type, godot_variant *r_ret) { + const Variant *args[1] = { reinterpret_cast(p_what) }; + Callable::CallError ce; + Variant ret; + Variant::construct(Variant::Type(p_type), ret, args, 1, ce); + if (ce.error != Callable::CallError::CALL_OK) { + memnew_placement(r_ret, Variant); + ERR_FAIL_MSG("Unable to convert parameter from '" + + Variant::get_type_name(reinterpret_cast(p_what)->get_type()) + + "' to '" + Variant::get_type_name(Variant::Type(p_type)) + "'."); + } + memnew_placement(r_ret, Variant(ret)); +} + +GD_PINVOKE_EXPORT Object *godotsharp_instance_from_id(uint64_t p_instance_id) { + return ObjectDB::get_instance(ObjectID(p_instance_id)); +} + +GD_PINVOKE_EXPORT void godotsharp_object_to_string(Object *p_ptr, godot_string *r_str) { +#ifdef DEBUG_ENABLED + // Cannot happen in C#; would get an ObjectDisposedException instead. + CRASH_COND(p_ptr == nullptr); +#endif + // Can't call 'Object::to_string()' here, as that can end up calling 'ToString' again resulting in an endless circular loop. + memnew_placement(r_str, + String("[" + p_ptr->get_class() + ":" + itos(p_ptr->get_instance_id()) + "]")); +} + #ifdef __cplusplus } #endif // We need this to prevent the functions from being stripped. -void *godotsharp_pinvoke_funcs[138] = { +void *godotsharp_pinvoke_funcs[164] = { (void *)godotsharp_method_bind_get_method, (void *)godotsharp_get_class_constructor, (void *)godotsharp_invoke_class_constructor, @@ -982,5 +1166,31 @@ void *godotsharp_pinvoke_funcs[138] = { (void *)godotsharp_node_path_get_name_count, (void *)godotsharp_node_path_get_subname, (void *)godotsharp_node_path_get_subname_count, - (void *)godotsharp_node_path_is_absolute + (void *)godotsharp_node_path_is_absolute, + (void *)godotsharp_randomize, + (void *)godotsharp_randi, + (void *)godotsharp_randf, + (void *)godotsharp_randi_range, + (void *)godotsharp_randf_range, + (void *)godotsharp_randfn, + (void *)godotsharp_seed, + (void *)godotsharp_rand_from_seed, + (void *)godotsharp_weakref, + (void *)godotsharp_str, + (void *)godotsharp_print, + (void *)godotsharp_print_rich, + (void *)godotsharp_printerr, + (void *)godotsharp_printt, + (void *)godotsharp_prints, + (void *)godotsharp_printraw, + (void *)godotsharp_pusherror, + (void *)godotsharp_pushwarning, + (void *)godotsharp_var2str, + (void *)godotsharp_str2var, + (void *)godotsharp_var2bytes, + (void *)godotsharp_bytes2var, + (void *)godotsharp_hash, + (void *)godotsharp_convert, + (void *)godotsharp_instance_from_id, + (void *)godotsharp_object_to_string, }; diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index b45b71aea70..bf387be4c88 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -442,14 +442,12 @@ bool GDMono::_are_api_assemblies_out_of_sync() { return out_of_sync; } -void godot_register_gd_icalls(); void godot_register_object_icalls(); void godot_register_scene_tree_icalls(); void godot_register_placeholder_icalls(); void GDMono::_register_internal_calls() { // Registers internal calls that were not generated. - godot_register_gd_icalls(); godot_register_object_icalls(); godot_register_scene_tree_icalls(); godot_register_placeholder_icalls(); @@ -987,6 +985,8 @@ Error GDMono::_load_scripts_domain() { Error GDMono::_unload_scripts_domain() { ERR_FAIL_NULL_V(scripts_domain, ERR_BUG); + CSharpLanguage::get_singleton()->_on_scripts_domain_about_to_unload(); + print_verbose("Mono: Finalizing scripts domain..."); if (mono_domain_get() != root_domain) { @@ -1040,8 +1040,6 @@ 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."); diff --git a/modules/mono/mono_gd/gd_mono_cache.cpp b/modules/mono/mono_gd/gd_mono_cache.cpp index 08660e37016..54e49cf5f58 100644 --- a/modules/mono/mono_gd/gd_mono_cache.cpp +++ b/modules/mono/mono_gd/gd_mono_cache.cpp @@ -116,7 +116,6 @@ void CachedData::clear_godot_api_cache() { methodthunk_GodotObject_Dispose.nullify(); methodthunk_SignalAwaiter_SignalCallback.nullify(); - methodthunk_GodotTaskScheduler_Activate.nullify(); methodthunk_Delegate_Equals.nullify(); @@ -137,8 +136,6 @@ void CachedData::clear_godot_api_cache() { methodthunk_Marshaling_SetFieldValue.nullify(); methodthunk_MarshalUtils_TypeHasFlagsAttribute.nullify(); - - task_scheduler_handle = Ref(); } #define GODOT_API_CLASS(m_class) (GDMono::get_singleton()->get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, #m_class)) @@ -197,7 +194,6 @@ void update_godot_api_cache() { CACHE_METHOD_THUNK_AND_CHECK(GodotObject, Dispose, CACHED_CLASS(GodotObject)->get_method("Dispose", 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)); @@ -233,10 +229,16 @@ void update_godot_api_cache() { CACHE_METHOD_THUNK_AND_CHECK(DebuggingUtils, GetStackFrameInfo, GODOT_API_CLASS(DebuggingUtils)->get_method("GetStackFrameInfo", 4)); #endif - // TODO Move to CSharpLanguage::init() and do handle disposal - MonoObject *task_scheduler = mono_object_new(mono_domain_get(), GODOT_API_CLASS(GodotTaskScheduler)->get_mono_ptr()); - GDMonoUtils::runtime_object_init(task_scheduler, GODOT_API_CLASS(GodotTaskScheduler)); - cached_data.task_scheduler_handle = MonoGCHandleRef::create_strong(task_scheduler); + MonoException *exc = nullptr; + GDMono::get_singleton() + ->get_core_api_assembly() + ->get_class("Godot", "Dispatcher") + ->get_method("InitializeDefaultGodotTaskScheduler") + ->invoke(nullptr, &exc); + + if (exc) { + GDMonoUtils::debug_unhandled_exception(exc); + } cached_data.godot_api_cache_updated = true; } diff --git a/modules/mono/mono_gd/gd_mono_cache.h b/modules/mono/mono_gd/gd_mono_cache.h index 0db32b58857..58765832601 100644 --- a/modules/mono/mono_gd/gd_mono_cache.h +++ b/modules/mono/mono_gd/gd_mono_cache.h @@ -90,7 +90,6 @@ struct CachedData { GDMonoMethodThunk methodthunk_GodotObject_Dispose; GDMonoMethodThunk methodthunk_SignalAwaiter_SignalCallback; - GDMonoMethodThunk methodthunk_GodotTaskScheduler_Activate; GDMonoMethodThunkR methodthunk_Delegate_Equals; @@ -114,8 +113,6 @@ struct CachedData { GDMonoMethodThunkR methodthunk_MarshalUtils_TypeHasFlagsAttribute; - Ref task_scheduler_handle; - bool corlib_cache_updated; bool godot_api_cache_updated; diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp index ef6a008a25d..8828ec588bb 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.cpp +++ b/modules/mono/mono_gd/gd_mono_marshal.cpp @@ -40,9 +40,7 @@ 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) { - if (p_type.type_encoding == MONO_TYPE_VOID) { - return Variant::NIL; - } + CRASH_COND(p_type.type_class == nullptr); MonoReflectionType *refltype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type()); MonoBoolean nil_is_variant = false; @@ -137,59 +135,6 @@ 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) { - if (p_obj == nullptr) { - return String("null"); - } - - 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()'. - MonoException *exc = nullptr; - MonoString *mono_str = GDMonoUtils::object_to_string(p_obj, &exc); - - if (exc) { - if (r_exc) { - *r_exc = exc; - } - return String(); - } - - return GDMonoMarshal::mono_string_to_godot(mono_str); - } else { - return var.operator String(); - } -} - -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); - - 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) { - return ret; - } - int length = mono_array_length(p_array); - ret.resize(length); - - for (int i = 0; i < length; i++) { - MonoObject *elem = mono_array_get(p_array, MonoObject *, i); - ret[i] = mono_object_to_variant(elem); - } - - return ret; -} - MonoArray *PackedStringArray_to_mono_array(const PackedStringArray &p_array) { const String *r = p_array.ptr(); int length = p_array.size(); diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h index 16683de51aa..fbe5795df5b 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.h +++ b/modules/mono/mono_gd/gd_mono_marshal.h @@ -88,15 +88,6 @@ _FORCE_INLINE_ MonoObject *variant_to_mono_object(const Variant *p_var) { Variant mono_object_to_variant(MonoObject *p_obj); 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); - -// Array - -MonoArray *Array_to_mono_array(const Array &p_array); -Array mono_array_to_Array(MonoArray *p_array); - // PackedStringArray MonoArray *PackedStringArray_to_mono_array(const PackedStringArray &p_array); diff --git a/modules/mono/mono_gd/gd_mono_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp index 04f6005338c..97d3c822300 100644 --- a/modules/mono/mono_gd/gd_mono_method.cpp +++ b/modules/mono/mono_gd/gd_mono_method.cpp @@ -253,9 +253,13 @@ const MethodInfo &GDMonoMethod::get_method_info() { method_info.name = name; bool nil_is_variant = false; - method_info.return_val = PropertyInfo(GDMonoMarshal::managed_to_variant_type(return_type, &nil_is_variant), ""); - if (method_info.return_val.type == Variant::NIL && nil_is_variant) { - method_info.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; + if (return_type.type_encoding == MONO_TYPE_VOID) { + method_info.return_val = PropertyInfo(Variant::NIL, ""); + } else { + method_info.return_val = PropertyInfo(GDMonoMarshal::managed_to_variant_type(return_type, &nil_is_variant), ""); + if (method_info.return_val.type == Variant::NIL && nil_is_variant) { + method_info.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; + } } Vector names; From 513ee857a938c466e0f7146f66db771b9c6e2024 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Rold=C3=A1n=20Etcheverry?= Date: Sun, 12 Sep 2021 20:21:15 +0200 Subject: [PATCH 05/31] C#: Restructure code prior move to .NET Core The main focus here was to remove the majority of code that relied on Mono's embedding APIs, specially the reflection APIs. The embedding APIs we still use are the bare minimum we need for things to work. A lot of code was moved to C#. We no longer deal with any managed objects (`MonoObject*`, and such) in native code, and all marshaling is done in C#. The reason for restructuring the code and move away from embedding APIs is that once we move to .NET Core, we will be limited by the much more minimal .NET hosting. PERFORMANCE REGRESSIONS ----------------------- Some parts of the code were written with little to no concern about performance. This includes code that calls into script methods and accesses script fields, properties and events. The reason for this is that all of that will be moved to source generators, so any work prior to that would be a waste of time. DISABLED FEATURES ----------------- Some code was removed as it no longer makes sense (or won't make sense in the future). Other parts were commented out with `#if 0`s and TODO warnings because it doesn't make much sense to work on them yet as those parts will change heavily when we switch to .NET Core but also when we start introducing source generators. As such, the following features were disabled temporarily: - Assembly-reloading (will be done with ALCs in .NET Core). - Properties/fields exports and script method listing (will be handled by source generators in the future). - Exception logging in the editor and stack info for errors. - Exporting games. - Building of C# projects. We no longer copy the Godot API assemblies to the project directory, so MSBuild won't be able to find them. The idea is to turn them into NuGet packages in the future, which could also be obtained from local NuGet sources during development. --- modules/mono/csharp_script.cpp | 1339 +++++++---------- modules/mono/csharp_script.h | 85 +- .../GodotTools/Build/BuildManager.cs | 11 - .../GodotTools/Export/ExportPlugin.cs | 63 +- .../GodotTools/GodotTools/GodotSharpEditor.cs | 23 +- .../GodotTools/GodotTools/GodotTools.csproj | 3 +- .../GodotTools/Internals/EditorProgress.cs | 24 +- .../GodotTools/Internals/Globals.cs | 48 +- .../GodotTools/Internals/GodotSharpDirs.cs | 142 +- .../GodotTools/Internals/Internal.cs | 73 +- .../editor/GodotTools/GodotTools/Utils/OS.cs | 38 +- modules/mono/editor/bindings_generator.cpp | 119 +- modules/mono/editor/bindings_generator.h | 2 + modules/mono/editor/editor_internal_calls.cpp | 237 +-- modules/mono/editor/godotsharp_export.cpp | 4 +- modules/mono/editor/godotsharp_export.h | 4 - .../glue/GodotSharp/GodotSharp/Core/Array.cs | 4 +- .../Attributes/AssemblyHasScriptsAttribute.cs | 17 +- .../Core/Attributes/ScriptPathAttribute.cs | 4 +- .../Core/Bridge/CSharpInstanceBridge.cs | 123 ++ .../GodotSharp/Core/Bridge/GCHandleBridge.cs | 11 + .../Core/Bridge/ScriptManagerBridge.cs | 501 ++++++ .../GodotSharp/Core/DelegateUtils.cs | 14 +- .../GodotSharp/GodotSharp/Core/Dictionary.cs | 4 +- .../Core/Extensions/SceneTreeExtensions.cs | 62 +- .../glue/GodotSharp/GodotSharp/Core/GD.cs | 4 +- .../GodotSharp/Core/GodotTraceListener.cs | 5 +- .../Core/NativeInterop/InteropStructs.cs | 48 +- .../Core/NativeInterop/InteropUtils.cs | 102 +- .../Core/NativeInterop/Marshaling.cs | 30 +- .../Core/NativeInterop/NativeFuncs.cs | 2 +- .../NativeInterop/NativeFuncs.extended.cs | 2 +- .../Core/NativeInterop/VariantUtils.cs | 2 +- .../GodotSharp/GodotSharp/Core/NodePath.cs | 12 +- .../GodotSharp/GodotSharp/Core/Object.base.cs | 270 +++- .../GodotSharp/Core/ScriptManager.cs | 10 - .../GodotSharp/Core/SignalAwaiter.cs | 34 +- .../GodotSharp/Core/StringExtensions.cs | 6 +- .../GodotSharp/GodotSharp/Core/StringName.cs | 4 +- .../GodotSharp/GodotSharp/GodotSharp.csproj | 4 +- modules/mono/glue/base_object_glue.cpp | 26 +- modules/mono/glue/placeholder_glue.cpp | 161 +- modules/mono/glue/runtime_interop.cpp | 12 +- modules/mono/glue/scene_tree_glue.cpp | 81 - modules/mono/godotsharp_defs.h | 1 + modules/mono/godotsharp_dirs.cpp | 38 +- modules/mono/godotsharp_dirs.h | 6 +- modules/mono/managed_callable.cpp | 19 +- modules/mono/managed_callable.h | 7 +- modules/mono/mono_gc_handle.cpp | 36 +- modules/mono/mono_gc_handle.h | 53 +- modules/mono/mono_gd/gd_mono.cpp | 446 +----- modules/mono/mono_gd/gd_mono.h | 70 +- modules/mono/mono_gd/gd_mono_assembly.cpp | 102 +- modules/mono/mono_gd/gd_mono_assembly.h | 41 - modules/mono/mono_gd/gd_mono_cache.cpp | 232 +-- modules/mono/mono_gd/gd_mono_cache.h | 120 +- modules/mono/mono_gd/gd_mono_class.cpp | 569 ------- modules/mono/mono_gd/gd_mono_class.h | 160 -- modules/mono/mono_gd/gd_mono_field.cpp | 149 -- modules/mono/mono_gd/gd_mono_field.h | 78 - modules/mono/mono_gd/gd_mono_header.h | 52 - modules/mono/mono_gd/gd_mono_internals.cpp | 80 +- modules/mono/mono_gd/gd_mono_internals.h | 4 +- modules/mono/mono_gd/gd_mono_marshal.cpp | 151 -- modules/mono/mono_gd/gd_mono_marshal.h | 97 -- modules/mono/mono_gd/gd_mono_method.cpp | 295 ---- modules/mono/mono_gd/gd_mono_method.h | 96 -- modules/mono/mono_gd/gd_mono_method_thunk.h | 264 +--- modules/mono/mono_gd/gd_mono_property.cpp | 197 --- modules/mono/mono_gd/gd_mono_property.h | 78 - modules/mono/mono_gd/gd_mono_utils.cpp | 262 +--- modules/mono/mono_gd/gd_mono_utils.h | 38 - modules/mono/mono_gd/i_mono_class_member.h | 70 - modules/mono/mono_gd/managed_type.cpp | 58 - modules/mono/mono_gd/managed_type.h | 55 - .../mono/mono_gd/support/android_support.cpp | 2 +- modules/mono/signal_awaiter_utils.cpp | 81 +- modules/mono/signal_awaiter_utils.h | 8 +- 79 files changed, 2562 insertions(+), 5223 deletions(-) create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs delete mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/ScriptManager.cs delete mode 100644 modules/mono/glue/scene_tree_glue.cpp delete mode 100644 modules/mono/mono_gd/gd_mono_class.cpp delete mode 100644 modules/mono/mono_gd/gd_mono_class.h delete mode 100644 modules/mono/mono_gd/gd_mono_field.cpp delete mode 100644 modules/mono/mono_gd/gd_mono_field.h delete mode 100644 modules/mono/mono_gd/gd_mono_header.h delete mode 100644 modules/mono/mono_gd/gd_mono_marshal.cpp delete mode 100644 modules/mono/mono_gd/gd_mono_marshal.h delete mode 100644 modules/mono/mono_gd/gd_mono_method.cpp delete mode 100644 modules/mono/mono_gd/gd_mono_method.h delete mode 100644 modules/mono/mono_gd/gd_mono_property.cpp delete mode 100644 modules/mono/mono_gd/gd_mono_property.h delete mode 100644 modules/mono/mono_gd/i_mono_class_member.h delete mode 100644 modules/mono/mono_gd/managed_type.cpp delete mode 100644 modules/mono/mono_gd/managed_type.h diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 4316f38bd2e..b088271b18d 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -59,8 +59,6 @@ #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" #include "mono_gd/gd_mono_utils.h" #include "signal_awaiter_utils.h" #include "utils/macros.h" @@ -590,6 +588,8 @@ String CSharpLanguage::debug_get_stack_level_source(int p_level) const { return String(); } +#warning TODO +#if 0 Vector CSharpLanguage::debug_get_current_stack_info() { #ifdef DEBUG_ENABLED // Printing an error here will result in endless recursion, so we must be careful @@ -682,6 +682,11 @@ Vector CSharpLanguage::stack_trace_get_info(MonoObjec return si; } #endif +#else +Vector CSharpLanguage::debug_get_current_stack_info() { + return Vector(); +} +#endif void CSharpLanguage::post_unsafe_reference(Object *p_obj) { #ifdef DEBUG_ENABLED @@ -706,37 +711,13 @@ void CSharpLanguage::pre_unsafe_unreference(Object *p_obj) { void CSharpLanguage::frame() { if (gdmono && gdmono->is_runtime_initialized() && gdmono->get_core_api_assembly() != nullptr) { MonoException *exc = nullptr; - gdmono->get_core_api_assembly() - ->get_class("Godot", "ScriptManager") - ->get_method("FrameCallback") - ->invoke(nullptr, &exc); - + GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_FrameCallback.invoke(&exc); if (exc) { GDMonoUtils::debug_unhandled_exception(exc); } } } -struct CSharpScriptDepSort { - // must support sorting so inheritance works properly (parent must be reloaded first) - bool operator()(const Ref &A, const Ref &B) const { - if (A == B) { - return false; // shouldn't happen but.. - } - GDMonoClass *I = B->base; - while (I) { - if (I == A->script_class) { - // A is a base of B - return true; - } - - I = I->get_parent_class(); - } - - return false; // not a base - } -}; - void CSharpLanguage::reload_all_scripts() { #ifdef GD_MONO_HOT_RELOAD if (is_assembly_reloading_needed()) { @@ -803,6 +784,8 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { return; } +#warning TODO ALCs after switching to .NET 6 +#if 0 // There is no soft reloading with Mono. It's always hard reloading. List> scripts; @@ -829,7 +812,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { MonoObject *managed_serialized_data = GDMonoMarshal::variant_to_mono_object(serialized_data); MonoException *exc = nullptr; - bool success = (bool)CACHED_METHOD_THUNK(DelegateUtils, TrySerializeDelegateWithGCHandle) + bool success = (bool)GDMonoCache::cached_data.methodthunk_DelegateUtils_TrySerializeDelegateWithGCHandle .invoke(managed_callable->delegate_handle, managed_serialized_data, &exc); @@ -909,7 +892,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { CSharpInstance *csi = static_cast(obj->get_script_instance()); // Call OnBeforeSerialize - if (csi->script->script_class->implements_interface(CACHED_CLASS(ISerializationListener))) { + if (csi->script->script_class->implements_interface(GDMonoCache::cached_data.class_ISerializationListener)) { obj->get_script_instance()->call(string_names.on_before_serialize); } @@ -977,7 +960,6 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { #ifdef TOOLS_ENABLED script->exports_invalidated = true; #endif - script->signals_invalidated = true; if (!script->get_path().is_empty()) { script->reload(p_soft_reload); @@ -1011,7 +993,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { continue; } - bool obj_type = CACHED_CLASS(GodotObject)->is_assignable_from(script_class); + bool obj_type = GDMonoCache::cached_data.class_GodotObject->is_assignable_from(script_class); if (!obj_type) { // The class no longer inherits Godot.Object, can't reload script->pending_reload_instances.clear(); @@ -1103,20 +1085,20 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { const StringName &name = G.first; const Array &serialized_data = G.second; - HashMap::Iterator match = script->event_signals.find(name); + HashMap::Iterator match = script->event_signals.find(name); if (!match) { // The event or its signal attribute were removed continue; } - const CSharpScript::EventSignal &event_signal = match->value; + GDMonoField *event_signal_field = match->value; MonoObject *managed_serialized_data = GDMonoMarshal::variant_to_mono_object(serialized_data); MonoDelegate *delegate = nullptr; MonoException *exc = nullptr; - bool success = (bool)CACHED_METHOD_THUNK(DelegateUtils, TryDeserializeDelegate).invoke(managed_serialized_data, &delegate, &exc); + bool success = (bool)GDMonoCache::cached_data.methodthunk_DelegateUtils_TryDeserializeDelegate.invoke(managed_serialized_data, &delegate, &exc); if (exc) { GDMonoUtils::debug_print_unhandled_exception(exc); @@ -1125,14 +1107,14 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { if (success) { ERR_CONTINUE(delegate == nullptr); - event_signal.field->set_value(csi->get_mono_object(), (MonoObject *)delegate); + event_signal_field->set_value(csi->get_mono_object(), (MonoObject *)delegate); } else if (OS::get_singleton()->is_stdout_verbose()) { OS::get_singleton()->print("Failed to deserialize event signal delegate\n"); } } // Call OnAfterDeserialization - if (csi->script->script_class->implements_interface(CACHED_CLASS(ISerializationListener))) { + if (csi->script->script_class->implements_interface(GDMonoCache::cached_data.class_ISerializationListener)) { obj->get_script_instance()->call(string_names.on_after_deserialize); } } @@ -1153,7 +1135,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { void *delegate = nullptr; MonoException *exc = nullptr; - bool success = (bool)CACHED_METHOD_THUNK(DelegateUtils, TryDeserializeDelegateWithGCHandle) + bool success = (bool)GDMonoCache::cached_data.methodthunk_DelegateUtils_TryDeserializeDelegateWithGCHandle .invoke(managed_serialized_data, &delegate, &exc); if (exc) { @@ -1179,63 +1161,10 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { NodeDock::get_singleton()->update_lists(); } #endif +#endif } #endif -void CSharpLanguage::lookup_script_for_class(GDMonoClass *p_class) { - if (!p_class->has_attribute(CACHED_CLASS(ScriptPathAttribute))) { - return; - } - - MonoObject *attr = p_class->get_attribute(CACHED_CLASS(ScriptPathAttribute)); - String path = CACHED_FIELD(ScriptPathAttribute, path)->get_string_value(attr); - - dotnet_script_lookup_map[path] = DotNetScriptLookupInfo( - p_class->get_namespace(), p_class->get_name(), p_class); -} - -void CSharpLanguage::lookup_scripts_in_assembly(GDMonoAssembly *p_assembly) { - if (p_assembly->has_attribute(CACHED_CLASS(AssemblyHasScriptsAttribute))) { - MonoObject *attr = p_assembly->get_attribute(CACHED_CLASS(AssemblyHasScriptsAttribute)); - bool requires_lookup = CACHED_FIELD(AssemblyHasScriptsAttribute, requiresLookup)->get_bool_value(attr); - - if (requires_lookup) { - // This is supported for scenarios where specifying all types would be cumbersome, - // such as when disabling C# source generators (for whatever reason) or when using a - // language other than C# that has nothing similar to source generators to automate it. - MonoImage *image = p_assembly->get_image(); - - int rows = mono_image_get_table_rows(image, MONO_TABLE_TYPEDEF); - - for (int i = 1; i < rows; i++) { - // We don't search inner classes, only top-level. - MonoClass *mono_class = mono_class_get(image, (i + 1) | MONO_TOKEN_TYPE_DEF); - - if (!mono_class_is_assignable_from(CACHED_CLASS_RAW(GodotObject), mono_class)) { - continue; - } - - GDMonoClass *current = p_assembly->get_class(mono_class); - if (current) { - lookup_script_for_class(current); - } - } - } else { - // This is the most likely scenario as we use C# source generators - MonoArray *script_types = (MonoArray *)CACHED_FIELD(AssemblyHasScriptsAttribute, scriptTypes)->get_value(attr); - - int length = mono_array_length(script_types); - - for (int i = 0; i < length; i++) { - MonoReflectionType *reftype = mono_array_get(script_types, MonoReflectionType *, i); - ManagedType type = ManagedType::from_reftype(reftype); - ERR_CONTINUE(!type.type_class); - lookup_script_for_class(type.type_class); - } - } - } -} - void CSharpLanguage::get_recognized_extensions(List *p_extensions) const { p_extensions->push_back("cs"); } @@ -1308,8 +1237,6 @@ void CSharpLanguage::_on_scripts_domain_about_to_unload() { } } #endif - - dotnet_script_lookup_map.clear(); } #ifdef TOOLS_ENABLED @@ -1318,18 +1245,20 @@ void CSharpLanguage::_editor_init_callback() { // Initialize GodotSharpEditor - GDMonoClass *editor_klass = GDMono::get_singleton()->get_tools_assembly()->get_class("GodotTools", "GodotSharpEditor"); + MonoClass *editor_klass = mono_class_from_name( + GDMono::get_singleton()->get_tools_assembly()->get_image(), + "GodotTools", "GodotSharpEditor"); CRASH_COND(editor_klass == nullptr); - MonoObject *mono_object = mono_object_new(mono_domain_get(), editor_klass->get_mono_ptr()); - CRASH_COND(mono_object == nullptr); + MonoMethod *create_instance = mono_class_get_method_from_name(editor_klass, "InternalCreateInstance", 0); + CRASH_COND(create_instance == nullptr); MonoException *exc = nullptr; - GDMonoUtils::runtime_object_init(mono_object, editor_klass, &exc); + MonoObject *ret = mono_runtime_invoke(create_instance, nullptr, nullptr, (MonoObject **)&exc); UNHANDLED_EXCEPTION(exc); - EditorPlugin *godotsharp_editor = Object::cast_to( - GDMonoMarshal::mono_object_to_variant(mono_object).operator Object *()); + EditorPlugin *godotsharp_editor = *(EditorPlugin **)mono_object_unbox(ret); + CRASH_COND(godotsharp_editor == nullptr); // Enable it as a plugin @@ -1353,24 +1282,17 @@ void CSharpLanguage::release_script_gchandle(MonoGCHandleData &p_gchandle) { } } -void CSharpLanguage::release_script_gchandle(MonoObject *p_expected_obj, MonoGCHandleData &p_gchandle) { - uint32_t pinned_gchandle = GDMonoUtils::new_strong_gchandle_pinned(p_expected_obj); // We might lock after this, so pin it +void CSharpLanguage::release_script_gchandle(void *p_expected_mono_obj_unused, MonoGCHandleData &p_gchandle) { +#warning KNOWN BUG. DO NOT USE THIS IN PRODUCTION + // KNOWN BUG: + // I removed the patch from commit e558e1ec09aa27852426bbd24dfa21e9b60cfbfc. + // This may cause data races. Re-implementing it without the Mono embedding API would be + // too painful and would make the code even more of a mess than it already was. + // We will switch from scripts to the new extension system before a release with .NET 6 support. + // The problem the old patch was working around won't be present at all with the new extension system. - if (!p_gchandle.is_released()) { // Do not lock unnecessarily - MutexLock lock(get_singleton()->script_gchandle_release_mutex); - - MonoObject *target = p_gchandle.get_target(); - - // We release the gchandle if it points to the MonoObject* we expect (otherwise it was - // already released and could have been replaced) or if we can't get its target MonoObject* - // (which doesn't necessarily mean it was released, and we want it released in order to - // avoid locking other threads unnecessarily). - if (target == p_expected_obj || target == nullptr) { - p_gchandle.release(); - } - } - - GDMonoUtils::free_gchandle(pinned_gchandle); + (void)p_expected_mono_obj_unused; + return release_script_gchandle(p_gchandle); } CSharpLanguage::CSharpLanguage() { @@ -1402,18 +1324,25 @@ bool CSharpLanguage::setup_csharp_script_binding(CSharpScriptBinding &r_script_b ERR_FAIL_NULL_V(classinfo, false); type_name = classinfo->name; - GDMonoClass *type_class = GDMonoUtils::type_get_proxy_class(type_name); + bool parent_is_object_class = ClassDB::is_parent_class(p_object->get_class_name(), type_name); + ERR_FAIL_COND_V_MSG(!parent_is_object_class, false, + "Type inherits from native type '" + type_name + "', so it can't be instantiated in object of type: '" + p_object->get_class() + "'."); - ERR_FAIL_NULL_V(type_class, false); + MonoException *exc = nullptr; + GCHandleIntPtr strong_gchandle = + GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_CreateManagedForGodotObjectBinding + .invoke(&type_name, p_object, &exc); - MonoObject *mono_object = GDMonoUtils::create_managed_for_godot_object(type_class, type_name, p_object); + if (exc) { + GDMonoUtils::set_pending_exception(exc); + return false; + } - ERR_FAIL_NULL_V(mono_object, false); + ERR_FAIL_NULL_V(strong_gchandle.value, false); r_script_binding.inited = true; r_script_binding.type_name = type_name; - r_script_binding.wrapper_class = type_class; // cache - r_script_binding.gchandle = MonoGCHandleData::new_strong_handle(mono_object); + r_script_binding.gchandle = MonoGCHandleData(strong_gchandle, gdmono::GCHandleType::STRONG_HANDLE); r_script_binding.owner = p_object; // Tie managed to unmanaged @@ -1478,11 +1407,13 @@ void CSharpLanguage::_instance_binding_free_callback(void *, void *, void *p_bin if (script_binding.inited) { // Set the native instance field to IntPtr.Zero, if not yet garbage collected. // This is done to avoid trying to dispose the native instance from Dispose(bool). - MonoObject *mono_object = script_binding.gchandle.get_target(); - if (mono_object) { - CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, nullptr); - } + MonoException *exc = nullptr; + GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_SetGodotObjectPtr + .invoke(script_binding.gchandle.get_intptr(), nullptr, &exc); + UNHANDLED_EXCEPTION(exc); + script_binding.gchandle.release(); + script_binding.inited = false; } csharp_lang->script_bindings.erase(data); @@ -1517,15 +1448,23 @@ GDNativeBool CSharpLanguage::_instance_binding_reference_callback(void *p_token, // This means the owner is being referenced again by the unmanaged side, // so the owner must hold the managed side alive again to avoid it from being GCed. - MonoObject *target = gchandle.get_target(); - if (!target) { + // Release the current weak handle and replace it with a strong handle. + + GCHandleIntPtr old_gchandle = gchandle.get_intptr(); + gchandle.handle = GCHandleIntPtr(); // No longer owns the handle (released by swap function) + + GCHandleIntPtr new_gchandle; + bool create_weak = false; + MonoException *exc = nullptr; + bool target_alive = GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_SwapGCHandleForType + .invoke(old_gchandle, &new_gchandle, create_weak, &exc); + UNHANDLED_EXCEPTION(exc); + + if (!target_alive) { return false; // Called after the managed side was collected, so nothing to do here } - // Release the current weak handle and replace it with a strong handle. - MonoGCHandleData strong_gchandle = MonoGCHandleData::new_strong_handle(target); - gchandle.release(); - gchandle = strong_gchandle; + gchandle = MonoGCHandleData(new_gchandle, gdmono::GCHandleType::STRONG_HANDLE); } return false; @@ -1537,15 +1476,23 @@ GDNativeBool CSharpLanguage::_instance_binding_reference_callback(void *p_token, // If owner owner is no longer referenced by the unmanaged side, // the managed instance takes responsibility of deleting the owner when GCed. - MonoObject *target = gchandle.get_target(); - if (!target) { + // Release the current strong handle and replace it with a weak handle. + + GCHandleIntPtr old_gchandle = gchandle.get_intptr(); + gchandle.handle = GCHandleIntPtr(); // No longer owns the handle (released by swap function) + + GCHandleIntPtr new_gchandle; + bool create_weak = true; + MonoException *exc = nullptr; + bool target_alive = GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_SwapGCHandleForType + .invoke(old_gchandle, &new_gchandle, create_weak, &exc); + UNHANDLED_EXCEPTION(exc); + + if (!target_alive) { return refcount == 0; // Called after the managed side was collected, so nothing to do here } - // Release the current strong handle and replace it with a weak handle. - MonoGCHandleData weak_gchandle = MonoGCHandleData::new_weak_handle(target); - gchandle.release(); - gchandle = weak_gchandle; + gchandle = MonoGCHandleData(new_gchandle, gdmono::GCHandleType::WEAK_HANDLE); return false; } @@ -1590,6 +1537,111 @@ void CSharpLanguage::set_instance_binding(Object *p_object, void *p_binding) { bool CSharpLanguage::has_instance_binding(Object *p_object) { return p_object->has_instance_binding(get_singleton()); } +void CSharpLanguage::tie_native_managed_to_unmanaged(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged, const StringName *p_native_name, bool p_ref_counted) { + // This method should not fail + + CRASH_COND(!p_unmanaged); + + // All mono objects created from the managed world (e.g.: 'new Player()') + // need to have a CSharpScript in order for their methods to be callable from the unmanaged side + + RefCounted *rc = Object::cast_to(p_unmanaged); + + CRASH_COND(p_ref_counted != (bool)rc); + + MonoGCHandleData gchandle = MonoGCHandleData(p_gchandle_intptr, + p_ref_counted ? gdmono::GCHandleType::WEAK_HANDLE : gdmono::GCHandleType::STRONG_HANDLE); + + // If it's just a wrapper Godot class and not a custom inheriting class, then attach a + // script binding instead. One of the advantages of this is that if a script is attached + // later and it's not a C# script, then the managed object won't have to be disposed. + // Another reason for doing this is that this instance could outlive CSharpLanguage, which would + // be problematic when using a script. See: https://github.com/godotengine/godot/issues/25621 + + CSharpScriptBinding script_binding; + + script_binding.inited = true; + script_binding.type_name = *p_native_name; + script_binding.gchandle = gchandle; + script_binding.owner = p_unmanaged; + + if (p_ref_counted) { + // Unsafe refcount increment. The managed instance also counts as a reference. + // This way if the unmanaged world has no references to our owner + // but the managed instance is alive, the refcount will be 1 instead of 0. + // See: godot_icall_RefCounted_Dtor(MonoObject *p_obj, Object *p_ptr) + + // May not me referenced yet, so we must use init_ref() instead of reference() + if (rc->init_ref()) { + CSharpLanguage::get_singleton()->post_unsafe_reference(rc); + } + } + + // The object was just created, no script instance binding should have been attached + CRASH_COND(CSharpLanguage::has_instance_binding(p_unmanaged)); + + void *data; + { + MutexLock lock(CSharpLanguage::get_singleton()->get_language_bind_mutex()); + data = (void *)CSharpLanguage::get_singleton()->insert_script_binding(p_unmanaged, script_binding); + } + + // Should be thread safe because the object was just created and nothing else should be referencing it + CSharpLanguage::set_instance_binding(p_unmanaged, data); +} + +void CSharpLanguage::tie_user_managed_to_unmanaged(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged, CSharpScript *p_script, bool p_ref_counted) { + // This method should not fail + + CRASH_COND(!p_unmanaged); + + // All mono objects created from the managed world (e.g.: 'new Player()') + // need to have a CSharpScript in order for their methods to be callable from the unmanaged side + + RefCounted *rc = Object::cast_to(p_unmanaged); + + CRASH_COND(p_ref_counted != (bool)rc); + + MonoGCHandleData gchandle = MonoGCHandleData(p_gchandle_intptr, + p_ref_counted ? gdmono::GCHandleType::WEAK_HANDLE : gdmono::GCHandleType::STRONG_HANDLE); + + Ref script = p_script; + + CSharpScript::initialize_for_managed_type(script); + + CRASH_COND(script.is_null()); + + CSharpInstance *csharp_instance = CSharpInstance::create_for_managed_type(p_unmanaged, script.ptr(), gchandle); + + p_unmanaged->set_script_and_instance(script, csharp_instance); +} + +void CSharpLanguage::tie_managed_to_unmanaged_with_pre_setup(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged) { + // This method should not fail + + CRASH_COND(!p_unmanaged); + + CSharpInstance *instance = CAST_CSHARP_INSTANCE(p_unmanaged->get_script_instance()); + + if (!instance) { + return; + } + + CRASH_COND(!instance->gchandle.is_released()); + + // Tie managed to unmanaged + instance->gchandle = MonoGCHandleData(p_gchandle_intptr, gdmono::GCHandleType::STRONG_HANDLE); + + if (instance->base_ref_counted) { + instance->_reference_owner_unsafe(); // Here, after assigning the gchandle (for the refcount_incremented callback) + } + + { + MutexLock lock(CSharpLanguage::get_singleton()->get_script_instances_mutex()); + // instances is a set, so it's safe to insert multiple times (e.g.: from _internal_new_managed) + instance->script->instances.insert(instance->owner); + } +} CSharpInstance *CSharpInstance::create_for_managed_type(Object *p_owner, CSharpScript *p_script, const MonoGCHandleData &p_gchandle) { CSharpInstance *instance = memnew(CSharpInstance(Ref(p_script))); @@ -1609,11 +1661,6 @@ CSharpInstance *CSharpInstance::create_for_managed_type(Object *p_owner, CSharpS return instance; } -MonoObject *CSharpInstance::get_mono_object() const { - ERR_FAIL_COND_V(gchandle.is_released(), nullptr); - return gchandle.get_target(); -} - Object *CSharpInstance::get_owner() { return owner; } @@ -1623,50 +1670,14 @@ bool CSharpInstance::set(const StringName &p_name, const Variant &p_value) { GD_MONO_SCOPE_THREAD_ATTACH; - MonoObject *mono_object = get_mono_object(); - ERR_FAIL_NULL_V(mono_object, false); + MonoException *exc = nullptr; + bool ret = GDMonoCache::cached_data.methodthunk_CSharpInstanceBridge_Set.invoke( + gchandle.get_intptr(), &p_name, &p_value, &exc); - GDMonoClass *top = script->script_class; - - while (top && top != script->native) { - GDMonoField *field = top->get_field(p_name); - - if (field) { - field->set_value_from_variant(mono_object, p_value); - return true; - } - - GDMonoProperty *property = top->get_property(p_name); - - if (property) { - property->set_value_from_variant(mono_object, p_value); - return true; - } - - top = top->get_parent_class(); - } - - // Call _set - - top = script->script_class; - - while (top && top != script->native) { - GDMonoMethod *method = top->get_method(CACHED_STRING_NAME(_set), 2); - - if (method) { - Variant name = p_name; - const Variant *args[2] = { &name, &p_value }; - - MonoObject *ret = method->invoke(mono_object, args); - - if (ret && GDMonoMarshal::unbox(ret)) { - return true; - } - - break; - } - - top = top->get_parent_class(); + if (exc) { + GDMonoUtils::set_pending_exception(exc); + } else if (ret) { + return true; } return false; @@ -1677,64 +1688,24 @@ bool CSharpInstance::get(const StringName &p_name, Variant &r_ret) const { GD_MONO_SCOPE_THREAD_ATTACH; - MonoObject *mono_object = get_mono_object(); - ERR_FAIL_NULL_V(mono_object, false); + Variant ret_value; - GDMonoClass *top = script->script_class; + MonoException *exc = nullptr; + bool ret = GDMonoCache::cached_data.methodthunk_CSharpInstanceBridge_Get.invoke( + gchandle.get_intptr(), &p_name, &ret_value, &exc); - while (top && top != script->native) { - GDMonoField *field = top->get_field(p_name); - - if (field) { - MonoObject *value = field->get_value(mono_object); - r_ret = GDMonoMarshal::mono_object_to_variant(value); - return true; - } - - GDMonoProperty *property = top->get_property(p_name); - - if (property) { - MonoException *exc = nullptr; - MonoObject *value = property->get_value(mono_object, &exc); - if (exc) { - r_ret = Variant(); - GDMonoUtils::set_pending_exception(exc); - } else { - r_ret = GDMonoMarshal::mono_object_to_variant(value); - } - return true; - } - - top = top->get_parent_class(); - } - - // Call _get - - top = script->script_class; - - while (top && top != script->native) { - GDMonoMethod *method = top->get_method(CACHED_STRING_NAME(_get), 1); - - if (method) { - Variant name = p_name; - const Variant *args[1] = { &name }; - - MonoObject *ret = method->invoke(mono_object, args); - - if (ret) { - r_ret = GDMonoMarshal::mono_object_to_variant(ret); - return true; - } - - break; - } - - top = top->get_parent_class(); + if (exc) { + GDMonoUtils::set_pending_exception(exc); + } else if (ret) { + r_ret = ret_value; + return true; } return false; } +#warning TODO +#if 0 void CSharpInstance::get_properties_state_for_reloading(List> &r_state) { List property_list; get_property_list(&property_list); @@ -1773,10 +1744,10 @@ void CSharpInstance::get_event_signals_state_for_reloading(List &E : script->event_signals) { - const CSharpScript::EventSignal &event_signal = E.value; + for (const KeyValue &E : script->event_signals) { + GDMonoField *event_signal_field = E.value; - MonoDelegate *delegate_field_value = (MonoDelegate *)event_signal.field->get_value(owner_managed); + MonoDelegate *delegate_field_value = (MonoDelegate *)event_signal_field->get_value(owner_managed); if (!delegate_field_value) { continue; // Empty } @@ -1785,7 +1756,7 @@ void CSharpInstance::get_event_signals_state_for_reloading(List(event_signal.field->get_name(), serialized_data)); + r_state.push_back(Pair(event_signal_field->get_name(), serialized_data)); } else if (OS::get_singleton()->is_stdout_verbose()) { OS::get_singleton()->print("Failed to serialize event signal delegate\n"); } } } +#endif void CSharpInstance::get_property_list(List *p_properties) const { List props; @@ -1811,28 +1783,24 @@ void CSharpInstance::get_property_list(List *p_properties) const { GD_MONO_SCOPE_THREAD_ATTACH; - MonoObject *mono_object = get_mono_object(); - ERR_FAIL_NULL(mono_object); + StringName method = SNAME("_get_property_list"); - GDMonoClass *top = script->script_class; + Variant ret; + Callable::CallError call_error; + MonoException *exc = nullptr; + GDMonoCache::cached_data.methodthunk_CSharpInstanceBridge_Call.invoke( + gchandle.get_intptr(), &method, nullptr, 0, &call_error, &ret, &exc); - while (top && top != script->native) { - GDMonoMethod *method = top->get_method(CACHED_STRING_NAME(_get_property_list), 0); + if (exc) { + GDMonoUtils::set_pending_exception(exc); + } - if (method) { - MonoObject *ret = method->invoke(mono_object); + ERR_FAIL_COND_MSG(call_error.error != Callable::CallError::CALL_OK, + "Error calling '_get_property_list': " + Variant::get_call_error_text(method, nullptr, 0, call_error)); - if (ret) { - Array array = Array(GDMonoMarshal::mono_object_to_variant(ret)); - for (int i = 0, size = array.size(); i < size; i++) { - props.push_back(PropertyInfo::from_dict(array.get(i))); - } - } - - break; - } - - top = top->get_parent_class(); + Array array = ret; + for (int i = 0, size = array.size(); i < size; i++) { + p_properties->push_back(PropertyInfo::from_dict(array.get(i))); } for (const PropertyInfo &prop : props) { @@ -1860,34 +1828,26 @@ bool CSharpInstance::property_can_revert(const StringName &p_name) const { GD_MONO_SCOPE_THREAD_ATTACH; - MonoObject *mono_object = get_mono_object(); - ERR_FAIL_NULL_V(mono_object, false); + Callable::CallError call_error; - GDMonoClass *top = script->script_class; + Variant name_arg = p_name; + const Variant *args[1] = { &name_arg }; - while (top && top != script->native) { - GDMonoMethod *method = top->get_method(CACHED_STRING_NAME(_property_can_revert), 1); + Variant ret; + MonoException *exc = nullptr; + GDMonoCache::cached_data.methodthunk_CSharpInstanceBridge_Call.invoke( + gchandle.get_intptr(), &CACHED_STRING_NAME(_property_can_revert), args, 1, &call_error, &ret, &exc); - if (method) { - Variant name = p_name; - const Variant *args[1] = { &name }; - - MonoObject *ret = method->invoke(mono_object, args); - - if (ret) { - bool can_revert = GDMonoMarshal::mono_object_to_variant(ret); - if (can_revert) { - return true; - } - } - - break; - } - - top = top->get_parent_class(); + if (exc) { + GDMonoUtils::set_pending_exception(exc); + return false; } - return false; + if (call_error.error != Callable::CallError::CALL_OK) { + return false; + } + + return (bool)ret; } bool CSharpInstance::property_get_revert(const StringName &p_name, Variant &r_ret) const { @@ -1895,35 +1855,32 @@ bool CSharpInstance::property_get_revert(const StringName &p_name, Variant &r_re GD_MONO_SCOPE_THREAD_ATTACH; - MonoObject *mono_object = get_mono_object(); - ERR_FAIL_NULL_V(mono_object, false); + Callable::CallError call_error; - GDMonoClass *top = script->script_class; + Variant name_arg = p_name; + const Variant *args[1] = { &name_arg }; - while (top && top != script->native) { - GDMonoMethod *method = top->get_method(CACHED_STRING_NAME(_property_get_revert), 1); + Variant ret; + MonoException *exc = nullptr; + GDMonoCache::cached_data.methodthunk_CSharpInstanceBridge_Call.invoke( + gchandle.get_intptr(), &CACHED_STRING_NAME(_property_get_revert), args, 1, &call_error, &ret, &exc); - if (method) { - Variant name = p_name; - const Variant *args[1] = { &name }; - - MonoObject *ret = method->invoke(mono_object, args); - - if (ret) { - r_ret = GDMonoMarshal::mono_object_to_variant(ret); - return true; - } - - break; - } - - top = top->get_parent_class(); + if (exc) { + GDMonoUtils::set_pending_exception(exc); + return false; } - return false; + if (call_error.error != Callable::CallError::CALL_OK) { + return false; + } + + r_ret = ret; + return true; } void CSharpInstance::get_method_list(List *p_list) const { +#warning TODO +#if 0 if (!script->is_valid() || !script->script_class) { return; } @@ -1944,6 +1901,7 @@ void CSharpInstance::get_method_list(List *p_list) const { top = top->get_parent_class(); } +#endif } bool CSharpInstance::has_method(const StringName &p_method) const { @@ -1953,17 +1911,19 @@ bool CSharpInstance::has_method(const StringName &p_method) const { GD_MONO_SCOPE_THREAD_ATTACH; - GDMonoClass *top = script->script_class; - - while (top && top != script->native) { - if (top->has_fetched_method_unknown_params(p_method)) { - return true; - } - - top = top->get_parent_class(); + if (!GDMonoCache::cached_data.godot_api_cache_updated) { + return false; } - return false; + String method = p_method; + bool deep = true; + + MonoException *exc = nullptr; + bool found = GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_HasMethodUnknownParams + .invoke(script.ptr(), &method, deep, &exc); + UNHANDLED_EXCEPTION(exc); + + return found; } Variant CSharpInstance::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { @@ -1971,36 +1931,16 @@ Variant CSharpInstance::callp(const StringName &p_method, const Variant **p_args GD_MONO_SCOPE_THREAD_ATTACH; - MonoObject *mono_object = get_mono_object(); + Variant ret; + MonoException *exc = nullptr; + GDMonoCache::cached_data.methodthunk_CSharpInstanceBridge_Call.invoke( + gchandle.get_intptr(), &p_method, p_args, p_argcount, &r_error, &ret, &exc); - if (!mono_object) { - r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL; - ERR_FAIL_V(Variant()); + if (exc) { + GDMonoUtils::set_pending_exception(exc); } - GDMonoClass *top = script->script_class; - - while (top && top != script->native) { - GDMonoMethod *method = top->get_method(p_method, p_argcount); - - if (method) { - MonoObject *return_value = method->invoke(mono_object, p_args); - - r_error.error = Callable::CallError::CALL_OK; - - if (return_value) { - return GDMonoMarshal::mono_object_to_variant(return_value); - } else { - return Variant(); - } - } - - top = top->get_parent_class(); - } - - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - - return Variant(); + return ret; } bool CSharpInstance::_reference_owner_unsafe() { @@ -2046,48 +1986,32 @@ bool CSharpInstance::_unreference_owner_unsafe() { return static_cast(owner)->unreference(); } -MonoObject *CSharpInstance::_internal_new_managed() { - // Search the constructor first, to fail with an error if it's not found before allocating anything else. - GDMonoMethod *ctor = script->script_class->get_method(CACHED_STRING_NAME(dotctor), 0); - ERR_FAIL_NULL_V_MSG(ctor, nullptr, - "Cannot create script instance because the class does not define a parameterless constructor: '" + script->get_path() + "'."); - +bool CSharpInstance::_internal_new_managed() { CSharpLanguage::get_singleton()->release_script_gchandle(gchandle); - ERR_FAIL_NULL_V(owner, nullptr); - ERR_FAIL_COND_V(script.is_null(), nullptr); + ERR_FAIL_NULL_V(owner, false); + ERR_FAIL_COND_V(script.is_null(), false); - MonoObject *mono_object = mono_object_new(mono_domain_get(), script->script_class->get_mono_ptr()); + MonoException *exc = nullptr; + GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance + .invoke(script.ptr(), owner, nullptr, 0, &exc); + + if (exc) { + GDMonoUtils::set_pending_exception(exc); - if (!mono_object) { // Important to clear this before destroying the script instance here script = Ref(); - - bool die = _unreference_owner_unsafe(); - // Not ok for the owner to die here. If there is a situation where this can happen, it will be considered a bug. - CRASH_COND(die); - owner = nullptr; - ERR_FAIL_V_MSG(nullptr, "Failed to allocate memory for the object."); + return false; } - // Tie managed to unmanaged - gchandle = MonoGCHandleData::new_strong_handle(mono_object); + CRASH_COND(gchandle.is_released()); - if (base_ref_counted) { - _reference_owner_unsafe(); // Here, after assigning the gchandle (for the refcount_incremented callback) - } - - CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, owner); - - // Construct - ctor->invoke_raw(mono_object, nullptr); - - return mono_object; + return true; } -void CSharpInstance::mono_object_disposed(MonoObject *p_obj) { +void CSharpInstance::mono_object_disposed() { // Must make sure event signals are not left dangling disconnect_event_signals(); @@ -2095,10 +2019,10 @@ void CSharpInstance::mono_object_disposed(MonoObject *p_obj) { CRASH_COND(base_ref_counted); CRASH_COND(gchandle.is_released()); #endif - CSharpLanguage::get_singleton()->release_script_gchandle(p_obj, gchandle); + CSharpLanguage::get_singleton()->release_script_gchandle(nullptr, gchandle); } -void CSharpInstance::mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_finalizer, bool &r_delete_owner, bool &r_remove_script_instance) { +void CSharpInstance::mono_object_disposed_baseref(bool p_is_finalizer, bool &r_delete_owner, bool &r_remove_script_instance) { #ifdef DEBUG_ENABLED CRASH_COND(!base_ref_counted); CRASH_COND(gchandle.is_released()); @@ -2114,7 +2038,7 @@ void CSharpInstance::mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_f r_delete_owner = true; } else { r_delete_owner = false; - CSharpLanguage::get_singleton()->release_script_gchandle(p_obj, gchandle); + CSharpLanguage::get_singleton()->release_script_gchandle(nullptr, gchandle); if (!p_is_finalizer) { // If the native instance is still alive and Dispose() was called @@ -2126,27 +2050,20 @@ void CSharpInstance::mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_f // unreference and delete it, so we want to keep it. // GC.ReRegisterForFinalize(this) is not safe because the objects referenced by 'this' // could have already been collected. Instead we will create a new managed instance here. - MonoObject *new_managed = _internal_new_managed(); - if (!new_managed) { + if (!_internal_new_managed()) { r_remove_script_instance = true; } } } } -void CSharpInstance::connect_event_signals() { - for (const KeyValue &E : script->event_signals) { - const CSharpScript::EventSignal &event_signal = E.value; +void CSharpInstance::connect_event_signal(const StringName &p_event_signal) { + // TODO: Use pooling for ManagedCallable instances. + EventSignalCallable *event_signal_callable = memnew(EventSignalCallable(owner, p_event_signal)); - StringName signal_name = event_signal.field->get_name(); - - // TODO: Use pooling for ManagedCallable instances. - EventSignalCallable *event_signal_callable = memnew(EventSignalCallable(owner, &event_signal)); - - Callable callable(event_signal_callable); - connected_event_signals.push_back(callable); - owner->connect(signal_name, callable); - } + Callable callable(event_signal_callable); + connected_event_signals.push_back(callable); + owner->connect(p_event_signal, callable); } void CSharpInstance::disconnect_event_signals() { @@ -2174,9 +2091,22 @@ void CSharpInstance::refcount_incremented() { // so the owner must hold the managed side alive again to avoid it from being GCed. // Release the current weak handle and replace it with a strong handle. - MonoGCHandleData strong_gchandle = MonoGCHandleData::new_strong_handle(gchandle.get_target()); - gchandle.release(); - gchandle = strong_gchandle; + + GCHandleIntPtr old_gchandle = gchandle.get_intptr(); + gchandle.handle = GCHandleIntPtr(); // No longer owns the handle (released by swap function) + + GCHandleIntPtr new_gchandle; + bool create_weak = false; + MonoException *exc = nullptr; + bool target_alive = GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_SwapGCHandleForType + .invoke(old_gchandle, &new_gchandle, create_weak, &exc); + UNHANDLED_EXCEPTION(exc); + + if (!target_alive) { + return; // Called after the managed side was collected, so nothing to do here + } + + gchandle = MonoGCHandleData(new_gchandle, gdmono::GCHandleType::STRONG_HANDLE); } } @@ -2197,9 +2127,22 @@ bool CSharpInstance::refcount_decremented() { // the managed instance takes responsibility of deleting the owner when GCed. // Release the current strong handle and replace it with a weak handle. - MonoGCHandleData weak_gchandle = MonoGCHandleData::new_weak_handle(gchandle.get_target()); - gchandle.release(); - gchandle = weak_gchandle; + + GCHandleIntPtr old_gchandle = gchandle.get_intptr(); + gchandle.handle = GCHandleIntPtr(); // No longer owns the handle (released by swap function) + + GCHandleIntPtr new_gchandle; + bool create_weak = true; + MonoException *exc = nullptr; + bool target_alive = GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_SwapGCHandleForType + .invoke(old_gchandle, &new_gchandle, create_weak, &exc); + UNHANDLED_EXCEPTION(exc); + + if (!target_alive) { + return refcount == 0; // Called after the managed side was collected, so nothing to do here + } + + gchandle = MonoGCHandleData(new_gchandle, gdmono::GCHandleType::WEAK_HANDLE); return false; } @@ -2235,11 +2178,9 @@ void CSharpInstance::notification(int p_notification) { _call_notification(p_notification); - MonoObject *mono_object = get_mono_object(); - ERR_FAIL_NULL(mono_object); - MonoException *exc = nullptr; - GDMonoUtils::dispose(mono_object, &exc); + GDMonoCache::cached_data.methodthunk_CSharpInstanceBridge_CallDispose + .invoke(gchandle.get_intptr(), /* okIfNull */ false, &exc); if (exc) { GDMonoUtils::set_pending_exception(exc); @@ -2254,43 +2195,31 @@ void CSharpInstance::notification(int p_notification) { void CSharpInstance::_call_notification(int p_notification) { GD_MONO_ASSERT_THREAD_ATTACHED; - MonoObject *mono_object = get_mono_object(); - ERR_FAIL_NULL(mono_object); + Variant arg = p_notification; + const Variant *args[1] = { &arg }; + StringName method_name = SNAME("_notification"); - // Custom version of _call_multilevel, optimized for _notification + Callable::CallError call_error; - int32_t arg = p_notification; - void *args[1] = { &arg }; - StringName method_name = CACHED_STRING_NAME(_notification); + Variant ret; + MonoException *exc = nullptr; + GDMonoCache::cached_data.methodthunk_CSharpInstanceBridge_Call.invoke( + gchandle.get_intptr(), &method_name, args, 1, &call_error, &ret, &exc); - GDMonoClass *top = script->script_class; - - while (top && top != script->native) { - GDMonoMethod *method = top->get_method(method_name, 1); - - if (method) { - method->invoke_raw(mono_object, args); - return; - } - - top = top->get_parent_class(); + if (exc) { + GDMonoUtils::set_pending_exception(exc); } } String CSharpInstance::to_string(bool *r_valid) { GD_MONO_SCOPE_THREAD_ATTACH; - MonoObject *mono_object = get_mono_object(); - - if (mono_object == nullptr) { - if (r_valid) { - *r_valid = false; - } - return String(); - } + String res; + bool valid; MonoException *exc = nullptr; - MonoString *result = GDMonoUtils::object_to_string(mono_object, &exc); + GDMonoCache::cached_data.methodthunk_CSharpInstanceBridge_CallToString + .invoke(gchandle.get_intptr(), &res, &valid, &exc); if (exc) { GDMonoUtils::set_pending_exception(exc); @@ -2300,14 +2229,11 @@ String CSharpInstance::to_string(bool *r_valid) { return String(); } - if (result == nullptr) { - if (r_valid) { - *r_valid = false; - } - return String(); + if (r_valid) { + *r_valid = valid; } - return GDMonoMarshal::mono_string_to_godot(result); + return res; } Ref